Code snippets for symfony 1.x

Navigation

Popular Tags

action admin ajax batch cache cli criteria css culture custom database date debug doctrine email filter form forms generator helper i18n javascript model navigation object pager pagination pake propel query routing session sql symfony template test user validation validator view widget
This is a public source code repository where you can find, rate and comment code snippets published by others, or store you owns and categorize them with tags for easy retrieval.

Latest snippets

Validator for Hebrew-character integers

The Hebrew alphabet has its own traditional representation for integers. Here is a validator to clean them into conventional Arabic integers. There may be gotchas involved with the Unicode processing. I developed this on a 64-bit Ubuntu system.

<?php
class ynValidatorIntegerHebrew extends sfValidatorInteger
{
  protected function configure($options = array(), $messages = array())
  {
    parent::configure( $options, $messages );
 
    // value to add to Hebrew entries, useful for assumed millenia
    $this->addOption('add_to_hebrew', 0);
  }
 
  public function clean( $value )
  {
    if ( preg_match( '/^\p{Hebrew}+(?:"\p{Hebrew})?$/u', $value ) ) {
      $value = $this->convertStringToInt( $value );
    }
 
    return parent::clean( $value );
  }
 
  /**
   * Iterates through the Hebrew characters in the string, converting
   * each to its Unicode code and evaluating for numerical value
   *
   * @param string $value The integer represented in Hebrew characters
   * @return integer
   */
  protected function convertStringToInt( $value )
  {
    if ( ! is_string( $value ) ) {
      throw new UnexpectedValueException( 'Argument must be a string' );
    }
 
    preg_match_all( '/\p{Hebrew}/u', $value, $matches );
 
    $value_int = 0;
 
    foreach ( $matches[0] as $char ) {
      $char     = iconv( 'UTF-8', 'UTF-32', $char );
      $char_dec = unpack( 'L*', $char );
      $char_dec = $char_dec[2];
 
      if ( $char_dec > 1510 ) {
        $base = $char_dec - 1510;
        $value_int += $base * 100;
      }
      elseif ( $char_dec > 1496 ) {
        $base = $char_dec - 1496;
 
        // have to deal with final forms
        if ( $base > 2 ) $base--;
        if ( $base > 4 ) $base--;
        if ( $base > 5 ) $base--;
        if ( $base > 8 ) $base--;
        if ( $base > 9 ) $base--;
 
        $value_int += $base * 10;
      }
      else {
        $base = $char_dec - 1487;
        $value_int += $base;
      }
    }
 
    $value_int += $this->getOption('add_to_hebrew');
 
    return $value_int;
  }
}
 
// UNIT TEST
 
// The Hebrew literals are displaying oddly because of right-to-left; it should
// still work
 
require_once dirname(__FILE__).'/../../bootstrap/Doctrine.php';
 
$t = new lime_test();
 
$val = new ynValidatorIntegerHebrew();
 
$t->is( $val->clean('א'), 1 );
$t->is( $val->clean('ב'), 2 );
$t->is( $val->clean('ג'), 3 );
$t->is( $val->clean('ד'), 4 );
$t->is( $val->clean('ה'), 5 );
$t->is( $val->clean('ו'), 6 );
$t->is( $val->clean('ז'), 7 );
$t->is( $val->clean('ח'), 8 );
$t->is( $val->clean('ט'), 9 );
$t->is( $val->clean('י'), 10 );
$t->is( $val->clean('כ'), 20 );
$t->is( $val->clean('ך'), 20 );
$t->is( $val->clean('ל'), 30 );
$t->is( $val->clean('מ'), 40 );
$t->is( $val->clean('ם'), 40 );
$t->is( $val->clean('נ'), 50 );
$t->is( $val->clean('ן'), 50 );
$t->is( $val->clean('ס'), 60 );
$t->is( $val->clean('ע'), 70 );
$t->is( $val->clean('פ'), 80 );
$t->is( $val->clean('ף'), 80 );
$t->is( $val->clean('צ'), 90 );
$t->is( $val->clean('ץ'), 90 );
$t->is( $val->clean('ק'), 100 );
$t->is( $val->clean('ר'), 200 );
$t->is( $val->clean('ש'), 300 );
$t->is( $val->clean('ת'), 400 );
$t->is( $val->clean('תשע'), 770 );
$t->is( $val->clean('תתרתשע'), 1770 );
 
