Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "object"

Conditional object actions for the admin generator Explained

ReUse of: Conditional object actions for the admin generator By Georg Sorst

The list view of the admin generator currently always displays all defined object actions for each object. There is no way to display an object action for an object only if some condition on this object itself is met.

In order to extend the admin generator with this functionality only a small enhancement is required. You can either apply this change per module or create a new admin generator theme as described in the Symfony book.

The templates/_list_td_actions.php has to be extended to look roughly like this, depending on whether you already have your own modifications in there.

I use sf v1.4.11, for make this enhancement...

First, you should find the partial _list_td_actions.php found in

lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/data/generator/sfDoctrineModule/admin/template/templates/_list_td_actions.php 
 

And modify the original code like this:

<td>
    <?php if ($this->configuration->getValue('list.object_actions')): ?> 
    <ul class="sf_admin_td_actions">
    <?php foreach ($this->configuration->getValue('list.object_actions') as $name => $params): ?>
        <?php if ( isset( $params['condition'] ) ): ?>
            [?php if ( <?php echo ( isset( $params['condition']['invert'] ) && $params['condition']['invert'] ? '!' : '') . '$' . $this->getSingularName( ) . '->' . $params['condition']['function'] ?>( <?php echo ( isset( $params['condition']['params'] ) ? $params['condition']['params'] : '' ) ?> ) ): ?] 
        <?php endif; ?>
 
        <?php if ('_delete' == $name): ?>
            <?php echo $this->addCredentialCondition('[?php echo $helper->linkToDelete($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
        <?php elseif ('_edit' == $name): ?>
            <?php echo $this->addCredentialCondition('[?php echo $helper->linkToEdit($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
        <?php elseif ('_show' == $name): ?>
            <?php echo $this->addCredentialCondition('[?php echo $helper->linkToShow($'.$this->getSingularName().', '.$this->asPhp($params).') ?]', $params) ?>
        <?php else: ?>
            <li class="sf_admin_action_<?php echo $params['class_suffix'] ?>">
                <?php echo $this->addCredentialCondition($this->getLinkToAction($name, $params, true), $params) ?>
            </li>
        <?php endif; ?>
 
        <?php if ( isset( $params['condition'] ) ): ?>
            [?php endif; ?]
        <?php endif; ?>
    <?php endforeach; ?>
    </ul>
    <?php endif; ?>
</td>
 

Second, in the generator.yml you should add aditional lines like

actionName:
            label:          actionLabel
            action:         executeAction
            #Now the enhancement =D
            condition:
              # function is the name of function in the model::functionName and must return boolean
              function:     functionName 
              # params are the params to send to functionName
              params:       "$model->getDbField(), $sf_user, 'test'"
              # invert is used if you need invert the result of functionName
              invert:       false
 

Third and final step, go to

lib/model/doctrine/model.class.php
 

and create the functionName like:

/**
* $param1 is $model->getDbField()
* $param2 is $sf_user
* $param3 is 'test'
*/
public function functionName($param1, $param2, $param3){
        //code personal
        return boolean;
    }
 

Final, important!

php symfony cache:clear 
 

Well that's all, sorry for my bad English =S Thanks Georg Sorst and Google Translator

by Krikor Krikorian on 2011-05-26, tagged actions  admin  condition  conditional  generator  object 

Default culture content for i18n table

The table i18n doesn't support default culture. This snippet add the support of the default culture.

This snippet is a port of the snippet "default culture content fallback for i18n tables" for Symfony 1.2

Fallback I18n content

To enable the fallback, edit your table object class in lib/model/TableClassName.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Fetch the i18n object for this object culture.
   *
   * @param   string          $culture    The culture to set
   * @return  mixed                       A i18n object
   * @throws  PropelException             Any exceptions caught during processing will be rethrown wrapped into a PropelException.
   * @link    http://snippets.symfony-project.org/snippet/237 -- modified for Symfony 1.2
   */
  public function getCurrentTableClassNameI18n($culture = null)
  {
    if (is_null($culture)) {
      $culture = is_null($this->culture) ? sfPropel::getDefaultCulture() : $this->culture;
    }
 
    if (!isset($this->current_i18n[$culture]))
    {
      $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $culture);
      if ($obj !== null) { // Test if there is a translation for current culture
        $this->setTableClassNameI18nForCulture($obj, $culture);
      } else { // Create a translation for this culture
        $new_i18n = new TableClassNameI18n();
 
        $default_culture = sfConfig::get('sf_default_culture');
 
        // We try to fetch the default culture translation to initialise the new culture.
        if (!isset($this->current_i18n[$default_culture])) {
          $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $default_culture);
          if ($obj !== null) { // Test if there is a translation for current culture
            $this->setTableClassNameI18nForCulture($obj, $default_culture);
          }
        } else {
          $obj = $this->current_i18n[$default_culture];
        }
 
        if ($obj !== null) {
          $obj->copyInto($new_i18n);
        }
 
        $new_i18n->setId($this->getId());
        $new_i18n->setCulture($culture);
 
        $this->setTableClassNameI18nForCulture($new_i18n, $culture);
      }
    }
 
    return $this->current_i18n[$culture];
  }
 

