Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "custom"

performing custom joins and hydrations with Propel

People using Propel who want to perform custom joins and especially hydrations probably all faced the limited functionality that Propel offers for this: the probably well known doSelectJoinAll and doSelectJoinXXX methods (and similar for the count functionality)

Whenever you want to join only two related tables (or more, but not all), a related-related table (or even deeper) or a table that is related by multiple foreign-keys (like created_by and updated_by both refering to a user) you will find it gets hard to do this with the default functionality offered by Propel.

I have written a Helper/Plugin that solves this limitation, that can be downloaded from the plugin-pages: http://www.symfony-project.org/plugins/sfPropelHelperPlugin

The plugin contains:

The plugin does not change the way how to work with Propel, it will only expand the possibilities while remaining completely backwards compatible.

An example of how to use this helper:

Imagine you have a class of albums, and that every album can contain photos as well as other albums. You request all photo's sorted reversed on album name and join the related table to get the table-name in one go.

propel:
  photo:
    id:
    album_id:
    name:     varchar(50)
 
  album:
    id:
    album_id:
    name:     varchar(50)
 

php code:

$this->getContext()->getConfiguration()->loadHelpers('sfPropelPropertyPath');
 
$criteria = new Criteria();
 
$sortColumn = AlbumPeer::alias(str_replace('.', '_', 'Photo.Album') , AlbumPeer::NAME); // this should be done nicer some day
$criteria->addDescendingOrderByColumn($sortColumn);
 
$objectPaths = array('Photo', 'Photo.Album'); // this can be reduced (is similar) to array('Photo.Album');
 
$criteria = addJoinsAndSelectColumns($criteria, $objectPaths);
$photos = hydrate($criteria, $objectPaths, $connection = null); // I haven't decided if I want hydrate to perform the joining as well
 
foreach ($photos as $photo)
{
  echo $photo->getAlbum()->getName()." -> ".$photo->getName()."<br>\n";
}
 

The resulting sql is:

SELECT Photo.ID, Photo.ALBUM_ID, Photo.NAME, Photo_Album.ID, Photo_Album.ALBUM_ID, Photo_Album.NAME FROM `photo` `Photo` LEFT JOIN album Photo_Album ON (Photo.ALBUM_ID=Photo_Album.ID) ORDER BY Photo_Album.NAME DESC
 

I designed so called ObjectPaths and PropertyPaths together with a colleague of mine (Frans van der Lek), to make it easier to define classes and respectively properties of the (related) classes. The Object Paths contain the name of the base-class, followed by the relation-names defined in the peer-classes. These relation-names are defined by the builder, but can be extended/modified by extending the basePeer class (getRelations)

You can extend your Classes with custom Get-methods that can be accessed with the property-paths. I will show how this all comes to use, in my next article about the sfDataSourcePlugin, that I will release soon. (A plugin that provides a generic interface to select, sort, filter and iterate-over rows, no matter if they are provided by Propel, Doctrine, an Array, an Imap connection, etc. The Datasource interface is very light (in contradiction to dbFinder (no offense)) which makes it easy to write your own implementation)

Please provide feedback, questions and if desired I can add more examples/info.

by Leon van der Ree on 2009-06-07, tagged criteria  custom  database  hydrate  join  model  propel  sfpropelhelperplugin 
(1 comment)

Custom Query Objects

Features:

Example 1: ->doTree(0,200) : a model function that i wrote, returns a nested set result set.

Example 2: a simple custom query

Example 3: a custom prepared join query using sf_guard model tables

Example 4: a more complex prepared custom join query using sf_guard model tables

<?php
 
define('SF_ROOT_DIR',    realpath(dirname(__FILE__).'/..'));
define('SF_APP',         'backend');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG',       false);
 
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
 
class CustomObject {
 
    private $objectName;
 
    public function __construct($name){
        $this->objectName = $name;
    }
 
    public function getObjectName(){
        return $this->objectName();
    }
 
    public function __call($funcname, $args = array()) {
        if (!function_exists($funcname)){
            $method = substr($funcname, 3);
            $methodType = substr($funcname, 0, 3);
            switch($methodType){
                case "set":
                    $this->{$method} = $args[0];
                    break;
                case "get":
                    return $this->{$method};
                    break;
            }
 
        } else {
            trigger_error("Call to Function with call_user_func_array failed", E_USER_ERROR);
        }       
    }
 
