Browse Source

Merge pull request #7905 from cakephp/issue-7901

Add useful exception when ExistsIn is applied to an undefined association
José Lorenzo Rodríguez 10 years ago
parent
commit
75747eb8df
2 changed files with 65 additions and 17 deletions
  1. 42 17
      src/ORM/Rule/ExistsIn.php
  2. 23 0
      tests/TestCase/ORM/RulesCheckerIntegrationTest.php

+ 42 - 17
src/ORM/Rule/ExistsIn.php

@@ -16,6 +16,7 @@ namespace Cake\ORM\Rule;
 
 use Cake\Datasource\EntityInterface;
 use Cake\ORM\Association;
+use RuntimeException;
 
 /**
  * Checks that the value provided in a field exists as the primary key of another
@@ -57,22 +58,34 @@ class ExistsIn
      * @param \Cake\Datasource\EntityInterface $entity The entity from where to extract the fields
      * @param array $options Options passed to the check,
      * where the `repository` key is required.
+     * @throws \RuntimeException When the rule refers to an undefined association.
      * @return bool
      */
     public function __invoke(EntityInterface $entity, array $options)
     {
         if (is_string($this->_repository)) {
-            $this->_repository = $options['repository']->association($this->_repository);
-        }
-
-        $source = !empty($options['repository']) ? $options['repository'] : $this->_repository;
+            $alias = $this->_repository;
+            $this->_repository = $options['repository']->association($alias);
 
-        $source = $source instanceof Association ? $source->source() : $source;
-        $target = $this->_repository;
+            if (empty($this->_repository)) {
+                throw new RuntimeException(sprintf(
+                    "ExistsIn rule for '%s' is invalid. The '%s' association is not defined.",
+                    implode(', ', $this->_fields),
+                    $alias
+                ));
+            }
+        }
 
+        $source = $target = $this->_repository;
+        if (!empty($options['repository'])) {
+            $source = $options['repository'];
+        }
+        if ($source instanceof Association) {
+            $source = $source->source();
+        }
         if ($target instanceof Association) {
             $bindingKey = (array)$target->bindingKey();
-            $target = $this->_repository->target();
+            $target = $target->target();
         } else {
             $bindingKey = (array)$target->primaryKey();
         }
@@ -85,25 +98,37 @@ class ExistsIn
             return true;
         }
 
-        $nulls = 0;
-        $schema = $source->schema();
-        foreach ($this->_fields as $field) {
-            if ($schema->column($field) && $schema->isNullable($field) && $entity->get($field) === null) {
-                $nulls++;
-            }
-        }
-        if ($nulls === count($this->_fields)) {
+        if ($this->_fieldsAreNull($entity, $source)) {
             return true;
         }
 
         $primary = array_map(
-            [$this->_repository, 'aliasField'],
+            [$target, 'aliasField'],
             $bindingKey
         );
         $conditions = array_combine(
             $primary,
             $entity->extract($this->_fields)
         );
-        return $this->_repository->exists($conditions);
+        return $target->exists($conditions);
+    }
+
+    /**
+     * Check whether or not the entity fields nullable and null.
+     *
+     * @param \Cake\ORM\EntityInterface $entity The entity to check.
+     * @param \Cake\ORM\Table $source The table to use schema from.
+     * @return bool
+     */
+    protected function _fieldsAreNull($entity, $source)
+    {
+        $nulls = 0;
+        $schema = $source->schema();
+        foreach ($this->_fields as $field) {
+            if ($schema->column($field) && $schema->isNullable($field) && $entity->get($field) === null) {
+                $nulls++;
+            }
+        }
+        return $nulls === count($this->_fields);
     }
 }

+ 23 - 0
tests/TestCase/ORM/RulesCheckerIntegrationTest.php

@@ -461,6 +461,29 @@ class RulesCheckerIntegrationTest extends TestCase
     }
 
     /**
+     * Tests existsIn with invalid associations
+     *
+     * @group save
+     * @expectedException RuntimeException
+     * @expectedExceptionMessage ExistsIn rule for 'author_id' is invalid. The 'NotValid' association is not defined.
+     * @return void
+     */
+    public function testExistsInInvalidAssociation()
+    {
+        $entity = new Entity([
+            'title' => 'An Article',
+            'author_id' => 500
+        ]);
+
+        $table = TableRegistry::get('Articles');
+        $table->belongsTo('Authors');
+        $rules = $table->rulesChecker();
+        $rules->add($rules->existsIn('author_id', 'NotValid'));
+
+        $table->save($entity);
+    }
+
+    /**
      * Tests the checkRules save option
      *
      * @group save