Snippets

Create an account or login to be able to add, comment and rate snippets.

Navigation

Popular Tags

action admin ajax batch cache cli criteria css culture custom database debug doctrine filter form forms generator helper i18n javascript model mysql navigation object pager pagination pake propel query routing session sql template test user validation validator view

Latest comments latest comments

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 latest snippets

JQuery Code to help handle attaching an embedded video/audio list to a post

<textarea id="comment" style="height:35px;max-height:100px; line-height:12px; font-size:12px;" class="span-24 last"></textarea>
 
    <div id="b" class="span-24 last">
        <button id="vid">Vid</button> <button id="pic">Pic</button> 
    </div>
 
 
 
 
[javascript]
<script type="text/javascript"> 
$(document).ready(function() {
    var vidIndex = 0;
    var vidList = new Array();
    $('#vid').click(function() {
        $('#vidContainer').slideDown();
    });
    $('#closeVid').click(function() {
        $('#vidContainer').slideUp();
    });
    $('#addVid').click(function() {
        $('#vidList').append('<div id="vid'+vidIndex+'"><span class="video">' + $('#vidUrl').val() + '</span> <button title="'+vidIndex+'" class="removeVid">Remove</button></div>');
        ++vidIndex;
 
        var chosen = '';
        $('.video').each(function () {
            chosen += $(this).text() + ',';
        });
        $('#params').html(chosen);
    });
    $('.removeVid').live("click", function(){
        var index = $(this).attr('title');
        $('#vid'+index).remove();
        --vidIndex;
 
        var chosen = '';
        $('.video').each(function () {
            chosen += $(this).text() + ',';
        });
        $('#params').html(chosen);
    });
</script>
 
by Willie T on 2010-02-05, tagged jquery 

Dump fixtures using Criteria

In case you have a populated database and you need to generate a fixtures for it, you can use symfony propel:data-dump tasks or manually call methods of sfPropelData.

But If your database contains too much data to be fetched, this methods will not work for you.

For such cases I have extended sfPropelData to use Propel Criteria objects to filter dumped data. For example, you can set limit 30 records for every model to prevent dumping all records from database.

Usage:

You can create special tasks to generate fixtures and use there class listed below

$data = new fxPropelData();
 
// Defining criterias
$c1 = new Criteria();
$c1->setLimit(30);
$c2 = clone($c1);
$c2->add('IS_DELETED',0);
 
// array with models and criterias for them
    $models = array(
      'Fruit'=> $c1,
      'Vegetable' => $c2,
    );
 
$data->dumpDataWithCriteria(sfConfig::get('sf_data_dir').'/fixtures/food.yml',$models, $options['connection']);
 

class fxPropelData

class fxPropelData extends sfPropelData {
 
  public function dumpDataWithCriteria($directoryOrFile, $models = array(), $connectionName = null) {
 
    $dumpData = $this->getDataWithCriteria($models, $connectionName);
 
    // save to file(s)
    if (!is_dir($directoryOrFile)) {
      file_put_contents($directoryOrFile, sfYaml::dump($dumpData, 3));
    }
    else {
      $i = 0;
      foreach ($models as $tableName => $criteria) {
        if (!isset($dumpData[$tableName])) {
          continue;
        }
 
        file_put_contents(sprintf("%s/%03d-%s.yml", $directoryOrFile, ++$i, $tableName), sfYaml::dump(array($tableName => $dumpData[$tableName]), 3));
      }
    }
  }
 
  protected function getDataWithCriteria($models, $connectionName = 'propel') {
    $this->loadMapBuilders();
    $this->con = Propel::getConnection($connectionName);
    $this->dbMap = Propel::getDatabaseMap($connectionName);
 
 
    $dumpData = array();
 
    foreach ($models as $model => $criteria) {
      $tableName = $model;
 
      $tableMap = $this->dbMap->getTable(constant(constant($tableName.'::PEER').'::TABLE_NAME'));
      $hasParent = false;
      $haveParents = false;
      $fixColumn = null;
      foreach ($tableMap->getColumns() as $column) {
        $col = strtolower($column->getName());
        if ($column->isForeignKey()) {
          $relatedTable = $this->dbMap->getTable($column->getRelatedTableName());
          if ($tableName === $relatedTable->getPhpName()) {
            if ($hasParent) {
              $haveParents = true;
            }
            else {
              $fixColumn = $column;
              $hasParent = true;
            }
          }
        }
      }
 
      if ($haveParents) {
        // unable to dump tables having multi-recursive references
        continue;
      }
 
      // get db info
      $resultsSets = array();
      if ($hasParent) {
        $resultsSets[] = $this->fixOrderingOfForeignKeyDataInSameTableWithCriteria($resultsSets, $criteria, $tableName, $fixColumn);
      }
      else {
        $stmt = call_user_func(array($tableName.'Peer', 'doSelectStmt'),$criteria,$this->con);
        $resultsSets[] = array_merge($stmt->fetchAll(PDO::FETCH_ASSOC),$resultsSets);
        $stmt->closeCursor();
        unset($stmt);
      }
 
 
      foreach ($resultsSets as $rows) {
        if(count($rows) > 0) {
          if (!isset($dumpData[$tableName])) $dumpData[$tableName] = array();
 
          foreach ($rows as $row) {
            $pk = $tableName;
            $values = array();
            $primaryKeys = array();
            $foreignKeys = array();
 
            foreach ($tableMap->getColumns() as $column) {
              $col = $column->getName();
              $isPrimaryKey = $column->isPrimaryKey();
 
              if (is_null($row[$col])) {
                continue;
              }
 
              if ($isPrimaryKey) {
                $value = $row[$col];
                $pk .= '_'.$value;
                $primaryKeys[$col] = $value;
              }
 
              if ($column->isForeignKey()) {
                $relatedTable = $this->dbMap->getTable($column->getRelatedTableName());
                if ($isPrimaryKey) {
                  $foreignKeys[$col] = $row[$col];
                  $primaryKeys[$col] = $relatedTable->getPhpName().'_'.$row[$col];
                }
                else {
                  $values[$col] = $relatedTable->getPhpName().'_'.$row[$col];
 
                  $values[$col] = strlen($row[$col]) ? $relatedTable->getPhpName().'_'.$row[$col] : '';
                }
              }
              elseif (!$isPrimaryKey || ($isPrimaryKey && !$tableMap->isUseIdGenerator())) {
                // We did not want auto incremented primary keys
                $values[$col] = $row[$col];
              }
            }
 
            if (count($primaryKeys) > 1 || (count($primaryKeys) > 0 && count($foreignKeys) > 0)) {
              $values = array_merge($primaryKeys, $values);
            }
 
            $dumpData[$tableName][$pk] = $values;
          }
        }
      }
    }
 
    return $dumpData;
  }
 
  protected function fixOrderingOfForeignKeyDataInSameTableWithCriteria($resultsSets, $criteria, $tableName, $column, $in = null)
  {
    is_null($in) ? $criteria->add($column->getName(),NULL) : $criteria->add($column->getName(),$in,Criteria::IN);
 
    $stmt = call_user_func(array($tableName.'Peer', 'doSelectStmt'),$criteria,$this->con);
 
    $in = array();
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC))
    {
      $in[] = "'".$row[strtolower($column->getRelatedColumnName())]."'";
      $resultsSets[] = $row;
    }
 
    if ($in = implode(', ', $in))
    {
      $resultsSets = $this->fixOrderingOfForeignKeyDataInSameTableWithCriteria($resultsSets, $tableName, $column, $in);
    }
 
    return $resultsSets;
  }
 
 
}
 