Saving fallback values at creation

You now need to add default translation when you create a new object. We do that with this doSave function.

To use this function, edit your table object class in lib/model/TableClassName.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Stores the object in the database while setting default culture if necessary.
   *
   * If the object is new, it inserts it; otherwise an update is performed.
   * All related objects are also updated in this method.
   *
   * @param      Connection      $con The database connection
   * @return     int             The number of rows affected by this insert/update and any referring fk objects' save() operations.
   * @throws     PropelException Any exceptions caught during processing will be rethrown wrapped into a PropelException.
   * @see        save()
   * @link       http://snippets.symfony-project.org/snippet/237 -- modified for Symfony 1.2
   */
  protected function doSave(PropelPDO $con)
  {
    $default_culture = sfConfig::get('sf_default_culture');
    $current_culture = is_null($this->culture) ? sfPropel::getDefaultCulture() : $this->culture;
    $obj = null;
 
    // We try to fetch the default culture translation to initialise the new culture.
    if (!isset($this->current_i18n[$default_culture])) {
      $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $default_culture, $con);
      if ($obj !== null) { // Test if there is a translation for current culture
        $this->setTableClassNameI18nForCulture($obj, $default_culture);
      }
    } else {
      $obj = $this->current_i18n[$default_culture];
    }
 
    if($obj === null && isset($this->current_i18n[$current_culture])) {
      $new_i18n = new TableClassNameI18n();
      $this->current_i18n[$current_culture]->copyInto($new_i18n);
 
      $new_i18n->setId($this->getId());
      $new_i18n->setCulture($default_culture);
 
      $this->setTableClassNameI18nForCulture($new_i18n, $default_culture);
    }
 
    return parent::doSave($con);
  }
 

Fetching a list with default translations

To complete this snippet, here is a fallback version of doSelectWithI18n.

To enable the fallback, edit your table object peer class in lib/model/TableClassNamePeer.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Selects a collection of TableClassName objects pre-filled with their i18n objects.
   *
   * @param     Criteria         $criteria   The criteria's object
   * @param     string           $culture    The selected culture.
   * @param     PropelPDO        $con        An optional database connection
   * @return    array                        Array of TableClassName objects.
   * @throws    PropelException              Any exceptions caught during processing will be rethrown wrapped into a PropelException.
   * @link      http://snippets.symfony-project.org/snippet/237 -- modified for Symfony 1.2
   */
  public static function doSelectWithI18n(Criteria $criteria, $culture = null, PropelPDO $con = null)
  {
    $criteria = clone $criteria;
 
    if ($culture === null) {
      $culture = sfContext::getInstance()->getUser()->getCulture();
    }
 
    $default_culture = sfConfig::get('sf_default_culture');
 
    // Set the correct dbName if it has not been overridden
    if ($criteria->getDbName() == Propel::getDefaultDB()) {
      $criteria->setDbName(self::DATABASE_NAME);
    }
 
    TableClassNamePeer::addSelectColumns($c);
    $startcol = (TableClassNamePeer::NUM_COLUMNS - TableClassNamePeer::NUM_LAZY_LOAD_COLUMNS) + 1;
 
    TableClassNameI18nPeer::addSelectColumns($c);
 
    $criteria->addJoin(TableClassNamePeer::ID, TableClassNameI18nPeer::ID);
    $criterion = $criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $culture);
    $criterion->addOr($criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $default_culture));
    $criteria->add($criterion);
 
    $stmt = BasePeer::doSelect($c, $con);
    $results = array();
    $uncultured_results = array();
 
    while($row = $stmt->fetch(PDO::FETCH_NUM)) {
 
      $obj1 = new TableClassName();
      $obj1->hydrate($row);
      $obj1->setCulture($culture);
 
      if(isset($results[$obj1->getId()])) {
        $obj1 = $results[$obj1->getId()];
      }
 
      $omClass = TableClassNameI18nPeer::getOMClass($row, $startcol);
 
      $cls = Propel::importClass($omClass);
      $obj2 = new $cls();
      $obj2->hydrate($row, $startcol);
 
      $obj1->setTableClassNameI18nForCulture($obj2, $obj2->getCulture());
      $obj2->setTableClassName($obj1);
 
      if(!isset($uncultured_results[$obj1->getId()])) {
        $uncultured_results[$obj1->getId()] = $obj1;
      }
 
      if($obj2->getCulture() == $culture) {
        $uncultured_results[$obj1->getId()] = false;
      }
 
      if(!isset($results[$obj1->getId()])) {
        $results[$obj1->getId()] = $obj1;
      } elseif($obj2->getCulture() == $culture) {
        // Move result to the end of results array to fit eventual sort
        // criteria (ugly fix).
        unset($results[$obj1->getId()]);
        $results[$obj1->getId()] = $obj1;
      }
    }
 
    foreach ($uncultured_results as $obj1) {
      if ($obj1) {
        $obj1->setCulture($default_culture);
        $default_culture_object = $obj1->getCurrentTableClassNameI18n();
        if ($default_culture_object) {
          $obj2 = new TableClassNameI18n();
          $default_culture_object->copyInto($obj2);
          $obj2->setCulture($culture);
          $obj2->setTableClassName($obj1);
          $obj1->setTableClassNameI18nForCulture($obj2, $obj2->getCulture());
        }
        $obj1->setCulture($culture);
      }
    }
 
    return array_values($results);
  }
 

