From 0c6f8bb52de4a1c2ad977e987fdcb2d0f00507b8 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Fri, 23 Jul 2021 16:37:32 +0300 Subject: [PATCH] orm: select item --- .../Espo/Core/Select/SelectBuilder.php | 3 + .../Espo/ORM/Query/Part/SelectExpression.php | 73 +++++++++++++++++++ .../Espo/ORM/Query/Part/SelectItem.php | 37 ++++++++++ application/Espo/ORM/Query/Select.php | 24 +++++- application/Espo/ORM/Query/SelectBuilder.php | 57 +++++++++++++-- application/Espo/ORM/QueryBuilder.php | 7 +- .../Espo/ORM/Repository/RDBRelation.php | 7 +- .../Repository/RDBRelationSelectBuilder.php | 7 +- .../Espo/ORM/Repository/RDBRepository.php | 9 ++- .../Espo/ORM/Repository/RDBSelectBuilder.php | 7 +- .../unit/Espo/ORM/Query/SelectBuilderTest.php | 69 ++++++++++++++++-- tests/unit/Espo/ORM/QueryBuilderTest.php | 18 ++++- 12 files changed, 289 insertions(+), 29 deletions(-) create mode 100644 application/Espo/ORM/Query/Part/SelectExpression.php create mode 100644 application/Espo/ORM/Query/Part/SelectItem.php diff --git a/application/Espo/Core/Select/SelectBuilder.php b/application/Espo/Core/Select/SelectBuilder.php index beff89a353..4c27c91837 100644 --- a/application/Espo/Core/Select/SelectBuilder.php +++ b/application/Espo/Core/Select/SelectBuilder.php @@ -68,6 +68,9 @@ class SelectBuilder private $sourceQuery = null; + /** + * @var SearchParams + */ private $searchParams = null; private $applyAccessControlFilter = false; diff --git a/application/Espo/ORM/Query/Part/SelectExpression.php b/application/Espo/ORM/Query/Part/SelectExpression.php new file mode 100644 index 0000000000..53ca8958f0 --- /dev/null +++ b/application/Espo/ORM/Query/Part/SelectExpression.php @@ -0,0 +1,73 @@ +expression = $expression; + $this->alias = $alias; + } + + public function getExpression(): Expression + { + return $this->expression; + } + + public function getAlias(): ?string + { + return $this->alias; + } + + public static function create(Expression $expression, ?string $alias = null): self + { + return new self($expression, $alias); + } + + public static function fromString(string $expression): self + { + return self::create( + Expression::create($expression) + ); + } + + public function withAlias(?string $alias): self + { + $obj = clone $this; + $obj->alias = $alias; + + return $obj; + } +} diff --git a/application/Espo/ORM/Query/Part/SelectItem.php b/application/Espo/ORM/Query/Part/SelectItem.php new file mode 100644 index 0000000000..8e07af9686 --- /dev/null +++ b/application/Espo/ORM/Query/Part/SelectItem.php @@ -0,0 +1,37 @@ +params['select'] ?? []; + return array_map( + function ($item) { + if (is_array($item) && count($item)) { + return SelectExpr::fromString($item[0]) + ->withAlias($item[1] ?? null); + } + + if (is_string($item)) { + return SelectExpr::fromString($item); + } + + throw new RuntimeException("Bad select item."); + }, + $this->params['select'] ?? [] + ); } /** diff --git a/application/Espo/ORM/Query/SelectBuilder.php b/application/Espo/ORM/Query/SelectBuilder.php index 47d356a80f..86448a790a 100644 --- a/application/Espo/ORM/Query/SelectBuilder.php +++ b/application/Espo/ORM/Query/SelectBuilder.php @@ -30,6 +30,7 @@ namespace Espo\ORM\Query; use Espo\ORM\Query\Part\Expression; +use Espo\ORM\Query\Part\SelectItem; use InvalidArgumentException; use RuntimeException; @@ -126,21 +127,23 @@ class SelectBuilder implements Builder /** * Specify SELECT. Columns and expressions to be selected. If not called, then * all entity attributes will be selected. Passing an array will reset - * previously set items. Passing a string will append an item. + * previously set items. Passing a string|Expression|SelectItem will append the item. * * Usage options: * * `select([$expr1, $expr2, ...])` * * `select([[$expr1, $alias1], [$expr2, $alias2], ...])` + * * `select([$selectItem1, $selectItem2, ...])` * * `select(string|Expression $expression)` * * `select(string|Expression $expression, string $alias)` + * * `select(SelectItem $selectItem)` * - * @param array|string|Expression $select An array of attributes or one attribute. + * @param array|string|Expression|SelectItem $select An array of expressions or one expression. * @param string|null $alias An alias. Actual if the first parameter is a string. */ public function select($select, ?string $alias = null): self { if (is_array($select)) { - $this->params['select'] = $this->normilizeExpressionItemArray($select); + $this->params['select'] = $this->normilizeSelectItemArray($select); return $this; } @@ -148,12 +151,15 @@ class SelectBuilder implements Builder if ($select instanceof Expression) { $select = $select->getValue(); } + else if ($select instanceof SelectItem) { + $alias = $alias ?? $select->getAlias(); + $select = $select->getExpression()->getValue(); + } if (is_string($select)) { $this->params['select'] = $this->params['select'] ?? []; - $this->params['select'][] = - $alias ? + $this->params['select'][] = $alias ? [$select, $alias] : $select; @@ -166,7 +172,7 @@ class SelectBuilder implements Builder /** * Specify GROUP BY. * Passing an array will reset previously set items. - * Passing a string will append an item. + * Passing a string|Expression will append an item. * * Usage options: * * `groupBy([$expr1, $expr2, ...])` @@ -264,4 +270,43 @@ class SelectBuilder implements Builder return $this; } + + private function normilizeSelectItemArray(array $itemList): array + { + $resultList = []; + + foreach ($itemList as $item) { + if ($item instanceof Expression) { + $resultList[] = $item->getValue(); + + continue; + } + + if ($item instanceof SelectItem) { + $resultList[] = $item->getAlias() ? + [$item->getExpression()->getValue(), $item->getAlias()] : + [$item->getExpression()->getValue()]; + + continue; + } + + if (!is_array($item) || !count($item) || !$item[0] instanceof Expression) { + $resultList[] = $item; + + continue; + } + + $newItem = [$item[0]->getValue()]; + + if (count($item) > 1) { + $newItem[] = $item[1]; + } + + $resultList[] = $newItem; + + continue; + } + + return $resultList; + } } diff --git a/application/Espo/ORM/QueryBuilder.php b/application/Espo/ORM/QueryBuilder.php index 1bbdc029de..7b5916f969 100644 --- a/application/Espo/ORM/QueryBuilder.php +++ b/application/Espo/ORM/QueryBuilder.php @@ -38,6 +38,7 @@ use Espo\ORM\{ Query\Query, Query\Builder, Query\Part\Expression, + Query\Part\SelectItem, }; use ReflectionClass; @@ -51,15 +52,17 @@ class QueryBuilder /** * Specify SELECT. Columns and expressions to be selected. If not called, then * all entity attributes will be selected. Passing an array will reset - * previously set items. Passing a string will append an item. + * previously set items. Passing a string|Expression|SelectItem will append the item. * * Usage options: * * `select([$expr1, $expr2, ...])` * * `select([[$expr1, $alias1], [$expr2, $alias2], ...])` + * * `select([$selectItem1, $selectItem2, ...])` * * `select(string|Expression $expression)` * * `select(string|Expression $expression, string $alias)` + * * `select(SelectItem $selectItem)` * - * @param array|string|Expression $select An array of attributes or one attribute. + * @param array|string|Expression|SelectItem $select An array of expressions or one expression. * @param string|null $alias An alias. Actual if the first parameter is a string. */ public function select($select = null, ?string $alias = null): SelectBuilder diff --git a/application/Espo/ORM/Repository/RDBRelation.php b/application/Espo/ORM/Repository/RDBRelation.php index e315445cdb..904b6c9e4d 100644 --- a/application/Espo/ORM/Repository/RDBRelation.php +++ b/application/Espo/ORM/Repository/RDBRelation.php @@ -35,6 +35,7 @@ use Espo\ORM\{ EntityManager, Query\Select, Query\Part\WhereItem, + Query\Part\SelectItem, Mapper\RDBMapper, Repository\RDBRelationSelectBuilder as Builder, }; @@ -285,15 +286,17 @@ class RDBRelation /** * Specify SELECT. Columns and expressions to be selected. If not called, then * all entity attributes will be selected. Passing an array will reset - * previously set items. Passing a string will append an item. + * previously set items. Passing a string|Expression|SelectItem will append the item. * * Usage options: * * `select([$expr1, $expr2, ...])` * * `select([[$expr1, $alias1], [$expr2, $alias2], ...])` + * * `select([$selectItem1, $selectItem2, ...])` * * `select(string|Expression $expression)` * * `select(string|Expression $expression, string $alias)` + * * `select(SelectItem $selectItem)` * - * @param array|string|Expression $select An array of attributes or one attribute. + * @param array|string|Expression|SelectItem $select An array of expressions or one expression. * @param string|null $alias An alias. Actual if the first parameter is a string. */ public function select($select = [], ?string $alias = null): Builder diff --git a/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php b/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php index 5c1ce901b1..f14f96973c 100644 --- a/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php +++ b/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php @@ -37,6 +37,7 @@ use Espo\ORM\{ Query\Select, Query\SelectBuilder, Query\Part\WhereItem, + Query\Part\SelectItem, Mapper\Mapper, }; @@ -363,15 +364,17 @@ class RDBRelationSelectBuilder /** * Specify SELECT. Columns and expressions to be selected. If not called, then * all entity attributes will be selected. Passing an array will reset - * previously set items. Passing a string will append an item. + * previously set items. Passing a string|Expression|SelectItem will append the item. * * Usage options: * * `select([$expr1, $expr2, ...])` * * `select([[$expr1, $alias1], [$expr2, $alias2], ...])` + * * `select([$selectItem1, $selectItem2, ...])` * * `select(string|Expression $expression)` * * `select(string|Expression $expression, string $alias)` + * * `select(SelectItem $selectItem)` * - * @param array|string|Expression $select An array of attributes or one attribute. + * @param array|string|Expression|SelectItem $select An array of expressions or one expression. * @param string|null $alias An alias. Actual if the first parameter is a string. */ public function select($select, ?string $alias = null): self diff --git a/application/Espo/ORM/Repository/RDBRepository.php b/application/Espo/ORM/Repository/RDBRepository.php index c4cae51c48..48e76bdc19 100644 --- a/application/Espo/ORM/Repository/RDBRepository.php +++ b/application/Espo/ORM/Repository/RDBRepository.php @@ -38,6 +38,7 @@ use Espo\ORM\{ Mapper\RDBMapper, Query\Select, Query\Part\WhereItem, + Query\Part\SelectItem, }; use StdClass; @@ -331,7 +332,7 @@ class RDBRepository extends Repository $selectItemList = $select->getSelect(); - if (empty($selectItemList)) { + if ($selectItemList === []) { $selectItemList[] = '*'; } @@ -781,15 +782,17 @@ class RDBRepository extends Repository /** * Specify SELECT. Columns and expressions to be selected. If not called, then * all entity attributes will be selected. Passing an array will reset - * previously set items. Passing a string will append an item. + * previously set items. Passing a string|Expression|SelectItem will append the item. * * Usage options: * * `select([$expr1, $expr2, ...])` * * `select([[$expr1, $alias1], [$expr2, $alias2], ...])` + * * `select([$selectItem1, $selectItem2, ...])` * * `select(string|Expression $expression)` * * `select(string|Expression $expression, string $alias)` + * * `select(SelectItem $selectItem)` * - * @param array|string|Expression $select An array of attributes or one attribute. + * @param array|string|Expression|SelectItem $select An array of expressions or one expression. * @param string|null $alias An alias. Actual if the first parameter is a string. */ public function select($select = [], ?string $alias = null): RDBSelectBuilder diff --git a/application/Espo/ORM/Repository/RDBSelectBuilder.php b/application/Espo/ORM/Repository/RDBSelectBuilder.php index 9e60e6c67b..6c110fb7cf 100644 --- a/application/Espo/ORM/Repository/RDBSelectBuilder.php +++ b/application/Espo/ORM/Repository/RDBSelectBuilder.php @@ -37,6 +37,7 @@ use Espo\ORM\{ Query\Select, Query\SelectBuilder, Query\Part\WhereItem, + Query\Part\SelectItem, Mapper\Mapper, }; @@ -303,15 +304,17 @@ class RDBSelectBuilder /** * Specify SELECT. Columns and expressions to be selected. If not called, then * all entity attributes will be selected. Passing an array will reset - * previously set items. Passing a string will append an item. + * previously set items. Passing a string|Expression|SelectItem will append the item. * * Usage options: * * `select([$expr1, $expr2, ...])` * * `select([[$expr1, $alias1], [$expr2, $alias2], ...])` + * * `select([$selectItem1, $selectItem2, ...])` * * `select(string|Expression $expression)` * * `select(string|Expression $expression, string $alias)` + * * `select(SelectItem $selectItem)` * - * @param array|string|Expression $select An array of attributes or one attribute. + * @param array|string|Expression|SelectItem $select An array of expressions or one expression. * @param string|null $alias An alias. Actual if the first parameter is a string. */ public function select($select, ?string $alias = null): self diff --git a/tests/unit/Espo/ORM/Query/SelectBuilderTest.php b/tests/unit/Espo/ORM/Query/SelectBuilderTest.php index 0108a63522..0d9060c040 100644 --- a/tests/unit/Espo/ORM/Query/SelectBuilderTest.php +++ b/tests/unit/Espo/ORM/Query/SelectBuilderTest.php @@ -33,6 +33,7 @@ use Espo\ORM\{ Query\SelectBuilder, Query\Part\Condition as Cond, Query\Part\Expression as Expr, + Query\Part\SelectExpression, }; class SelectBuilderTest extends \PHPUnit\Framework\TestCase @@ -65,7 +66,14 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ->select('test') ->build(); - $this->assertEquals(['id', 'name', 'test'], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('id'), + SelectExpression::fromString('name'), + SelectExpression::fromString('test'), + ], + $select->getSelect() + ); } public function testSelect2() @@ -76,7 +84,13 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ->select(['id', 'name']) ->build(); - $this->assertEquals(['id', 'name'], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('id'), + SelectExpression::fromString('name'), + ], + $select->getSelect() + ); } public function testSelect3() @@ -86,7 +100,12 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ->select('test', 'hello') ->build(); - $this->assertEquals([['test', 'hello']], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('test')->withAlias('hello'), + ], + $select->getSelect() + ); } public function testSelect4() @@ -96,7 +115,12 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ->select(Expr::create('test'), 'hello') ->build(); - $this->assertEquals([['test', 'hello']], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('test')->withAlias('hello'), + ], + $select->getSelect() + ); } public function testSelect5() @@ -107,7 +131,13 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ->select([Expr::create('id'), Expr::create('name')]) ->build(); - $this->assertEquals(['id', 'name'], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('id'), + SelectExpression::fromString('name'), + ], + $select->getSelect() + ); } public function testSelect6() @@ -121,7 +151,34 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ]) ->build(); - $this->assertEquals([['id', 'id'], ['name', 'name']], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('id')->withAlias('id'), + SelectExpression::fromString('name')->withAlias('name'), + ], + $select->getSelect() + ); + } + + public function testSelect7() + { + $select = $this->builder + ->from('Test') + ->select([ + 'id', + SelectExpression::create(Expr::create('name')) + ]) + ->select(SelectExpression::fromString('test')->withAlias('testAlias')) + ->build(); + + $this->assertEquals( + [ + SelectExpression::fromString('id'), + SelectExpression::fromString('name'), + SelectExpression::fromString('test')->withAlias('testAlias'), + ], + $select->getSelect() + ); } public function testCloneNotSame() diff --git a/tests/unit/Espo/ORM/QueryBuilderTest.php b/tests/unit/Espo/ORM/QueryBuilderTest.php index 7d8231a07b..bdb0456808 100644 --- a/tests/unit/Espo/ORM/QueryBuilderTest.php +++ b/tests/unit/Espo/ORM/QueryBuilderTest.php @@ -36,6 +36,7 @@ use Espo\ORM\{ Query\Update, Query\Delete, Query\Union, + Query\Part\SelectExpression, }; use RuntimeException; @@ -96,7 +97,13 @@ class QueryBuilderTest extends \PHPUnit\Framework\TestCase ->from('Test') ->build(); - $this->assertEquals(['col1', 'col2'], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('col1'), + SelectExpression::fromString('col2'), + ], + $select->getSelect() + ); } public function testSelectFrom2() @@ -106,7 +113,12 @@ class QueryBuilderTest extends \PHPUnit\Framework\TestCase ->from('Test') ->build(); - $this->assertEquals([['col1', 'alias1']], $select->getSelect()); + $this->assertEquals( + [ + SelectExpression::fromString('col1')->withAlias('alias1') + ], + $select->getSelect() + ); } public function testInsert1() { @@ -237,5 +249,5 @@ class QueryBuilderTest extends \PHPUnit\Framework\TestCase $this->queryBuilder ->union() ->build(); - } + } }