by Davert on 2009-12-11, tagged creiteria  fixtures  functional  propel  test  unit 

How to generate routes (links) from the model or from a task

The symfony routing system is tied to the sfContext resource via the factory handler.

It might an issue if you need to build routes in the cli mode, where sfContext has no instance. Sample: one of your cron jobs sends emails that contain links.

The best way I've found is to provide an access to the routing system linked to the ProjectConfiguration class. Add a static protected "$routing" variable and a static public method "getRouting()" :

class ProjectConfiguration extends sfProjectConfiguration
{
  static protected
    $routing = null # Symfony routing system
    ;
 
 
  /**
   * Returns the routing resource for the active application
   */
  static public function getRouting()
  {
    if (null !== self::$routing)
    {
      return self::$routing;
    }
 
    // If sfContext has an instance, returns the loaded routing resource
    if (sfContext::hasInstance() && sfContext::getInstance()->getRouting())
    {
      self::$routing = sfContext::getInstance()->getRouting();
    }
    else
    {
      // Initialization
      if (!self::hasActive())
      {
        throw new sfException('No sfApplicationConfiguration loaded');
      }
      $appConfig = self::getActive();
      $config = sfFactoryConfigHandler::getConfiguration($appConfig->getConfigPaths('config/factories.yml'));
      $params = array_merge(
        $config['routing']['param'],
        array(
          'load_configuration' => false,
          'logging'            => false,
          'context'            => array(
            'host'      => sfConfig::get('app_host',   'localhost'),
            'prefix'    => sfConfig::get('app_prefix', sfConfig::get('sf_no_script_name') ? '' : '/'.$appConfig->getApplication().'_'.$appConfig->getEnvironment().'.php'),
            'is_secure' => sfConfig::get('app_host',   false),
          ),
        )
      );
      $handler = new sfRoutingConfigHandler();
      $routes = $handler->evaluate($appConfig->getConfigPaths('config/routing.yml'));
      $routeClass = $config['routing']['class'];
      self::$routing = new $routeClass($appConfig->getEventDispatcher(), null, $params);
      self::$routing->setRoutes($routes);
    }
 
    return self::$routing;
  }
}
 

