Skip to content

Commit

Permalink
Requested changes to Query::contain() and related methods
Browse files Browse the repository at this point in the history
  • Loading branch information
burzum committed Apr 1, 2017
1 parent e559d88 commit b3f30a0
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 124 deletions.
129 changes: 5 additions & 124 deletions src/ORM/Query.php
Expand Up @@ -370,135 +370,20 @@ public function eagerLoader(EagerLoader $instance = null)
* If called with an empty first argument and `$override` is set to true, the
* previous list will be emptied.
*
* @deprecated Use setContain(), getContain() and clearContain() instead.
* @param array|string|null $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.
* @return array|$this
*/
public function contain($associations = null, $override = false)
{
if ($associations === null) {
if ($override === true) {
$this->clearContain();
}

return $this->getContain();
}

return $this->setContain($associations, $override);
}

/**
* Sets the list of associations that should be eagerly loaded along with this
* query. The list of associated tables passed must have been previously set as
* associations using the Table API.
*
* ### Example:
*
* ```
* // Bring articles' author information
* $query->setContain('Author');
*
* // Also bring the category and tags associated to each article
* $query->setContain(['Category', 'Tag']);
* ```
*
* Associations can be arbitrarily nested using dot notation or nested arrays,
* this allows this object to calculate joins or any additional queries that
* must be executed to bring the required associated data.
*
* ### Example:
*
* ```
* // Eager load the product info, and for each product load other 2 associations
* $query->setContain(['Product' => ['Manufacturer', 'Distributor']);
*
* // Which is equivalent to calling
* $query->setContain(['Products.Manufactures', 'Products.Distributors']);
*
* // For an author query, load his region, state and country
* $query->setContain('Regions.States.Countries');
* ```
*
* It is possible to control the conditions and fields selected for each of the
* contained associations:
*
* ### Example:
*
* ```
* $query->setContain(['Tags' => function ($q) {
* return $q->where(['Tags.is_popular' => true]);
* }]);
*
* $query->setContain(['Products.Manufactures' => function ($q) {
* return $q->select(['name'])->where(['Manufactures.active' => true]);
* }]);
* ```
*
* Each association might define special options when eager loaded, the allowed
* 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. `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.
* - `finder`: The finder to use when loading associated records. Either the name of the
* finder as a string, or an array to define options to pass to the finder.
* - `queryBuilder`: Equivalent to passing a callable instead of an options array.
*
* ### Example:
*
* ```
* // Set options for the hasMany articles that will be eagerly loaded for an author
* $query->setContain([
* 'Articles' => [
* 'fields' => ['title', 'author_id']
* ]
* ]);
* ```
*
* Finders can be configured to use options.
*
* ```
* // Retrieve translations for the articles, but only those for the `en` and `es` locales
* $query->setContain([
* 'Articles' => [
* 'finder' => [
* 'translations' => [
* 'locales' => ['en', 'es']
* ]
* ]
* ]
* ]);
* ```
*
* When containing associations, it is important to include foreign key columns.
* Failing to do so will trigger exceptions.
*
* ```
* // Use special join conditions for getting an Articles's belongsTo 'authors'
* $query->setContain([
* 'Authors' => [
* 'foreignKey' => false,
* 'queryBuilder' => function ($q) {
* return $q->where(...); // Add full filtering conditions
* }
* ]
* ]);
* ```
*
* @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.
* @return $this
*/
public function setContain($associations, $override = false)
{
$loader = $this->getEagerLoader();
if ($override === true) {
$loader->clearContain();
$this->_dirty();
$this->clearContain();
}

if ($associations === null) {
return $this->getContain();
}

$result = $loader->contain($associations);
Expand Down Expand Up @@ -930,10 +815,6 @@ public function __clone()
*/
public function count()
{
return $this->getCount();
}

public function getCount() {
if ($this->_resultsCount === null) {
$this->_resultsCount = $this->_performCount();
}
Expand Down
28 changes: 28 additions & 0 deletions tests/TestCase/ORM/QueryTest.php
Expand Up @@ -1804,6 +1804,34 @@ public function collectionMethodsProvider()
];
}

/**
* testClearContain
*
* @return void
*/
public function testClearContain()
{
$query = $this->getMockBuilder('\Cake\ORM\Query')
->setMethods(['all'])
->setConstructorArgs([$this->connection, $this->table])
->getMock();

$query->contain([
'Articles'
]);

$result = $query->contain();
$this->assertInternalType('array', $result);
$this->assertNotEmpty($result);

$result = $query->clearContain();
$this->assertInstanceOf(Query::class, $result);

$result = $query->contain();
$this->assertInternalType('array', $result);
$this->assertEmpty($result);
}

/**
* Tests that query can proxy collection methods
*
Expand Down

0 comments on commit b3f30a0

Please sign in to comment.