$val->setOption('add_to_hebrew', 5000);
$t->is( $val->clean('תשע'), 5770 );
 
by yitznewton on 2011-02-17, tagged hebrew  integer  validator 

Validator and widget for fuzzy dates

If I understand correctly, ISO-8601 allows for dates represented as YYYY, YYYY-MM, and YYYY-MM-DD, among others. I did not see the possibility of supporting these date formats with the built-in widgets and validators, so I wrote some.

class ynWidgetFormDateFuzzy extends sfWidgetFormDate
{
  public function render($name, $value = null, $attributes = array(), $errors = array())
  {
    // convert value to an array
    $default = array('year' => null, 'month' => null, 'day' => null);
    if (is_array($value))
    {
      $value = array_merge($default, $value);
    }
    else if ( preg_match( '/^(\d+)$/', $value, $matches ) )
    {
      $value = array(
        'year'  => $matches[1],
        'month' => '',
        'day'   => '',
      );
    }
    else if ( preg_match( '/^(\d+)-(\d+)$/', $value, $matches ) )
    {
      $value = array(
        'year'  => $matches[1],
        'month' => $matches[2],
        'day'   => '',
      );
    }
    else
    {
      $value = (string) $value == (string) (integer) $value ? (integer) $value : strtotime($value);
      if (false === $value)
      {
        $value = $default;
      }
      else
      {
        $value = array('year' => date('Y', $value), 'month' => date('n', $value), 'day' => date('j', $value));
      }
    }
 
    $date = array();
    $emptyValues = $this->getOption('empty_values');
 
    $date['%day%'] = $this->renderDayWidget(
      $name.'[day]',
      $value['day'] ? (int) $value['day'] : '',
      array(
        'choices' => array('' => $emptyValues['day']) + $this->getOption('days'),
        'id_format' => $this->getOption('id_format')
      ),
      array_merge($this->attributes, $attributes)
    );
 
    $date['%month%'] = $this->renderMonthWidget(
      $name.'[month]',
      $value['month'] ? (int) $value['month'] : '',
      array(
        'choices' => array('' => $emptyValues['month']) + $this->getOption('months'),
        'id_format' => $this->getOption('id_format')
      ),
      array_merge($this->attributes, $attributes)
    );
 
    $date['%year%'] = $this->renderYearWidget($name.'[year]', $value['year'], array('choices' => $this->getOption('can_be_empty') ? array('' => $emptyValues['year']) + $this->getOption('years') : $this->getOption('years'), 'id_format' => $this->getOption('id_format')), array_merge($this->attributes, $attributes));
 
    return strtr($this->getOption('format'), $date);
  }
}
 
 
class ynValidatorDateFuzzy extends sfValidatorBase
{
  protected function configure($options = array(), $messages = array())
  {
    $this->addMessage('max', 'The date must be before %max%.');
    $this->addMessage('min', 'The date must be after %min%.');
 
    $this->addOption('min', null);
    $this->addOption('max', null);
  }
 
  protected function doClean($value)
  {
    if ( ! is_array( $value ) ) {
      throw new sfValidatorError($this, 'bad_format');
    }
 
    // convert array to date string
    if (is_array($value))
    {
      $value = $this->convertDateArrayToString($value);
    }
 
    if ($max = $this->getOption('max')) {
      $date_max = new DateTime( $max );
 
      if ( $this->lowestPossible( $value )->format('Ymd') > $date_max->format('Ymd') ) {
        throw new sfValidatorError($this, 'max', array('value' => $value, 'max' => 'out of range'));
      }
    }
 
    if ($min = $this->getOption('min')) {
      $date_min = new DateTime( $min );
 
      if ( $this->highestPossible( $value )->format('Ymd') < $date_min->format('Ymd') ) {
        throw new sfValidatorError($this, 'max', array('value' => $value, 'min' => 'out of range'));
      }
    }
 
    return $value;
  }
 
