Uuid Behavior

By Billy Gunn (Divagater)
I have a requirement to use UUID's as primary keys. I was initially using MySQL triggers to call uuid() on insert but in the end decided a behavior makes more sense. This behavior adds a UUID to the field specified in your models.

Installation

  1. Create app/models/behaviors/uuid.php using the code provided in the behavior section below.

Usage

Add the behavior name to the $actsAs array for your model. For example, let's assume you have a model named Post (which maps to a database table named posts). Edit your app/models/post.php file and add $actsAs as follows:

Model Class:

Download code <?php 
class Post extends AppModel {
    var 
$name 'Post';
    var 
$actsAs = array('Uuid' => array('field' => 'id'));
}
?>

That's all there is to it. When you use the function save() in your model (native CakePHP function) the behavior will add a UUID to the field specified. The field can be any you choose and defaults to id.

The field you choose to set a UUID on will need to be configured appropriately in your database. For instance my id column definition in MySQL looks like: `id` char(36) NOT NULL

Behavior

Here's the code for the behavior. Save this as a file named uuid.php in your app/models/behaviors folder.
Download code
<?php
/**
 * UUID Behavior class file.
 *
 * Model Behavior to support adding UUID's when a record is saved.
 *
 * This behavior implements the beforeSave() callback for updating the
 * specified field with a UUID. The actual randomness of the generated
 * UUID has not been tested. Use at your own risk.
 *
 * Usage in model:
 *
 * Add Uuid to the $actsAs array of your model:
 * var $actsAs = array('Uuid' => array('field' => 'id'));
 *
 * @filesource
 * @package     app
 * @subpackage  models.behaviors
 */

/**
 * Add UUID behavior to a model.
 *
 * @author      Billy Gunn
 * @package     app
 * @subpackage  models.behaviors
 */
class UuidBehavior extends ModelBehavior {
    
/**
     * Default model settings
     */
    
var $defaultSettings = array('field' => 'id');

    
/**
     * Initiate behaviour for the model using settings.
     *
     * @param object $model    Model using the behaviour
     * @param array $settings    Settings to override for model.
     *
     * @access public
     */
    
function setup(&$model$settings = array()) {
        
$field $this->defaultSettings['field'];

        if (!empty(
$settings['field'])) {
            
$field $settings['field'];
        }

        if (
$model->hasField($field)) {
            
$this->settings[$model->name] = array('field' => $field);
        }
    }

    
/**
     * Generates a pseudo-random UUID.
     * Slightly modified version of a function submitted to php.net:
     * http://us2.php.net/manual/en/function.com-create-guid.php#52354
     *
     * @access public
     */
    
function uuid() {
        if (
function_exists('com_create_guid')) {
            return 
com_create_guid();
        } else {
            
mt_srand((double)microtime()*10000);
            
$charid md5(uniqid(rand(), true));
            
$hyphen chr(45);// "-"
            
$uuid substr($charid08).$hyphen
                  
substr($charid84).$hyphen
                  
substr($charid,124).$hyphen
                  
substr($charid,164).$hyphen
                  
substr($charid,20,12);

            return 
$uuid;
        }
    }

    
/**
     * Run before a model is saved to add a UUID to a field.
     *
     * @param object $model    Model about to be saved.
     *
     * @access public
     */
    
function beforeSave(&$model) {
        if (
$this->settings[$model->name]) {
            
$field $this->settings[$model->name]['field'];
            if (!isset(
$model->data[$model->name][$field])) {
                
$model->data[$model->name][$field] = $this->uuid();
            }
        }
    }
}
?>

Test Case

If you haven't already, read Testing Models with CakePHP 1.2 test suite as a starting point for getting your test environment configured.

Before continuing you should have the Uuid behavior and your test environment installed and working. Create a file named uuid_test_fixture.php in the app/tests/fixtures folder with the following contents.

Download code
<?php
/**
 * Uuid Behavior test fixture class file.
 *
 * Fixture for tests in uuid Behavior.
 *
 * @filesource
 * @package     app
 * @subpackage  app.tests.fixtures
 */