Counting results filtered on translations

Before enable the fallback, you must create a constant in your table object peer class in lib/model/TableClassNamePeer.php and replace table by your table name

const COUNT_DISTINCT = 'COUNT(DISTINCT table.ID)';
 

To enable the fallback, edit your table object peer class in lib/model/TableClassNamePeer.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Returns the number of rows matching criteria with I18N criteria.
   *
   * @param       Criteria    $criteria   The criteria's object
   * @param       boolean     $distinct   Whether to select only distinct columns (You can also set DISTINCT modifier in Criteria).
   * @param       Connection  $con        An optional database connection
   * @param       string      $culture    The selected culture.
   * @return      int                     Number of matching rows.
   * @link        http://snippets.symfony-project.org/snippet/237 -- modified for Symfony 1.2
   */
  public static function doCountWithI18n(Criteria $criteria = null, $distinct = false, PropelPDO $con = null, $culture = null)
  {
    // we're going to modify criteria, so copy it first
    if ($criteria === null){
      $criteria = new Criteria();
    } else {
      $criteria = clone $criteria;
    }
 
    $default_culture = sfConfig::get('sf_default_culture');
 
    if ($culture === null) {
      // We use current user culture.
      $culture = sfContext::getInstance()->getUser()->getCulture();
    }
 
    // clear out anything that might confuse the ORDER BY clause
    $criteria->clearSelectColumns()->clearOrderByColumns();
    $criteria->addSelectColumn(TableClassNamePeer::COUNT_DISTINCT);
 
    // just in case we're grouping: add those columns to the select statement
    foreach($criteria->getGroupByColumns() as $column)
    {
      $criteria->addSelectColumn($column);
    }
 
    $criteria->addJoin(TableClassNamePeer::ID, TableClassNameI18nPeer::ID);
    $criterion = $criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $culture);
    $criterion->addOr($criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $default_culture));
    $criteria->add($criterion);
 
    $rs = TableClassNamePeer::doSelectStmt($criteria, $con);
    if ($res = $rs->fetchColumn(0)) {
      return $res;
    } else {
      // no rows returned; we infer that means 0 matches.
      return 0;
    }
  }
 
by Simon Leblanc on 2009-09-20, tagged culture  data  database  i18n  object  propel  symfony12 

Conditional object actions for the admin generator

The list view of the admin generator currently always displays all defined object actions for each object. There is no way to display an object action for an object only if some condition on this object is met.

In order to extend the admin generator with this functionality only a small enhancement is required. You can either apply this change per module or create a new admin generator theme as described in the Symfony book.

The templates/_list_td_actions.php has to be extended to look roughly like this, depending on whether you already have your own modifications in there:

<?php if ($this->getParameterValue('list.object_actions')): ?>
<td>
<ul class="sf_admin_td_actions">
<?php foreach ($this->getParameterValue('list.object_actions') as $actionName => $params): ?>
  <?php if ( isset( $params['condition'] ) ): ?>
    [?php if ( <?php echo ( isset( $params['condition']['invert'] ) && $params['condition']['invert'] ? '!' : '') . '$' . $this->getSingularName(  ) . '->' . $params['condition']['function'] ?>( <?php echo ( isset( $params['condition']['params'] ) ? $params['condition']['params'] : '' ) ?> ) ):  ?]
  <?php endif; ?>
      <?php echo $this->addCredentialCondition($this->getLinkToAction($actionName, $params, true), $params) ?>
  <?php if ( isset( $params['condition'] ) ): ?>
    [?php endif; ?]
  <?php endif; ?>
