From f1ee5cc3809bff8bd97f6d59a893f97df44aa4c3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= <me@jiripudil.cz>
Date: Fri, 28 Jul 2023 14:41:17 +0200
Subject: [PATCH] implement ordering by specific values

---
 src/ExpressionWithShorthands.php | 14 +++++++++++--
 src/OrderBy/OrderBy.php          | 26 +++---------------------
 src/OrderBy/OrderByDirection.php | 35 ++++++++++++++++++++++++++++++++
 src/OrderBy/OrderByValues.php    | 33 ++++++++++++++++++++++++++++++
 src/TableManager.php             |  3 ++-
 tests/TableTest.php              | 10 +++++++++
 6 files changed, 95 insertions(+), 26 deletions(-)
 create mode 100644 src/OrderBy/OrderByDirection.php
 create mode 100644 src/OrderBy/OrderByValues.php

diff --git a/src/ExpressionWithShorthands.php b/src/ExpressionWithShorthands.php
index 37f3c85..d56b407 100644
--- a/src/ExpressionWithShorthands.php
+++ b/src/ExpressionWithShorthands.php
@@ -10,6 +10,8 @@ use Grifart\Tables\Conditions\IsNull;
 use Grifart\Tables\OrderBy\Direction;
 use Grifart\Tables\OrderBy\Nulls;
 use Grifart\Tables\OrderBy\OrderBy;
+use Grifart\Tables\OrderBy\OrderByDirection;
+use Grifart\Tables\OrderBy\OrderByValues;
 
 /**
  * @template ValueType
@@ -35,11 +37,19 @@ abstract class ExpressionWithShorthands implements Expression
 
 	public function ascending(Nulls|null $nulls = null): OrderBy
 	{
-		return new OrderBy($this, nulls: $nulls);
+		return new OrderByDirection($this, nulls: $nulls);
 	}
 
 	public function descending(Nulls|null $nulls = null): OrderBy
 	{
-		return new OrderBy($this, direction: Direction::Descending, nulls: $nulls);
+		return new OrderByDirection($this, direction: Direction::Descending, nulls: $nulls);
+	}
+
+	/**
+	 * @param list<ValueType> $values
+	 */
+	public function byValues(array $values): OrderBy
+	{
+		return new OrderByValues($this, $values);
 	}
 }
diff --git a/src/OrderBy/OrderBy.php b/src/OrderBy/OrderBy.php
index a5fe538..932fdc4 100644
--- a/src/OrderBy/OrderBy.php
+++ b/src/OrderBy/OrderBy.php
@@ -5,31 +5,11 @@ declare(strict_types=1);
 namespace Grifart\Tables\OrderBy;
 
 use Dibi\Expression as DibiExpression;
-use Grifart\Tables\Expression;
 
-final class OrderBy
+interface OrderBy
 {
-	private readonly Nulls $nulls;
-
 	/**
-	 * @param Expression<mixed> $expression
+	 * @return DibiExpression the ORDER BY part of a Dibi query
 	 */
-	public function __construct(
-		private readonly Expression $expression,
-		private readonly Direction $direction = Direction::Ascending,
-		Nulls|null $nulls = null,
-	)
-	{
-		$this->nulls = $nulls ?? Nulls::default($this->direction);
-	}
-
-	public function toSql(): DibiExpression
-	{
-		return new DibiExpression(
-			'? %ex %ex',
-			$this->expression->toSql(),
-			$this->direction->toSql(),
-			$this->nulls->toSql(),
-		);
-	}
+	public function toSql(): DibiExpression;
 }