  protected function convertDateArrayToString($value)
  {
    // all elements must be empty or a number
    foreach (array('year', 'month', 'day', 'hour', 'minute', 'second') as $key)
    {
      if (isset($value[$key]) && !preg_match('#^\d+$#', $value[$key]) && !empty($value[$key]))
      {
        throw new sfValidatorError($this, 'invalid', array('value' => $value));
      }
    }
 
    // check empty value correspondence
 
    if (
      ! $value['year'] && ! $value['month'] && ! $value['day']
    ) {
      return $this->getEmptyValue();
    }
    else if (
      ! $value['year']
      && ($value['month'] || $value['day'])
    ) {
      throw new sfValidatorError($this, 'invalid', array('value' => $value));
    }
    else if (
      $value['year'] && ! $value['month'] && $value['day']
    ) {
      throw new sfValidatorError($this, 'invalid', array('value' => $value));
    }
 
    if ( $value['month'] && ! in_array( (int) $value['month'], range(1,12) ) ) {
      throw new sfValidatorError($this, 'invalid', array('value' => $value));
    }
 
    if ( $value['day'] && ! in_array( (int) $value['day'], range(1,31) ) ) {
      throw new sfValidatorError($this, 'invalid', array('value' => $value));
    }
 
    $clean = '';
 
    if ( $value['year'] ) {
      $clean .= sprintf( '%04d', intval( $value['year'] ) );
    }
 
    if ( $value['month'] ) {
      $clean .= sprintf( '-%02d', intval( $value['month'] ) );
    }
 
    if ( $value['day'] ) {
      $clean .= sprintf( '-%02d', intval( $value['day'] ) );
    }
 
    return $clean;
  }
 
  protected function highestPossible( $date )
  {
    if ( preg_match( '/^\d+$/', $date, $matches ) ) {
      return new DateTime( $date . '-12-31' );
    }
    else if ( preg_match( '/^\d+-(\d+)$/', $date, $matches ) ) {
      switch ( $matches[1] ) {
        case '2':
          return new DateTime( $date . '-28' );
        case '1':
        case '3':
        case '5':
        case '7':
        case '8':
        case '10':
        case '12':
          return new DateTime( $date . '-31' );
        default:
          return new DateTime( $date . '-30' );
      }
    }
    else {
      return new DateTime( $date );
    }
  }
 
  protected function lowestPossible( $date )
  {
    if ( preg_match( '/^\d+$/', $date, $matches ) ) {
      return new DateTime( $date . '-01-01' );
    }
    else if ( preg_match( '/^\d+-(\d+)$/', $date, $matches ) ) {
      return new DateTime( $date . '-01' );
    }
    else {
      return new DateTime( $date );
    }
  }
}
 
by yitznewton on 2011-02-16, tagged date  validator  widget 

SysLogger

This snippet shows an implementation of a logger that logs to the syslog

<?php
class SysLogger extends sfLogger
{
  const LOG_NAME = 'SymfonySysLogger';
 
  protected
    $timeFormat = '%b %d %H:%M:%S', 
    $format     = '[%priority%] %message%';
 
 
  /**
   * Initializes this logger.
   *
   *
   * @param  sfEventDispatcher $dispatcher  A sfEventDispatcher instance
   * @param  array             $options     An array of options.
   *
   * @return Boolean      true, if initialization completes successfully, otherwise false.
   */
  public function initialize(sfEventDispatcher $dispatcher, $options = array())
  {
    $appName = $this->getApplicationName();
 
    openlog($appName, LOG_PID, LOG_USER);
 
    return parent::initialize($dispatcher, $options);
  }
 
  /**
   * this method tries to resolve the request url of a web app, or
   * the terminal arguments of a console app
   * else a default app-name is returned 
   * 
   * @return string the applicationName
   */
  protected function getApplicationName()
  {
    $appName = 'unknown-app';
    // request url or 
    if (isset($_SERVER['REQUEST_URI']))
    {
      $appName = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']; 
    }
    // else console arguments
    elseif (implode($_SERVER['argv'], ' '))
    {
      $appName = implode($_SERVER['argv'], ' ');
    }    
 
    $appName = self::LOG_NAME.'(' // the default name, so infra can filter +
      .$appName.')';
 
    return $appName; 
  }
 