<?php endforeach; ?>
</ul>
</td>
<?php endif; ?>
 

With this enhancement you can now use conditions for your actions in the generator.yml. The syntax should be pretty self-explanatory. An example would look like this:

object_actions:
  subscribe:    { name: Notify when changed, action: subscribe, icon: pencil_add.png }
    condition:
      function: isUserSubscribed
      params:   "$sf_user, 'test'"
      invert:   true
 

As you can see each object action now also takes a condition parameter, which again takes a number of parameter.

Enjoy!

by Georg Sorst on 2007-12-07, tagged actions  admin  condition  conditional  generator  object 
(10 comments)

default culture content fallback for i18n tables

By default, I18n content in database does not support fallback in default culture. This snippet allow you to enable I18n content fallback in order to always have a default value for your texts.

This snippet is sponsored by Dorigo consultants.

Fallback I18n content

To enable the fallback, edit your table object class in lib/model/TableClassName.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Fetch the i18n object for this object culture.
   * 
   * @return     mixed A i18n object
   * @throws     PropelException Any exceptions caught during processing will be
   *     rethrown wrapped into a PropelException.
   */
  public function getCurrentTableClassNameI18n()
  {
    if (!isset($this->current_i18n[$this->culture]))
    {
      $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $this->culture);
      if ($obj) // Test if there is a translation for current culture
      {
        $this->setTableClassNameI18nForCulture($obj, $this->culture);
      }
      else // Create a translation for this culture
      {
        $new_i18n = new TableClassNameI18n();
 
        $default_culture = sfConfig::get('sf_i18n_default_culture');
 
        // We try to fetch the default culture translation to initialise the new culture.
        if (!isset($this->current_i18n[$default_culture]))
        {
          $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $default_culture);
          if ($obj) // Test if there is a translation for current culture
          {
            $this->setTableClassNameI18nForCulture($obj, $default_culture);
          }
        }
        else
        {
          $obj = $this->current_i18n[$default_culture];
        }
 
        if ($obj)
        {
          $obj->copyInto($new_i18n);
        }
 
        $new_i18n->setId($this->getId());
        $new_i18n->setCulture($this->culture);
 
        $this->setTableClassNameI18nForCulture($new_i18n, $this->culture);
      }
    }
 
    return $this->current_i18n[$this->culture];
  }
 

Saving fallback values at creation

You now need to add default translation when you create a new object. We do that with this doSave function.

To use this function, edit your table object class in lib/model/TableClassName.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Stores the object in the database while setting default culture if necessary.
   *
   * If the object is new, it inserts it; otherwise an update is performed.
   * All related objects are also updated in this method.
   *
   * @param      Connection $con The database connection
   * @return     int The number of rows affected by this insert/update and any referring fk objects' save() operations.
   * @throws     PropelException Any exceptions caught during processing will be
   *     rethrown wrapped into a PropelException.
   * @see        save()
   */
  protected function doSave($con)
  {
 
    $default_culture = sfConfig::get('sf_i18n_default_culture');
 
    // We try to fetch the default culture translation to initialise the new culture.
    if (!isset($this->current_i18n[$default_culture]))
    {
      $obj = TableClassNameI18nPeer::retrieveByPK($this->getId(), $default_culture, $con);
      if ($obj) // Test if there is a translation for current culture
      {
        $this->setTableClassNameI18nForCulture($obj, $default_culture);
      }
    }
    else
    {
      $obj = $this->current_i18n[$default_culture];
    }
 
    if(!$obj && isset($this->current_i18n[$this->culture]))
    {
      $new_i18n = new TableClassNameI18n();
      $this->current_i18n[$this->culture]->copyInto($new_i18n);
 
      $new_i18n->setId($this->getId());
      $new_i18n->setCulture($default_culture);
 
      $this->setTableClassNameI18nForCulture($new_i18n, $default_culture);
    }
 
    return parent::doSave($con);
  }
 

Fetching a list with default translations

To complete this snippet, here is a fallback version of doSelectWithI18n.