    public function __set($name, $value){
        $this->{$name} = $value;        
    }
 
    public function __get($name){
        return $this->{$name};
    }
 
}
 
class CustomQueryResultSet {
 
    private $columns;
    private $queryColumns;
    private $resultSet;
 
    public function __construct($callerObject = false, $className = false, $peerClassName = false) {
        if (is_object($callerObject) && !empty($className) && !empty($peerClassName)){          
            $fieldConstants = call_user_func(array($peerClassName, 'getFieldNames'), BasePeer::TYPE_FIELDNAME );
            $phpConstants = call_user_func(array($peerClassName, 'getFieldNames'), BasePeer::TYPE_PHPNAME );            
            foreach($callerObject as $key => $object){
                if(1===$key){
                    $this->queryColumns = array_keys($object);
                    $this->columns = array_merge( $phpConstants, array_diff($this->queryColumns, $fieldConstants ));
                }
                $this->populateCustomObject($object, $key, $className);
            }
        } elseif (is_object($callerObject) && empty($className) && empty($peerClassName)){          
            foreach($callerObject as $key => $object){
                if(1===$key){
                    $this->queryColumns = array_keys($object);
                    foreach($this->queryColumns as $name){
                        $this->columns[] = sfInflector::camelize($name);
                    }
                }
                $this->populateCustomObject($object, $key, "CustomQuery");
            }       
 
        } else {
            trigger_error("You lost the object", E_USER_ERROR);
        }
    }
 
    private function populateCustomObject($callerObject, $key, $className){
        $customObject = new CustomObject($className);
        foreach($this->queryColumns as $ckey => $columnName){
            $methodName = "set" . ucfirst($this->columns[$ckey]);
            $customObject->{$methodName}($callerObject[$columnName]); 
        }       
        $this->resultSet[$key-1] = $customObject;       
    }
 
    public function getResultSet(){
        return $this->resultSet;
    }
 
    public function getColumns(){
        return $this->columns;
    }
}
 
class CustomQueryObject {
 
    private $className;
    private $peerClassName;
    private $module;
    private $properties;
    private $caller;
    private $resultSet;
    private $customized = false;
 
    public function __construct($callerClassName = false) {
        if (class_exists($callerClassName)){
            $this->caller = new $callerClassName();
            $this->setClass($callerClassName);
            $this->setPeerClass($callerClassName);
        } else if(false===$callerClassName){
            $this->customized = true;
        }
    }
 
    public function __call($funcname, $args = array()) {
        if (false===$this->isCustomized() && is_object($this->caller) && function_exists('call_user_func_array')){
            $this->caller = call_user_func_array(array(&$this->caller, $funcname), $args);
            $resultSet = new CustomQueryResultSet($this->caller, $this->getClass(), $this->getPeerClass());
            return $resultSet->getResultSet();
        } else {
            trigger_error("Call to Function with call_user_func_array failed", E_USER_ERROR);
        }        
    }
 
    protected function isCustomized(){
        return $this->customized;
    }
 
    public function query($query){
        $connection = Propel::getConnection();
        $statement = $connection->createStatement();
        $this->caller = $statement->executeQuery( $query ); 
        $resultSet = new CustomQueryResultSet($this->caller);   
        return $resultSet->getResultSet();  
    }
 
    public function prepare($query, $parameters = array()){
        $connection = Propel::getConnection();
        $statement = $connection->prepareStatement($query);
        if(!empty($parameters)){
            $increment = 1;
            foreach($parameters as $parameter){
                foreach($parameter as $type => $value){
                    switch($type){
                        case "int":
                        case "integer":     
                            $statement->setInt($increment, $value);
                            break;
                        case "str":
                        case "string":  
                            $statement->setString($increment, $value);
                            break;
                        case "decimal":
                        case "float":
                            $statement->setFloat($increment, $value);
                            break;
                        case "bool":
                        case "boolean":
                            $statement->setBoolean($increment, $value);
                            break;
                        case "blob":
                            $statement->setBlob($increment, $value);
                            break;
                        case "cblob":
                            $statement->setClob($increment, $value);
                            break;
                        case "date":
                            $statement->setDate($increment, $value);
                            break;
                        case "time":
                            $statement->setTime($increment, $value);
                            break;
                        case "timestamp":
                            $statement->setTimestamp($increment, $value);
                            break;
                        case "array":
                            $statement->setArray($increment, $value);
                            break;
                        case "NULL":
                        case "null":
                            $statement->setNull($increment, $value);
                            break;
                        case "double":
                            $statement->setDouble($increment, $value);
                            break;
                    }
                    $increment++;
                }
            }
 
        }
        $this->caller = $statement->executeQuery(); 
        $resultSet = new CustomQueryResultSet($this->caller);   
        return $resultSet->getResultSet();  
    }
 
