Browse Source

Fix documentation and add exception for foreignKey in contain

foreignKey = false in a contain() call for hasMany associations will not
work. While it could be made to work, I really don't think the effort is
justified. In the rare case that someone needs this kind of behavior
they can use the lower level join() API to generate the required
queries.

Refs #5143
Mark Story 11 years ago
parent
commit
a02af9f5dc
3 changed files with 73 additions and 3 deletions
  1. 5 0
      src/ORM/Association/HasMany.php
  2. 4 3
      src/ORM/Query.php
  3. 64 0
      tests/TestCase/ORM/QueryTest.php

+ 5 - 0
src/ORM/Association/HasMany.php

@@ -128,6 +128,11 @@ class HasMany extends Association
     {
         $links = [];
         $name = $this->alias();
+        if ($options['foreignKey'] === false) {
+            $msg = 'Cannot have foreignKey = false for hasMany associations. ' .
+                   'You must provide a foreignKey column.';
+            throw new \RuntimeException($msg);
+        }
 
         foreach ((array)$options['foreignKey'] as $key) {
             $links[] = sprintf('%s.%s', $name, $key);

+ 4 - 3
src/ORM/Query.php

@@ -226,7 +226,8 @@ class Query extends DatabaseQuery implements JsonSerializable
      * options that can be set per association are:
      *
      * - foreignKey: Used to set a different field to match both tables, if set to false
-     *   no join conditions will be generated automatically
+     *   no join conditions will be generated automatically. `false` can only be used on
+     *   joinable associations and cannot be used with hasMany or belongsToMany associations.
      * - fields: An array with the fields that should be fetched from the association
      * - queryBuilder: Equivalent to passing a callable instead of an options array
      *
@@ -245,9 +246,9 @@ class Query extends DatabaseQuery implements JsonSerializable
      * Failing to do so will trigger exceptions.
      *
      * {{{
-     * // Use special join conditions for getting an author's hasMany 'likes'
+     * // Use special join conditions for getting an Articles's belongsTo 'authors'
      * $query->contain([
-     *   'Likes' => [
+     *   'Authors' => [
      *     'foreignKey' => false,
      *     'queryBuilder' => function ($q) {
      *       return $q->where(...); // Add full filtering conditions

+ 64 - 0
tests/TestCase/ORM/QueryTest.php

@@ -1665,6 +1665,70 @@ class QueryTest extends TestCase
     }
 
     /**
+     * Integration test to ensure that filtering associations with the queryBuilder
+     * option works.
+     *
+     * @expectedException \RuntimeException
+     * @return void
+     */
+    public function testContainWithQueryBuilderHasManyError()
+    {
+        $table = TableRegistry::get('Authors');
+        $table->hasMany('Articles');
+        $query = new Query($this->connection, $table);
+        $query->select()
+            ->contain([
+                'Articles' => [
+                    'foreignKey' => false,
+                    'queryBuilder' => function ($q) {
+                        return $q->where(['articles.id' => 1]);
+                    }
+                ]
+            ]);
+        $query->toArray();
+    }
+
+    /**
+     * Integration test to ensure that filtering associations with the queryBuilder
+     * option works.
+     *
+     * @return void
+     */
+    public function testContainWithQueryBuilderJoinableAssociation()
+    {
+        $table = TableRegistry::get('Authors');
+        $table->hasOne('Articles');
+        $query = new Query($this->connection, $table);
+        $query->select()
+            ->contain([
+                'Articles' => [
+                    'foreignKey' => false,
+                    'queryBuilder' => function ($q) {
+                        return $q->where(['Articles.id' => 1]);
+                    }
+                ]
+            ]);
+        $result = $query->toArray();
+        $this->assertEquals(1, $result[0]->article->id);
+        $this->assertEquals(1, $result[1]->article->id);
+
+        $articles = TableRegistry::get('Articles');
+        $articles->belongsTo('Authors');
+        $query = new Query($this->connection, $articles);
+        $query->select()
+            ->contain([
+                'Authors' => [
+                    'foreignKey' => false,
+                    'queryBuilder' => function ($q) {
+                        return $q->where(['Authors.id' => 1]);
+                    }
+                ]
+            ]);
+        $result = $query->toArray();
+        $this->assertEquals(1, $result[0]->author->id);
+    }
+
+    /**
      * Tests the formatResults method
      *
      * @return void