Fix afterFind() called twice with hasMany relationship

It occurs when a model and the children models are related to a same model.
For example, such as the following:

* User hasMany Comment
* User hasMany Article
* Article hasMany Comment
This commit is contained in:
chinpei215 2014-08-11 00:08:33 +09:00
parent cb45821c43
commit c227c14bf2
2 changed files with 57 additions and 4 deletions

View file

@ -1205,6 +1205,30 @@ class DboSource extends DataSource {
return $filtering;
}
/**
* Passes association results through afterFind filters of the corresponding model.
*
* Similar to DboSource::_filterResults(), but this filters only specified models.
* The primary model can not be specified, because this call DboSource::_filterResults() internally.
*
* @param array &$resultSet Reference of resultset to be filtered.
* @param Model $Model Instance of model to operate against.
* @param array $toBeFiltered List of classes to be filtered.
* @return array Array of results that have been filtered through $Model->afterFind.
*/
protected function _filterResultsInclusive(&$resultSet, Model $Model, $toBeFiltered = array()) {
$exclude = array();
if (is_array($resultSet)) {
$current = reset($resultSet);
if (is_array($current)) {
$exclude = array_diff(array_keys($current), $toBeFiltered);
}
}
return $this->_filterResults($resultSet, $Model, $exclude);
}
/**
* Queries associations.
*
@ -1276,7 +1300,7 @@ class DboSource extends DataSource {
// Filter
if ($queryData['callbacks'] === true || $queryData['callbacks'] === 'after') {
$this->_filterResults($assocResultSet, $Model);
$this->_filterResultsInclusive($assocResultSet, $Model, array($association));
}
// Merge
@ -1305,7 +1329,7 @@ class DboSource extends DataSource {
// Filter
if ($queryData['callbacks'] === true || $queryData['callbacks'] === 'after') {
$this->_filterResults($assocResultSet, $Model);
$this->_filterResultsInclusive($assocResultSet, $Model, array($association, $with));
}
}

View file

@ -1463,7 +1463,7 @@ class DboSourceTest extends CakeTestCase {
}
/**
* Test that count how many times is afterFind called
* Test that count how many times afterFind is called
*
* @return void
*/
@ -1472,7 +1472,6 @@ class DboSourceTest extends CakeTestCase {
// Use alias to make testing "primary = true" easy
$Primary = $this->getMock('Comment', array('afterFind'), array(array('alias' => 'Primary')), '', true);
$Primary->expects($this->any())->method('afterFind')->will($this->returnArgument(0));
$Article = $this->getMock('Article', array('afterFind'), array(), '', true);
$User = $this->getMock('User', array('afterFind'), array(), '', true);
@ -1507,5 +1506,35 @@ class DboSourceTest extends CakeTestCase {
$result = $Primary->find('first', array('conditions' => array('Primary.id' => 5), 'recursive' => 2));
$this->assertCount(2, $result['Article']['Tag']);
$this->assertCount(2, $result['Article']['Comment']);
// hasMany special case
// Both User and Article has many Comments
$User = $this->getMock('User', array('afterFind'), array(), '', true);
$Article = $this->getMock('Article', array('afterFind'), array(), '', true);
$Comment = $this->getMock('Comment', array('afterFind'), array(), '', true);
$User->bindModel(array('hasMany' => array('Comment', 'Article')));
$Article->unbindModel(array('belongsTo' => array('User'), 'hasAndBelongsToMany' => array('Tag')));
$Comment->unbindModel(array('belongsTo' => array('User', 'Article'), 'hasOne' => 'Attachment'));
$User->Comment = $Comment;
$User->Article = $Article;
$User->Article->Comment = $Comment;
// primary = true
$User->expects($this->once())
->method('afterFind')->with($this->anything(), $this->isTrue())->will($this->returnArgument(0));
$Article->expects($this->exactly(2)) // User has 2 Articles
->method('afterFind')->with($this->anything(), $this->isFalse())->will($this->returnArgument(0));
$Comment->expects($this->exactly(7)) // User1 has 3 Comments, Article[id=1] has 4 Comments and Article[id=3] has 0 Comments
->method('afterFind')->with($this->anything(), $this->isFalse())->will($this->returnArgument(0));
$result = $User->find('first', array('conditions' => array('User.id' => 1), 'recursive' => 2));
$this->assertCount(3, $result['Comment']);
$this->assertCount(2, $result['Article']);
$this->assertCount(4, $result['Article'][0]['Comment']);
$this->assertCount(0, $result['Article'][1]['Comment']);
}
}