  /**
   * Logs a message.
   *
   * @param string $message   Message
   * @param string $priority  Message priority
   */
  protected function doLog($message, $priority)
  {
    $logMessage = strtr($this->format, array(
      '%message%'  => $message,
      '%time%'     => strftime($this->timeFormat),
      '%priority%' => $this->getPriority($priority),
    ));
 
    syslog($priority, $logMessage);
  }
 
  /**
   * Returns the priority string to use in log messages.
   *
   * @param  string $priority The priority constant
   *
   * @return string The priority to use in log messages
   */
  protected function getPriority($priority)
  {
    return sfLogger::getPriorityName($priority);
  }  
 
 
  /**
   * Executes the shutdown method.
   */
  public function shutdown()
  {
    closelog();
  }  
}
 
by Leon van der Ree on 2011-02-10, tagged log  syslog 

modified hasCredential method that loads permissions from the database

Tired of having to signout / signin your users to reload permissions? This method fetches the credentials directly from the database so the credential changes are effective as soon as the groups or permissions are changed.

Paste this in your lib/myUser.class.php file

  /**
   * Overridden method that actually reads the permission from DB
   * instead of relying on data present when the user logs in.
   *
   * @param  string  permission name
   *
   * @return boolean true if the user has credential
   */
  public function hasCredential($permission_name)
  {
    if (!$this->isAuthenticated()) {
      return false;
    }
    $gu = $this->getGuardUser();
    $groups = $gu->getGroups();
    $permissions = $gu->getPermissions();
 
    $permission_names = array();
    foreach($permissions as $permission) {
      $permission_names[] = $permission->getName();
    }
    foreach($groups as $group) {
      $group_permissions = $group->getPermissions();
      foreach($group_permissions as $group_permission) {
        $permission_names = array_merge($permission_names, array($group_permission->getName()));
      }
    }
    $permission_names = array_unique($permission_names);
    return (in_array($permission_name, $permission_names)) ? true : false;
  }
 
by Mathieu Comandon on 2011-02-07, tagged credentials  sfdoctrineguardplugin  sfguarduser 

Hide Web Debug Details and show it on errors

This snippet hides the Web Debug details and show it automatically whenever an error has detect by the Debug System.

This code uses a JavaScript file only for development environment, avoiding unnecessary requests and extra codes in production environment and files.

Obs: Requires jQuery JavaScript Library.

1) Copy the code below and save it as a JavaScript file in "/web/js/debug.js"

$(function(){
    //sfWebDebug Bar Toogler
    var JQsfWebDebug = $('#sfWebDebugDetails');
    (JQsfWebDebug.children('li').hasClass('sfWebDebugError')) ? JQsfWebDebug.show() : JQsfWebDebug.hide();
});
 

2) Place the PHP code below in your "/apps/application-name/config/view.yml"

default:
  // your metas, styles, etc... configuration
  javascripts:    [yourScript.js <?php if(sfConfig::get('sf_web_debug')) echo ',debug.js' ?>]
 

Tip: You can use "debug.js" for all JavaScript debug needs.

by Mario Rezende on 2011-01-15, tagged debug  javascript 

Generate modules from all available doctrine models

As I didn't want to run doctrine:generate-module for all models over and over again I created a little task overwriting the default on. With this task you can create modules for all available models from project and plugins at once.

Add under PROJECT_DIR/lib/task the file sfDoctrineGenerateAllModulesTask.class.php with the following content:

<?php
require_once(sfConfig::get('sf_symfony_lib_dir').'/plugins/sfDoctrinePlugin/lib/task/sfDoctrineGenerateModuleTask.class.php');
 
