From 5eaeabe231ea9eea5ff3fbfd10da121e2f0ee27a Mon Sep 17 00:00:00 2001 From: yuri Date: Wed, 26 Apr 2017 16:32:24 +0300 Subject: [PATCH] ORM: Subqueries IN and NOT --- application/Espo/Core/SelectManagers/Base.php | 1 + application/Espo/ORM/DB/Query/Base.php | 38 +++++++++++-- .../SelectManagers/{Base.php => BaseTest.php} | 30 +++++++++++ tests/unit/Espo/ORM/DB/QueryTest.php | 53 +++++++++++++++++++ 4 files changed, 118 insertions(+), 4 deletions(-) rename tests/unit/Espo/Core/SelectManagers/{Base.php => BaseTest.php} (90%) diff --git a/application/Espo/Core/SelectManagers/Base.php b/application/Espo/Core/SelectManagers/Base.php index 98ad14a892..e1b314b552 100644 --- a/application/Espo/Core/SelectManagers/Base.php +++ b/application/Espo/Core/SelectManagers/Base.php @@ -985,6 +985,7 @@ class Base switch ($item['type']) { case 'or': case 'and': + case 'not': if (is_array($item['value'])) { $arr = array(); foreach ($item['value'] as $i) { diff --git a/application/Espo/ORM/DB/Query/Base.php b/application/Espo/ORM/DB/Query/Base.php index 9969757320..8bcb77228f 100644 --- a/application/Espo/ORM/DB/Query/Base.php +++ b/application/Espo/ORM/DB/Query/Base.php @@ -62,13 +62,15 @@ abstract class Base ); protected static $comparisonOperators = array( + '!=s' => 'NOT IN', + '=s' => 'IN', '!=' => '<>', '*' => 'LIKE', '>=' => '>=', '<=' => '<=', '>' => '>', '<' => '<', - '=' => '=', + '=' => '=' ); protected $entityFactory; @@ -636,11 +638,21 @@ abstract class Base $field = 'AND'; } + if ($field === 'NOT') { + $field = 'id!=s'; + $value = array( + 'selectParams' => array( + 'select' => ['id'], + 'whereClause' => $value + ) + ); + } if (!in_array($field, self::$sqlOperators)) { $isComplex = false; $operator = '='; + $operatorOrm = '='; $leftPart = null; @@ -648,6 +660,7 @@ abstract class Base foreach (self::$comparisonOperators as $op => $opDb) { if (strpos($field, $op) !== false) { $field = trim(str_replace($op, '', $field)); + $operatorOrm = $op; $operator = $opDb; break; } @@ -745,9 +758,27 @@ abstract class Base } } } - if (!empty($leftPart)) { - if (!is_array($value)) { + + if ($operatorOrm === '=s' || $operatorOrm === '!=s') { + if (!is_array($value)) { + continue; + } + if (!empty($value['entityType'])) { + $subQueryEntityType = $value['entityType']; + } else { + $subQueryEntityType = $entity->getEntityType(); + } + $subQuerySelectParams = array(); + if (!empty($value['selectParams'])) { + $subQuerySelectParams = $value['selectParams']; + } + $withDeleted = false; + if (!empty($value['withDeleted'])) { + $withDeleted = true; + } + $whereParts[] = $leftPart . " " . $operator . " (" . $this->createSelectQuery($subQueryEntityType, $subQuerySelectParams, $withDeleted) . ")"; + } else if (!is_array($value)) { if (!is_null($value)) { $whereParts[] = $leftPart . " " . $operator . " " . $this->pdo->quote($value); } else { @@ -757,7 +788,6 @@ abstract class Base $whereParts[] = $leftPart . " IS NOT NULL"; } } - } else { $valArr = $value; foreach ($valArr as $k => $v) { diff --git a/tests/unit/Espo/Core/SelectManagers/Base.php b/tests/unit/Espo/Core/SelectManagers/BaseTest.php similarity index 90% rename from tests/unit/Espo/Core/SelectManagers/Base.php rename to tests/unit/Espo/Core/SelectManagers/BaseTest.php index 20a2a33491..886a47127b 100644 --- a/tests/unit/Espo/Core/SelectManagers/Base.php +++ b/tests/unit/Espo/Core/SelectManagers/BaseTest.php @@ -243,4 +243,34 @@ class BaseTest extends \PHPUnit_Framework_TestCase $this->assertEquals('2016-10-10', $selectParams['whereClause'][1]['OR'][0]['date=']); $this->assertEquals('2016-10-10 10:10:00', $selectParams['whereClause'][1]['OR'][1]['dateTime>']); } + + function testBuildSelectParamsNot() + { + $selectManager = new \Espo\Core\SelectManagers\Base($this->entityManager, $this->user, $this->acl, $this->aclManager, $this->metadata, $this->config); + $selectManager->setEntityType('Test2'); + + $params = array( + 'where' => array( + array( + 'type' => 'not', + 'value' => array( + array( + 'type' => 'equals', + 'field'=> 'date', + 'value' => '2016-10-10' + ), + array( + 'type' => 'after', + 'field'=> 'dateTime', + 'value' => '2016-10-10 10:10:00' + ) + ) + ) + ) + ); + + $selectParams = $selectManager->buildSelectParams($params); + + $this->assertEquals('2016-10-10', $selectParams['whereClause'][0]['NOT'][0]['date=']); + } } diff --git a/tests/unit/Espo/ORM/DB/QueryTest.php b/tests/unit/Espo/ORM/DB/QueryTest.php index 3dae842ae1..ba9b00c3a2 100644 --- a/tests/unit/Espo/ORM/DB/QueryTest.php +++ b/tests/unit/Espo/ORM/DB/QueryTest.php @@ -224,6 +224,59 @@ class QueryTest extends PHPUnit_Framework_TestCase $this->assertEquals($expectedSql, $sql); } + public function testSelectWithSubquery() + { + $sql = $this->query->createSelectQuery('Post', array( + 'select' => ['id', 'name'], + 'whereClause' => array( + 'post.id=s' => array( + 'entityType' => 'Post', + 'selectParams' => array( + 'select' => ['id'], + 'whereClause' => array( + 'name' => 'test' + ) + ) + ) + ) + )); + + $expectedSql = "SELECT post.id AS `id`, post.name AS `name` FROM `post` WHERE post.id IN (SELECT post.id AS `id` FROM `post` WHERE post.name = 'test' AND post.deleted = '0') AND post.deleted = '0'"; + $this->assertEquals($expectedSql, $sql); + + $sql = $this->query->createSelectQuery('Post', array( + 'select' => ['id', 'name'], + 'whereClause' => array( + 'post.id!=s' => array( + 'entityType' => 'Post', + 'selectParams' => array( + 'select' => ['id'], + 'whereClause' => array( + 'name' => 'test' + ) + ) + ) + ) + )); + + $expectedSql = "SELECT post.id AS `id`, post.name AS `name` FROM `post` WHERE post.id NOT IN (SELECT post.id AS `id` FROM `post` WHERE post.name = 'test' AND post.deleted = '0') AND post.deleted = '0'"; + $this->assertEquals($expectedSql, $sql); + + + $sql = $this->query->createSelectQuery('Post', array( + 'select' => ['id', 'name'], + 'whereClause' => array( + 'NOT'=> array( + 'name' => 'test', + 'post.createdById' => '1' + ) + ) + )); + + $expectedSql = "SELECT post.id AS `id`, post.name AS `name` FROM `post` WHERE post.id NOT IN (SELECT post.id AS `id` FROM `post` WHERE post.name = 'test' AND post.created_by_id = '1' AND post.deleted = '0') AND post.deleted = '0'"; + $this->assertEquals($expectedSql, $sql); + } + public function testOrderBy() { $sql = $this->query->createSelectQuery('Comment', array(