    public function getColumns(){
        return $this->resultSetColumns;
    }
 
 
    public function getPeerClass(){
        return $this->peerClassName;
    }
 
    public function setPeerClass($name){
        $this->peerClassName = $name . "Peer";
    }
 
    public function getClass(){
        return $this->className;
    }
 
    public function setClass($name){
        $this->className = $name ;
    }
}
 
?><html>
<body>
<p>
<?php
 
$case = 4;
switch($case){
    case 1:
        // executing a custom query from an existing model object 
        $custom = "sfNavigation";
        $customObject = new CustomQueryObject("sfNavigation");
        $objects = $customObject->doTree(0,50);
        foreach($objects as $key => $object){
            echo    $object->getComponentId() . " " .
                    $object->getCaption() . " " .
                    $object->getAction()  . " Level: " .
                    $object->getTreeLevel().  "<br>";
        }   
        break;
    case 2:
        //executing a custom query and create a custom object       
        $customObject = new CustomQueryObject();
        $objects = $customObject->query("SELECT * FROM md_components");
        foreach($objects as $key => $object){
            echo    $object->getComponentId() . " " .
                    $object->getCaption() . " " .
                    $object->getAction()  . "<br>";
        }
        break;
    case 3:
        // custom object with prepared statement 
        $customObject = new CustomQueryObject();
        $objects = $customObject->prepare("SELECT
u.username ,
g.id,
g.name
FROM
sf_guard_user AS u
Left Join sf_guard_user_group AS ug ON ug.user_id = u.id
Right Join sf_guard_group AS g ON g.id = ug.group_id
WHERE u.id = ?
;
        ", 
        array(
            array(
                "integer" => sfContext::getInstance()->getRequest()->getParameter("id")
                )
            )
        );
        foreach($objects as $key => $object){
            echo    $object->getUsername() . " " .
                    $object->getId() . " " .
                    $object->getName()  . "<br>";
        }
        break;
    case 4:
        // custom object with prepared statement 
        $customObject = new CustomQueryObject();
        $objects = $customObject->prepare("SELECT
u.username AS user_name,
g.id AS user_id,
g.name AS group_name,
p.name AS permission_name
FROM
sf_guard_user AS u
Left Join sf_guard_user_group AS ug ON ug.user_id = u.id
Right Join sf_guard_group AS g ON ug.group_id = g.id
Left Join sf_guard_group_permission AS gp ON g.id = gp.group_id
Left Join sf_guard_permission AS p ON p.id = gp.permission_id       
WHERE u.id = ?
;
        ", 
        array(
            array(
                "integer" => sfContext::getInstance()->getRequest()->getParameter("id")
                )
            )
        );
        foreach($objects as $key => $object){
            echo    $object->getUserName() . " " .
                    $object->getUserId() . " " .
                    $object->getGroupName()  . " ".
                    $object->getPermissionName()  . "<br>";
        }
        break;
 
}
 
?>
</p>
</body>
</html>
 
by Thomas Schäfer on 2008-03-23, tagged custom  model  query 

Enhancing a base model object by filling it with custom attributes from a custom query

The code below shows how to enhance a base model from a custom query.

<?php
 
/**
 * Subclass for representing a row from the 'md_components' table.
 *
 *
 *
 * @package lib.model
 */
class Navigation extends BaseNavigation
{
 
    private $customAttributes = array(
            "isLevel", 
            "selfFirstLevel", 
            "hasChilds", 
            "prevSibling", 
            "nextSibling", 
            "firstChild", 
            "lastChild", 
            "prevRoot", 
            "nextRoot", 
            "selfRoot"
            );
 
    protected function getCustomAttributes(){
        return $this->customAttributes();
    }
 
    public function makeNextRoot()
    {
        $max = $this->getMaxRight();
        $this->setLeftValue($max + 1);
        $this->setRightValue($max + 2);
    }
 
    public function getNavigationId(){
        return $this->getComponentId();
    }
 