class sfDoctrineGenerateAllModulesTask extends sfDoctrineGenerateModuleTask
{
    /**
   * @see sfTask
   */
  protected function configure()
  {
    $this->addArguments(array(
      new sfCommandArgument('application', sfCommandArgument::REQUIRED, 'The application name'),
    ));
 
    $this->addOptions(array(
      new sfCommandOption('theme', null, sfCommandOption::PARAMETER_REQUIRED, 'The theme name', 'default'),
      new sfCommandOption('generate-in-cache', null, sfCommandOption::PARAMETER_NONE, 'Generate the module in cache'),
      new sfCommandOption('non-verbose-templates', null, sfCommandOption::PARAMETER_NONE, 'Generate non verbose templates'),
      new sfCommandOption('with-show', null, sfCommandOption::PARAMETER_NONE, 'Generate a show method'),
      new sfCommandOption('singular', null, sfCommandOption::PARAMETER_REQUIRED, 'The singular name', null),
      new sfCommandOption('plural', null, sfCommandOption::PARAMETER_REQUIRED, 'The plural name', null),
      new sfCommandOption('route-prefix', null, sfCommandOption::PARAMETER_REQUIRED, 'The route prefix', null),
      new sfCommandOption('with-doctrine-route', null, sfCommandOption::PARAMETER_NONE, 'Whether you will use a Doctrine route'),
      new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
      new sfCommandOption('actions-base-class', null, sfCommandOption::PARAMETER_REQUIRED, 'The base class for the actions', 'sfActions'),
    ));
 
    $this->namespace = 'doctrine';
    $this->name = 'generate-all-modules';
    $this->briefDescription = 'Generates all Doctrine modules';
 
    $this->detailedDescription = <<<EOF
The [doctrine:generate-all-modules|INFO] task generates all Doctrine modules:
 
  [./symfony doctrine:generate-all-modules frontend|INFO]
 
The task creates all modules in the [%application%|COMMENT] application
for all existing model classes [%model%|COMMENT].
 
You can also create empty modules that inherit their actions and templates from
a runtime generated module in [%sf_app_cache_dir%/modules/auto%module%|COMMENT] by
using the [--generate-in-cache|COMMENT] option:
 
  [./symfony doctrine:generate-all-modules --generate-in-cache frontend|INFO]
 
The generator can use a customized theme by using the [--theme|COMMENT] option:
 
  [./symfony doctrine:generate-all-modules --theme="custom" frontend|INFO]
 
This way, you can create your very own module generator with your own conventions.
 
You can also change the default actions base class (default to sfActions) of
the generated modules:
 
  [./symfony doctrine:generate-all-modules --actions-base-class="ProjectActions" frontend|INFO]
EOF;
  }
 
  /**
   * @see sfTask
   */
  protected function execute($arguments = array(), $options = array())
  {
    $databaseManager = new sfDatabaseManager($this->configuration);
 
    $properties = parse_ini_file(sfConfig::get('sf_config_dir').'/properties.ini', true);
 
    $models = $this->getModels();
 
    foreach ($models as $model) {
        foreach ($model as $name => $data) {
            $arguments['model'] = $name;
            $arguments['module'] = strtolower($name);
 
            $this->constants = array(
                'PROJECT_NAME'   => isset($properties['symfony']['name']) ? $properties['symfony']['name'] : 'symfony',
                'APP_NAME'       => $arguments['application'],
                'MODULE_NAME'    => $arguments['module'],
                'UC_MODULE_NAME' => ucfirst($arguments['module']),
                'MODEL_CLASS'    => $arguments['model'],
                'AUTHOR_NAME'    => isset($properties['symfony']['author']) ? $properties['symfony']['author'] : 'Your name here',
                );
 
            $method = $options['generate-in-cache'] ? 'executeInit' : 'executeGenerate';
 
            $this->$method($arguments, $options);
        }
    }   
  }
 
  protected function getModels() {
      $plugin_schemas = sfFinder::type('files')->name('schema.yml')->in(sfConfig::get('sf_plugins_dir'));
      $models = array();
 
      foreach($plugin_schemas as $schema) {
          $models[] = sfYaml::load($schema);
      }
 
      $models[] = sfYaml::load(sfConfig::get('sf_config_dir') . '/doctrine/schema.yml');
 
      return $models;
  }
}
 

Than just run: ./symfony doctrine:generate-all-modules %APPLICATION_NAME%

by Thomas Ohms on 2011-01-09, tagged doctrine  generate  module  task 

Tha django-multilingual style for I18n forms (ability to remove selected translations)

