Code snippets for symfony 1.x

Navigation

Refine Tags

Snippets tagged "doctrine"

Doctrine_Hydrator_XmlDriver

Remember to register your hydrator first.

public function executeIndex(sfWebRequest $request)
  {
    $manager = Doctrine_Manager::getInstance();
    $manager->registerHydrator('xml', 'Doctrine_Hydrator_XmlDriver');
}
 

See http://www.doctrine-project.org/documentation/manual/1_2/en/data-hydrators

<?php
 
class Doctrine_Hydrator_XmlDriver extends Doctrine_Hydrator_ArrayDriver
{
    public function hydrateResultSet($stmt)
    {
        $array = parent::hydrateResultSet($stmt);
        return $this->arrayToXml($array);
    }
 
    /**
     * <pre>Converts an Doctrine array graph of the form:
     * .array
     * .  0 =>
     * .    array
     * .      'id' => '1'
     * .      'PersonaDomicilio' =>
     * .        array
     * .          0 =>
     * .            array
     * .              'id' => '1'
     * To a XML representation.
     * @param array $array
     * @return DOMDocument <pre> The XML with following structure:
     * . &lt;result&gt;
     * .   &lt;rootTable_Collection&gt;
     * .     &lt;rootTable&gt;
     * .       &lt;id&gt;&lt;/id&gt;
     * .       &lt;field1&gt;&lt;/field1&gt;
     * .       &lt;relatedTable_Collection&gt;
     * .         &lt;relatedTable&gt;
     * .         &lt;/relatedTable&gt;
     * .         &lt;relatedTable&gt;
     * .        &lt;/relatedTable&gt;
     * .         ::
     * .       &lt;/relatedTable_Collection&gt;
     * .     &lt;/rootTable&gt;
     * .     &lt;rootTable&gt;
     * .       ::
     * .     &lt;/rootTable&gt;
     * .     ::
     * .   &lt;/rootTable_Collection&gt;
     * . &lt;result&gt;
     * </pre>
     */
    public function arrayToXml(array $array) {
        $result = new DOMDocument();
        $rootNode = $result->createElement('result');
        $result->appendChild($rootNode);
        $iterator = new RecursiveIteratorIterator(
            new RecursiveArrayIterator($array),
            RecursiveIteratorIterator::SELF_FIRST
        );
        $prevLvl = 0;
        $component[$prevLvl] = $this->_queryComponents[$this->_rootAlias]['table']
            ->getComponentName();
        $obj = $result->createElement($component[$prevLvl] . '_Collection');
        $rootNode->appendChild($obj);
        foreach ($iterator as $k => $val) {
            $depth = $iterator->getDepth();
            if ($depth < $prevLvl) {
                for ($i = 0; $i < ($prevLvl - $depth); $i++) {
                    $obj = $obj->parentNode;
                }
            }
            if (! is_array($val)) {
                $son = $result->createElement($k, $val);
                $obj->appendChild($son);
            } else {
                if (is_numeric($k)) {
                    $son = $result->createElement($component[$depth]);
                } else {
                    $component[$depth + 1] = $k;
                    $son = $result->createElement($k . '_Collection');
                }
                $obj->appendChild($son);
                !empty($val) && $obj = $son;
            }
            $prevLvl = $depth;
        }
        return $result;
    }
}
 
by juan Manuel Fernandez on 2011-04-25, tagged doctrine  hydrator  orm 

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 

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 

symfony 1.x form framework: how to add extra fields into forms?

Assumption: there is a UserMessage class and the related form UserMessageForm. Both were generated by Doctrine based on a schema.yml declaration. Problem: one wishes to you add input fields into this form.

Approach: one can add any pairs of widgets/validators which have no direct relationship to the presented model. Doing so, one need to care about two main aspects:

  1. configuring the form with the extra field
  2. processing of the submitted value (if any) at the proper moment without disturbing the big rest of form functionality.

For step 1, configuring the form: add the widget and the validator to the form.

In .../UserMessageForm.class.php:
 
  public function configure()
  {
    $this->widgetSchema['comment_for_log'] = new sfWidgetFormInputText();
    $this->validatorSchema['comment_for_log'] = new sfValidatorString();
  }
 

For step 2 processing user input after submission: process the value and act accordingly. Typically this happens only after validation did succeed (i.e. not validation errors did occur).

For this purpose, a dedicated method for each field gets called (in case that the method exists). The following example checks whether a non-empty text was entered by the user. If so, it does store a log entry. The function in this example returns a boolean 'false' value to tell symfony not to process this value further.

In .../UserMessageForm.class.php:
 
  public function updateCommentForLogColumn($value)  // constructed method name with camelized field name
  {
    if (! empty($value)) {     // e.g. only if the user entered a comment (btw this is the cleaned value)
      // do whatever you need to do based on the contents of this field.
      // e.g. we create and save a log entry as follows:
      $logEntry = new AppLogEntry();    // assume we have this sort of logging feature
      $logEntry->setComment($value);    // the comment entered by the user
      $logEntry->save();
    }
    return false;  // tell symfony that we have cared about this value, so no further processing will occur
  }
}
 