In the method, the 'context' parameter needs further explanations.

In a classic context, symfony is loaded after a web request. Sample: https://www.example.com/frontend_dev.php/products

This request provides data that are reused when sf needs to generate a new dynamic url:

In the cli mode, as Symfony is not loaded by a request, you have to provide these parameters (prefix can be guessed).

In app.yml, add:

all:
  host: www.example.com
  is_secure: false
 

Now, let's see how to generate the link. If your routing.yml file looks like it:

product_show:
  url:     /products/:category_slug/:slug
  class:   sfDoctrineRoute
  options: { model: Product, type: object }
  # ...
 

Then building the link is as simple as:

class Product extends BaseProduct
{
  /**
   * Returns the public url
   */
  public function getUrl()
  {
    return ProjectConfiguration::getRouting()->generate('product', $this, true);
  }
 
 
  /* ... */
}
 

Regards.

by Éric Rogé on 2009-12-11, tagged cli  routing 
(1 comment)

How to send Emails from the model or from a task

For sf1.3/1.4 only.

It seems that the symfony mailer system is tied to the sfContext resource via the factory handler (correct me if I'm wrong).

It might be a problem when you want to send an email from one of your model methods because sfContext has no instance in cli mode.

It can also be an issue if you want to build a task that send emails. Sample: a nightly cron job that alert users by email when their free trial period is over.

The best way I've found is to provide an access to the mailer system linked to the ProjectConfiguration class.

Add a static protected "$mailer" variable and a static public method "getMailer()" :

class ProjectConfiguration extends sfProjectConfiguration
{
  static protected
    $mailer  = null # Symfony mailer system
    ;
 
  /**
   * Returns the project mailer
   */
  static public function getMailer()
  {
    if (null !== self::$mailer)
    {
      return self::$mailer;
    }
 
    // If sfContext has instance, returns the classic mailer resource
    if (sfContext::hasInstance() && sfContext::getInstance()->getMailer())
    {
      self::$mailer = sfContext::getInstance()->getMailer();
    }
    else
    {
      // Else, initialization
      if (!self::hasActive())
      {
        throw new sfException('No sfApplicationConfiguration loaded');
      }
      require_once sfConfig::get('sf_symfony_lib_dir').'/vendor/swiftmailer/classes/Swift.php';
      Swift::registerAutoload();
      sfMailer::initialize();
      $applicationConfiguration = self::getActive();
 
      $config = sfFactoryConfigHandler::getConfiguration($applicationConfiguration->getConfigPaths('config/factories.yml'));
 
      self::$mailer = new $config['mailer']['class']($applicationConfiguration->getEventDispatcher(), $config['mailer']['param']);
    }
 
    return self::$mailer;
  }
}
 

Now if you want to send an email from a task or from a model class:

ProjectConfiguration::getMailer()->composeAndSend(/* your email params*/);
 

In your unit tests, if you want to test which emails have been sent:

$sentEmails = ProjectConfiguration::getMailer()->getLogger()->getMessages();
 

Regards.

by Éric Rogé on 2009-12-11, tagged mailer  swift 
(2 comments)

Fetch a Random Record with Doctrine

To fetch a random user:

$userCount = Doctrine::getTable('User')->count();
$user = Doctrine::getTable('User')
  ->createQuery()
  ->limit(1)
  ->offset(rand(0, $userCount - 1))
  ->fetchOne();
 
by ericfreese on 2009-11-16, tagged doctrine  random  record 
(7 comments)

Adding stemming to Doctrine Searchable

Stemming reduces a word to its common stem, so if you search on Google for "Knit", you'll also be searching for "Knits" and "Knitting". Sadly, Doctrine's Searchable behaviour doesn't support stemming out of the box, but it's pretty easy to add.

I've chosen to use the PECL 'stem' extension in this implementation because it contains the excellent Porter2 stemmer. You may be able to find a free pure PHP implementation of the algorithm somewhere, or decide that the original Porter stemmer is good enough. I couldn't.

You'll need to compile and install the PECL stem extension, and make sure it's loaded.

The stemming analyzer class

Create lib/MyAnalyzer.class.php:

    class MyAnalyzer extends Doctrine_Search_Analyzer_Standard
    {
 
      public function analyze($text)
      {
 
        // First run the standard analyzer. This will do a lot of tidying on 
        // the text and remove stopwords, so we only need to do the stemming
        $text = parent::analyze($text);
 
        foreach ($text as &$keyword) {
          $keyword = stem_english($keyword);
        }
 
        return $text;
      }
    }
 

Making your project use it

First, create a doctrine listener, lib/MyConnectionListener.class.php

    class MyConnectionListener extends Doctrine_EventListener
    {
 
      public function postConnect(Doctrine_Event $e)
      {
          $entity_table = Doctrine_Core::getTable('Entity');
          $entity_table->getTemplate('Doctrine_Template_Searchable')
                       ->getPlugin()
                       ->setOption('analyzer', new MyAnalyzer);
        }
      }
    }
 

