Created
August 25, 2011 00:14
-
-
Save VanTanev/1169639 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* This file is part of the symfony package. | |
* (c) Fabien Potencier <[email protected]> | |
* | |
* 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 <[email protected]> | |
* @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