Basically that's it.

An alternative to the dedicated updateYourSpecialFieldColumn() method could be to override the doUpdate($values) method in the form, allowing to do more general things because there is access to all submitted values (that's basically the only difference between the two approaches, though). I personally prefer the dedicated method whenever possible, as it doesn't disturb any other logic that may (at the same time, or sometimes later) implemented in the same form.

Another good thing is that since all this code is all packed in a Doctrine transaction, so either both the form object (UserMessage) will be saved as well as the log (LogEntry) be generated, or none of the two objects be altered/created at all.

(15.9.2010/SCH)

by Raphael Schumacher on 2010-09-15, tagged doctrine  fields  form  symfony14  validator  widget 

script for to generate (all) modules

This Bash script transform: 'BaseModel.class.php' => 'BaseModel' => 'Model' then call doctrine:generate-admin frontend 'Model', for all files from lib/model/doctrine/base.

#!/bin/bash
for file_class in `ls lib/model/doctrine/base`  do
    base_model=${file_class/%.class.php/}      
    model=${base_model/#Base/}                 
    ./symfony doctrine:generate-admin --env="prod" frontend $model
done
 
by Vlad Bazon on 2010-08-01, tagged bash  doctrine  generateadmin  model  modul  symfony 

Automatically updated 'updated_by' and 'created_by' doctrine columns

Our sf1.4 project required recording the user_id of the person who updated or created a record as well as the timestamps and was using the sfDoctrineGuard plugin.

There are several ways of achieving this but the additional requirement of having constraints on these fields on a db level, being able to load fixtures and have the whole code properly unit testable made it a bit more difficult. why-sfcontextgetinstance-is-bad

After experimenting with other methods like custom save method and the following snippet snippet 281 I came to the following code:

Using actAs Timestampable was an option for the timestamps, but I have chosen to add a new Doctrine template and listener that has the timestamp incorporated so that every update/save only triggers one listener instead of two.

config/doctrine/schema.yml:

Address:
  columns:
    id:
      type: integer(4)
      primary: true
      autoincrement: true
    address_1:
      type: varchar(50)
    address_2:
      type: varchar(50)
    postal_code:
      type: varchar(6)
  relations:
    userCreate:
      class: sfGuardUser
      local: creater_id
      foreign: id
      foreignType: one
      onDelete: RESTRICT
      onUpdate: CASCADE
    userUpdate:
      class: sfGuardUser
      local: updater_id
      foreign: id
      foreignType: one
      onDelete: RESTRICT
      onUpdate: CASCADE
  actAs:
    Auditable:
  options:
    type: INNODB
    collate: utf8_unicode_ci
    charset: utf8
 

The relations part of the schema is only needed if you want to maintain data integrity.

The names of the creater and updater fields are defaulted to 'creater_id' and 'updater_id' but can be changed in the schema as per normal.

The template looks as follows and copies the logic from the timestampable template:

lib/doctrine/Doctrine/Template/Auditable.php:

<?php
class Doctrine_Template_Auditable extends Doctrine_Template
{
    /**
     * Array of userid options
     *
     * @var string
     */
    protected $_options = array(
        'creater' =>  array(
            'name'          => 'creater_id',
            'alias'         => null,
            'type'          => 'int',
            'disabled'      => false,
            'expression'    => false,
            'options'       =>  array('notnull' => true)
        ),
        'updater' =>  array(
            'name'          => 'updater_id',
            'alias'         => null,
            'type'          => 'int',
            'disabled'      => false,
            'expression'    => false,
            'onInsert'      => true,
            'options'       =>  array('notnull' => true)
        ),
        'created' =>  array(
            'name'          =>  'created_at',
            'alias'         =>  null,
            'type'          =>  'timestamp',
            'format'        =>  'Y-m-d H:i:s',
            'disabled'      =>  false,
            'expression'    =>  false,
            'options'       =>  array('notnull' => true)
        ),
        'updated' =>  array(
            'name'          =>  'updated_at',
            'alias'         =>  null,
            'type'          =>  'timestamp',
            'format'        =>  'Y-m-d H:i:s',
            'disabled'      =>  false,
            'expression'    =>  false,
            'onInsert'      =>  true,
            'options'       =>  array('notnull' => true)
        )
    );
 
 
    /**
     * setTableDefinition
     *
     * @return void
     */
    public function setTableDefinition()
    {
        if(!$this->_options['creater']['disabled']) {
            $name = $this->_options['creater']['name'];
            if ($this->_options['creater']['alias']) {
                $name .= ' as ' . $this->_options['creater']['alias'];
            }
            $this->hasColumn(
                $name,
                $this->_options['creater']['type'],
                null,
                $this->_options['creater']['options']
            );
        }
        if(!$this->_options['updater']['disabled']) {
            $name = $this->_options['updater']['name'];
            if ($this->_options['updater']['alias']) {
                $name .= ' as ' . $this->_options['updater']['alias'];
            }
            $this->hasColumn(
                $name,
                $this->_options['updater']['type'],
                null,
                $this->_options['updater']['options']
            );
        }
        if ( ! $this->_options['created']['disabled']) {
            $name = $this->_options['created']['name'];
            if ($this->_options['created']['alias']) {
                $name .= ' as ' . $this->_options['created']['alias'];
            }
            $this->hasColumn(
                $name,
                $this->_options['created']['type'],
                null,
                $this->_options['created']['options']
            );
        }
        if ( ! $this->_options['updated']['disabled']) {
            $name = $this->_options['updated']['name'];
            if ($this->_options['updated']['alias']) {
                $name .= ' as ' . $this->_options['updated']['alias'];
            }
            $this->hasColumn(
                $name,
                $this->_options['updated']['type'],
                null,
                $this->_options['updated']['options']);
        }
 
        $this->addListener(new Doctrine_Template_Listener_Auditable($this->_options));
    }
}
 

The listener that makes sure the fields are set before saving looks like this:

lib/doctrine/Doctrine/Template/Listener/Auditable.php:

<?php
class Doctrine_Template_Listener_Auditable extends Doctrine_Record_Listener
{
    /**
     * Array of userid options
     *
     * @var string
     */
    protected $_options = array();
 
    /**
     * __construct
     *
     * @param string $options.
     * @return void
     */
    public function __construct(array $options)
    {
        $this->_options = $options;
    }
 
    /**
     * preInsert
     *
     * @param Doctrine_Event $event.
     * @return void
     */
    public function preInsert(Doctrine_Event $event)
    {
        if(!$this->_options['creater']['disabled']) {
            $createrName = $this->_options['creater']['name'];
            $event->getInvoker()->$createrName = $this->getUserId($event);
        }
 
        if(!$this->_options['updater']['disabled'] && $this->_options['updater']['onInsert']) {
            $updaterName = $this->_options['updater']['name'];
            $event->getInvoker()->$updaterName = $this->getUserId($event);
        }
 
        if ( ! $this->_options['created']['disabled']) {
            $createdName = $event->getInvoker()->getTable()->getFieldName($this->_options['created']['name']);
            $modified = $event->getInvoker()->getModified();
            if ( ! isset($modified[$createdName])) {
                $event->getInvoker()->$createdName = $this->getTimestamp('created', $event->getInvoker()->getTable()->getConnection());
            }
        }
 
        if ( ! $this->_options['updated']['disabled'] && $this->_options['updated']['onInsert']) {
            $updatedName = $event->getInvoker()->getTable()->getFieldName($this->_options['updated']['name']);
            $modified = $event->getInvoker()->getModified();
            if ( ! isset($modified[$updatedName])) {
                $event->getInvoker()->$updatedName = $this->getTimestamp('updated', $event->getInvoker()->getTable()->getConnection());
            }
        }
    }
 
    /**
     * preUpdate
     *
     * @param Doctrine_Event $event.
     * @return void
     */
    public function preUpdate(Doctrine_Event $event)
    {
        if( ! $this->_options['updater']['disabled']) {
            $updaterName = $this->_options['updater']['name'];
            $event->getInvoker()->$updaterName = $this->getUserId($event);
        }
 
        if ( ! $this->_options['updated']['disabled']) {
            $updatedName = $event->getInvoker()->getTable()->getFieldName($this->_options['updated']['name']);
            $modified = $event->getInvoker()->getModified();
            if ( ! isset($modified[$updatedName])) {
                $event->getInvoker()->$updatedName = $this->getTimestamp('updated', $event->getInvoker()->getTable()->getConnection());
            }
        }
    }
 
    /**
     * getUserId
     *
     * Gets the userid
     *
     * @param Doctrine_Event $event.
     * @return int
     */
    public function getUserId(Doctrine_Event $event)
    {
      if (!$event->getInvoker()->getSystemUser())
      {
        throw new LogicException('The user must be set before saving');
      }
      return $event->getInvoker()->getSystemUser();
    }
 
    /**
     * Set the updated field for dql update queries
     *
     * @param Doctrine_Event $event
     * @return void
     */
    public function preDqlUpdate(Doctrine_Event $event)
    {
        if ( ! $this->_options['updated']['disabled']) {
            $params = $event->getParams();
            $updatedName = $event->getInvoker()->getTable()->getFieldName($this->_options['updated']['name']);
            $field = $params['alias'] . '.' . $updatedName;
            $query = $event->getQuery();
 
            if ( ! $query->contains($field)) {
                $query->set($field, '?', $this->getTimestamp('updated', $event->getInvoker()->getTable()->getConnection()));
            }
        }
    }
 
    /**
     * Gets the timestamp in the correct format based on the way the behavior is configured
     *
     * @param string $type
     * @return void
     */
    public function getTimestamp($type, $conn = null)
    {
        $options = $this->_options[$type];
 
        if ($options['expression'] !== false && is_string($options['expression'])) {
            return new Doctrine_Expression($options['expression'], $conn);
        } else {
            if ($options['type'] == 'date') {
                return date($options['format'], time());
            } else if ($options['type'] == 'timestamp') {
                return date($options['format'], time());
            } else {
                return time();
            }
        }
    }
}
 

As you can see, the user_id is retrieved from the object that is saved via the getSystemUser() method.

To not have to duplicate this method on each of the model classes, I made my base classes extend a custom class. This can to be done by adding a configureDoctrine function to the ProjectConfiguration class

confid/ProjectConfiguration.class.php:

public function configureDoctrine(Doctrine_Manager $manager)
  {
    // use a custom base class that has the system user methods used for updating
    // the creater and updater.
    $options = array('baseClassName' => 'MyDoctrineRecord');
    sfConfig::set('doctrine_model_builder_options', $options);
 
    // user id that is used when no logged in user is present. This is required
    // to be able to load fixtures.
    sfConfig::set('default_updater_id',1);
  }
 

After making these changes and regenerating the sql and model, all your base classes will now extend MyDoctrineRecord which is automatically created if not present already.

The created class is empty and we still need to add the custom functions to it:

lib/model/doctrine/MyDoctrineRecord.class:

<?php
 
abstract class MyDoctrineRecord extends sfDoctrineRecord
{
  protected $systemUserId = null;
 
  public function preSave($event)
  {
    if (!$this->getSystemUser())
    {
      if(!sfContext::hasInstance() || get_called_class()=='sfGuardUser')
      {
        $userId = sfConfig::get('default_updater_id');
        $this->setSystemUser($userId);
      }
      else
      {
        $this->setSystemUser(sfContext::getInstance()->getUser()->getGuardUser()->getId());
      }
    }
  }
 
  public function setSystemUser($userId)
  {
    $this->systemUserId = $userId;
  }
 
  public function getSystemUser()
  {
    return $this->systemUserId;
  }
}
?>
 

The above code allows for insertion of a userId for testing perposes. It will use the default_updater_id that is set in the projectConfiguration. This default is required for the field restrictions to play nice. Without this, it would try and save null which is not accepted by our database because it is not a valid user.

When a user is logged in, the userId of that user will be used.

note: If you add actAs: Auditable to the sfDoctrineGuard schema you even can track who made changes to the permissions tables.

2010-08-04: Updated the MyDoctrineRecord.class code to see if it was called by sfGuardUser. Not sure what goes wrong but I am sure it has to do with the fact that you can't get the logged in user before you are completely logged in.

2011-06-14: Updated the MyDoctrineRecord.class and replaced the save() function with preSave() as the system user was not being set in certain instances.

by Marcel Berteler on 2010-04-16, tagged created  doctrine  sfdoctrineguard  template  updated  userid  username 
(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 

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 

doctrine:drop-db task replacement for restricted servers/hosts

Instead of dropping the database all tables in it are dropped.

<?php
 
/**
 * Replacement for drop-db where one has no rights to drop a db.
 *
 * @package    symfony
 * @subpackage task
 * @author     Maik Riechert <maik.riechert@animey.net>
 */
class sfDoctrineDropTablesTask extends sfDoctrineBaseTask
{
    /**
     * @see sfTask
     */
    protected function configure()
    {
        $this->addOptions(array(
        new sfCommandOption('application', null, sfCommandOption::PARAMETER_OPTIONAL, 'The application name', true),
        new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
        new sfCommandOption('no-confirmation', null, sfCommandOption::PARAMETER_NONE, 'Whether to force dropping the tables of the database')
        ));
 
        $this->aliases = array('doctrine-drop-tables');
        $this->namespace = 'doctrine';
        $this->name = 'drop-tables';
        $this->briefDescription = 'Drops tables of the database for current model';
 
        $this->detailedDescription = <<<EOF
The [doctrine:drop-tables|INFO] task drops the tables of the database:
 
  [./symfony doctrine:drop-tables|INFO]
 
The task read connection information in [config/doctrine/databases.yml|COMMENT]:
EOF;
    }
 
    protected function execute($arguments = array(), $options = array())
    {
        if (
        !$options['no-confirmation']
        &&
        !$this->askConfirmation(array('This command will remove all data in your database.', 'Are you sure you want to proceed? (y/N)'), null, false)
        )
        {
            $this->logSection('doctrine', 'task aborted');
 
            return 1;
        }
 
        $this->logSection('doctrine', 'dropping tables in database');
 
        $databaseManager = new sfDatabaseManager($this->configuration);
 
        // find out db name
        $dsn = Doctrine_Manager::connection()->getOption('dsn');
        $info = Doctrine_Manager::getInstance()->parsePdoDsn($dsn);
        $dbname = $info['dbname'];
 
        $pdo = Doctrine_Manager::connection()->getDbh();
 
        // disable constraint checking for mysql when innodb tables are used (TODO: more abstraction)
        $pdo->exec('SET foreign_key_checks = 0');
 
        // get all tables of db from information_schema db
        $sql = 'SELECT table_name FROM information_schema.tables WHERE information_schema.tables.table_schema = "'.$dbname.'"';
        $stmt = $pdo->query($sql);
 
        // drop all tables
        foreach ($stmt as $row) {
            $sql = 'DROP TABLE IF EXISTS `'.$dbname.'`.`'.$row[0].'`';
            $this->logSection('doctrine', 'dropping '.$dbname.'.'.$row[0]);
            $pdo->exec($sql);
        }
 
    }
 
}
 

Sorry for non-sf indentation / codestyle, but you will get that right..;)

by neo on 2009-10-04, tagged doctrine  dropdb  droptables 

auto save embedded form foreign key

public function saveEmbeddedForms($con = null, $forms = null)
  {
    if (is_null($con))
    {
      $con = $this->getConnection();
    }
 
    if (is_null($forms))
    {
      $forms = $this->embeddedForms;
    }
 
    foreach ($forms as $form)
    {
      if ($form instanceof sfFormDoctrine)
      {
        // The magic start here
        $field_name  = $this->getObject()->getTable()->getTableName().'_id';
        if($form->getObject()->contains($field_name)) {
          $method_name = 'set'.sfInflector::camelize($field_name);
          $form->getObject()->$method_name($this->getObject()->getId());
        }
        // Here it ends
        $form->getObject()->save($con);
        $form->saveEmbeddedForms($con);
      }
      else
      {
        $this->saveEmbeddedForms($con, $form->getEmbeddedForms());
      }
    }
 
by Marc Carlucci on 2009-09-01, tagged doctrine  embed  embedform 
(3 comments)

nonHydratingPager class (a pager for when you don't want to hydrate your Doctrine object)

This is just a version of the nonHydratingPager for Propel, adapted for Doctrine.

<?php
/* Designed to be compatible with sfDoctrinePager only accept raw sql queries instead
 * of a Doctrine_Query object and spit back a resultset which you most likely want to
 * fill an array with instead of the standard array of hydrated objects.
 *
 * This solution is based on Propel's by Noel Tarnoff, Oz Basarir, dev AT (NOSPAM) naturalcapitalDOTorg
 *
 * @author  David Morales, davidmoralesmojica AT (NOSPAM) gmailDOTcom
 *
 * Typical usage scenario:
 * 1) build 2 query strings with common WHERE clause, one for count one for selecting the rows
 * 2) pass the queries in with page and maxPerPage into the constructor ( no need to ->init() )
 * 3) iterate through your result set (array of values)
 * 4) sit back and watch the fun
 *
 * ex.
 *  $results = new nonHydratingPager($query_select, $query_count, $page, $max);
 *
 *  foreach( $results as $result )
 *  {
 *    ...
 *  }
 */
 
 
class nonHydratingPager extends sfPager
{
  private $resultSet = null;
  private $query = null;
  private $query_count = null;
 
  public function __construct($query, $query_count, $page = 1, $maxPerPage = 25)
  {
    $this->setPage($page);
    $this->setMaxPerPage($maxPerPage);
    $this->query = $query;
    $this->query_count = $query_count;
  }
 
  public function init()
  {
    $rs = Doctrine_Manager::getInstance()->getCurrentConnection()->fetchAssoc($this->query_count);
 
    $this->setNbResults($rs[0]['count']);
 
    $this->setLastPage(ceil($this->getNbResults() / $this->getMaxPerPage()));
 
    $startIndex = (($this->getPage()) - 1) * $this->getMaxPerPage();
 
 
    $this->query .= ' LIMIT ' . $this->getMaxPerPage() . ' OFFSET ' . $startIndex;
 
    $this->resultSet = Doctrine_Manager::getInstance()->getCurrentConnection()->fetchAssoc($this->query);
  }
 
  public function getResults() {
    return $this->resultSet;
  }
 
  protected function retrieveObject($offset) {}
}
 
by David Morales on 2009-04-02, tagged doctrine  nohydrate  nonhydrating  nonhydratingpager  pager  pagination  rawsql  sql 

doctrine_admin_double_list in Symfony 1.2

When working with the admin generator in Symfony 1.2, I found I could no longer use the doctrine_admin_double_list field type. After hours of searching, I finally figured out how to get this functionality back. It can no longer be done from the generator.yml file, instead you need to modify your table's form class, e.g. lib/form/doctrine/XXXForm.class.php:

class XXXForm extends BaseXXXForm
{
  public function configure()
  {
    $this->widgetSchema['field_name'] = new sfWidgetFormDoctrineChoice(array(
      'model'           => 'ModelName',
      'add_empty'       =>  false,
      'renderer_class'  => 'sfWidgetFormSelectDoubleList',
    ));
  }
}
 

This requires the sfFormExtraPlugin plugin to be installed.

I assume this will work equally well with Propel. See symfony Forms in Action for more info.

by Ryan Hayle on 2009-04-02, tagged admindoublelist  doctrine  doctrineadmindoublelist  manytomany 

How to trigger an event from a Model Class

[UPDATE]

I've found a cleaner way to deal with it :

sfProjectConfiguration::getActive()->getEventDispatcher()->notify(new sfEvent($this, 'foo.bar'))
 

The old version of this snippet is now useless, you don't need to read it.

[/UPDATE]

This snippet is not a tutorial on symfony events, but you can find some documentation in the book and or in this good tutorial.

The event dispatcher is a very powerfull tool, but you might meet the same issue than me if you want to use it from a Model Class.

Let's take the classic Author/Article project sample.

When an article is added, it might be interesting to trigger an "article.new" event. This event coud be listened by different actors.

Like for instance :

The most obvious place to trigger the event is the Article save() method. Here is the code for Doctrine (the logic is exactly the same for Propel):

Article.class.php

public function save(Doctrine_Connection $conn = null)
{
  if ($this->isNew())
  {
    // Let's trigger the event
    sfContext::getInstance()->getEventDispatcher()->notify(new sfEvent($this, 'article.new'));
  }
  return parent::save($conn);
}
 

Unfortunatly, this code won't work. If you try the "symfony doctrine:build-all-reload" command, here is the error that will be displayed :

Cli

sfException: The "default" context does not exist.
 

Why ? Because symfony tasks don't initialise the sfContext class. And for the moment (symfony version 1.2.4) I don't think there's a clean way to access to the dispatcher object from a Doctrine Class.

Here is my solution : let's build a (very) little singleton class that will provide the dispatcher ressource everywhere in the project, and even when sfContext is not avaible

yourProjectRoot/lib/Fedex.class.php

class Fedex
{
  static protected $instance;
  protected $dispatcher;
  /**
   * The singleton logic http://en.wikipedia.org/wiki/Singleton_pattern#PHP_5
   */
  static public function getInstance()
  {
    if (!self::$instance instanceof self)
    {
      self::$instance = new self;
    }
    return self::$instance;
  }
 
  public function setEventDispatcher(sfEventDispatcher $dispatcher)
  {
    $this->dispatcher = $dispatcher;
  }
 
  public function getEventDispatcher()
  {
    return $this->dispatcher;
  }
}
 

Of course, you first have to give to the Fedex class a link the delivery ressource. Let's do it with the very first class called by the script of the project :

yourProjectRoot/config/ProjectConfiguration.class.php

public function setup()
{
  // add after all your setup stuffs
 
  require_once(dirname(__FILE__).'/../lib/Fedex.class.php');
  Fedex::getInstance()->setEventDispatcher($this->dispatcher);
}
 

That's it, you're done. The Fedex class will deliver the dispatcher ressource everywhere in your project through Fedex::getInstance()->getEventDispatcher() And it works even when the sfContext class is not avaible

Article.class.php

public function save(Doctrine_Connection $conn = null)
{
  if ($this->isNew())
  {
    Fedex::getInstance()->getEventDispatcher()->notify(new sfEvent($this, 'article.new'));
  }
  return parent::save($conn);
}
 

The important thing is that this little class doesn't modify any part of the symfony event system. It just provide you an alternative way to access to it.

by Éric Rogé on 2009-02-23, tagged doctrine  event  orm 
(5 comments)

Add `created_by` and `updated_by` columns to Doctrine

I wanted to add created_by and updated_by columns to my Doctrine schema. But the existing templates doesn't support that, so i've rewritten the Timestampable template.

Now Doctrine can automatically update these fields to the specific username oder userid.

config/doctrine/schema.yml:

ExampleUser:
  tableName: example_user
  columns:
    id:
      primary: true
      autoincrement: true
      type: integer(4)
    name:
      type: string(255)
    password:
      type: string(255)
  actAs:
    Userid:
      created:
        name: created_by
        type: integer
      updated:
        name: updated_by
        type: string
 

If you are writing integer as type the template will call $sf_user->getId(), if you write string it will call $sf_user->getUsername().

So the above example will write the ID to created_by column and the username to updated_by column.

lib/doctrine/Doctrine/Template/Userid.php:

<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.org>.
 */
 
/**
 * Doctrine_Template_Userid
 *
 * Easily add created and updated by ids or usernames to your doctrine records
 *
 * @package     Doctrine
 * @subpackage  Template
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @link        www.phpdoctrine.org
 * @since       1.0
 * @version     $Revision$
 * @author      Thomas Boerger <tb@mosez.net>
 */
class Doctrine_Template_Userid extends Doctrine_Template
{
    /**
     * Array of userid options
     *
     * @var string
     */
    protected $_options = array(
        'created' =>  array(
            'name'          =>  'created_by',
            'type'          =>  'integer',
            'disabled'      => false,
            'expression'    => false,
            'options'       =>  array()
        ),
        'updated' =>  array(
            'name'          =>  'updated_by',
            'type'          =>  'integer',
            'disabled'      => false,
            'expression'    => false,
            'onInsert'      => true,
            'options'       =>  array()
        )
    );
 
    /**
     * __construct
     *
     * @param string $array.
     * @return void
     */
    public function __construct(array $options)
    {
        $this->_options = Doctrine_Lib::arrayDeepMerge($this->_options, $options);
    }
 
    /**
     * setTableDefinition
     *
     * @return void
     */
    public function setTableDefinition()
    {
        if(!$this->_options['created']['disabled']) {
            $this->hasColumn(
                $this->_options['created']['name'],
                $this->_options['created']['type'],
                null,
                $this->_options['created']['options']
            );
        }
        if(!$this->_options['updated']['disabled']) {
            $this->hasColumn(
                $this->_options['updated']['name'],
                $this->_options['updated']['type'],
                null,
                $this->_options['updated']['options']
            );
        }
        $this->addListener(new Doctrine_Template_Listener_Userid($this->_options));
    }
}
 

lib/doctrine/Doctrine/Template/Listener/Userid.php:

<?php
/*
 *  $Id$
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
 * <http://www.phpdoctrine.org>.
 */
 
/**
 * Doctrine_Template_Listener_Userid
 *
 * @package     Doctrine
 * @subpackage  Template
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
 * @link        www.phpdoctrine.org
 * @since       1.0
 * @version     $Revision$
 * @author      Thomas Boerger <tb@mosez.net>
 */
class Doctrine_Template_Listener_Userid extends Doctrine_Record_Listener
{
    /**
     * Array of userid options
     *
     * @var string
     */
    protected $_options = array();
 
    /**
     * __construct
     *
     * @param string $options.
     * @return void
     */
    public function __construct(array $options)
    {
        $this->_options = $options;
    }
 
    /**
     * preInsert
     *
     * @param object $Doctrine_Event.
     * @return void
     */
    public function preInsert(Doctrine_Event $event)
    {
        if(!$this->_options['created']['disabled']) {
            $createdName = $this->_options['created']['name'];
            $event->getInvoker()->$createdName = $this->getUserId('created');
        }
 
        if(!$this->_options['updated']['disabled'] && $this->_options['updated']['onInsert']) {
            $updatedName = $this->_options['updated']['name'];
            $event->getInvoker()->$updatedName = $this->getUserId('updated');
        }
    }
 
    /**
     * preUpdate
     *
     * @param object $Doctrine_Event.
     * @return void
     */
    public function preUpdate(Doctrine_Event $event)
    {
        if( ! $this->_options['updated']['disabled']) {
            $updatedName = $this->_options['updated']['name'];
            $event->getInvoker()->$updatedName = $this->getUserId('updated');
        }
    }
 
    /**
     * getUserId
     *
     * Gets the userid or username depending on field format
     *
     * @param string $type.
     * @return void
     */
    public function getUserId($type)
    {
        $options = $this->_options[$type];
 
        if ($options['expression'] !== false && is_string($options['expression'])) {
            return new Doctrine_Expression($options['expression']);
        } elseif (!class_exists("pakeApp")) {
            switch($options['type']) 
            {
                case 'integer':
                    if (class_exists('sfGuardUser')) {
                      return sfContext::getInstance()->getUser()->getAttribute('user_id', null, 'sfGuardSecurityUser');
                    } else {
                      return sfContext::getInstance()->getUser()->getId();
                    }
                    break;
                case 'string':
                    return sfContext::getInstance()->getUser()->getUsername();
                    break;
                default:
                    return 'n/a';
                    break;
            }
        }
    }
}
 
by Thomas Boerger on 2008-03-10, tagged created  doctrine  template  updated  userid  username 
(6 comments)

Doctrine import from propel improvement

Sometimes a table needs to have non-autoincremented primary keys (for example, to be able to import data from other sources keeping the same id's for objects).

I use the following procedure:

  1. DBDesigner -> Propel schema (config/schema.xml) through DBDesigner2Propel

  2. Generate Doctrine schema (config/doctrine/schema.yml) through symfony doctrine-import.

The problem is that the resulting schema.yml no longer includes information about the primary keys, so doctrine-insert-sql and doctrine-build-model will create an id field as autoincremented primary key.

The solution is a small patch which doesn't change the default behaviour. Look at sfDoctrineSchemaPropelLoader.class.php around the line 52 and modify a few lines in the code to be as the following:

// columns
foreach ($propelDatabaseSchema->getChildren($tableDesc) as $col_name => $columnDescription)
{
  $docCol = new sfDoctrineColumnSchema($col_name, $columnDescription, true);
 
  if($col_name == 'id') {
          if($docCol->getColumnInfo()->get('autoIncrement') === true) {
                  // We skip integer auto-increment primary keys, but we keep the rest
                  continue;
          }
  }
  $class->addColumn($docCol);
}

There is a bug in sfDoctrineColumnSchema.class.php which should be fixed too. The problem is that it is intersecting with the non-translated constraints instead the ones already translated from propel.

The fix is easy:

Original line:

    if ($constraints = array_intersect_key($columnDescription, array_flip(self::$allowedConstraints)))

Change to this:

    // Change to this:
    if ($constraints = array_intersect_key($this->columnInfo->getAll(), array_flip(self::$allowedConstraints)))
by Fernando Monera on 2007-08-07, tagged autoincrement  convert  dbdesigner  doctrine  import  primary  propel 

Doctrine-build-all(-load) workaround

It's very annoying that the pake tasks @doctrine-build-all@ and @doctrine-build-all-load@ don't work. So I've thought, why don't write a shell script which executes the single steps for me.

Just create a new file in your batch directory. For example 'build' or 'doctrine'

#!/usr/bin/env bash
 
symfony doctrine-drop-db <your app>
symfony doctrine-build-db <your app>
symfony doctrine-build-model <your app>
symfony doctrine-build-sql <your app>
symfony doctrine-insert-sql <your app>
 
php batch/<your load-data-batch-file>.php
by Halil Köklü on 2007-07-25, tagged batch  doctrine  model 

Saving records w/ Doctrine from a form using object helpers

// This is meant to be in actions.class.php as one of your actions. // Note how it redirects to your view action after saving.

public function executeSave() { $req = $this->getRequest();

$q = new Doctrine_Query();
$user = $q-&gt;from(&#039;UserInfo&#039;)-&gt;where(&#039;id = ?&#039;,$this-&gt;getRequestParameter(&#039;id&#039;))-&gt;execute()-&gt;getFirst();
 
$user-&gt;merge($req-&gt;getParameterHolder()-&gt;getAll());
$user-&gt;save();
 
$this-&gt;redirect(&#039;user/view?did=save&amp;id=&#039;.$user-&gt;id);

}

by whoknows on 2007-06-12, tagged doctrine  objecthelper  save 

Set relational id by name of record

This is a model function with a field named avatar_id, that links to the avatar table, id field, and allows you to set the id based on the name of the avatar.

static function getAvatarByName($name)
{
  $query = new Doctrine_Query();
  $query->select('a.id');
  $query->from('Avatar a');
  $query->where('name = ?', $name);
  $avatar = $query->execute();
 
  return $avatar[0];
}
 
public function setAvatarIdByName($name)
{
  $avatar = AvatarTable::getAvatarByName($name);
  $this->setAvatarId($avatar->getId());
}
by Jonathan Wage on 2007-04-05, tagged doctrine 

Simple orderby, groupby and count

$query = new Doctrine_Query();
$query->select('c.*, COUNT(u.id) num_users'); // soon it may be possible to do COUNT(u.id) as num_users like in sql
$query->from('Church c');
$query->leftjoin('c.User u');
$query->orderby('c.name asc');
$query->groupby('c.id');
$churches = $query->execute();
 
foreach($churches AS $church)
{
   echo $church->name.' - '.$church->Users[0]->num_users.'<br/>';
}

That last part is tricky, no real way to know that from any of the documentation that num_users is in $church->users[0]->num_users.

by Jonathan Wage on 2007-04-05, tagged doctrine 
(1 comment)

Access individual records. Getters/setters

$user = new User(); // new record
$user = Doctrine_Manager::getInstance()->getTable('User')->find(1); // existing record
 
$user['username'] = 'username';
$user->username = 'username';
$user->setUsername('username');
$user->set('username', 'username');
 
echo $user['username']; // username
echo $user->username; // username
echo $user->getUsername(); // username
echo $user->get('username'); // username
by Jonathan Wage on 2007-04-05, tagged doctrine 

Build queries dynamically...

Example Table static methods for retrieving data. Passing query objects to allow you to modify and reuse existing methods.

static function retrieveUsers($query = null)
{
  if( $query === null )
  {
    $query = new Doctrine_Query();
  }
 
  $query->select('u.*');
  $query->from('User u');
  $users = $query->execute();
 
  return $users;
}
 
static function retrieveUsersWithProfile($query)
{
  if( $query === null )
  {
    $query = new Doctrine_Query();
  }
 
  $query->select('p.*')
  $query->innerJoin('u.UserProfile p');
 
  return UserTable::retrieveUsers($query);
}
 
static function retrieveUserByUsername($username, $query = null)
{
   if( $query === null )
   {
     $query = new Doctrine_Query();
   }
 
   $query->where('u.username = ?', $username);
   $users = UserTable::retrieveUsers($query);
 
   return $users[0];
}
by Jonathan Wage on 2007-04-05, tagged doctrine 

How to set time with a timestamp db field with doctrine

$dateFormat = new sfDateFormat();
$value = $dateFormat->format(time(), 'I');
$user->set('last_login',$value);
by hansbricks on 2007-04-05, tagged doctrine  timestamp