From d197391668ddf6fcd64e7c784bb808557ee43eaa Mon Sep 17 00:00:00 2001 From: yuri Date: Thu, 7 Mar 2019 17:40:28 +0200 Subject: [PATCH] orm select manager changes --- application/Espo/Core/SelectManagers/Base.php | 23 +++++-- application/Espo/ORM/DB/Query/Base.php | 61 ++++++++++++++++--- tests/unit/Espo/ORM/DB/QueryTest.php | 10 +++ 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/application/Espo/Core/SelectManagers/Base.php b/application/Espo/Core/SelectManagers/Base.php index d97f3ca2d2..ee4127b035 100644 --- a/application/Espo/Core/SelectManagers/Base.php +++ b/application/Espo/Core/SelectManagers/Base.php @@ -823,6 +823,12 @@ class Base $type = $w['type']; } + if ($forbidComplexExpressions) { + if ($type && in_array($type, ['subQueryIn', 'subQueryNotIn', 'not'])) { + throw new Forbidden("SelectManager::checkWhere: Sub-queries are forbidden."); + } + } + $entityType = $this->getEntityType(); if ($attribute && $forbidComplexExpressions) { @@ -835,7 +841,7 @@ class Base if (strpos($attribute, '.')) { list($link, $attribute) = explode('.', $attribute); if (!$this->getSeed()->hasRelation($link)) { - throw new Forbidden("SelectManager::checkWhere: Unknow relation '{$link}' in where."); + throw new Forbidden("SelectManager::checkWhere: Unknown relation '{$link}' in where."); } $entityType = $this->getSeed($this->getEntityType())->getRelationParam($link, 'entity'); if (!$entityType) { @@ -2312,7 +2318,7 @@ class Base return $selectParams1; } - protected function applyLeftJoinsFromWhere($where, array &$result) + public function applyLeftJoinsFromWhere($where, array &$result) { if (!is_array($where)) return; @@ -2321,7 +2327,7 @@ class Base } } - protected function applyLeftJoinsFromWhereItem($item, array &$result) + public function applyLeftJoinsFromWhereItem($item, array &$result) { $type = $item['type'] ?? null; @@ -2341,8 +2347,17 @@ class Base $attribute = $item['attribute'] ?? null; if (!$attribute) return; + $this->applyLeftJoinsFromAttribute($attribute, $result); + } + + protected function applyLeftJoinsFromAttribute(string $attribute, array &$result) + { if (strpos($attribute, ':') !== false) { - list($function, $attribute) = explode(':', $attribute); + $argumentList = \Espo\ORM\DB\Query\Base::getAllAttributesFromComplexExpression($attribute); + foreach ($argumentList as $argument) { + $this->applyLeftJoinsFromAttribute($argument, $result); + } + return; } if (strpos($attribute, '.') !== false) { diff --git a/application/Espo/ORM/DB/Query/Base.php b/application/Espo/ORM/DB/Query/Base.php index fef747c19d..95fff8ef7c 100644 --- a/application/Espo/ORM/DB/Query/Base.php +++ b/application/Espo/ORM/DB/Query/Base.php @@ -508,6 +508,39 @@ abstract class Base return $part; } + public static function getAllAttributesFromComplexExpression(string $expression, &$list = null) : array + { + if (!$list) $list = []; + + $arguments = $expression; + + if (strpos($expression, ':')) { + $dilimeterPosition = strpos($expression, ':'); + $function = substr($expression, 0, $dilimeterPosition); + $arguments = substr($expression, $dilimeterPosition + 1); + if (substr($arguments, 0, 1) === '(' && substr($arguments, -1) === ')') { + $arguments = substr($arguments, 1, -1); + } + } else { + if ( + !self::isArgumentString($expression) && + !self::isArgumentNumeric($expression) && + !self::isArgumentBoolOrNull($expression) + ) { + $list[] = $expression; + } + return []; + } + + $argumentList = self::parseArgumentListFromFunctionContent($arguments); + + foreach ($argumentList as $argument) { + self::getAllAttributesFromComplexExpression($argument, $list); + } + + return $list; + } + static protected function parseArgumentListFromFunctionContent($functionContent) { $functionContent = trim($functionContent); @@ -571,22 +604,36 @@ abstract class Base return $argumentList; } + protected static function isArgumentString(string $argument) + { + return + substr($argument, 0, 1) === '\'' && substr($argument, -1) === '\'' + || + substr($argument, 0, 1) === '"' && substr($argument, -1) === '"'; + } + + protected static function isArgumentNumeric(string $argument) + { + return is_numeric($argument); + } + + protected static function isArgumentBoolOrNull(string $argument) + { + return in_array(strtoupper($argument), ['NULL', 'TRUE', 'FALSE']); + } + protected function getFunctionArgumentPart($entity, $attribute, $distinct = false, &$params = null) { $argument = $attribute; - if ( - substr($argument, 0, 1) === '\'' && substr($argument, -1) === '\'' - || - substr($argument, 0, 1) === '"' && substr($argument, -1) === '"' - ) { + if (self::isArgumentString($argument)) { $string = substr($argument, 1, -1); $string = $this->quote($string); return $string; - } else if (is_numeric($argument)) { + } else if (self::isArgumentNumeric($argument)) { $string = $this->quote($argument); return $string; - } else if (in_array(strtoupper($argument), ['NULL', 'TRUE', 'FALSE'])) { + } else if (self::isArgumentBoolOrNull($argument)) { return strtoupper($argument); } diff --git a/tests/unit/Espo/ORM/DB/QueryTest.php b/tests/unit/Espo/ORM/DB/QueryTest.php index d7da31fd22..1a8662b2bb 100644 --- a/tests/unit/Espo/ORM/DB/QueryTest.php +++ b/tests/unit/Espo/ORM/DB/QueryTest.php @@ -739,4 +739,14 @@ class QueryTest extends \PHPUnit\Framework\TestCase $this->assertEquals($expectedSql, $sql); } + + public function testGetAllAttributesFromComplexExpression() + { + $expression = "CONCAT:(MONTH:comment.created_at,' ',CONCAT:(comment.name,'+'))"; + + $list = $this->query::getAllAttributesFromComplexExpression($expression); + + $this->assertTrue(in_array('comment.created_at', $list)); + $this->assertTrue(in_array('comment.name', $list)); + } }