From ec273055e7cfdfd2889f7224b9d66cfaa6dd9d05 Mon Sep 17 00:00:00 2001 From: indy koning Date: Thu, 24 Jul 2025 10:59:56 +0200 Subject: [PATCH 1/5] Support combined unique per store key --- .../ForCurrentStoreWithoutLimitScope.php | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php index 66434b3c0..084d3c8cc 100644 --- a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php +++ b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php @@ -5,13 +5,18 @@ use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; +use Illuminate\Support\Facades\DB; /** * Remove results from the default store when store view specific is found. */ class ForCurrentStoreWithoutLimitScope implements Scope { - public function __construct(public $uniquePerStoreKey, public $storeIdColumn = 'store_id') {} + public array $uniquePerStoreKeys; + + public function __construct(string|array $uniquePerStoreKey, public $storeIdColumn = 'store_id') { + $this->uniquePerStoreKeys = is_array($uniquePerStoreKey) ? $uniquePerStoreKey : [$uniquePerStoreKey]; + } public function apply(Builder $query, Model $model) { @@ -20,18 +25,28 @@ public function apply(Builder $query, Model $model) ->where($query->qualifyColumn($this->storeIdColumn), 0); } + $scope = $this; + return $query // Pre-filter results to be default and current store only. ->whereIn($query->qualifyColumn($this->storeIdColumn), [0, config('rapidez.store')]) // Remove values from the default store where values for the current store exist. ->where(fn ($query) => $query // Remove values where we already have values in the current store. - ->whereNotIn($query->qualifyColumn($this->uniquePerStoreKey), fn ($query) => $query - ->select($model->qualifyColumn($this->uniquePerStoreKey)) - ->from($model->getTable()) - ->whereColumn($model->qualifyColumn($this->uniquePerStoreKey), $model->qualifyColumn($this->uniquePerStoreKey)) - ->where($model->qualifyColumn($this->storeIdColumn), config('rapidez.store')) - ) + ->where(function ($query) use ($scope, $model) { + $columnKey = DB::Raw('CONCAT(' . collect($scope->uniquePerStoreKeys)->map(fn($key) => $query->qualifyColumn($key))->implode(",'-',") . ')'); + + $query + ->whereNotIn($columnKey, function ($query) use ($scope, $model, $columnKey) { + $query + ->select($columnKey) + ->from($model->getTable()) + ->where($model->qualifyColumn($scope->storeIdColumn), config('rapidez.store')); + foreach($scope->uniquePerStoreKeys as $uniquePerStoreKey) { + $query->whereColumn($model->qualifyColumn($uniquePerStoreKey), $model->qualifyColumn($uniquePerStoreKey)); + } + }); + }) // Unless the value IS the current store. ->orWhere($query->qualifyColumn($this->storeIdColumn), config('rapidez.store')) ); From c6465945d28ba6390554b23099616aa0dcff3fa5 Mon Sep 17 00:00:00 2001 From: indykoning Date: Thu, 24 Jul 2025 09:00:25 +0000 Subject: [PATCH 2/5] Apply fixes from Duster --- src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php index 084d3c8cc..b3b04bffd 100644 --- a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php +++ b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php @@ -14,7 +14,8 @@ class ForCurrentStoreWithoutLimitScope implements Scope { public array $uniquePerStoreKeys; - public function __construct(string|array $uniquePerStoreKey, public $storeIdColumn = 'store_id') { + public function __construct(string|array $uniquePerStoreKey, public $storeIdColumn = 'store_id') + { $this->uniquePerStoreKeys = is_array($uniquePerStoreKey) ? $uniquePerStoreKey : [$uniquePerStoreKey]; } @@ -34,7 +35,7 @@ public function apply(Builder $query, Model $model) ->where(fn ($query) => $query // Remove values where we already have values in the current store. ->where(function ($query) use ($scope, $model) { - $columnKey = DB::Raw('CONCAT(' . collect($scope->uniquePerStoreKeys)->map(fn($key) => $query->qualifyColumn($key))->implode(",'-',") . ')'); + $columnKey = DB::Raw('CONCAT(' . collect($scope->uniquePerStoreKeys)->map(fn ($key) => $query->qualifyColumn($key))->implode(",'-',") . ')'); $query ->whereNotIn($columnKey, function ($query) use ($scope, $model, $columnKey) { @@ -42,10 +43,10 @@ public function apply(Builder $query, Model $model) ->select($columnKey) ->from($model->getTable()) ->where($model->qualifyColumn($scope->storeIdColumn), config('rapidez.store')); - foreach($scope->uniquePerStoreKeys as $uniquePerStoreKey) { + foreach ($scope->uniquePerStoreKeys as $uniquePerStoreKey) { $query->whereColumn($model->qualifyColumn($uniquePerStoreKey), $model->qualifyColumn($uniquePerStoreKey)); } - }); + }); }) // Unless the value IS the current store. ->orWhere($query->qualifyColumn($this->storeIdColumn), config('rapidez.store')) From edf64c026be4954e115cc0bf769f39e228233e4c Mon Sep 17 00:00:00 2001 From: indykoning <15870933+indykoning@users.noreply.github.com> Date: Thu, 24 Jul 2025 11:10:45 +0200 Subject: [PATCH 3/5] Skip concat with a single key --- src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php index b3b04bffd..9320c1f24 100644 --- a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php +++ b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php @@ -35,7 +35,7 @@ public function apply(Builder $query, Model $model) ->where(fn ($query) => $query // Remove values where we already have values in the current store. ->where(function ($query) use ($scope, $model) { - $columnKey = DB::Raw('CONCAT(' . collect($scope->uniquePerStoreKeys)->map(fn ($key) => $query->qualifyColumn($key))->implode(",'-',") . ')'); + $columnKey = count($scope->uniquePerStoreKeys) === 1 ? $query->qualifyColumn($scope->uniquePerStoreKeys[0]) : DB::Raw('CONCAT(' . collect($scope->uniquePerStoreKeys)->map(fn ($key) => $query->qualifyColumn($key))->implode(",'-',") . ')'); $query ->whereNotIn($columnKey, function ($query) use ($scope, $model, $columnKey) { From 928224568e9bfce01c384510a9e4571af0ef4940 Mon Sep 17 00:00:00 2001 From: indykoning <15870933+indykoning@users.noreply.github.com> Date: Thu, 24 Jul 2025 12:59:36 +0200 Subject: [PATCH 4/5] Optimize query --- .../ForCurrentStoreWithoutLimitScope.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php index 9320c1f24..cf296acc9 100644 --- a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php +++ b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php @@ -14,8 +14,7 @@ class ForCurrentStoreWithoutLimitScope implements Scope { public array $uniquePerStoreKeys; - public function __construct(string|array $uniquePerStoreKey, public $storeIdColumn = 'store_id') - { + public function __construct(string|array $uniquePerStoreKey, public $storeIdColumn = 'store_id') { $this->uniquePerStoreKeys = is_array($uniquePerStoreKey) ? $uniquePerStoreKey : [$uniquePerStoreKey]; } @@ -35,18 +34,16 @@ public function apply(Builder $query, Model $model) ->where(fn ($query) => $query // Remove values where we already have values in the current store. ->where(function ($query) use ($scope, $model) { - $columnKey = count($scope->uniquePerStoreKeys) === 1 ? $query->qualifyColumn($scope->uniquePerStoreKeys[0]) : DB::Raw('CONCAT(' . collect($scope->uniquePerStoreKeys)->map(fn ($key) => $query->qualifyColumn($key))->implode(",'-',") . ')'); - $query - ->whereNotIn($columnKey, function ($query) use ($scope, $model, $columnKey) { + ->whereNotExists(function ($query) use ($scope, $model) { $query - ->select($columnKey) - ->from($model->getTable()) - ->where($model->qualifyColumn($scope->storeIdColumn), config('rapidez.store')); - foreach ($scope->uniquePerStoreKeys as $uniquePerStoreKey) { - $query->whereColumn($model->qualifyColumn($uniquePerStoreKey), $model->qualifyColumn($uniquePerStoreKey)); + ->select(DB::raw(1)) + ->from($model->getTable() . ' as comparison') + ->where('comparison.'.$this->storeIdColumn, config('rapidez.store')); + foreach($scope->uniquePerStoreKeys as $uniquePerStoreKey) { + $query->whereColumn('comparison.'.$uniquePerStoreKey, $model->qualifyColumn($uniquePerStoreKey)); } - }); + }); }) // Unless the value IS the current store. ->orWhere($query->qualifyColumn($this->storeIdColumn), config('rapidez.store')) From 6b15341e549ac88013301e75feab6ceb14653be7 Mon Sep 17 00:00:00 2001 From: indykoning Date: Thu, 24 Jul 2025 11:00:30 +0000 Subject: [PATCH 5/5] Apply fixes from Duster --- .../Scopes/ForCurrentStoreWithoutLimitScope.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php index cf296acc9..cb0321d18 100644 --- a/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php +++ b/src/Models/Scopes/ForCurrentStoreWithoutLimitScope.php @@ -14,7 +14,8 @@ class ForCurrentStoreWithoutLimitScope implements Scope { public array $uniquePerStoreKeys; - public function __construct(string|array $uniquePerStoreKey, public $storeIdColumn = 'store_id') { + public function __construct(string|array $uniquePerStoreKey, public $storeIdColumn = 'store_id') + { $this->uniquePerStoreKeys = is_array($uniquePerStoreKey) ? $uniquePerStoreKey : [$uniquePerStoreKey]; } @@ -39,11 +40,11 @@ public function apply(Builder $query, Model $model) $query ->select(DB::raw(1)) ->from($model->getTable() . ' as comparison') - ->where('comparison.'.$this->storeIdColumn, config('rapidez.store')); - foreach($scope->uniquePerStoreKeys as $uniquePerStoreKey) { - $query->whereColumn('comparison.'.$uniquePerStoreKey, $model->qualifyColumn($uniquePerStoreKey)); + ->where('comparison.' . $this->storeIdColumn, config('rapidez.store')); + foreach ($scope->uniquePerStoreKeys as $uniquePerStoreKey) { + $query->whereColumn('comparison.' . $uniquePerStoreKey, $model->qualifyColumn($uniquePerStoreKey)); } - }); + }); }) // Unless the value IS the current store. ->orWhere($query->qualifyColumn($this->storeIdColumn), config('rapidez.store'))