diff --git a/src/OrderBy/OrderByDirection.php b/src/OrderBy/OrderByDirection.php
new file mode 100644
index 0000000..30c2716
--- /dev/null
+++ b/src/OrderBy/OrderByDirection.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Grifart\Tables\OrderBy;
+
+use Dibi\Expression as DibiExpression;
+use Grifart\Tables\Expression;
+
+final class OrderByDirection implements OrderBy
+{
+	private readonly Nulls $nulls;
+
+	/**
+	 * @param Expression<mixed> $expression
+	 */
+	public function __construct(
+		private readonly Expression $expression,
+		private readonly Direction $direction = Direction::Ascending,
+		Nulls|null $nulls = null,
+	)
+	{
+		$this->nulls = $nulls ?? Nulls::default($this->direction);
+	}
+
+	public function toSql(): DibiExpression
+	{
+		return new DibiExpression(
+			'? %ex %ex',
+			$this->expression->toSql(),
+			$this->direction->toSql(),
+			$this->nulls->toSql(),
+		);
+	}
+}
diff --git a/src/OrderBy/OrderByValues.php b/src/OrderBy/OrderByValues.php
new file mode 100644
index 0000000..a318c34
--- /dev/null
+++ b/src/OrderBy/OrderByValues.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Grifart\Tables\OrderBy;
+
+use Dibi\Expression as DibiExpression;
+use Grifart\Tables\Expression;
+use Grifart\Tables\Types\ArrayType;
+
+/**
+ * @template ValueType
+ */
+final class OrderByValues implements OrderBy
+{
+	/**
+	 * @param Expression<ValueType> $expression
+	 * @param ValueType[] $values
+	 */
+	public function __construct(
+		private readonly Expression $expression,
+		private readonly array $values,
+	) {}
+
+	public function toSql(): DibiExpression
+	{
+		return new DibiExpression(
+			'array_position(?, ?)',
+			(ArrayType::of($this->expression->getType()))->toDatabase($this->values),
+			$this->expression->toSql(),
+		);
+	}
+}
diff --git a/src/TableManager.php b/src/TableManager.php
index 1299ae3..75bd5ea 100644
--- a/src/TableManager.php
+++ b/src/TableManager.php
@@ -8,6 +8,7 @@ use Dibi\Connection;
 use Grifart\Tables\Conditions\Composite;
 use Grifart\Tables\Conditions\Condition;
 use Grifart\Tables\OrderBy\OrderBy;
+use Grifart\Tables\OrderBy\OrderByDirection;
 use Nette\Utils\Paginator;
 use function Functional\map;
 
@@ -85,7 +86,7 @@ final class TableManager
 			'ORDER BY %by', \count($orderBy) > 0
 				? map($orderBy, function (OrderBy|Expression $orderBy) {
 					if ($orderBy instanceof Expression) {
-						$orderBy = new OrderBy($orderBy);
+						$orderBy = new OrderByDirection($orderBy);
 					}
 
 					return $orderBy->toSql()->getValues();
diff --git a/tests/TableTest.php b/tests/TableTest.php
index 0262c0d..edfccfa 100644
--- a/tests/TableTest.php
+++ b/tests/TableTest.php
@@ -72,6 +72,16 @@ $orderByNullsLast = $table->findBy([], orderBy: [$table->details()->ascending(Nu
 Assert::count(3, $orderByNullsLast);
 Assert::same(null, $orderByNullsLast[2]->getDetails());
 
+$orderByValues = $table->findBy([], orderBy: [$table->id()->byValues([
+	new Uuid('2bec3f23-a210-455c-b907-bb69a99d07b2'),
+	new Uuid('9493decd-4b9c-45d6-9960-0c94dc9be353'),
+	new Uuid('fb05a832-5729-4b1f-b064-fbc08cacbe43'),
+])]);
+Assert::count(3, $orderByValues);
+Assert::same('2bec3f23-a210-455c-b907-bb69a99d07b2', $orderByValues[0]->getId()->get());
+Assert::same('9493decd-4b9c-45d6-9960-0c94dc9be353', $orderByValues[1]->getId()->get());
+Assert::same('fb05a832-5729-4b1f-b064-fbc08cacbe43', $orderByValues[2]->getId()->get());
+
 $paginator = new Paginator();
 $paginator->setItemsPerPage(1);
 $paginator->setPage(2);
-- 
GitLab