To enable the fallback, edit your table object peer class in lib/model/TableClassNamePeer.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Selects a collection of TableClassName objects pre-filled with their i18n objects.
   *
   * @param      Criteria $criteria
   * @param      string $culture The selected culture.
   * @param      Connection $con An optional database connection
   * @return     array Array of TableClassName objects.
   * @throws     PropelException Any exceptions caught during processing will be
   *     rethrown wrapped into a PropelException.
   */
  public static function doSelectWithI18n(Criteria $c, $culture = null, $con = null)
  {
    if ($culture === null)
    {
      $culture = sfContext::getInstance()->getUser()->getCulture();
    }
 
    $default_culture = sfConfig::get('sf_i18n_default_culture');
 
    // Set the correct dbName if it has not been overridden
    if ($c->getDbName() == Propel::getDefaultDB())
    {
      $c->setDbName(self::DATABASE_NAME);
    }
 
    TableClassNamePeer::addSelectColumns($c);
    $startcol = (TableClassNamePeer::NUM_COLUMNS - TableClassNamePeer::NUM_LAZY_LOAD_COLUMNS) + 1;
 
    TableClassNameI18nPeer::addSelectColumns($c);
 
    $c->addJoin(TableClassNamePeer::ID, TableClassNameI18nPeer::ID);
    $criterion = $c->getNewCriterion(TableClassNameI18nPeer::CULTURE, $culture);
    $criterion->addOr($c->getNewCriterion(TableClassNameI18nPeer::CULTURE, $default_culture));
    $c->add($criterion);
 
    $rs = BasePeer::doSelect($c, $con);
    $results = array();
    $uncultured_results = array();
 
    while($rs->next()) {
 
      $omClass = TableClassNamePeer::getOMClass();
 
      $cls = Propel::import($omClass);
      $obj1 = new $cls();
      $obj1->hydrate($rs);
      $obj1->setCulture($culture);
 
      if(isset($results[$obj1->getId()]))
      {
        $obj1 = $results[$obj1->getId()];
      }
 
      $omClass = TableClassNameI18nPeer::getOMClass($rs, $startcol);
 
      $cls = Propel::import($omClass);
      $obj2 = new $cls();
      $obj2->hydrate($rs, $startcol);
 
      $obj1->setTableClassNameI18nForCulture($obj2, $obj2->getCulture());
      $obj2->setTableClassName($obj1);
 
      if(!isset($uncultured_results[$obj1->getId()]))
      {
        $uncultured_results[$obj1->getId()] = $obj1;
      }
 
      if($obj2->getCulture() == $culture)
      {
        $uncultured_results[$obj1->getId()] = false;
      }
 
      if(!isset($results[$obj1->getId()]))
      {
        $results[$obj1->getId()] = $obj1;
      }
      elseif($obj2->getCulture() == $culture)
      {
        // Move result to the end of results array to fit eventual sort
        // criteria (ugly fix).
        unset($results[$obj1->getId()]);
        $results[$obj1->getId()] = $obj1;
      }
 
    }
 
    foreach($uncultured_results as $obj1)
    {
      if($obj1)
      {
        $obj1->setCulture($default_culture);
        $default_culture_object = $obj1->getCurrentTableClassNameI18n();
        if($default_culture_object)
        {
          $obj2 = new TableClassNameI18n();
          $default_culture_object->copyInto($obj2);
          $obj2->setCulture($culture);
          $obj2->setTableClassName($obj1);
          $obj1->setTableClassNameI18nForCulture($obj2, $obj2->getCulture());
        }
        $obj1->setCulture($culture);
      }
    }
 
    return array_values($results);
  }
 

Counting results filtered on translations

If you want to use a pager with a filter on translations, you will need this count method. Once this method present, the magic is done by :

    $criteria->setDistinct();
    $pager->setCriteria($criteria);
    $pager->setPeerMethod('doSelectWithI18n');
    $pager->setPeerCountMethod('doCountWithI18n');
 

Note : The setDistinct is very important for this snippet to work. It should not falsify your results, and without it, the doCountWithI18n method could return bad results.

To enable the fallback, edit your table object peer class in lib/model/TableClassNamePeer.php and add the following code. Then search and replace TableClassName by your table object class name.

  /**
   * Returns the number of rows matching criteria with I18N criteria.
   *
   * @param      Criteria $criteria
   * @param      boolean $distinct Whether to select only distinct columns (You can also set DISTINCT modifier in Criteria).
   * @param      Connection $con An optional database connection
   * @param      string $culture The selected culture.
   * @return     int Number of matching rows.
   */
  public static function doCountWithI18n(Criteria $criteria, $distinct = false, $con = null, $culture = null)
  {
    // we're going to modify criteria, so copy it first
    $criteria = clone $criteria;
 
    $default_culture = sfConfig::get('sf_i18n_default_culture');
 
    if ($culture === null)
    {
      // We use current user culture.
      $culture = sfContext::getInstance()->getUser()->getCulture();
    }
 
    // clear out anything that might confuse the ORDER BY clause
    $criteria->clearSelectColumns()->clearOrderByColumns();
    $criteria->addSelectColumn(TableClassNamePeer::COUNT_DISTINCT);
 
    // just in case we're grouping: add those columns to the select statement
    foreach($criteria->getGroupByColumns() as $column)
    {
      $criteria->addSelectColumn($column);
    }
 
    $criteria->addJoin(TableClassNamePeer::ID, TableClassNameI18nPeer::ID);
    $criterion = $criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $culture);
    $criterion->addOr($criteria->getNewCriterion(TableClassNameI18nPeer::CULTURE, $default_culture));
    $criteria->add($criterion);
 
    $rs = TableClassNamePeer::doSelectRS($criteria, $con);
    if ($rs->next()) {
      return $rs->getInt(1);
    } else {
      // no rows returned; we infer that means 0 matches.
      return 0;
    }
  }
 