This snippet was created under the inspiration of using the django-multilingual package. Also an additional code is used to solve the problem of saving the many-to-many relations in embedded forms (http://trac.symfony-project.org/ticket/5867#comment:21).

The idea is that in all embedded I18n forms appears checkbox "delete" for each translation. When creating a new object this checkbox is checked for all translation form. To use the translation it's necessary to clear the checkbox in this translation form. When you save a form such I18n embedded form is also being validated and saved, and the forms with checkboxes checked are not validated and not saved. Also the objects of I18n embedded forms with checked checkbox are removed.

Probably it's not ideal solution but it works good for me. If you have any idea how to improve it you're always welcome.

The changes in code are only in one file /lib/forms/doctrine/BaseFormDoctrine.class.php:

<?php
 
/**
 * Project form base class.
 *
 * @package    symfony
 * @subpackage form
 * @author     Svyatoslav Mankovski <slawka@slawka.net>
 * @version    SVN: $Id: sfDoctrineFormBaseTemplate.php 23810 2009-11-12 11:07:44Z Kris.Wallsmith $
 */
abstract class BaseFormDoctrine extends sfFormDoctrine {
 
  protected static $I18nDeleteFieldName = '_i18n_delete';
 
  public function __construct($object = null, $options = array(), $CSRFSecret = null) {
 
    parent::__construct($object, $options, $CSRFSecret);
 
    $this->addI18nDeleteField();
 
    $this->resetFormFields();
  }
 
  public function addI18nDeleteField() {
 
    if ($this->getObject()->getTable()->getParentGenerator() instanceof Doctrine_I18n) {
      $this->widgetSchema[self::$I18nDeleteFieldName] = new sfWidgetFormInputCheckbox(array(
                'label' => sfContext::getInstance()->getI18N()->__('Remove'),
              ));
      $this->setDefault(self::$I18nDeleteFieldName, $this->isNew());
      $this->widgetSchema->moveField(self::$I18nDeleteFieldName, sfWidgetFormSchema::FIRST);
      $this->validatorSchema[self::$I18nDeleteFieldName] = new sfValidatorPass();
    }
 
    return $this;
  }
 
  public function bind(array $taintedValues = null, array $taintedFiles = null) {
 
    if ($this->getObject()->getTable()->hasGenerator('Doctrine_I18n')) {
      $forms = 0;
      $deletes = 0;
      $first_form = '';
      foreach ($taintedValues as $name => $field) {
        if (is_array($field)) {
          $first_form = $first_form == '' ? $name : $first_form;
          $forms++;
          if (isset($field[self::$I18nDeleteFieldName])) {
            $deletes++;
          }
        }
      }
      foreach ($taintedValues as $name => $field) {
        if (is_array($field) && isset($field[self::$I18nDeleteFieldName])) {
          if ($forms > $deletes || $forms == $deletes && $first_form != $name) {
            unset($this->validatorSchema[$name]);
            $this->validatorSchema[$name] = new sfValidatorPass();
          } else {
            unset($taintedValues[$name][self::$I18nDeleteFieldName]);
          }
        }
      }
    }
 
    parent::bind($taintedValues, $taintedFiles);
  }
 
  public function bindAndSave($taintedValues, $taintedFiles = null, $con = null) {
 
    if ($this->getObject()->getTable()->getParentGenerator() instanceof Doctrine_I18n) {
      if (isset($taintedValues[self::$I18nDeleteFieldName])) {
        unset($taintedValues[self::$I18nDeleteFieldName]);
 
        $object = clone $this->getObject();
 
        $object->delete($con);
 
        return true;
      }
    }
 
    return parent::bindAndSave($taintedValues, $taintedFiles, $con);
  }
 
  public function saveEmbeddedForms($con = null, $forms = null, $taintedValues = null) {
    if (null === $con) {
      $con = $this->getConnection();
    }
 
    if (null === $forms) {
      $forms = $this->embeddedForms;
    }
 
    if (null === $taintedValues) {
      $taintedValues = $this->taintedValues;
    }
 
    foreach ($forms as $name => $form) {
      $values = isset($taintedValues[$name]) ? $taintedValues[$name] : array();
      if ($form instanceof sfFormObject) {
        unset($form[self::$CSRFFieldName]);
 
        $form->bindAndSave($values, $this->taintedFiles, $con);
        $form->saveEmbeddedForms($con, null, $values);
      } else {
        $this->saveEmbeddedForms($con, $form->getEmbeddedForms(), $values);
      }
    }
  }
 
}
 
by Svyatoslav Mankovski on 2010-12-10, tagged form  i18n 

Get the actual version of Doctrine Migration

$migration_path = sfConfig::get('sf_lib_dir').DIRECTORY_SEPARATOR.'migration'.DIRECTORY_SEPARATOR.'doctrine';
    $doctrine_migration = new Doctrine_Migration($migration_path);
 
    $this->migration_current_version = $doctrine_migration->getCurrentVersion();
    $this->migration_latest_version  = $doctrine_migration->getLatestVersion();
 

Allow you to check if the database actually need to be migrated.

by Damien ALEXANDRE on 2010-11-19, tagged doctrine  migration  migrationversion 

Render partials in a Task

I needed to send emails from a task using partials. The technique for SF 1.3/4 is explained in the docs, but how to get links to render properly is not. I found some good info on the web that was a bit hidden. Here is what it lead me to come up with.

To be able to use partials you need to set the context correctly:

sfContext::createInstance($this->configuration);
 

Since tasks don't have a url as their base for the request, helpers like url_for and link_to won't work correctly and output things like "/symfony/symfony/mymodule/myaction".

to remedy this I created a helper:

class batchHelpers
{
  public static function url_for($str)
  {
    $url = 'http://'.
            sfConfig::get('app_server_url','www.kaizengine.com') .
            sfContext::getInstance()->getController()->genUrl($str);
 
    return $url;
  }
}
 

the define for the "server_url" is in app.yml

Then in the partial I used this code to call get a correctly generated url:

batchHelpers::url_for('/home', true), array(
          'style'=>"text-decoration:none;margin:0;")
          );
 

