Blue Horn New Zealand Web Design & Development (PHP 5, MySQL, Symfony Framework, Apache, Linux)

5Sep/090

sfDoctrine Behavior: Unique Random Column

I wanted a Symfony doctrine behavior / template to add a column that contains unique random string. I searched around but found nothing, so I developed these doctrine behavior / template for generating unique random string.

Files:

  • RandomUnique.php (see source code further below)
  • RandomUniqueListener.php (see source code further below)

Save the two files in your project's lib directory.

Example on how to use them in your doctrine schema:

SomeTable:
  actAs:
    RandomUnique:
  columns:
    id: { type: integer(5), primary: true, autoincrement: true }
    user_id: { type: integer(4) }
    org_name: { type: string(100) }
    title: { type: string(20) }
    first_name: { type: string(50) }
    last_name: { type: string(50) }
    email: { type: string(128), notnull: true }

In the example above, column 'unique_code' string (16) will be added to the table. When a new record is inserted, unique random string will be generated in that column.

Source code for UniqueRandom.php:

<?php
 
class RandomUnique extends Doctrine_Template
{
  public function setTableDefinition()
  {
    $column_name = 'unique_code';
    $column_size = 16;
    $this->hasColumn($column_name, 'string', $column_size, array('unique' => true, 'notnull' => true));
    $this->addListener(new RandomUniqueListener(array('column_name' => $column_name, 'column_size' => $column_size)));
  }
 
  public static function generateString($minlength, $maxlength, $useupper, $usespecial, $usenumbers)
  {
    /*
     Author: Peter Mugane Kionga-Kamau
     http://www.pmkmedia.com
 
     Description: string str_makerand(int $minlength, int $maxlength, bool $useupper, bool $usespecial, bool $usenumbers)
     returns a randomly generated string of length between $minlength and $maxlength inclusively.
 
     Notes:
     - If $useupper is true uppercase characters will be used; if false they will be excluded.
     - If $usespecial is true special characters will be used; if false they will be excluded.
     - If $usenumbers is true numerical characters will be used; if false they will be excluded.
     - If $minlength is equal to $maxlength a string of length $maxlength will be returned.
     - Not all special characters are included since they could cause parse errors with queries.
 
     Modify at will.
     */
    $charset = "abcdefghijklmnopqrstuvwxyz";
    if ($useupper) $charset .= "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if ($usenumbers) $charset .= "0123456789";
    if ($usespecial) $charset .= "~@#$%^*()_+-={}|]["; // Note: using all special characters this reads: "~!@#$%^&*()_+`-={}|\\]?[\":;'><,./";
    if ($minlength > $maxlength) $length = mt_rand ($maxlength, $minlength);
    else $length = mt_rand ($minlength, $maxlength);
    $key = '';
    for ($i=0; $i<$length; $i++) 
      $key .= $charset[(mt_rand(0,(strlen($charset)-1)))];
    return $key;
  }
}

And source code for UniqueRandomListener.php:

<?php
 
class RandomUniqueListener extends Doctrine_Record_Listener
{
  protected $column_name;
  protected $column_size;
 
  public function __construct($options)
  {
    $this->column_name = $options['column_name'];
    $this->column_size = $options['column_size'];
  }
 
  public function preInsert(Doctrine_Event $event)
  {
    $d = $event->getInvoker(); /* @var $d sfDoctrineRecord */
    $class_name = get_class($d);
    $i = 0;
    $s = null;
 
    $column_name = $this->column_name;
    $column_size = $this->column_size;
 
    while ($i < 100) // maximum 100 attempts
    {
      $s = RandomUnique::generateString($column_size, $column_size, false, false, true);
 
      $q = Doctrine_Query::create()
        ->select('t.id')
        ->from($class_name.' t')
        ->where('t.'.$column_name.'=?', $s);
 
      $result = $q->execute(array(), Doctrine::HYDRATE_ARRAY);
 
      // if we have found a unique code (e.g.: is not used, does not exist in the table)
      if (!$result || count($result) < 0) break;
 
      $i++;
    }
 
    $event->getInvoker()->$column_name = $s;
  }
}

I don't have time to make a plugin out of this, so feel free to turn this into a Symfony plugin, but please link to this website :)

Happy coding!

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

(required)

 

No trackbacks yet.