by Pierre-Yves LandurĂ© on 2007-10-10, tagged culture  data  database  i18n  object  propel 
(4 comments)

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 

sfMemcached with multiple servers/buckets

This is a version of sfMemcached modified to allow multiple servers to be used. Servers are grouped in buckets for organization.

A bucket must be initialized before it is used.

Example:

sfAdvMemcachedCache::createInstance('function', array (
  'lifetime' => 60
  'servers' => 
  array (
    array (
      'host' => '72.29.86.195',
      'port' => 11211,
      'persistent' => true,
      'weight' => 2,
      'timeout' => 1,
      'retry_interval' => 1,
      'status' => 1,
    )
  )
));

Then the bucket can be used through a call to getInstnace(bucketName)

Ex:

sfAdvMemcacheCache::getInstance('function')->set('helloKey', 'world_data');

For ease of use, multiple buckets and servers can be defined and auto-intialized through a YAML file called memcached.yml that will sit in the /config folder of your application.

Example configuration:

default:
  servers:
    default:
      host: localhost
      port: 11211
      timeout: 1
      weight: 1
      retry_interval: 1
      status: 1 
    host1:
      host: 72.29.86.195
      port: 11211
      weight: 2
    host2:
      host: 66.7.208.83
      port: 11211
      weight: 2
    host3:
      host: 66.7.208.87
      port: 11211
  buckets:
    default:
      lifetime: 60
      servers: host1
    session:
      servers: [host1, host2]
    object:
      servers: [host1]
    function:
      servers: host2
    view:

In order to use YAML configuration a file named "myMemcacheConfigHandler.class.php" must be created in the /lib folder of your application.

<?php
 class myMemcacheConfigHandler extends sfYamlConfigHandler
{
  /**
   * Executes this configuration handler.
   *
   * @param array An array of absolute filesystem path to a configuration file
   *
   * @return string Data to be written to a cache file
   *
   * @throws <b>sfParseException</b> If a requested configuration file is improperly formatted
   */
  public function execute($configFiles)
  {
    // parse the yaml
    $myConfig = $this->parseYamls($configFiles);
 
    $myConfig = sfToolkit::arrayDeepMerge(
      isset($myConfig['default']) && is_array($myConfig['default']) ? $myConfig['default'] : array(),
      isset($myConfig['all']) && is_array($myConfig['all']) ? $myConfig['all'] : array(),
      isset($myConfig[sfConfig::get('sf_environment')]) && is_array($myConfig[sfConfig::get('sf_environment')]) ? $myConfig[sfConfig::get('sf_environment')] : array()
    );
 
    // Set Up Servers
    $defaultServerConfig = array('host' => 'localhost',
                            'port' => 11211,
                            'persistent' => true,
                            'weight' => 1,
                            'timeout' => 1,
                            'retry_interval' => 15,
                            'status' => true,                                        
                            );                           
 
    if(!isset($myConfig['servers']) || !is_array($myConfig['servers']))
    {
        $myConfig['servers'] = array( 'default' => $defaultServerConfig );    
    }
 
    $myConfig['servers']['default'] = array_merge( $defaultServerConfig, isset($myConfig['servers']['default']) ? $myConfig['servers']['default'] : array() );  
 
    foreach($myConfig['servers'] as $serverName => &$server)
    {
        $server = array_merge($myConfig['servers']['default'], $server);
    }
 
    // Set Up Buckets
    if(!isset($myConfig['buckets']) || !is_array($myConfig['buckets']))
        throw new sfParseException(sprintf('Configuration file "%s" does not specify any cache buckets.', $configFiles[0]));        
 
    $inits = array();
    foreach($myConfig['buckets'] as $bucketName => &$bucket)
    {
        if(!isset($bucket['servers']))
        {
            $bucket['servers'] = 'default';
        }
 
        if(!is_array($bucket['servers']))
        {
            $bucket['servers'] = array( $bucket['servers'] );            
        }
 
 
        foreach($bucket['servers'] as $serverName => &$server)
        {
            if(!isset($myConfig['servers'][$server]))
                throw new sfParseException(sprintf('Configuration file "%s" requires server configuration \'%s\' for bucket \'%s\', but server configuration does not exist.', $configFiles[0], $server, $bucketName ));
 
            $server = $myConfig['servers'][$server];        
        }
 
        $inits[] = sprintf("sfAdvMemcachedCache::createInstance('%s', %s);", $bucketName, var_export($bucket, true) );
    }
 
    // Compile Return Value    
    return sprintf("<?php\n  %s", implode("\n", $inits));
  }
}

