Code snippets for symfony 1.x

Navigation

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 

Comments on this snippet

gravatar icon
#1 Michael Klein on 2008-03-27 at 11:17

I modified your snippet to provide some more features:

  • now the template works in CLI-mode, e.g. you try to load fixtures with symfony doctrine-build-all-reload
  • works with sfGuardDoctrinePlugin

to get this working i had to change one function in lib/doctrine/Doctrine/Template/Listener/Userid.php:

// ...
    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;
            }
        }
    }
 
gravatar icon
#2 Thomas Boerger on 2008-04-02 at 10:59

Nice work, i will add it directly to the snippet.

gravatar icon
#3 Kary Leong on 2009-05-07 at 03:50

This not work in Symfony 1.2

When using CLI-mode data-load, the sfContext::getInstance() will through exception.

#

The "default" context does not exist.

#

gravatar icon
#4 geoffrey on 2009-05-14 at 11:02

As mentioned by Kary Leong, this will not work on cli mode, which includes:

  • loading of fixtures
  • unit testing
  • functional testing

this is a recurring problem with sfContext, and you should avoid using it anyway.

One possible solution could be, before using sfContext, checking if the created_by and updated_by are already (this let the dev the chance to fill them, avoiding the sfContext exception) and/or to check for a context using sfContext::hasInstance();

As a side note, the "type: string" parameter would better trigger a __toString() call as this is the standard way to get a string representation from an object

gravatar icon
#5 Antonio Cifuentes on 2009-08-21 at 01:14

Thanks for your "snippet"

These are the things that strengthen a system

gravatar icon
#6 Marcel Berteler on 2010-04-16 at 01:57

I took this idea and implemented it slightly differently to allow for unit testing and cli mode:

http://snippets.symfony-project.org/snippet/405