Skip to content

Commit

Permalink
Merge pull request #8908 from ionas/fixExistsIn-New
Browse files Browse the repository at this point in the history
Add allowPartialNulls-flag to ExistsIn() that matches SQLs behavior of composite foreign keys having some nullable nulls
  • Loading branch information
lorenzo committed Jun 28, 2016
2 parents ee08911 + 230e06f commit 3b3a8b7
Show file tree
Hide file tree
Showing 3 changed files with 341 additions and 5 deletions.
29 changes: 27 additions & 2 deletions src/ORM/Rule/ExistsIn.php
Expand Up @@ -39,15 +39,29 @@ class ExistsIn
*/
protected $_repository;

/**
* Options for the constructor
*
* @var array
*/
protected $_options = [];

/**
* Constructor.
*
* Available option for $options is 'allowPartialNulls' flag.
* Set to true to accept composite foreign keys where one or more nullable columns are null.
*
* @param string|array $fields The field or fields to check existence as primary key.
* @param object|string $repository The repository where the field will be looked for,
* or the association name for the repository.
* @param array $options The options that modify the rules behavior.
*/
public function __construct($fields, $repository)
public function __construct($fields, $repository, array $options = [])
{
$options += ['allowPartialNulls' => false];
$this->_options = $options;

$this->_fields = (array)$fields;
$this->_repository = $repository;
}
Expand Down Expand Up @@ -99,6 +113,16 @@ public function __invoke(EntityInterface $entity, array $options)
return true;
}

if ($this->_options['allowPartialNulls']) {
$schema = $source->schema();
foreach ($this->_fields as $i => $field) {
if ($schema->column($field) && $schema->isNullable($field) && $entity->get($field) === null) {
unset($bindingKey[$i]);
unset($this->_fields[$i]);
}
}
}

$primary = array_map(
[$target, 'aliasField'],
$bindingKey
Expand All @@ -107,11 +131,12 @@ public function __invoke(EntityInterface $entity, array $options)
$primary,
$entity->extract($this->_fields)
);

return $target->exists($conditions);
}

/**
* Check whether or not the entity fields are nullable and null.
* Checks whether or not the given entity fields are nullable and null.
*
* @param \Cake\Datasource\EntityInterface $entity The entity to check.
* @param \Cake\ORM\Table $source The table to use schema from.
Expand Down
16 changes: 14 additions & 2 deletions src/ORM/RulesChecker.php
Expand Up @@ -78,14 +78,26 @@ public function isUnique(array $fields, $message = null)
* $rules->add($rules->existsIn('site_id', new SitesTable(), 'Invalid Site'));
* ```
*
* Available $options are error 'message' and 'allowPartialNulls' flag.
* 'message' sets a custom error message.
* Set 'allowPartialNulls' to true to accept composite foreign keys where one or more nullable columns are null.
*
* @param string|array $field The field or list of fields to check for existence by
* primary key lookup in the other table.
* @param object|string $table The table name where the fields existence will be checked.
* @param string|null $message The error message to show in case the rule does not pass.
* @param string|array|null $message The error message to show in case the rule does not pass. Can
* also be an array of options. When an array, the 'message' key can be used to provide a message.
* @return callable
*/
public function existsIn($field, $table, $message = null)
{
$options = [];
if (is_array($message)) {
$options = $message + ['message' => null];
$message = $options['message'];
unset($options['message']);
}

if (!$message) {
if ($this->_useI18n) {
$message = __d('cake', 'This value does not exist');
Expand All @@ -95,7 +107,7 @@ public function existsIn($field, $table, $message = null)
}

$errorField = is_string($field) ? $field : current($field);
return $this->_addError(new ExistsIn($field, $table), '_existsIn', compact('errorField', 'message'));
return $this->_addError(new ExistsIn($field, $table, $options), '_existsIn', compact('errorField', 'message'));
}

/**
Expand Down

0 comments on commit 3b3a8b7

Please sign in to comment.