Once that code is in place, modify your config.php in your applications /config directory.

Add the following code:

include(sfConfigCache::getInstance()->checkConfig(sfConfig::get('sf_app_config_dir_name').'/memcache.yml'));

Lastly create a file called sfAdvMemcachedCache.php in your /lib folder. All of the above is optional, and is only needed if you wanted to use YAML for auto-initialization.

... it seems there was too much code in here for a snippet. Trying to save the snippet would cause a silent failure. You can download the code here:

http://lu.scio.us/files/sfAdvMemcachedCache.php.text

Using this code as a base, it is trivial to create a Memcached class to handle session data, cache view content, or cache propel objects automatically.

The first two classes I have already written, and may be released if I repackage this code into a plugin.

by Kum Sackey on 2007-07-28, tagged memcached  object 
(1 comment)

Populate Object Filter

If you're like me, you hate having to populate your objects (from the model) with parameters from the request. Wouldn't it be nice to specify a little nugget of configuration, and then never worry about it again? Consider the following, which is tedious and boring:

Old Example

class mymoduleActions extends sfActions
{
  ...
 
  public function executeUpdate()
  {
    $employee = new Employee();
    $employee->setFirstName($this->getRequestParameter('first_name'));
    $employee->setLastName($this->getRequestParameter('last_name'));
    $employee->setSsn($this->getRequestParameter('ssn'));
    $employee->setDob($this->getRequestParameter('dob'));
    ...
    $this->employee = $employee;
  }
}

And the list goes on. Imagine, however, that you have the following in your module filters.yml file:

myPopulateObjectFilter:
  class: myPopulateObjectFilter
  param:
    use_database:  on
    model_object:  employee
    model_class:   Employee
    exclude:       [ssn]
    defaults:
      first_name:  example default name

If you had a filter that automatically created an object, and based upon the previous yaml, populated that object with data from the request (after a user hit submit on a form containing this information), then subsequently set that object for retrieval as a request attribute, you'd be a pretty happy person right?

New Example

class mymoduleActions extends sfActions
{
  ...
 
  public function executeUpdate()
  {
    $this->employee = $this->getRequest()->getAttribute('employee');
    ...
  }
}

Of course, you'd be free to do whatever you want with the results of the prepopulated object.

A great application would be an alternative to the fillin form filter. If you have your template set up to populate inputs from an object from the model, this filter can populate that object for you, and you can simply pass it on to the template.

The Code

<?php
 
/**
 * myPopulateObjectFilter
 * Uses configuration in filters.yml to create an object,
 * populate it with data from the request,
 * and set a request attribute with the result.
 *
 * @author  Stephen Riesenberg
 */
class myPopulateObjectFilter extends sfFilter
{
    protected
        $defaults       = null,
        $controller     = null,
        $request        = null;
 
    public function initialize($context, $parameters = array())
    {
        parent::initialize($context, $parameters);
 
        // get defaults from filters.yml (if any) and create new parameter holder to store them
        $this->defaults = new sfParameterHolder();
        $this->defaults->add($this->getParameter('defaults', array()));
 
        // get controller and request
        $this->controller   = $this->getContext()->getController();
        $this->request      = $this->getContext()->getRequest();
    }
 
    public function execute($filterChain)
    {
        // get request variable list
        $vars = $this->request->getParameterHolder()->getNames();
        $exclude = array_merge($this->getParameter('exclude', array()), array('module', 'action'));
        $vars = array_diff($vars, $exclude);
 
        $funcs = array();
        foreach ($vars as $var)
        {
            $funcs[$var] = 'set'.ucfirst(sfInflector::camelize($var));
        }
 
        // get model_object and model_class
        $object = $this->getParameter('model_object');
        $class = $this->getParameter('model_class');
 
        // fetch from the database or create the object to fill in
        if ($this->getParameter('use_database', false) && null !== ($id = $this->request->getParameter('id')))
        {
            $peer_class = $class . "Peer";
            $record = $peer_class::retrieveByPk($id);
        }
        else
        {
            $record = new $class();
        }
 
        // something like: array('id' => 'setId', 'my_field' => 'setMyField', ...)
        foreach ($funcs as $var => $func)
        {
            if (is_callable(array($record, $func)))
            {
                $record->$func($this->getValue($var));
            }
        }
 
        // set the updated record into a request attribute
        $this->request->setAttribute($object, $record);
 
        // execute next filter
        $filterChain->execute();
    }
 