I also render the same partial (with the same url_for replacement) inside the web interface and since the urls are absolute, they work correctly.

by Alex Knol on 2010-10-26, tagged email  partial  task  urlfor 
(2 comments)

Default trailing slash for simple routes

Here is my solution for trailing slash in simple localized urls.

In routing.yml add:

homepage_lang:
  url:   /:sf_culture:sf_slash
  param: { module: main, action: index }
  requirements:
    sf_culture: (?:pl|en|de)
    sf_slash: '\/?'
  options:
    suffix: ''
    segment_separators: []
 

Both urls will work now: '/en' & '/en/'

But if you try to use link_to helper

<?php
echo link_to('Homepage', '@homepage_lang');
 

It will require sf_slash parameter.

Notice: sf_culture will be set automagically from current user culture unless you add to route: '@homepage_lang?sf_culture=de'.

There are (at least) 2 solutions for this. First is to add default sf_slash parameter in routing:

homepage_lang:
  url:   /:sf_culture:sf_slash
  param: { module: main, action: index, sf_slash: '' }
  requirements:
    sf_culture: (?:pl|en|de)
    sf_slash: '\/?'
  options:
    suffix: ''
    segment_separators: []
 

And everything will work just fine, link_to() helper will generate urls like: '/en', '/de' etc.

But what if we want trailing slash to be added every time we generate a link? It's more difficult and it's our 2nd solution for required parameter.

Routing rule should look like in the first example:

homepage_lang:
  url:   /:sf_culture:sf_slash
  param: { module: main, action: index }
  requirements:
    sf_culture: (?:pl|en|de)
    sf_slash: '\/?'
  options:
    suffix: ''
    segment_separators: []
 

But we need set sf_flash somehow. To do so, I used filters (was suggested in symfony book 1.2, chapter 9).

Create slashFilter in applications lib directory (app/lib/slashFilter.class.php):

<?php
 
class slashFilter extends sfFilter
{
   public function execute($filterChain)
   {
      $this->getContext()->getRouting()->setDefaultParameter('sf_slash', '/');
      $filterChain->execute();
   }
}
 

And add it to filters.yml:

# You can find more information about this file on the symfony website:
# http://www.symfony-project.org/reference/1_4/en/12-Filters
 
rendering: ~
security:  ~
 
# insert your own filters here
slashFilter:
  class: slashFilter
 
cache:     ~
execution: ~
 

Eventually clear your cache and trailing slash will be added to @homepage_lang route.

But there can be another problem and your slash / will be added as '%2F'. You can play with .htaccess or server configuration but I just created my own link_to() or url_for() helpers which replaces '%2F' with '/' like:

<?php
$url = str_replace('%2F', '/', $url);
 

Any improvements are appreciated ;)

by rozwell on 2010-10-03, tagged routing  slash  trailing