Browse Source

Implements QueryExpression::exists and QueryExpression::notExists methods

Yevgeny Tomenko 9 years ago
parent
commit
ebeeb731f7
2 changed files with 69 additions and 1 deletions
  1. 22 0
      src/Database/Expression/QueryExpression.php
  2. 47 1
      tests/TestCase/Database/QueryTest.php

+ 22 - 0
src/Database/Expression/QueryExpression.php

@@ -373,6 +373,28 @@ class QueryExpression implements ExpressionInterface, Countable
     }
 
     /**
+     * Adds a new condition to the expression object in the form "EXISTS (...)".
+     *
+     * @param \Cake\Database\ExpressionInterface $query the inner query
+     * @return $this
+     */
+    public function exists(ExpressionInterface $query)
+    {
+        return $this->add(new UnaryExpression('EXISTS', $query, UnaryExpression::PREFIX));
+    }
+
+    /**
+     * Adds a new condition to the expression object in the form "NOT EXISTS (...)".
+     *
+     * @param \Cake\Database\ExpressionInterface $query the inner query
+     * @return $this
+     */
+    public function notExists(ExpressionInterface $query)
+    {
+        return $this->add(new UnaryExpression('NOT EXISTS', $query, UnaryExpression::PREFIX));
+    }
+
+    /**
      * Adds a new condition to the expression object in the form
      * "field BETWEEN from AND to".
      *

+ 47 - 1
tests/TestCase/Database/QueryTest.php

@@ -1046,7 +1046,7 @@ class QueryTest extends TestCase
      */
     public function testSelectWhereOperatorMethods()
     {
-        $this->loadFixtures('Articles', 'Comments');
+        $this->loadFixtures('Articles', 'Comments', 'Authors');
         $query = new Query($this->connection);
         $result = $query
             ->select(['title'])
@@ -2266,6 +2266,52 @@ class QueryTest extends TestCase
     }
 
     /**
+     * Tests that Query objects can be included inside the where clause
+     * and be used as a EXISTS and NOT EXISTS conditions
+     *
+     * @return void
+     */
+    public function testSubqueryExistsWhere()
+    {
+        $this->loadFixtures('Articles', 'Authors');
+        $query = new Query($this->connection);
+        $subQuery = (new Query($this->connection))
+            ->select(['id'])
+            ->from('articles')
+            ->where(function ($exp) {
+                return $exp->equalFields('authors.id', 'articles.author_id');
+            });
+        $result = $query
+            ->select(['id'])
+            ->from('authors')
+            ->where(function ($exp) use ($subQuery) {
+                return $exp->exists($subQuery);
+            })
+            ->execute();
+        $this->assertCount(2, $result);
+        $this->assertEquals(['id' => 1], $result->fetch('assoc'));
+        $this->assertEquals(['id' => 3], $result->fetch('assoc'));
+
+        $query = new Query($this->connection);
+        $subQuery = (new Query($this->connection))
+            ->select(['id'])
+            ->from('articles')
+            ->where(function ($exp) {
+                return $exp->equalFields('authors.id', 'articles.author_id');
+            });
+        $result = $query
+            ->select(['id'])
+            ->from('authors')
+            ->where(function ($exp) use ($subQuery) {
+                return $exp->notExists($subQuery);
+            })
+            ->execute();
+        $this->assertCount(2, $result);
+        $this->assertEquals(['id' => 2], $result->fetch('assoc'));
+        $this->assertEquals(['id' => 4], $result->fetch('assoc'));
+    }
+
+    /**
      * Tests that it is possible to use a subquery in a join clause
      *
      * @return void