    protected function getDefault($var)
    {
        return $this->defaults->get($var);
    }
 
    protected function getValue($var)
    {
        return $this->request->getParameter($var) != '' ? $this->request->getParameter($var) : $this->getDefault($var);
    }
}

NOTE: Place this in a file called myPopulateObjectFilter.class.php in the myproject/apps/myapp/lib/ directory.

by Stephen Riesenberg on 2007-01-06, tagged filter  model  object 
(2 comments)

initiate new XMLHttpRequest object - browser crossing

function getxmlhttp() {
    //Create a boolean variable to check for a valid IE instance.
    var xmlhttp = false;
 
    //Check if we are using IE.
    try {
        //If the javascript version is greater than 5.
        xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
        //If not, then use the older active x object.
        try {
            //If we are using IE.
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (E) {
            //Else we must be using a non-IE browser.
            xmlhttp = false;
        }
    }
 
    //If we are using a non-IE browser, create a JavaScript instance of the object.
    if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
        xmlhttp = new XMLHttpRequest();
    }
 
    return xmlhttp;
}
by Ken Vu on 2006-12-16, tagged ajax  javascript  new  object  xmlhttprequest 

initiate new XMLHttpRequest object - browser crossing

function getxmlhttp() {
    //Create a boolean variable to check for a valid IE instance.
    var xmlhttp = false;
 
    //Check if we are using IE.
    try {
        //If the javascript version is greater than 5.
        xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
        //If not, then use the older active x object.
        try {
            //If we are using IE.
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (E) {
            //Else we must be using a non-IE browser.
            xmlhttp = false;
        }
    }
 
    //If we are using a non-IE browser, create a JavaScript instance of the object.
    if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
        xmlhttp = new XMLHttpRequest();
    }
 
    return xmlhttp;
}
[/code/
by Ken Vu on 2006-12-16, tagged ajax  javascript  new  object  xmlhttprequest 
(2 comments)

Debug an object or variable (easy way)

This in the easiest way (I found) to debug an object (or variable).

In an action

$this->debugMessage(sprintf('<h1>object</h1><pre>%s</pre>', print_r(@$this->object, true)));

In a template

<?php use_helper('Debug') ?>
<?php echo debug_message(sprintf('<h1>object</h1><pre>%s</pre>', print_r(@$object, true))) ?>

Based on symfony book debug page.

by brikou on 2006-06-24, tagged debug  object 
(1 comment)

debug objects or variables...

my approach to the previous snippet is to create a class called debugTools.class.php and throw it in the \lib directory. It should contain the following code:

<? 
 
Class debugTools {
 
/**
* renders to the page a proper object map for looking over contents of an object as it occurs.
*
* @param    object       $object        (required) the object for inpection
*/
Public Static function inspect($object)
    {
    echo "<PRE>";
    print_r($object);
    echo "</PRE>";
    }
 
 
}// ends class  
?>

you can then call it easily (with only two or three keystrokes if your IDE does autocompletion) by calling:

echo debugTools::inspect($object);

the resulting formating looks like so (i used it to analyze a usp shipping object):

ups Object
(
    [_errors] => Array
        (
        )
 
    [_action] => 3
    [_delivery_code] => GND
    [_src_country] => US
    [_dst_country] => US
    [_rate_chart] => 5
    [_container] => 0
    [_rescom] => 1
    [_rate_charts] => Array
        (
            [0] => Regular+Daily+Pickup
            [1] => On+Call+Air
            [2] => One+Time+Pickup
            [3] => Letter+Center
            [4] => Customer+Counter
            [5] => Drop+Off
        )
 
    [_containers] => Array
        (
            [0] => 00
            [1] => 01
            [2] => 21
            [3] => 22
            [4] => 23
            [5] => 1
            [6] => 2
        )
 
    [_rescoms] => Array
        (
            [0] => 1
            [1] => 2
        )
 
    [_src_zip] => 97499
    [_dst_zip] => 90120
    [_weight] => 2
)

a handy way of viewing objects that can be accessed from the layout or pretty much anywhere, if you're new to symfony use it to look over $sf_user in the template, you will have a much better idea what's going on.

by Russ Cann on 2006-06-02, tagged debug  object  variables 
(1 comment)