Then edit your ProjectConfiguration class:

    public function configureDoctrineConnection(Doctrine_Connection $conn)
    {
      $conn->addListener(new MyConnectionListener)
    }
 

Finally, in your frontend code, make sure you stem your search terms before running a query.

    $terms = 'my search string';
 
    $stemmer = new MyAnalyzer();
    $terms = join($stemmer->analyze($terms), ' ');
 
    $results = Doctrine::getTable('Entity')->search($terms);
 

A couple of notes:

by Matt Robinson on 2009-11-14, tagged doctrine  search  searchable  stemming 

Master & Slave connections in Doctrine 1.2 and Symfony 1.3

Doctrine's built-in support for multiple connections follows a baffling use-case that allows different Model objects to be saved on different connections. A more common usage of multiple connections is to send read commands to a slave database and write commands to a master. Here's how to do it.

  1. Declare your connections in databases.yml as usual, name one of them master and one of them slave.

  2. Override Doctrine's default Query and Record objects. Edit config/ProjectConfiguration.class.php and add the following method:

    public function configureDoctrine(Doctrine_Manager $manager)
    {
        // Configure custom query and custom record classes
        $manager->setAttribute(Doctrine::ATTR_QUERY_CLASS, 'MyQuery');
        $options = array('baseClassName' => 'MyRecord');
        sfConfig::set('doctrine_model_builder_options', $options);  
    }
     
  3. Create your MyQuery class in lib/MyQuery

    class MyQuery extends Doctrine_Query
    {
      public function preQuery()
      {
          // If this is a select query then set connection to the slave
          if (Doctrine_Query::SELECT == $this->getType()) {
              $this->_conn = Doctrine_Manager::getInstance()->getConnection('slave');
          // All other queries are writes so they need to go to the master
          } else {
              $this->_conn = Doctrine_Manager::getInstance()->getConnection('master');
          }
      }
    }
     
  4. Create your MyRecord class in lib/MyRecord.class.php

    abstract class MyRecord extends sfDoctrineRecord
    {
        public function save(Doctrine_Connection $conn = null)
        {
          $conn = Doctrine_Manager::getInstance()->getConnection('master');
          parent::save($conn);
        }
    }
     
  5. If you're using a Doctrine behaviour that creates new tables, like the Versionable behaviour, tell it to use MyRecord too.

    Entity:
      ActAs:
        Versionable:
          builderOptions:
            baseClassName: MyRecord
     
  6. Finally, rebuild your model

    ./symfony doctrine:build --all-classes
     