/**
 * Fixture used in tests for the Uuid behavior.
 *
 * @author      Billy Gunn
 * @package     app
 * @subpackage  app.tests.fixtures
 */
class UuidTestFixture extends CakeTestFixture {
        var 
$name 'UuidTest';
        var 
$fields = array('id' => array('type' => 'integer''key' => 'primary'),
                            
'uuid' => array('type' => 'string''null' => false'length' => 36),
                            
'name' => array('type' => 'string''null' => false),
                            
'description' => array('type' => 'string''null' => false),
                            
'created' => 'datetime',
                            
'updated' => 'datetime'
        
);

        var 
$records = array(
                array (
'id' => 1'uuid' => '758372bc-6fd4-102a-ae1c-00065becda85''name' => 'First record''description' => 'First record''created' => '2007-03-18 10:39:23''updated' => '2007-03-18 10:41:31')
        );
}
?>

Next, create a file called uuid.test.php in the app/tests/cases/behaviors folder with the following contents.

Download code
<?php
/**
 * Uuid Behavior test case.
 *
 * Test cases for Uuid Behavior.
 *
 * @filesource
 * @package     app
 * @subpackage  app.tests.cases.behaviors
 */

/**
 * Model used in tests for Uuid.
 *
 * @author      Billy Gunn
 * @package     app
 * @subpackage  app.tests.cases.behaviors
 */
class UuidTest extends CakeTestModel {
        var 
$name 'UuidTest';
        var 
$actsAs = array('Uuid' => array('field' => 'uuid'));
}

/**
 * Uuid Behavior test case.
 *
 * @author      Billy Gunn
 * @package     app
 * @subpackage  app.tests.cases.behaviors
 */
class UuidTestCase extends CakeTestCase {

        var 
$fixtures = array( 'uuid_test' );

    
/**
     * testCreateRecord
     *
     * Create a new record and verify that a valid uuid
     * was added to the the uuid field.
     *
     * @access public
     * @return void
     */
    
function testCreateRecord() {

        
$this->Record =& new UuidTest();

        
$data = array('UuidTest' => 
                   array(
'id' => 4
                         
'uuid' => null
                         
'name' => 'New record')
                );

        
$this->Record->save($data);

        
$result $this->Record->read(null4);

        
$match preg_match("/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/"$result['UuidTest']['uuid']);

        
$this->assertEqual($match1);
    }


    
/**
     * testUpdateRecord
     *
     * An existing record should not have its uuid updated on save
     *
     * @access public
     * @return void
     */
    
function testUpdateRecord() {

        
$this->Record =& new UuidTest();

        
$data = array('UuidTest' => array ( 'id' => 1'uuid' => '758372bc-6fd4-102a-ae1c-00065becda85''description' => 'modified record'));
        
$this->Record->save($data);

        
$result $this->Record->findAll(null, array('id''uuid''name''description'));

        
$expected = array(
                array(
'UuidTest' => array(
                      
'id' => 1,
                      
'uuid' => '758372bc-6fd4-102a-ae1c-00065becda85',
                      
'name' => 'First record',
                      
'description' => 'modified record'
                
))
       );

       
$this->assertEqual($result$expected);
    }
}
?>

Point your browser to: http:///test.php. Once there, click on App Test Cases, and then look for the option behaviors/uuid.test.php and click it. You will see the results of the test in your browser.

 

Comments 414

CakePHP Team Comments Author Comments
 

Comment

1 but what are..

What are UUID's i don't understand 0_o
Posted Jul 6, 2007 by chris
 

Comment

2 but what are..

What are UUID's i don't understand 0_o
Have a look here: http://en.wikipedia.org/wiki/UUID
Now, why would you want to use UUID/GUI with your database records? Well that really depends on your application but one really basic reason would be to prevent primary key collisions when importing data from another application.
Posted Jul 18, 2007 by Billy Gunn
 

Comment

3 uuid

Last cake 1.2 build support uuid for your primary key column. You just need have field ID with type varchar(36)
Posted Aug 27, 2007 by Yevgeny Tomenko