    public function getMaxRight(){
 
        $connection = Propel::getConnection();
        $query = 'SELECT MAX(%s) AS max FROM %s';
 
        $query = sprintf($query, NavigationPeer::TREE_RIGHT, NavigationPeer::TABLE_NAME);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $resultset->next();
        return $resultset->getInt('max');
    }
 
    public function getMaxLeft(){
        $connection = Propel::getConnection();
        $query = 'SELECT MAX(%s) AS max FROM %s';
        $query = sprintf($query, NavigationPeer::TREE_LEFT, NavigationPeer::TABLE_NAME);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $resultset->next();
        return $resultset->getInt('max');
    }
 
    public function getRootNodes(){
 
        $connection = Propel::getConnection();
        $query = 'SELECT * FROM %s WHERE %s %s %s;';
 
        $query = sprintf($query, NavigationPeer::TABLE_NAME, NavigationPeer::TREE_PARENT, Criteria::EQUAL, 0);
        $statement = $connection->prepareStatement($query);
        $resultset = $statement->executeQuery();
        $credential = array();
        foreach($resultset as $result){
            $credential[$result["name"]]["component_id"] = $result["component_id"];
            $credential[$result["name"]]["name"] = $result["name"];
            $credential[$result["name"]]["left"] = $result["tree_left"];
            $credential[$result["name"]]["right"] = $result["tree_right"];
        }
        return $credential;
    }
 
 
    public function getTree($offset = 0, $limit = 10, $options = null){
 
 
        $query = '
SELECT
    *,
    (
        SELECT
            CONCAT(COUNT(*))
        FROM md_components MA
        WHERE
        (
            (
                MA.TREE_LEFT < MM.TREE_LEFT AND MA.TREE_RIGHT > MM.TREE_RIGHT
            )
            AND MA.TREE_SCOPE = MM.TREE_SCOPE
        )
    )
    AS isLevel,
    (
        SELECT MZ4.COMPONENT_ID
                FROM md_components MZ4 , md_components MZ5
                WHERE MZ5.TREE_LEFT BETWEEN MZ4.TREE_LEFT AND MZ4.TREE_RIGHT
                AND MZ5.COMPONENT_ID = MM.COMPONENT_ID
                ORDER BY MZ4.TREE_LEFT  LIMIT 1,1
    )
    AS selfFirstLevel,
    (
        SELECT
            IF( MB.TREE_RIGHT - MB.TREE_LEFT > 1, 1, 0)
        FROM md_components MB
        WHERE MB.COMPONENT_ID = MM.COMPONENT_ID
        AND MB.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS hasChilds,
    (
        SELECT MC.COMPONENT_ID FROM md_components MC
        WHERE (MM.TREE_LEFT = MC.TREE_RIGHT + 1)
        AND MC.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS prevSibling,
    (
        SELECT
            MD.COMPONENT_ID
        FROM md_components MD
        WHERE (MM.TREE_RIGHT = MD.TREE_LEFT - 1)
        AND MD.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS nextSibling,
    (
        SELECT
            IF
            (
                (
                    SELECT
                        IF
                        (
                            MQ.TREE_RIGHT - MQ.TREE_LEFT > 1,
                            1,
                            0
                        )
                    FROM md_components MQ
                    WHERE MQ.COMPONENT_ID = MM.COMPONENT_ID
                    AND MQ.TREE_SCOPE = MM.TREE_SCOPE
                ) = 1 ,
                ME.COMPONENT_ID,
                NULL
            )
        FROM md_components ME
        WHERE ME.TREE_LEFT = MM.TREE_LEFT + 1
        AND ME.TREE_SCOPE = MM.TREE_SCOPE
    )
    AS firstChild,
    (
        SELECT
            IF
            (
                (
                    SELECT
                        IF(
                            MS.TREE_RIGHT - MS.TREE_LEFT > 1,
                            1,
                            0
                        )
                    FROM md_components MS
                    WHERE MS.COMPONENT_ID = MM.COMPONENT_ID
                    AND MS.TREE_SCOPE = MM.TREE_SCOPE
                ) = 1 ,
                MF.COMPONENT_ID,
                NULL
            )
        FROM md_components MF
        WHERE MF.TREE_RIGHT = MM.TREE_RIGHT - 1
        AND MF.TREE_SCOPE = MM.TREE_SCOPE
 
    )
    AS lastChild,
    (
              SELECT
            MX.COMPONENT_ID
            FROM md_components MX
        WHERE MX.TREE_LEFT - 1 =
        (
                  SELECT
            MZ.TREE_RIGHT
            FROM md_components MZ
            WHERE (MZ.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ.TREE_LEFT < MM.TREE_LEFT) AND (MZ.TREE_PARENT = 0)
            AND MZ.TREE_SCOPE = MM.TREE_SCOPE
        )
 
    )
    AS nextRoot,
    (
        SELECT
            MX.COMPONENT_ID
            FROM md_components MX
        WHERE MX.TREE_RIGHT + 1 =
        (
                  SELECT
            MZ2.TREE_LEFT
            FROM md_components MZ2
            WHERE (MZ2.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ2.TREE_LEFT < MM.TREE_LEFT) AND (MZ2.TREE_PARENT = 0)
            AND MZ2.TREE_SCOPE = MM.TREE_SCOPE
        )
 
    )
    AS prevRoot,
    (
            SELECT
            MZ3.COMPONENT_ID
            FROM md_components MZ3
            WHERE (MZ3.TREE_RIGHT > MM.TREE_RIGHT) AND (MZ3.TREE_LEFT < MM.TREE_LEFT) AND (MZ3.TREE_PARENT = 0)
            AND MZ3.TREE_SCOPE = MM.TREE_SCOPE
 
    )
    AS selfRoot
 
FROM md_components MM
';
 
        $query .= ' ORDER BY MM.TREE_LEFT ASC ';
        $query .= 'LIMIT '. $offset . ', ' . $limit;
 
        $connection = Propel::getConnection();
        $statement = $connection->createStatement(  );
        $result = $statement->executeQuery( $query , ResultSet::FETCHMODE_NUM);
 
        $objects = NavigationPeer::populateObjects( $result );
 
        $phpNames = NavigationPeer::getFieldNames(BasePeer::TYPE_PHPNAME);
 
        // merge custom and native attributes
        $additionalColumns = array_merge( $phpNames, $this->getCustomAttributes() );
 
        foreach($result as $key => $object){
            foreach($object as $column => $value){
                if($column > count($phpNames)-1){
                    $method = $additionalColumns[$column];
                    $objects[$key-1]->$method = $value;
                }
            }
        }
        return $objects;
    }
 
    /*custom getter functions*/
    public function lastChild(){
        return $this->lastChild;
    }
 
    public function selfFirstLevel(){
        return $this->selfFirstLevel;
    }
 
    public function firstChild(){
        return $this->firstChild;
    }
 
    public function prevRoot(){
        return $this->prevRoot;
    }
 
    public function nextRoot(){
        return $this->nextRoot;
    }
 
    public function selfRoot(){
        return $this->selfRoot;
    }
 
    public function nextSibling(){
        return $this->nextSibling;
    }
 
    public function prevSibling(){
        return $this->prevSibling;
    }
 
    public function isLevel(){
        return $this->isLevel;
    }
 
    public function hasChilds(){
        return $this->hasChilds;
    }
}
 
$columns_map = array
(
    'left'   => NavigationPeer::TREE_LEFT,
    'right'  => NavigationPeer::TREE_RIGHT,
    'parent' => NavigationPeer::TREE_PARENT,
    'scope'  => NavigationPeer::TREE_SCOPE
);
 
sfPropelBehavior::add('Navigation', array('actasnestedset' => array('columns' => $columns_map)));
 
by Thomas Schäfer on 2008-03-19, tagged custom  model  query 

Custom ORDER BY with Criteria

Hello everyone,

i accidently found a neat feature of MYSQL on the web, which isn't even documented in the official MySQL 5.x manual (original blog entry to be found at http://www.cfdan.com/posts/Handy_MySQL_-_ORDER_BY_FIELD.cfm">original Blog Posting)

Basicly i needed a way to create some custom order for my records, so i started to build a raw SQL query. Then i ran into troubles when i tried to get this working with a pager.. all in all, i tried it to solve it with Criteria.

I created a new class, myCriteria which inherits from the original Criteria class.

/**
 * Add order by column name with a custom Order for a given Field
 *
 * @param unknown_type $propertyBracket
 * @return unknown
 */
public function addCustomOrderByColum($propertyField, $propertyList) {
    $this->orderByColumns[] = 'FIELD('.$propertyField.', ' . $propertyList .' )';
    return $this;
}
 

An example could look like:

$c->addCustomOrderByColum(CustomPeer::TYPE, " 'Sold', 'Pending', 'Announced' ");
 

This way you can create ORDER BY Statements which are not bound to common ASC/DESC rules.

by Christian Weyand on 2007-12-12, tagged criteria  custom  mysql 
(2 comments)

Get a propel record set via associative

You might've noticed that the default behavior of propel is to use numeric retrieval when grabbing rows from the database, and it is hard to override this action.

Sure you can do a custom query, but if you want to use criteria methods instead, this class will be helpful in grabbing a a result set via associative (thus preserving your associative keys and making coding easier on you).

I'd consider this beta level code. It's worked twice for me, both when I had a complex join that I wanted to use criteria for but failed once on a simpler query.

I haven't been able to decode why exactly the failure occured yet.

<?php
/*
*
*  @author: Kum Sackey
* 
*  Defines extension to propel that tweak its core behavior
* 
*/
class sfPropelExtension
{
    /**
    * @desc Return a result select from a criteria object. Unlike the implementation in BasePeer::doSelect(), this version returns the result set in associative mode
    */
    public static function getRSFromCriteria($criteria)
    {
        if(!($criteria instanceOf Criteria))
            throw new sfException('Passed parameter must be an instance of criteria, instead is of type: '.gettype($c));
 
        $dbMap = Propel::getDatabaseMap($criteria->getDbName());
        $con = Propel::getConnection();            
 
        $params = array();
 
        $sql = BasePeer::createSelectSql($criteria, $params);
        $stmt = $con->prepareStatement($sql);            
        $stmt->setLimit($criteria->getLimit());
        $stmt->setOffset($criteria->getOffset());            
 
        self::populateStmtValues($stmt, $params, $dbMap);                        
 
        $rs = $stmt->executeQuery($sql, ResultSet::FETCHMODE_ASSOC);  
 
        return $rs;        
    }
 
    /**
     * Populates values in a prepared statement.
     *
     * @param PreparedStatement $stmt
     * @param array $params array('column' => ..., 'table' => ..., 'value' => ...)
     * @param DatabaseMap $dbMap
     * @return int The number of params replaced.
     */
    private static function populateStmtValues($stmt, $params, DatabaseMap $dbMap)
    {
        $i = 1;
        foreach($params as $param) {
            $tableName = $param['table'];
            $columnName = $param['column'];
            $value = $param['value'];
 
            if ($value === null) {
                $stmt->setNull($i++);
            } else {
                $cMap = $dbMap->getTable($tableName)->getColumn($columnName);
                $setter = 'set' . CreoleTypes::getAffix($cMap->getCreoleType());
                $stmt->$setter($i++, $value);
            }
        }
    } 
}
 
by Kum Sackey on 2007-09-20, tagged associative  custom  propel  query 

Retrieve model objects with custom SQL query

$sql = 'select * from ( select * from book order by weight desc limit 5 ) as T order by popularity';
$connection = Propel::getConnection();
$statement = $connection->createStatement(  );
$result = $statement->executeQuery( $sql , ResultSet::FETCHMODE_NUM);
return BookPeer::populateObjects( $result );
by Yuriy Smirnov on 2007-08-07, tagged custom  model  object  propel  sql 

Including custom partials in admin generator pages

Here is a method i developed to include some custom partials at the end of the edit action of a module which adapts admin generator.

i just added this at the end of editSuccess.php file of my generator theme ($sf_data_dir/generator/sfPropelAdmin/<myCustomTheme>/templates/editSuccess.php)

[?php // include xtra partials ?]
<?php if ($this->getParameterValue('edit.partials')): ?>
<?php
$partials = $this->getParameterValue('edit.partials');
foreach( $partials as $partial => $vars ) : ?>
[?php include_partial('<?php echo $this->getModuleName() ?>/<?php echo $partial ?>', array('<?php echo $this->getSingularName() ?>' => $<?php echo $this->getSingularName() ?> <?php foreach ($vars as $var): ?> , '<?php echo $var ?>' => $<?php echo $var; endforeach; ?>)); ?]
<?php endforeach; ?>

then you just have to add the partials in the generator.yml like this :

edit:
  partials:
    partial1: [ var1a, var1b ]
    partial2: [ var2a, var2b ]

where var.. are the variables you want to include in each partial.

this can be easily made for list success also.

by Kostas Papadimitriou on 2007-01-08, tagged admin  custom  generator  include  partial  partials 
(2 comments)

ColorPicker Helper

Here is a code for new helper functions : input_color_tag and object_input_color_tag which display a input field. When the focus is on the input a color-picker appear.

See the demo on the bottom of this webpage

2 colorpickers are available by switching the button on the right : a small and simple one, a bigger one like photoshop color picker.


To work, the colorpicker need :

You have to specify in your template or yml config file you want to use this helper php file.


Files are available here (zip files)

by Sylvain PAPET on 2006-10-19, tagged colorpicker  custom  helper 
(7 comments)

How to create a custom admin generator theme ...

... without having trouble with your PEAR directory.

If you use the admin generator but don't like the generated html code, you either can use a custom css file to redo the style, or you have to copy the original theme from

/path/to/$PEAR/symfony/generator/sfPropelAdmin/default

and redo the html here. The problem with that technique is, that you have to manage this PEAR directory from now on, instead having the new theme located in your project folder. That's bad, because your theme will be lost if you clear your PEAR installation or decide to uninstall the symfony version and re-install another one.

After doing some research in the forums and docs, I tried to create the theme in my project directory, with the expected folder structure. I created a folder structure in my projects data dir, like this:

/path/to/$PEAR/data/generator/sfPropelAdmin/$THEME_NAME

I just copied all directories and files from the default sfPropelAdmin theme to this folder, but this didn't worked out. symfony wasn't able to find this theme. So, I created a symbolic link to this folder:

ln -s \
/path/to/$PROJECT_NAME/data/generator/sfPropelAdmin/$THEME_NAME \
/path/to/$PEAR/data/symfony/generator/sfPropelAdmin/$THEME_NAME

Now, you I modify the template to fit my needs, e.g. reset the css classes, add a new table structure and so on. After upgrading/uninstalling/installing a new copy of symfony, don't forget to check if the symbolic link still exists. If not, just re-create it, and everything should work.

If your webserver and php understoods the following of symbolic links (Windows doesn't), you can use this new theme for your sfPropelAdmin generated modules via the theme configuration handle in your generator.yml:

generator:
  class:              sfPropelAdminGenerator
  param:
    model_class:      ModelClass
    theme:            $THEME_NAME
by Pierre Minnieur on 2006-09-17, tagged admin  custom  generator  pear  theme 
(4 comments)

How to get a propel pager working with a custom SQL query

The problem

Let us take the example of snipeet! :-) The function that selects snippets by tags is a custom SQL query. When filtering by tags there is no pager anymore.

Why is that? The problem is that sfPropelPaginate only works with a Criteria object, not with a raw query. As a result you cannot have pagination with raw SQL query which is a rather severe limitation.

The solution

The solution is to first encode the parameter of the query in the criteria (that's the tricky bit) and then to set up a custom peer method.

As i said earlier we will take a simplified version of snipeet as an example. We assume that we have a function that creates a sql statement from a list of tags.

Here we go about setting the dummy criteria that encodes an array of tags:

getCriteriaFromTags($tags)
{
  $c = new Criteria();
  for ($i = 1; $i <= count($tags); ++$i)
  {
    $prefix = 't'.$i;
    // note that that SnippetTagPeer::TABLE_NAME  SnippetTagPeer::NAME are in fact arbitrary
    // any other table and table.column combination would work as well
    $c->addAlias($prefix.SnippetTagPeer::TABLE_NAME, SnippetTagPeer::TABLE_NAME);
    $c->add($prefix.SnippetTagPeer::NAME, $tags[$i-1]);
  }
  return $c;
}

Now you can initialise a sfPropelPaginate object as usual with that Criteria object. When that is done you tell it to use your custom peer method:

$pager->setPeerMethod('doSelectByTags');

Next you code the doSelectByTags method that performs the actual selection from a criteria object:

function doSelectByTags($c)
{
  // first you fetch the tags from the query
  $tags = array();
  foreach($c->keys() as $key)
    $tags[] = $c->get($key);
 
  // here comes your custom sql query 
  // it basically creates a $statement variable from the $tags array
  // .....
 
  // now you have to add the limit and offset:
 $statement->setLimit($c->getLimit());
 $statement->setOffset($c->getOffset());
 // and the rest of the code is as usual
}
by Olivier Verdier on 2006-05-25, tagged custom  pager  pagination  propel  query  raw  sql