If you have multiple slaves, modify the ProjectConfiguration to pick one on startup then change the method on your Query object to retrieve it.

by Matt Robinson on 2009-11-13, tagged database  doctrine 

Get module and action from an absolute url

I happened to come across this problem - parse referer to get its module and action. I thought there was an official way to do this in Symfony. But it turns out not(At least 1.0, maybe I didn't dig deep enough, then please inform me). I don't like to parse it by myself either, so after digging into symfony's source code. I come up with below solution:

function getModuleActionFrom($absurl) {
        $_uri = $_SERVER['REQUEST_URI'];
        if (isset($_SERVER['PATH_INFO'])) $_path = $_SERVER['PATH_INFO'];
        $_SERVER['REQUEST_URI'] = parse_url($absurl, PHP_URL_PATH);
        if (isset($_SERVER['PATH_INFO'])) $_SERVER['PATH_INFO'] = null;
        $tempReq = new sfWebRequest();
        $tempReq->initialize(sfContext::getInstance());
        $result  = array(
            'module' => $tempReq->getParameter('module'),
            'action' => $tempReq->getParameter('action')
        );
 
        $_SERVER['REQUEST_URI'] = $_uri;
        if (isset($_SERVER['PATH_INFO'])) $_SERVER['PATH_INFO'] = $_path;
        // I didn't notice sfRouting sets currentRouteName when it parses url, so ...
        $tempReq->initialize(sfContext::getInstance());
 
        return $result;
}
 

This function doesn't check whether the url belongs to the current application, as sfWebRequest doesn't do that.

by Kail Zhang on 2009-11-13, tagged url 

Doctrine custom grouping

This is how to create a complex grouping using doctrine query.

sample sql statement

    select ... from ... where x = 1 and (a in(1,2,3) or a is not null)
 

Extend Doctrine_Query

    class myDQ extends Doctrine_Query
    {
      public static function create($conn = null) {
        return new myDQ($conn);
      }
 
      public function toGroup()
      {
        $where = $this->_dqlParts['where'];
        if (count($where) > 0) {
          array_splice($where, count($where) - 1, 0, '(');
          $this->_dqlParts['where'] = $where;
        }
 
        return $this;
 
      }
 
      public function endGroup()
      {
        $where = $this->_dqlParts['where'];
        if (count($where) > 0) 
        {
          $where[] = ')';
          $this->_dqlParts['where'] = $where;
        }  
 
        return $this;
      }
 
    }
 

Usage

    $q = myDQ::create()
      ->from('Some s')
      ->where('s.x = ?', 1)
      ->andWhereIn('s.a', array(1,2,3))
      ->toGroup()
      ->orWhere('s.a is not null')
      ->endGroup();
 
by ken marfilla on 2009-11-11, tagged doctrine  grouping  parenthesis  query 

Tired to type "php symfony" in your terminal ?

Yeah, so am I.

Here's how to create a "sf" command in your terminal that will do exactly the same job than when you use the "php symfony xxx" command.

There's many ways to do it, here's mine, it should works on all unix systems.

In the "/usr/bin" directory, create an "sf" file with this content :

#!/usr/bin/env php
<?php
 
// This file provides the cli "sf" shortcut instead of "php symfony" in syfony projects
 
if (file_exists('config/ProjectConfiguration.class.php'))
{
  require_once('config/ProjectConfiguration.class.php');
  $dir = sfCoreAutoload::getInstance()->getBaseDir();
}
else
{
  die('You must be in a symfony project directory.'."\n");
}
include($dir.'/command/cli.php');
 

Be carefull, don't forget the first line !

Now, open the rights on execution for the file: "sudo chmod +x /usr/bin/sf"

That's it, you're done. Go in a symfony project and try your brand new "sf" command in your terminal.

May the laziness be with you !

[UPDATE]

Pedro Casado has reported in comments a cleaner method that uses the native bash configuration file :

It worked pretty great for me.

[/UPDATE]

by Éric Rogé on 2009-10-27, tagged cli  lazy  terminal 
(5 comments)