Skip to content

Instantly share code, notes, and snippets.

@VanTanev
Created August 25, 2011 00:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VanTanev/1169639 to your computer and use it in GitHub Desktop.
Save VanTanev/1169639 to your computer and use it in GitHub Desktop.
<?php
/*
* This file is part of the symfony package.
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* sfValidatorPropelUnique validates that the uniqueness of a column.
* This validator can only be used as a post validator.
*
* Warning: sfValidatorPropelUnique is susceptible to race conditions.
* To avoid this issue, wrap the validation process and the model saving
* inside a transaction.
*
* @package symfony
* @subpackage validator
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @version SVN: $Id: sfValidatorPropelUnique.class.php 21908 2009-09-11 12:06:21Z fabien $
*/
class sfValidatorPropelUnique extends sfValidatorSchema
{
/**
* Constructor.
*
* @param array $options An array of options
* @param array $messages An array of error messages
*
* @see sfValidatorSchema
*/
public function __construct($options = array(), $messages = array())
{
parent::__construct(null, $options, $messages);
}
/**
* Configures the current validator.
*
* Available options:
*
* * model: The model class (required)
* * column: The unique column name in Propel field name format (required)
* If the uniquess is for several columns, you can pass an array of field names
* * query_methods: An array of method names listing the methods to execute
* on the model's query object
* * field Field name used by the form, other than the column name
* * primary_key: The primary key column name in Propel field name format (optional, will be introspected if not provided)
* You can also pass an array if the table has several primary keys
* * allow_null_uniques: Whether to allow null values (false by default). If a field is not "NOT NULL" the validator will accept null values.
* Check this for database support: http://troels.arvin.dk/db/rdbms/#constraints-unique
* * connection: The Propel connection to use (null by default)
* * throw_global_error: Whether to throw a global error (false by default) or an error tied to the first field related to the column option array
*
* @see sfValidatorBase
*/
protected function configure($options = array(), $messages = array())
{
$this->addRequiredOption('model');
$this->addRequiredOption('column');
$this->addOption('query_methods', array());
$this->addOption('field', null);
$this->addOption('primary_key', null);
$this->addOption('allow_null_uniques', false);
$this->addOption('connection', null);
$this->addOption('throw_global_error', false);
$this->setMessage('invalid', 'An object with the same "%column%" already exist.');
}
/**
* @see sfValidatorBase
*/
protected function doClean($values)
{
if (!is_array($values))
{
throw new InvalidArgumentException('You must pass an array parameter to the clean() method (this validator can only be used as a post validator).');
}
if (!is_array($this->getOption('column')))
{
$this->setOption('column', array($this->getOption('column')));
}
if (!is_array($field = $this->getOption('field')))
{
$this->setOption('field', $field ? array($field) : array());
}
$fields = $this->getOption('field');
$criteria = PropelQuery::from($this->getOption('model'));
foreach ($this->getOption('query_methods') as $methodName => $methodParams)
{
if(is_array($methodParams))
{
call_user_func_array(array($criteria, $methodName), $methodParams);
}
else
{
$criteria->$methodParams();
}
}
if ($this->getOption('allow_null_uniques'))
{
$tableMap = call_user_func(array($this->getOption('model') . 'Peer', 'getTableMap'));
$nullColsCount = 0;
}
foreach ($this->getOption('column') as $i => $column)
{
$name = isset($fields[$i]) ? $fields[$i] : $column;
if (!array_key_exists($name, $values))
{
// one of the column has been removed from the form
return $values;
}
$colName = call_user_func(array(constant($this->getOption('model').'::PEER'), 'translateFieldName'), $column, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_COLNAME);
// handle null unique indexes
if ($this->getOption('allow_null_uniques') && null === $values[$name] && !$tableMap->getColumn($colName)->isNotNull())
{
$nullColsCount++;
continue;
}
$criteria->add($colName, $values[$name]);
}
if ($this->getOption('allow_null_uniques') && $nullColsCount == count($this->getOption('column')))
{
// all columns for checking were both empty and null unique
$object = null;
}
else
{
// perform query for normal unique check
$object = $criteria->findOne($this->getOption('connection'));
}
// if no object or if we're updating the object, it's ok
if (null === $object || $this->isUpdate($object, $values))
{
return $values;
}
$error = new sfValidatorError($this, 'invalid', array('column' => implode(', ', $this->getOption('column'))));
if ($this->getOption('throw_global_error'))
{
throw $error;
}
$columns = $this->getOption('column');
throw new sfValidatorErrorSchema($this, array($columns[0] => $error));
}
/**
* Returns whether the object is being updated.
*
* @param BaseObject $object A Propel object
* @param array $values An array of values
*
* @return Boolean true if the object is being updated, false otherwise
*/
protected function isUpdate(BaseObject $object, $values)
{
// check each primary key column
foreach ($this->getPrimaryKeys() as $column)
{
$columnPhpName = call_user_func(array(constant($this->getOption('model').'::PEER'), 'translateFieldName'), $column, BasePeer::TYPE_FIELDNAME, BasePeer::TYPE_PHPNAME);
$method = 'get'.$columnPhpName;
if (!isset($values[$column]) or $object->$method() != $values[$column])
{
return false;
}
}
return true;
}
/**
* Returns the primary keys for the model.
*
* @return array An array of primary keys
*/
protected function getPrimaryKeys()
{
if (null === $this->getOption('primary_key'))
{
$primaryKeys = array();
$tableMap = call_user_func(array(constant($this->getOption('model').'::PEER'), 'getTableMap'));
foreach ($tableMap->getPrimaryKeys() as $column)
{
$primaryKeys[] = call_user_func(array(constant($this->getOption('model').'::PEER'), 'translateFieldName'), $column->getPhpName(), BasePeer::TYPE_PHPNAME, BasePeer::TYPE_FIELDNAME);
}
$this->setOption('primary_key', $primaryKeys);
}
if (!is_array($this->getOption('primary_key')))
{
$this->setOption('primary_key', array($this->getOption('primary_key')));
}
return $this->getOption('primary_key');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment