From fab113db25a415893689d8df1fb5f96936c4ea06 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Sun, 25 Jul 2021 18:30:50 +0300 Subject: [PATCH] orm order refactoring --- .../Espo/ORM/Query/Part/Expression.php | 12 +++++++ .../Espo/ORM/Query/Part/OrderExpression.php | 22 +++++++++++-- .../Espo/ORM/Query/SelectingBuilderTrait.php | 2 +- .../ORM/QueryComposer/BaseQueryComposer.php | 3 ++ .../Espo/ORM/QueryComposer/Functions.php | 2 ++ .../Espo/ORM/Repository/RDBRelation.php | 2 +- .../Repository/RDBRelationSelectBuilder.php | 2 +- .../Espo/ORM/Repository/RDBRepository.php | 2 +- .../Espo/ORM/Repository/RDBSelectBuilder.php | 2 +- .../unit/Espo/ORM/MysqlQueryComposerTest.php | 33 +++++++++++++++++-- .../ORM/Query/Parts/OrderExpressionTest.php | 13 ++++++-- .../unit/Espo/ORM/Query/SelectBuilderTest.php | 4 --- 12 files changed, 83 insertions(+), 16 deletions(-) diff --git a/application/Espo/ORM/Query/Part/Expression.php b/application/Espo/ORM/Query/Part/Expression.php index 34be4978a9..88e57e49c7 100644 --- a/application/Espo/ORM/Query/Part/Expression.php +++ b/application/Espo/ORM/Query/Part/Expression.php @@ -541,6 +541,18 @@ class Expression implements WhereItem return self::composeFunction('REPLACE', $haystack, $needle, $replaceWith); } + /** + * 'FIELD' operator (in MySQL). Returns an index (position) of an expression + * in a list. Returns `0` if not found. The first index is `1`. + * + * @param Expression $expression + * @param Expression[]|string[]|int[]|float[] $list + */ + public static function positionInList(Expression $expression, array $list): self + { + return self::composeFunction('POSITION_IN_LIST', $expression, ...$list); + } + /** * 'ADD' function. Adds two or more numbers. * diff --git a/application/Espo/ORM/Query/Part/OrderExpression.php b/application/Espo/ORM/Query/Part/OrderExpression.php index e722943be1..7dd9abecc7 100644 --- a/application/Espo/ORM/Query/Part/OrderExpression.php +++ b/application/Espo/ORM/Query/Part/OrderExpression.php @@ -88,11 +88,16 @@ class OrderExpression } /** - * Create with a position in SELECT. + * Create an order by position in list. + * Note: Reverses the list and applies DESC order. + * + * @param string[]|int[]|float[] $list */ - public static function createWithPosition(int $positionInSelect): self + public static function createByPositionInList(Expression $expression, array $list): self { - return self::fromString((string) $positionInSelect); + $orderExpression = Expression::positionInList($expression, array_reverse($list)); + + return self::create($orderExpression)->withDesc(); } /** @@ -134,4 +139,15 @@ class OrderExpression return $obj; } + + /** + * Clone with a reverse direction. + */ + public function withReverseDirection(): self + { + $obj = clone $this; + $obj->isDesc = !$this->isDesc; + + return $obj; + } } diff --git a/application/Espo/ORM/Query/SelectingBuilderTrait.php b/application/Espo/ORM/Query/SelectingBuilderTrait.php index 3fb8328e00..6dbf5cd66f 100644 --- a/application/Espo/ORM/Query/SelectingBuilderTrait.php +++ b/application/Espo/ORM/Query/SelectingBuilderTrait.php @@ -107,7 +107,7 @@ trait SelectingBuilderTrait * * `order([$expr1, $expr2, ...]) * * `order(string $expression, string $direction) * - * @param OrderExpression|OrderExpression[]|Expression|string|int $orderBy + * @param OrderExpression|OrderExpression[]|Expression|string $orderBy * An attribute to order by or an array or order items. * Passing an array will reset a previously set order. * @param string|bool|null $direction OrderExpression::ASC|OrderExpression::DESC. diff --git a/application/Espo/ORM/QueryComposer/BaseQueryComposer.php b/application/Espo/ORM/QueryComposer/BaseQueryComposer.php index dedbec22d2..2ad8829d4c 100644 --- a/application/Espo/ORM/QueryComposer/BaseQueryComposer.php +++ b/application/Espo/ORM/QueryComposer/BaseQueryComposer.php @@ -1021,6 +1021,9 @@ abstract class BaseQueryComposer implements QueryComposer case 'TIMESTAMPDIFF_SECOND': return 'TIMESTAMPDIFF(SECOND, ' . implode(', ', $argumentPartList) . ')'; + + case 'POSITION_IN_LIST': + return 'FIELD(' . implode(', ', $argumentPartList) . ')'; } if ($distinct) { diff --git a/application/Espo/ORM/QueryComposer/Functions.php b/application/Espo/ORM/QueryComposer/Functions.php index c8567c2905..e33ffd996d 100644 --- a/application/Espo/ORM/QueryComposer/Functions.php +++ b/application/Espo/ORM/QueryComposer/Functions.php @@ -129,6 +129,7 @@ class Functions 'TIMESTAMPDIFF_HOUR', 'TIMESTAMPDIFF_MINUTE', 'TIMESTAMPDIFF_SECOND', + 'POSITION_IN_LIST', ]; public const MULTIPLE_ARGUMENT_FUNCTION_LIST = [ @@ -165,6 +166,7 @@ class Functions 'TIMESTAMPDIFF_HOUR', 'TIMESTAMPDIFF_MINUTE', 'TIMESTAMPDIFF_SECOND', + 'POSITION_IN_LIST', ]; public const COMPARISON_FUNCTION_LIST = [ diff --git a/application/Espo/ORM/Repository/RDBRelation.php b/application/Espo/ORM/Repository/RDBRelation.php index 1d58b72939..1199fca569 100644 --- a/application/Espo/ORM/Repository/RDBRelation.php +++ b/application/Espo/ORM/Repository/RDBRelation.php @@ -266,7 +266,7 @@ class RDBRelation * * `order([$expr1, $expr2, ...]) * * `order(string $expression, string $direction) * - * @param OrderExpression|OrderExpression[]|Expression|string|int $orderBy + * @param OrderExpression|OrderExpression[]|Expression|string $orderBy * An attribute to order by or an array or order items. * Passing an array will reset a previously set order. * @param string|bool|null $direction Select::ORDER_ASC|Select::ORDER_DESC. diff --git a/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php b/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php index bc4ba31dae..f546ebe19f 100644 --- a/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php +++ b/application/Espo/ORM/Repository/RDBRelationSelectBuilder.php @@ -343,7 +343,7 @@ class RDBRelationSelectBuilder * * `order([$expr1, $expr2, ...]) * * `order(string $expression, string $direction) * - * @param OrderExpression|OrderExpression[]|Expression|string|int $orderBy + * @param OrderExpression|OrderExpression[]|Expression|string $orderBy * An attribute to order by or an array or order items. * Passing an array will reset a previously set order. * @param string|bool|null $direction Select::ORDER_ASC|Select::ORDER_DESC. diff --git a/application/Espo/ORM/Repository/RDBRepository.php b/application/Espo/ORM/Repository/RDBRepository.php index 428866c614..30c7d467bc 100644 --- a/application/Espo/ORM/Repository/RDBRepository.php +++ b/application/Espo/ORM/Repository/RDBRepository.php @@ -762,7 +762,7 @@ class RDBRepository extends Repository * * `order([$expr1, $expr2, ...]) * * `order(string $expression, string $direction) * - * @param OrderExpression|OrderExpression[]|Expression|string|int $orderBy + * @param OrderExpression|OrderExpression[]|Expression|string $orderBy * An attribute to order by or an array or order items. * Passing an array will reset a previously set order. * @param string|bool|null $direction Select::ORDER_ASC|Select::ORDER_DESC. diff --git a/application/Espo/ORM/Repository/RDBSelectBuilder.php b/application/Espo/ORM/Repository/RDBSelectBuilder.php index aa9754ad41..90c91205ef 100644 --- a/application/Espo/ORM/Repository/RDBSelectBuilder.php +++ b/application/Espo/ORM/Repository/RDBSelectBuilder.php @@ -280,7 +280,7 @@ class RDBSelectBuilder * * `order([$expr1, $expr2, ...]) * * `order(string $expression, string $direction) * - * @param OrderExpression|OrderExpression[]|Expression|string|int $orderBy + * @param OrderExpression|OrderExpression[]|Expression|string $orderBy * An attribute to order by or an array or order items. * Passing an array will reset a previously set order. * @param string|bool|null $direction Select::ORDER_ASC|Select::ORDER_DESC. diff --git a/tests/unit/Espo/ORM/MysqlQueryComposerTest.php b/tests/unit/Espo/ORM/MysqlQueryComposerTest.php index ea0a79dd8b..e5369e9b32 100644 --- a/tests/unit/Espo/ORM/MysqlQueryComposerTest.php +++ b/tests/unit/Espo/ORM/MysqlQueryComposerTest.php @@ -37,6 +37,7 @@ use Espo\ORM\{ EntityManager, MetadataDataProvider, Query\Part\Expression, + Query\Part\OrderExpression, }; use Espo\ORM\Query\{ @@ -984,7 +985,7 @@ class MysqlQueryComposerTest extends \PHPUnit\Framework\TestCase $this->assertEquals($expectedSql, $sql); } - public function testOrderBy() + public function testOrderBy1() { $sql = $this->query->compose(Select::fromRaw([ 'from' => 'Comment', @@ -1038,7 +1039,7 @@ class MysqlQueryComposerTest extends \PHPUnit\Framework\TestCase $this->assertEquals($expectedSql, $sql); } - public function testOrderByList() + public function testOrderByList1() { $select = $this->queryBuilder ->select('id') @@ -1061,6 +1062,34 @@ class MysqlQueryComposerTest extends \PHPUnit\Framework\TestCase $this->assertEquals($expectedSql, $sql); } + public function testOrderByList2() + { + $select = $this->queryBuilder + ->select('id') + ->from('Comment') + ->leftJoin('post') + ->order( + OrderExpression::createByPositionInList( + Expression::column('post.name'), + [ + 'Test', + 'Hello', + ] + ) + ) + ->build(); + + $sql = $this->query->compose($select); + + $expectedSql = + "SELECT comment.id AS `id` FROM `comment` " . + "LEFT JOIN `post` AS `post` ON comment.post_id = post.id " . + "WHERE comment.deleted = 0 " . + "ORDER BY FIELD(post.name, 'Hello', 'Test') DESC"; + + $this->assertEquals($expectedSql, $sql); + } + public function testOrderByExpression1() { $select = $this->queryBuilder diff --git a/tests/unit/Espo/ORM/Query/Parts/OrderExpressionTest.php b/tests/unit/Espo/ORM/Query/Parts/OrderExpressionTest.php index 39fa8e247d..14f769ad74 100644 --- a/tests/unit/Espo/ORM/Query/Parts/OrderExpressionTest.php +++ b/tests/unit/Espo/ORM/Query/Parts/OrderExpressionTest.php @@ -53,9 +53,9 @@ class OrderExpressionTest extends \PHPUnit\Framework\TestCase public function testCreate3(): void { - $order = OrderExpr::createWithPosition(1)->withAsc(); + $order = OrderExpr::fromString('test')->withAsc(); - $this->assertEquals(Expr::create('1'), $order->getExpression()); + $this->assertEquals(Expr::create('test'), $order->getExpression()); $this->assertEquals(OrderExpr::ASC, $order->getDirection()); $this->assertEquals(false, $order->isDesc()); } @@ -66,4 +66,13 @@ class OrderExpressionTest extends \PHPUnit\Framework\TestCase $this->assertEquals(OrderExpr::DESC, $order->getDirection()); } + + public function testReverseOrder(): void + { + $order = OrderExpr::fromString('test') + ->withDirection(OrderExpr::DESC) + ->withReverseDirection(); + + $this->assertEquals(OrderExpr::ASC, $order->getDirection()); + } } diff --git a/tests/unit/Espo/ORM/Query/SelectBuilderTest.php b/tests/unit/Espo/ORM/Query/SelectBuilderTest.php index e8f3c95c14..b5b3256d7b 100644 --- a/tests/unit/Espo/ORM/Query/SelectBuilderTest.php +++ b/tests/unit/Espo/ORM/Query/SelectBuilderTest.php @@ -342,14 +342,12 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ->from('Test') ->order(OrderExpression::fromString('test')->withDesc()) ->order('hello', true) - ->order(2, OrderExpression::ASC) ->build(); $this->assertEquals( [ OrderExpression::fromString('test')->withDesc(), OrderExpression::fromString('hello')->withDesc(), - OrderExpression::createWithPosition(2)->withAsc(), ], $select->getOrder() ); @@ -362,7 +360,6 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase ->order([ OrderExpression::fromString('test')->withDesc(), ['hello', true], - [2, OrderExpression::ASC], ]) ->build(); @@ -370,7 +367,6 @@ class SelectBuilderTest extends \PHPUnit\Framework\TestCase [ OrderExpression::fromString('test')->withDesc(), OrderExpression::fromString('hello')->withDesc(), - OrderExpression::createWithPosition(2)->withAsc(), ], $select->getOrder() );