diff --git a/CHANGELOG.md b/CHANGELOG.md index b21466c..d1cee5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,3 +6,4 @@ - `Torann\LaravelRepository\Repositories\AbstractRepository` renamed to `Torann\LaravelRepository\Repository` - `Torann\LaravelRepository\Traits\Cacheable` renamed to `Torann\LaravelRepository\Concerns\Cacheable` - Updated existing and added missing type declarations +- `orderBy()` can only be applied once (intended limitation) diff --git a/src/Concerns/Ordering.php b/src/Concerns/Ordering.php index 2ed98fb..60034ec 100644 --- a/src/Concerns/Ordering.php +++ b/src/Concerns/Ordering.php @@ -19,29 +19,32 @@ trait Ordering protected array $orderBy = []; /** - * One time skip of ordering. This is done when the - * ordering is overwritten by the orderBy method. + * Override of default order by column and direction pairs. */ - protected bool $skipOrderingOnce = false; + protected array|null $orderByOverride = null; /** * {@inheritDoc} */ public function orderBy(mixed $column, string|null $direction): static { - // Ensure the sort is valid - if (in_array($column, $this->getOrderableKeys()) === false) { - return $this; + if (in_array($column, $this->getOrderableKeys())) { + $this->orderByOverride = []; + + $this->orderByOverride[$column] = in_array(strtolower($direction ?? ''), ['desc', 'asc']) + ? $direction + : 'asc'; } - /** @var Scope|null $scope */ - if ($scope = $this->resolveScope('order_by')) { - $this->skipOrderingOnce = true; + return $this; + } - $this->addScopeQuery($scope::make( - Arr::get($this->getOrderable(), $column, $column), $direction - )); - } + /** + * @return static + */ + public function resetOrderBy(): static + { + $this->orderByOverride = null; return $this; } @@ -64,6 +67,16 @@ public function setOrderBy(array $order_by): static * @return array */ public function getOrderBy(): array + { + return $this->orderByOverride ?? $this->getDefaultOrderBy(); + } + + /** + * Return the default order by array. + * + * @return array + */ + public function getDefaultOrderBy(): array { return $this->orderBy; } @@ -117,6 +130,25 @@ public function getOrderableKeys(): array }, $return, array_keys($return))); } + /** + * @param string $column + * @param string $direction + * + * @return $this + */ + protected function applyOrderableScope(string $column, string $direction): static + { + /** @var Scope|null $scope */ + if ($scope = $this->resolveScope('order_by')) { + $this->addScopeQuery($scope::make( + Arr::get($this->getOrderable(), $column, $column), + $direction + ), 'order_by'); + } + + return $this; + } + /** * Add aa order by join to the query. * diff --git a/src/Concerns/Scopes.php b/src/Concerns/Scopes.php index 59d6c00..a8a1e1e 100644 --- a/src/Concerns/Scopes.php +++ b/src/Concerns/Scopes.php @@ -88,12 +88,22 @@ public function getScopeQuery(): array * Add query scope. * * @param Closure|Scope $scope + * @param string|null $key * * @return static */ - public function addScopeQuery(Closure|Scope $scope): static + public function addScopeQuery(Closure|Scope $scope, string $key = null): static { - $this->scopeQuery[] = $scope; + if ($scope instanceof Scope && $scope->shouldSkip()) { + return $this; + } + + // Prevent dupes when using some scopes + if ($key) { + $this->scopeQuery[$key] = $scope; + } else { + $this->scopeQuery[] = $scope; + } return $this; } diff --git a/src/Concerns/Searching.php b/src/Concerns/Searching.php index 5541074..551fb2b 100644 --- a/src/Concerns/Searching.php +++ b/src/Concerns/Searching.php @@ -86,10 +86,10 @@ public function search(string|array|null $queries): static ]; } - if (is_array($queries)) { + if (is_array($queries) && empty($queries) === false) { /** @var Scope|null $scope */ if ($scope = $this->resolveScope('search')) { - $this->addScopeQuery($scope::make($queries)); + $this->addScopeQuery($scope::make($queries), 'search'); } } diff --git a/src/Contracts/Scope.php b/src/Contracts/Scope.php index aae8417..c0883a9 100644 --- a/src/Contracts/Scope.php +++ b/src/Contracts/Scope.php @@ -13,6 +13,13 @@ interface Scope */ public static function make(...$arguments): static; + /** + * Determine if the scope be skipped + * + * @return bool + */ + public function shouldSkip(): bool; + /** * Apply the scope * diff --git a/src/Repository.php b/src/Repository.php index ec8914a..111535a 100755 --- a/src/Repository.php +++ b/src/Repository.php @@ -95,16 +95,16 @@ public function newQuery(bool $skipOrdering = false): static $this->query = $this->getNew()->newQuery(); - // Apply order by - if ($skipOrdering === false && $this->skipOrderingOnce === false) { - foreach ($this->getOrderBy() as $column => $dir) { - $this->query->orderBy($column, $dir); + // Apply order by scope. Only one order by is allowed, for more create a + // new scope or override the method. + if ($skipOrdering === false) { + if (empty($order_by = $this->getOrderBy()) === false) { + $this->applyOrderableScope( + key($order_by), reset($order_by) + ); } } - // Reset the one time skip - $this->skipOrderingOnce = false; - $this->applyScope(); return $this; diff --git a/src/Scopes/OrderBy.php b/src/Scopes/OrderBy.php index ae95cde..803ebe0 100644 --- a/src/Scopes/OrderBy.php +++ b/src/Scopes/OrderBy.php @@ -21,6 +21,14 @@ public function __construct(mixed $column, string|null $direction) $this->setDirection($direction); } + /** + * {@inheritDoc} + */ + public function shouldSkip(): bool + { + return false; + } + /** * {@inheritDoc} */ diff --git a/src/Scopes/Search.php b/src/Scopes/Search.php index 43e19b8..633c5f2 100644 --- a/src/Scopes/Search.php +++ b/src/Scopes/Search.php @@ -19,6 +19,14 @@ public function __construct(array $queries) }); } + /** + * {@inheritDoc} + */ + public function shouldSkip(): bool + { + return empty($this->queries); + } + /** * {@inheritDoc} */