diff --git a/src/ORM/Association.php b/src/ORM/Association.php index 267ce128132..036ec10452b 100644 --- a/src/ORM/Association.php +++ b/src/ORM/Association.php @@ -499,14 +499,6 @@ public function attachTo(Query $query, array $options = []) } } - if ($options['negateMatch']) { - $primaryKey = $query->aliasFields((array)$target->primaryKey(), $this->_name); - $query->andWhere(function ($exp) use ($primaryKey) { - array_map([$exp, 'isNull'], $primaryKey); - return $exp; - }); - } - list($finder, $opts) = $this->_extractFinder($options['finder']); $dummy = $this ->find($finder, $opts) @@ -531,6 +523,19 @@ public function attachTo(Query $query, array $options = []) $this->_appendFields($query, $dummy, $options); $this->_formatAssociationResults($query, $dummy, $options); $this->_bindNewAssociations($query, $dummy, $options); + $this->_appendNotMatching($query, $options); + } + + protected function _appendNotMatching($query, $options) + { + $target = $this->_targetTable; + if (!empty($options['negateMatch'])) { + $primaryKey = $query->aliasFields((array)$target->primaryKey(), $this->_name); + $query->andWhere(function ($exp) use ($primaryKey) { + array_map([$exp, 'isNull'], $primaryKey); + return $exp; + }); + } } /** diff --git a/src/ORM/Association/BelongsToMany.php b/src/ORM/Association/BelongsToMany.php index d057be51733..1036450ebb6 100644 --- a/src/ORM/Association/BelongsToMany.php +++ b/src/ORM/Association/BelongsToMany.php @@ -262,6 +262,18 @@ public function attachTo(Query $query, array $options = []) $query->eagerLoader()->addToJoinsMap($junction->alias(), $assoc, true); } + protected function _appendNotMatching($query, $options) + { + $target = $junction = $this->junction(); + if (!empty($options['negateMatch'])) { + $primaryKey = $query->aliasFields((array)$target->primaryKey(), $target->alias()); + $query->andWhere(function ($exp) use ($primaryKey) { + array_map([$exp, 'isNull'], $primaryKey); + return $exp; + }); + } + } + /** * {@inheritDoc} */ diff --git a/src/ORM/EagerLoader.php b/src/ORM/EagerLoader.php index 2a138d652c3..076da01b43e 100644 --- a/src/ORM/EagerLoader.php +++ b/src/ORM/EagerLoader.php @@ -191,9 +191,11 @@ public function matching($assoc = null, callable $builder = null, $options = []) $containments = []; $pointer =& $containments; $options += ['joinType' => 'INNER']; + $opts = ['matching' => true] + $options; + unset($opts['negateMatch']); foreach ($assocs as $name) { - $pointer[$name] = ['matching' => true] + $options; + $pointer[$name] = $opts; $pointer =& $pointer[$name]; } diff --git a/tests/TestCase/ORM/QueryTest.php b/tests/TestCase/ORM/QueryTest.php index 693213cff42..cbded6ccaff 100644 --- a/tests/TestCase/ORM/QueryTest.php +++ b/tests/TestCase/ORM/QueryTest.php @@ -2848,7 +2848,7 @@ public function testInnerJoinWithSelect() public function testNotMatching() { $table = TableRegistry::get('authors'); - $articles = $table->hasMany('articles'); + $table->hasMany('articles'); $results = $table->find() ->hydrate(false) @@ -2874,4 +2874,35 @@ public function testNotMatching() ]; $this->assertEquals($expected, $results); } + + public function testNotMatchingBelongsToMany() + { + $table = TableRegistry::get('articles'); + $table->belongsToMany('tags'); + + $results = $table->find() + ->hydrate(false) + ->notMatching('tags', function ($q) { + return $q->where(['tags.name' => 'tag2']); + }) + ->toArray(); + + $expected = [ + [ + 'id' => 2, + 'author_id' => 3, + 'title' => 'Second Article', + 'body' => 'Second Article Body', + 'published' => 'Y' + ], + [ + 'id' => 3, + 'author_id' => 1, + 'title' => 'Third Article', + 'body' => 'Third Article Body', + 'published' => 'Y' + ] + ]; + $this->assertEquals($expected, $results); + } }