From 693e0bf8b2a6b2d7eb6f40cab79dac3189ea352d Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 22 Aug 2025 11:19:37 +0100 Subject: [PATCH 1/5] Use `QueriesJsonColumns` trait in entry query builder --- src/Entries/EntryQueryBuilder.php | 113 +++++++----------------------- 1 file changed, 26 insertions(+), 87 deletions(-) diff --git a/src/Entries/EntryQueryBuilder.php b/src/Entries/EntryQueryBuilder.php index 5e6633fa..5d43bf76 100644 --- a/src/Entries/EntryQueryBuilder.php +++ b/src/Entries/EntryQueryBuilder.php @@ -5,19 +5,23 @@ use Illuminate\Support\Str; use Statamic\Contracts\Entries\QueryBuilder; use Statamic\Eloquent\Entries\Entry as EloquentEntry; +use Statamic\Eloquent\QueriesJsonColumns; use Statamic\Entries\EntryCollection; use Statamic\Facades\Blink; use Statamic\Facades\Collection; use Statamic\Facades\Entry; use Statamic\Facades\Taxonomy; +use Statamic\Fields\Field; use Statamic\Query\EloquentQueryBuilder; use Statamic\Stache\Query\QueriesEntryStatus; use Statamic\Stache\Query\QueriesTaxonomizedEntries; +use Illuminate\Support\Collection as IlluminateCollection; class EntryQueryBuilder extends EloquentQueryBuilder implements QueryBuilder { use QueriesEntryStatus, - QueriesTaxonomizedEntries; + QueriesTaxonomizedEntries, + QueriesJsonColumns; private $selectedQueryColumns; @@ -28,92 +32,6 @@ class EntryQueryBuilder extends EloquentQueryBuilder implements QueryBuilder 'date', 'collection', 'created_at', 'updated_at', 'order', 'blueprint', ]; - public function orderBy($column, $direction = 'asc') - { - $actualColumn = $this->column($column); - - if (Str::contains($actualColumn, 'data->')) { - $wheres = collect($this->builder->getQuery()->wheres); - - if ($wheres->where('column', 'collection')->count() == 1) { - $collectionWhere = $wheres->firstWhere('column', 'collection'); - if (isset($collectionWhere['values']) && count($collectionWhere['values']) == 1) { - $collectionWhere['value'] = $collectionWhere['values'][0]; - } - - if (isset($collectionWhere['value'])) { - if ($collection = Collection::find($collectionWhere['value'])) { - $blueprintField = $collection->entryBlueprints() - ->flatMap(function ($blueprint) { - return $blueprint->fields() - ->all() - ->filter(function ($field) { - return in_array($field->type(), ['float', 'integer', 'date']); - }); - }) - ->filter() - ->get($column); - - if ($blueprintField) { - $castType = ''; - $fieldType = $blueprintField->type(); - - $grammar = $this->builder->getConnection()->getQueryGrammar(); - $actualColumn = $grammar->wrap($actualColumn); - - if (in_array($fieldType, ['float'])) { - $castType = 'float'; - } elseif (in_array($fieldType, ['integer'])) { - $castType = 'float'; // bit sneaky but mysql doesnt support casting as integer, it wants unsigned - } elseif (in_array($fieldType, ['date'])) { - // Take time into account when enabled - if ($blueprintField->get('time_enabled')) { - $castType = 'datetime'; - } else { - $castType = 'date'; - } - - // take range into account - if ($blueprintField->get('mode') == 'range') { - $actualColumnStartDate = $grammar->wrap($this->column($column).'->start'); - $actualColumnEndDate = $grammar->wrap($this->column($column).'->end'); - if (str_contains(get_class($grammar), 'SQLiteGrammar')) { - $this->builder - ->orderByRaw("datetime({$actualColumnStartDate}) {$direction}") - ->orderByRaw("datetime({$actualColumnEndDate}) {$direction}"); - } else { - $this->builder - ->orderByRaw("cast({$actualColumnStartDate} as {$castType}) {$direction}") - ->orderByRaw("cast({$actualColumnEndDate} as {$castType}) {$direction}"); - } - - return $this; - } - - // sqlite casts dates to year, which is pretty unhelpful - if (str_contains(get_class($grammar), 'SQLiteGrammar')) { - $this->builder->orderByRaw("datetime({$actualColumn}) {$direction}"); - - return $this; - } - } - - if ($castType) { - $this->builder->orderByRaw("cast({$actualColumn} as {$castType}) {$direction}"); - - return $this; - } - } - } - } - } - } - - parent::orderBy($column, $direction); - - return $this; - } - protected function transform($items, $columns = []) { $items = EntryCollection::make($items)->map(function ($model) use ($columns) { @@ -292,4 +210,25 @@ private function getKeysForTaxonomyWhereIn($where) ->pluck('id'); }); } + + protected function getJsonCasts(): IlluminateCollection + { + $wheres = collect($this->builder->getQuery()->wheres); + $collectionWhere = $wheres->firstWhere('column', 'collection'); + + if (! $collectionWhere || ! isset($collectionWhere['value'])) { + return []; + } + + $collection = Collection::find($collectionWhere['value']); + + return $collection->entryBlueprints() + ->flatMap(function ($blueprint) { + return $blueprint->fields() + ->all() + ->filter(fn ($field) => in_array($field->type(), ['float', 'integer', 'date'])) + ->map(fn (Field $field) => $this->toCast($field)); + }) + ->filter(); + } } From 6aa83cb4a1a999f1d024065b69710dd44c9e1901 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 22 Aug 2025 11:19:43 +0100 Subject: [PATCH 2/5] Rename tests --- tests/Data/Entries/EntryQueryBuilderTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Data/Entries/EntryQueryBuilderTest.php b/tests/Data/Entries/EntryQueryBuilderTest.php index 980708a7..500fbb61 100644 --- a/tests/Data/Entries/EntryQueryBuilderTest.php +++ b/tests/Data/Entries/EntryQueryBuilderTest.php @@ -764,7 +764,7 @@ public function entries_can_be_ordered_by_an_integer_json_field() } #[Test] - public function entries_can_be_ordered_by_an_float_json_field() + public function entries_can_be_ordered_by_a_float_json_field() { $blueprint = Blueprint::makeFromFields(['float' => ['type' => 'float']]); Blueprint::shouldReceive('in')->with('collections/posts')->andReturn(collect(['posts' => $blueprint])); @@ -781,7 +781,7 @@ public function entries_can_be_ordered_by_an_float_json_field() } #[Test] - public function entries_can_be_ordered_by_an_date_json_field() + public function entries_can_be_ordered_by_a_date_json_field() { $blueprint = Blueprint::makeFromFields(['date_field' => ['type' => 'date', 'time_enabled' => true]]); Blueprint::shouldReceive('in')->with('collections/posts')->andReturn(collect(['posts' => $blueprint])); From dc3a13979f586b2fc0a334d330e50b9d7f0e5674 Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Fri, 22 Aug 2025 10:20:07 +0000 Subject: [PATCH 3/5] Fix styling --- src/Entries/EntryQueryBuilder.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Entries/EntryQueryBuilder.php b/src/Entries/EntryQueryBuilder.php index 5d43bf76..a0904bd3 100644 --- a/src/Entries/EntryQueryBuilder.php +++ b/src/Entries/EntryQueryBuilder.php @@ -2,6 +2,7 @@ namespace Statamic\Eloquent\Entries; +use Illuminate\Support\Collection as IlluminateCollection; use Illuminate\Support\Str; use Statamic\Contracts\Entries\QueryBuilder; use Statamic\Eloquent\Entries\Entry as EloquentEntry; @@ -15,13 +16,12 @@ use Statamic\Query\EloquentQueryBuilder; use Statamic\Stache\Query\QueriesEntryStatus; use Statamic\Stache\Query\QueriesTaxonomizedEntries; -use Illuminate\Support\Collection as IlluminateCollection; class EntryQueryBuilder extends EloquentQueryBuilder implements QueryBuilder { use QueriesEntryStatus, - QueriesTaxonomizedEntries, - QueriesJsonColumns; + QueriesJsonColumns, + QueriesTaxonomizedEntries; private $selectedQueryColumns; From c850e7667120c2ac5e2861730d7b11f5b0212e1d Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 22 Aug 2025 11:26:24 +0100 Subject: [PATCH 4/5] Handle cast where `Collection::find()` result is null --- src/Entries/EntryQueryBuilder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Entries/EntryQueryBuilder.php b/src/Entries/EntryQueryBuilder.php index a0904bd3..d0063fa2 100644 --- a/src/Entries/EntryQueryBuilder.php +++ b/src/Entries/EntryQueryBuilder.php @@ -220,7 +220,9 @@ protected function getJsonCasts(): IlluminateCollection return []; } - $collection = Collection::find($collectionWhere['value']); + if (! ($collection = Collection::find($collectionWhere['value']))) { + return []; + } return $collection->entryBlueprints() ->flatMap(function ($blueprint) { From e1a3f069c2b97038bebc15ffe951f7627df0b3fc Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 22 Aug 2025 11:27:36 +0100 Subject: [PATCH 5/5] simplify conditional --- src/Entries/EntryQueryBuilder.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Entries/EntryQueryBuilder.php b/src/Entries/EntryQueryBuilder.php index d0063fa2..2bec7f03 100644 --- a/src/Entries/EntryQueryBuilder.php +++ b/src/Entries/EntryQueryBuilder.php @@ -216,11 +216,11 @@ protected function getJsonCasts(): IlluminateCollection $wheres = collect($this->builder->getQuery()->wheres); $collectionWhere = $wheres->firstWhere('column', 'collection'); - if (! $collectionWhere || ! isset($collectionWhere['value'])) { - return []; - } - - if (! ($collection = Collection::find($collectionWhere['value']))) { + if ( + ! $collectionWhere + || ! isset($collectionWhere['value']) + || ! ($collection = Collection::find($collectionWhere['value'])) + ) { return []; }