Browse Source

Fix contained associations not having types mapped.

When contain() is called, use the normalized information to set type
mappings. This forces associations to be normalized earlier, but allows
the ORM to be more consistent and not require developers to set type
information.

Refs #6975
Mark Story 10 years ago
parent
commit
4d5ec31fe9

+ 1 - 1
src/Datasource/QueryTrait.php

@@ -178,7 +178,7 @@ trait QueryTrait
     }
 
     /**
-     * Sets the query instance to be the eager loaded query. If no argument is
+     * Sets the query instance to be an eager loaded query. If no argument is
      * passed, the current configured query `_eagerLoaded` value is returned.
      *
      * @param bool|null $value Whether or not to eager load.

+ 12 - 4
src/ORM/Query.php

@@ -150,8 +150,8 @@ class Query extends DatabaseQuery implements JsonSerializable
     /**
      * Hints this object to associate the correct types when casting conditions
      * for the database. This is done by extracting the field types from the schema
-     * associated to the passed table object. This prevents the user from repeating
-     * himself when specifying conditions.
+     * associated to the passed table object. This prevents developers from repeating
+     * themselves when specifying conditions.
      *
      * This method returns the same query object for chaining.
      *
@@ -281,6 +281,10 @@ class Query extends DatabaseQuery implements JsonSerializable
      * If called with an empty first argument and $override is set to true, the
      * previous list will be emptied.
      *
+     * Contained associations will have their column types mapped allowing you
+     * to use complex types in where() conditions. Nested associations will not have
+     * their types mapped.
+     *
      * @param array|string $associations list of table aliases to be queried
      * @param bool $override whether override previous list with the one passed
      * defaults to merging previous list with the new one.
@@ -300,6 +304,10 @@ class Query extends DatabaseQuery implements JsonSerializable
             return $result;
         }
 
+        foreach ($this->eagerLoader()->normalized($this->repository()) as $loader) {
+            $this->addDefaultTypes($loader->instance()->target());
+        }
+
         return $this;
     }
 
@@ -851,8 +859,7 @@ class Query extends DatabaseQuery implements JsonSerializable
         $this->triggerBeforeFind();
 
         $this->_transformQuery();
-        $sql = parent::sql($binder);
-        return $sql;
+        return parent::sql($binder);
     }
 
     /**
@@ -880,6 +887,7 @@ class Query extends DatabaseQuery implements JsonSerializable
      * specified and applies the joins required to eager load associations defined
      * using `contain`
      *
+     * @see \Cake\ORM\Query::sql()
      * @see \Cake\Database\Query::execute()
      * @return void
      */

+ 22 - 0
tests/TestCase/ORM/QueryRegressionTest.php

@@ -995,6 +995,28 @@ class QueryRegressionTest extends TestCase
     }
 
     /**
+     * Test that contain queries map types correctly.
+     *
+     * @return void
+     */
+    public function testComplexTypesInJoinedWhere()
+    {
+        $table = TableRegistry::get('Users');
+        $table->hasOne('Comments', [
+            'foreignKey' => 'user_id',
+        ]);
+        $query = $table->find()
+            ->contain('Comments')
+            ->where([
+                'Comments.updated >' => new \DateTime('2007-03-18 10:55:00')
+            ]);
+
+        $result = $query->first();
+        $this->assertNotEmpty($result);
+        $this->assertInstanceOf('Cake\I18n\Time', $result->comment->updated);
+    }
+
+    /**
      * Tests that it is possible to use matching with dot notation
      * even when part of the part of the path in the dot notation is
      * shared for two different calls

+ 21 - 7
tests/TestCase/ORM/QueryTest.php

@@ -37,7 +37,7 @@ class QueryTest extends TestCase
      * @var array
      */
     public $fixtures = ['core.articles', 'core.authors', 'core.tags',
-        'core.articles_tags', 'core.posts'];
+        'core.comments', 'core.articles_tags', 'core.posts'];
 
     /**
      * setUp method
@@ -813,6 +813,21 @@ class QueryTest extends TestCase
      */
     public function testApplyOptions()
     {
+        $this->table->belongsTo('articles');
+        $typeMap = new TypeMap([
+            'foo.id' => 'integer',
+            'id' => 'integer',
+            'articles.id' => 'integer',
+            'articles.author_id' => 'integer',
+            'author_id' => 'integer',
+            'articles.title' => 'string',
+            'title' => 'string',
+            'articles.body' => 'text',
+            'body' => 'text',
+            'articles.published' => 'string',
+            'published' => 'string',
+        ]);
+
         $options = [
             'fields' => ['field_a', 'field_b'],
             'conditions' => ['field_a' => 1, 'field_b' => 'something'],
@@ -821,7 +836,7 @@ class QueryTest extends TestCase
             'offset' => 5,
             'group' => ['field_a'],
             'having' => ['field_a >' => 100],
-            'contain' => ['table_a' => ['table_b']],
+            'contain' => ['articles'],
             'join' => ['table_a' => ['conditions' => ['a > b']]]
         ];
         $query = new Query($this->connection, $this->table);
@@ -829,13 +844,13 @@ class QueryTest extends TestCase
 
         $this->assertEquals(['field_a', 'field_b'], $query->clause('select'));
 
-        $expected = new QueryExpression($options['conditions'], $this->fooTypeMap);
+        $expected = new QueryExpression($options['conditions'], $typeMap);
         $result = $query->clause('where');
         $this->assertEquals($expected, $result);
 
         $this->assertEquals(1, $query->clause('limit'));
 
-        $expected = new QueryExpression(['a > b'], $this->fooTypeMap);
+        $expected = new QueryExpression(['a > b'], $typeMap);
         $result = $query->clause('join');
         $this->assertEquals([
             'table_a' => ['alias' => 'table_a', 'type' => 'INNER', 'conditions' => $expected]
@@ -847,11 +862,10 @@ class QueryTest extends TestCase
         $this->assertEquals(5, $query->clause('offset'));
         $this->assertEquals(['field_a'], $query->clause('group'));
 
-        $expected = new QueryExpression($options['having']);
-        $expected->typeMap($this->fooTypeMap);
+        $expected = new QueryExpression($options['having'], $typeMap);
         $this->assertEquals($expected, $query->clause('having'));
 
-        $expected = ['table_a' => ['table_b' => []]];
+        $expected = ['articles' => []];
         $this->assertEquals($expected, $query->contain());
     }