From 97b016366e92f8ee62dea2ff4eeeeab5c2fa25cd Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 12:38:46 +0100 Subject: [PATCH 01/17] Add tests Modelled after similar tests in the EntryQueryBuilderTest --- tests/Data/Assets/AssetQueryBuilderTest.php | 95 +++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/tests/Data/Assets/AssetQueryBuilderTest.php b/tests/Data/Assets/AssetQueryBuilderTest.php index 67bde735..9d37bbdf 100644 --- a/tests/Data/Assets/AssetQueryBuilderTest.php +++ b/tests/Data/Assets/AssetQueryBuilderTest.php @@ -508,6 +508,101 @@ public function assets_are_found_using_where_column() $this->assertEquals(['Post 1', 'Post 2', 'Post 5', 'Post 6'], $entries->map->foo->all()); } + #[Test] + public function assets_can_be_ordered_by_an_integer_json_column() + { + $blueprint = Blueprint::makeFromFields(['integer' => ['type' => 'integer']]); + Blueprint::shouldReceive('find')->with('assets/test')->andReturn($blueprint); + + Asset::find('test::a.jpg')->data(['integer' => 3])->save(); + Asset::find('test::b.txt')->data(['integer' => 5])->save(); + Asset::find('test::c.txt')->data(['integer' => 1])->save(); + Asset::find('test::d.jpg')->data(['integer' => 35])->save(); + Asset::find('test::e.jpg')->data(['integer' => 20])->save(); + Asset::find('test::f.jpg')->data(['integer' => 12])->save(); + + $assets = Asset::query()->where('container', 'test')->orderBy('integer', 'asc')->get(); + + $this->assertCount(6, $assets); + $this->assertEquals(['c', 'a', 'b', 'f', 'e', 'd'], $assets->map->filename()->all()); + } + + #[Test] + public function assets_can_be_ordered_by_a_float_json_column() + { + $blueprint = Blueprint::makeFromFields(['float' => ['type' => 'float']]); + Blueprint::shouldReceive('find')->with('assets/test')->andReturn($blueprint); + + Asset::find('test::a.jpg')->data(['float' => 3.3])->save(); + Asset::find('test::b.txt')->data(['float' => 5.5])->save(); + Asset::find('test::c.txt')->data(['float' => 1.1])->save(); + Asset::find('test::d.jpg')->data(['float' => 35.5])->save(); + Asset::find('test::e.jpg')->data(['float' => 20.0])->save(); + Asset::find('test::f.jpg')->data(['float' => 12.2])->save(); + + $assets = Asset::query()->where('container', 'test')->orderBy('float', 'asc')->get(); + + $this->assertCount(6, $assets); + $this->assertEquals(['c', 'a', 'b', 'f', 'e', 'd'], $assets->map->filename()->all()); + } + + #[Test] + public function assets_can_be_ordered_by_a_date_json_field() + { + $blueprint = Blueprint::makeFromFields(['date_field' => ['type' => 'date', 'time_enabled' => true]]); + Blueprint::shouldReceive('find')->with('assets/test')->andReturn($blueprint); + + Asset::find('test::a.jpg')->data(['date_field' => '2021-06-15 20:31:04'])->save(); + Asset::find('test::b.txt')->data(['date_field' => '2021-01-13 20:31:04'])->save(); + Asset::find('test::c.txt')->data(['date_field' => '2021-11-17 20:31:04'])->save(); + Asset::find('test::d.jpg')->data(['date_field' => '2023-01-01 20:31:04'])->save(); + Asset::find('test::e.jpg')->data(['date_field' => '2025-01-01 20:31:04'])->save(); + Asset::find('test::f.jpg')->data(['date_field' => '2024-01-01 20:31:04'])->save(); + + $assets = Asset::query()->where('container', 'test')->orderBy('date_field', 'asc')->get(); + + $this->assertCount(6, $assets); + $this->assertEquals(['b', 'a', 'c', 'd', 'f', 'e'], $assets->map->filename()->all()); + } + + #[Test] + public function assets_can_be_ordered_by_a_datetime_range_json_field() + { + $blueprint = Blueprint::makeFromFields(['date_field' => ['type' => 'date', 'time_enabled' => true, 'mode' => 'range']]); + Blueprint::shouldReceive('find')->with('assets/test')->andReturn($blueprint); + + Asset::find('test::a.jpg')->data(['date_field' => ['start' => '2021-06-15 20:31:04', 'end' => '2021-06-15 21:00:00']])->save(); + Asset::find('test::b.txt')->data(['date_field' => ['start' => '2021-01-13 20:31:04', 'end' => '2021-06-16 20:31:04']])->save(); + Asset::find('test::c.txt')->data(['date_field' => ['start' => '2021-11-17 20:31:04', 'end' => '2021-11-17 20:31:04']])->save(); + Asset::find('test::d.jpg')->data(['date_field' => ['start' => '2021-06-15 20:31:04', 'end' => '2021-06-15 22:00:00']])->save(); + Asset::find('test::e.jpg')->data(['date_field' => ['start' => '2024-06-15 20:31:04', 'end' => '2024-06-15 22:00:00']])->save(); + Asset::find('test::f.jpg')->data(['date_field' => ['start' => '2025-06-15 20:31:04', 'end' => '2025-06-15 22:00:00']])->save(); + + $assets = Asset::query()->where('container', 'test')->orderBy('date_field', 'asc')->get(); + + $this->assertCount(6, $assets); + $this->assertEquals(['b', 'a', 'd', 'c', 'e', 'f'], $assets->map->filename()->all()); + } + + #[Test] + public function assets_can_be_ordered_by_a_date_range_json_field() + { + $blueprint = Blueprint::makeFromFields(['date_field' => ['type' => 'date', 'time_enabled' => false, 'mode' => 'range']]); + Blueprint::shouldReceive('find')->with('assets/test')->andReturn($blueprint); + + Asset::find('test::a.jpg')->data(['date_field' => ['start' => '2021-06-15', 'end' => '2021-06-15']])->save(); + Asset::find('test::b.txt')->data(['date_field' => ['start' => '2021-01-13', 'end' => '2021-06-16']])->save(); + Asset::find('test::c.txt')->data(['date_field' => ['start' => '2021-11-17', 'end' => '2021-11-17']])->save(); + Asset::find('test::d.jpg')->data(['date_field' => ['start' => '2021-06-15', 'end' => '2021-06-15']])->save(); + Asset::find('test::e.jpg')->data(['date_field' => ['start' => '2024-06-15', 'end' => '2024-06-15']])->save(); + Asset::find('test::f.jpg')->data(['date_field' => ['start' => '2025-06-15', 'end' => '2025-06-15']])->save(); + + $assets = Asset::query()->where('container', 'test')->orderBy('date_field', 'asc')->get(); + + $this->assertCount(6, $assets); + $this->assertEquals(['b', 'a', 'd', 'c', 'e', 'f'], $assets->map->filename()->all()); + } + #[Test] public function it_can_get_assets_using_when() { From 28607bf126c385ef4c000b7b0b9dd78f65192637 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 12:39:19 +0100 Subject: [PATCH 02/17] Unrelated, but tweak method names in EntryQueryBuilderTest --- tests/Data/Entries/EntryQueryBuilderTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/Data/Entries/EntryQueryBuilderTest.php b/tests/Data/Entries/EntryQueryBuilderTest.php index 980708a7..a895b553 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])); @@ -808,6 +808,7 @@ public function entries_can_be_ordered_by_a_datetime_range_json_field() EntryFactory::id('2')->slug('post-2')->collection('posts')->data(['title' => 'Post 2', 'date_field' => ['start' => '2021-01-13 20:31:04', 'end' => '2021-06-16 20:31:04']])->create(); EntryFactory::id('3')->slug('post-3')->collection('posts')->data(['title' => 'Post 3', 'date_field' => ['start' => '2021-11-17 20:31:04', 'end' => '2021-11-17 20:31:04']])->create(); EntryFactory::id('4')->slug('post-4')->collection('posts')->data(['title' => 'Post 4', 'date_field' => ['start' => '2021-06-15 20:31:04', 'end' => '2021-06-15 22:00:00']])->create(); + $entries = Entry::query()->where('collection', 'posts')->orderBy('date_field', 'asc')->get(); $this->assertCount(4, $entries); From a82cb194e40e686239bfab07be964ec336f205f1 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 14:47:40 +0100 Subject: [PATCH 03/17] Fix asset sorting --- src/Assets/AssetQueryBuilder.php | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index 56ccdf25..f06da4c8 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -2,8 +2,11 @@ namespace Statamic\Eloquent\Assets; +use Illuminate\Support\Str; use Statamic\Assets\AssetCollection; use Statamic\Contracts\Assets\QueryBuilder; +use Statamic\Facades\Collection; +use Statamic\Fields\Field; use Statamic\Query\EloquentQueryBuilder; class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder @@ -16,6 +19,88 @@ class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder 'size', 'width', 'height', 'duration', 'mime_type', 'last_modified', ]; + public function orderBy($column, $direction = 'asc') + { + $actualColumn = $this->column($column); + + if (Str::contains($actualColumn, 'meta->')) { + $wheres = collect($this->builder->getQuery()->wheres); + + if ($wheres->where('column', 'container')->count() == 1) { + $containerWhere = $wheres->firstWhere('column', 'container'); + if (isset($containerWhere['values']) && count($containerWhere['values']) == 1) { + $containerWhere['value'] = $containerWhere['values'][0]; + } + + if (isset($containerWhere['value'])) { + // todo: the entryquerybuilder expects value to be a string, but here it seems to be an AssetContainer instance + if ($container = $containerWhere['value']) { + $blueprintField = $container->blueprint()->fields()->all() + ->filter(fn ($field) => in_array($field->type(), ['float', 'integer', 'date'])) + ->filter() + ->merge(['size' => new Field('size', ['type' => 'integer'])]) + ->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 column($column) { if (in_array($column, self::META_COLUMNS)) { From 16b042bd61ecec827baff9bc3adb072e68f4b520 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 15:26:54 +0100 Subject: [PATCH 04/17] wip --- src/Assets/AssetQueryBuilder.php | 149 ++++++++++++++++--------------- 1 file changed, 79 insertions(+), 70 deletions(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index f06da4c8..c51caa0f 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -2,10 +2,10 @@ namespace Statamic\Eloquent\Assets; +use Illuminate\Support\Collection as IlluminateCollection; use Illuminate\Support\Str; use Statamic\Assets\AssetCollection; use Statamic\Contracts\Assets\QueryBuilder; -use Statamic\Facades\Collection; use Statamic\Fields\Field; use Statamic\Query\EloquentQueryBuilder; @@ -23,77 +23,26 @@ public function orderBy($column, $direction = 'asc') { $actualColumn = $this->column($column); - if (Str::contains($actualColumn, 'meta->')) { - $wheres = collect($this->builder->getQuery()->wheres); + if ( + Str::contains($actualColumn, 'meta->') + && $metaColumnCast = $this->getMetaColumnCasts()->get($column) + ) { + $grammar = $this->builder->getConnection()->getQueryGrammar(); + $actualColumn = $grammar->wrap($actualColumn); + + // SQLite casts dates to year, which is pretty unhelpful. + if ( + in_array($metaColumnCast['cast'], ['date', 'datetime']) + && Str::contains(get_class($grammar), 'SQLiteGrammar') + ) { + $this->builder->orderByRaw("datetime({$actualColumn}) {$direction}"); + + return $this; + } - if ($wheres->where('column', 'container')->count() == 1) { - $containerWhere = $wheres->firstWhere('column', 'container'); - if (isset($containerWhere['values']) && count($containerWhere['values']) == 1) { - $containerWhere['value'] = $containerWhere['values'][0]; - } + $this->builder->orderByRaw("cast({$actualColumn} as {$metaColumnCast['cast']}) {$direction}"); - if (isset($containerWhere['value'])) { - // todo: the entryquerybuilder expects value to be a string, but here it seems to be an AssetContainer instance - if ($container = $containerWhere['value']) { - $blueprintField = $container->blueprint()->fields()->all() - ->filter(fn ($field) => in_array($field->type(), ['float', 'integer', 'date'])) - ->filter() - ->merge(['size' => new Field('size', ['type' => 'integer'])]) - ->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; - } - } - } - } - } + return $this; } parent::orderBy($column, $direction); @@ -124,4 +73,64 @@ public function with($relations, $callback = null) { return $this; } + + private function getMetaColumnCasts(): IlluminateCollection + { + $grammar = $this->builder->getConnection()->getQueryGrammar(); + + $wheres = collect($this->builder->getQuery()->wheres); + $containerWhere = $wheres->firstWhere('column', 'container'); + + if (! $containerWhere || ! isset($containerWhere['value'])) { + return []; + } + + $container = $containerWhere['value']; + + return $container->blueprint()->fields()->all() + ->filter(fn (Field $field) => in_array($field->type(), ['float', 'integer', 'date'])) + ->filter() + ->map(function (Field $field) use ($grammar) { + $cast = null; + + if ($field->type() === 'float') { + $cast = 'float'; + } + + if ($field->type() === 'integer') { + $cast = 'float'; // bit sneaky but mysql doesnt support casting as integer, it wants unsigned + } + + if ($field->type() === 'date') { + $cast = $field->get('time_enabled') ? 'datetime' : 'date'; + + if ($field->get('mode') === 'range') { + $columnWithoutTheJsonBit = Str::after($field->handle(), '->'); + + $actualColumnStartDate = $grammar->wrap($this->column($columnWithoutTheJsonBit).'->start'); + $actualColumnEndDate = $grammar->wrap($this->column($columnWithoutTheJsonBit).'->end'); + + } + + // if ($field->get('mode') === 'range') { + // 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; + // } + + return [ + 'column' => $field->handle(), + 'cast' => $cast, + ]; + } + }); + } } From 54ecad3763b14866b237973cb2f282e898473dd6 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:04:27 +0100 Subject: [PATCH 05/17] wip --- src/Assets/AssetQueryBuilder.php | 95 ++++++++++++++++---------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index c51caa0f..aa0a5047 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -2,12 +2,14 @@ namespace Statamic\Eloquent\Assets; -use Illuminate\Support\Collection as IlluminateCollection; +use Statamic\Facades; +use Illuminate\Support\Collection; use Illuminate\Support\Str; use Statamic\Assets\AssetCollection; use Statamic\Contracts\Assets\QueryBuilder; use Statamic\Fields\Field; use Statamic\Query\EloquentQueryBuilder; +use Statamic\Contracts\Assets\AssetContainer; class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder { @@ -30,9 +32,28 @@ public function orderBy($column, $direction = 'asc') $grammar = $this->builder->getConnection()->getQueryGrammar(); $actualColumn = $grammar->wrap($actualColumn); + if (Str::contains($metaColumnCast, 'range_')) { + $metaColumnCast = Str::after($metaColumnCast, 'range_'); + + $actualColumnStartDate = $actualColumn.'->start'; + $actualColumnEndDate = $actualColumn.'->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 {$metaColumnCast}) {$direction}") + ->orderByRaw("cast({$actualColumnEndDate} as {$metaColumnCast}) {$direction}"); + } + + return $this; + } + // SQLite casts dates to year, which is pretty unhelpful. if ( - in_array($metaColumnCast['cast'], ['date', 'datetime']) + in_array($metaColumnCast, ['date', 'datetime']) && Str::contains(get_class($grammar), 'SQLiteGrammar') ) { $this->builder->orderByRaw("datetime({$actualColumn}) {$direction}"); @@ -40,7 +61,7 @@ public function orderBy($column, $direction = 'asc') return $this; } - $this->builder->orderByRaw("cast({$actualColumn} as {$metaColumnCast['cast']}) {$direction}"); + $this->builder->orderByRaw("cast({$actualColumn} as {$metaColumnCast}) {$direction}"); return $this; } @@ -74,10 +95,8 @@ public function with($relations, $callback = null) return $this; } - private function getMetaColumnCasts(): IlluminateCollection + private function getMetaColumnCasts(): Collection { - $grammar = $this->builder->getConnection()->getQueryGrammar(); - $wheres = collect($this->builder->getQuery()->wheres); $containerWhere = $wheres->firstWhere('column', 'container'); @@ -87,50 +106,34 @@ private function getMetaColumnCasts(): IlluminateCollection $container = $containerWhere['value']; + if (! $container instanceof AssetContainer) { + $container = Facades\AssetContainer::find($container); + } + return $container->blueprint()->fields()->all() ->filter(fn (Field $field) => in_array($field->type(), ['float', 'integer', 'date'])) ->filter() - ->map(function (Field $field) use ($grammar) { - $cast = null; - - if ($field->type() === 'float') { - $cast = 'float'; + ->map(function (Field $field): ?string { + $cast = match (true) { + $field->type() === 'float' => 'float', + $field->type() === 'integer' => 'float', // A bit sneaky, but MySQL doesn't support casting as integer, it wants unsigned. + $field->type() === 'date' => $field->get('time_enabled') ? 'datetime' : 'date', + default => null, + }; + + // Date Ranges are dealt with a little bit differently. + if ($field->type() === 'date' && $field->get('mode') === 'range') { + $cast = "range_{$cast}"; } - if ($field->type() === 'integer') { - $cast = 'float'; // bit sneaky but mysql doesnt support casting as integer, it wants unsigned - } - - if ($field->type() === 'date') { - $cast = $field->get('time_enabled') ? 'datetime' : 'date'; - - if ($field->get('mode') === 'range') { - $columnWithoutTheJsonBit = Str::after($field->handle(), '->'); - - $actualColumnStartDate = $grammar->wrap($this->column($columnWithoutTheJsonBit).'->start'); - $actualColumnEndDate = $grammar->wrap($this->column($columnWithoutTheJsonBit).'->end'); - - } - - // if ($field->get('mode') === 'range') { - // 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; - // } - - return [ - 'column' => $field->handle(), - 'cast' => $cast, - ]; - } - }); + return $cast; + }) + ->filter() + ->merge([ + 'size' => 'float', + 'width' => 'float', + 'height' => 'float', + 'duration' => 'float', + ]); } } From 90528d0f56a909d7e1b5efd3215b073fa8a39582 Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Thu, 21 Aug 2025 15:04:53 +0000 Subject: [PATCH 06/17] Fix styling --- src/Assets/AssetQueryBuilder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index aa0a5047..23c8ad70 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -2,14 +2,14 @@ namespace Statamic\Eloquent\Assets; -use Statamic\Facades; use Illuminate\Support\Collection; use Illuminate\Support\Str; use Statamic\Assets\AssetCollection; +use Statamic\Contracts\Assets\AssetContainer; use Statamic\Contracts\Assets\QueryBuilder; +use Statamic\Facades; use Statamic\Fields\Field; use Statamic\Query\EloquentQueryBuilder; -use Statamic\Contracts\Assets\AssetContainer; class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder { From 157da20db2ba798e4bc0c6e73f309cd759c3edad Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:17:20 +0100 Subject: [PATCH 07/17] wip --- src/Assets/AssetQueryBuilder.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index 23c8ad70..8964471a 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -30,22 +30,22 @@ public function orderBy($column, $direction = 'asc') && $metaColumnCast = $this->getMetaColumnCasts()->get($column) ) { $grammar = $this->builder->getConnection()->getQueryGrammar(); - $actualColumn = $grammar->wrap($actualColumn); + $wrappedColumn = $grammar->wrap($actualColumn); if (Str::contains($metaColumnCast, 'range_')) { $metaColumnCast = Str::after($metaColumnCast, 'range_'); - $actualColumnStartDate = $actualColumn.'->start'; - $actualColumnEndDate = $actualColumn.'->end'; + $wrappedStartDateColumn = $grammar->wrap("{$actualColumn}->start"); + $wrappedEndDateColumn = $grammar->wrap("{$actualColumn}->end"); if (str_contains(get_class($grammar), 'SQLiteGrammar')) { $this->builder - ->orderByRaw("datetime({$actualColumnStartDate}) {$direction}") - ->orderByRaw("datetime({$actualColumnEndDate}) {$direction}"); + ->orderByRaw("datetime({$wrappedStartDateColumn}) {$direction}") + ->orderByRaw("datetime({$wrappedEndDateColumn}) {$direction}"); } else { $this->builder - ->orderByRaw("cast({$actualColumnStartDate} as {$metaColumnCast}) {$direction}") - ->orderByRaw("cast({$actualColumnEndDate} as {$metaColumnCast}) {$direction}"); + ->orderByRaw("cast({$wrappedStartDateColumn} as {$metaColumnCast}) {$direction}") + ->orderByRaw("cast({$wrappedEndDateColumn} as {$metaColumnCast}) {$direction}"); } return $this; @@ -56,12 +56,12 @@ public function orderBy($column, $direction = 'asc') in_array($metaColumnCast, ['date', 'datetime']) && Str::contains(get_class($grammar), 'SQLiteGrammar') ) { - $this->builder->orderByRaw("datetime({$actualColumn}) {$direction}"); + $this->builder->orderByRaw("datetime({$wrappedColumn}) {$direction}"); return $this; } - $this->builder->orderByRaw("cast({$actualColumn} as {$metaColumnCast}) {$direction}"); + $this->builder->orderByRaw("cast({$wrappedColumn} as {$metaColumnCast}) {$direction}"); return $this; } From 99c75d1f9f98dd64ff4bbd7082fd95b928b037da Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:18:33 +0100 Subject: [PATCH 08/17] Always cast default meta data --- src/Assets/AssetQueryBuilder.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index 8964471a..f8d6da52 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -101,7 +101,12 @@ private function getMetaColumnCasts(): Collection $containerWhere = $wheres->firstWhere('column', 'container'); if (! $containerWhere || ! isset($containerWhere['value'])) { - return []; + return [ + 'size' => 'float', + 'width' => 'float', + 'height' => 'float', + 'duration' => 'float', + ]; } $container = $containerWhere['value']; From 6ff98e322cfd04acd869af606c329f53c89cc08b Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:28:50 +0100 Subject: [PATCH 09/17] Extract some of the logic into a trait --- src/Assets/AssetQueryBuilder.php | 56 ++------------------------- src/Assets/QueriesJsonColumns.php | 63 +++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 52 deletions(-) create mode 100644 src/Assets/QueriesJsonColumns.php diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index f8d6da52..4cb63c2a 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -13,6 +13,8 @@ class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder { + use QueriesJsonColumns; + const COLUMNS = [ 'id', 'container', 'folder', 'basename', 'filename', 'extension', 'path', 'created_at', 'updated_at', ]; @@ -21,57 +23,7 @@ class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder 'size', 'width', 'height', 'duration', 'mime_type', 'last_modified', ]; - public function orderBy($column, $direction = 'asc') - { - $actualColumn = $this->column($column); - - if ( - Str::contains($actualColumn, 'meta->') - && $metaColumnCast = $this->getMetaColumnCasts()->get($column) - ) { - $grammar = $this->builder->getConnection()->getQueryGrammar(); - $wrappedColumn = $grammar->wrap($actualColumn); - - if (Str::contains($metaColumnCast, 'range_')) { - $metaColumnCast = Str::after($metaColumnCast, 'range_'); - - $wrappedStartDateColumn = $grammar->wrap("{$actualColumn}->start"); - $wrappedEndDateColumn = $grammar->wrap("{$actualColumn}->end"); - - if (str_contains(get_class($grammar), 'SQLiteGrammar')) { - $this->builder - ->orderByRaw("datetime({$wrappedStartDateColumn}) {$direction}") - ->orderByRaw("datetime({$wrappedEndDateColumn}) {$direction}"); - } else { - $this->builder - ->orderByRaw("cast({$wrappedStartDateColumn} as {$metaColumnCast}) {$direction}") - ->orderByRaw("cast({$wrappedEndDateColumn} as {$metaColumnCast}) {$direction}"); - } - - return $this; - } - - // SQLite casts dates to year, which is pretty unhelpful. - if ( - in_array($metaColumnCast, ['date', 'datetime']) - && Str::contains(get_class($grammar), 'SQLiteGrammar') - ) { - $this->builder->orderByRaw("datetime({$wrappedColumn}) {$direction}"); - - return $this; - } - - $this->builder->orderByRaw("cast({$wrappedColumn} as {$metaColumnCast}) {$direction}"); - - return $this; - } - - parent::orderBy($column, $direction); - - return $this; - } - - protected function column($column) + protected function column($column): string { if (in_array($column, self::META_COLUMNS)) { $column = 'meta->'.$column; @@ -95,7 +47,7 @@ public function with($relations, $callback = null) return $this; } - private function getMetaColumnCasts(): Collection + protected function getMetaColumnCasts(): Collection { $wheres = collect($this->builder->getQuery()->wheres); $containerWhere = $wheres->firstWhere('column', 'container'); diff --git a/src/Assets/QueriesJsonColumns.php b/src/Assets/QueriesJsonColumns.php new file mode 100644 index 00000000..25f736ed --- /dev/null +++ b/src/Assets/QueriesJsonColumns.php @@ -0,0 +1,63 @@ +column($column); + + if ( + Str::contains($actualColumn, 'meta->') + && $metaColumnCast = $this->getMetaColumnCasts()->get($column) + ) { + $grammar = $this->builder->getConnection()->getQueryGrammar(); + $wrappedColumn = $grammar->wrap($actualColumn); + + if (Str::contains($metaColumnCast, 'range_')) { + $metaColumnCast = Str::after($metaColumnCast, 'range_'); + + $wrappedStartDateColumn = $grammar->wrap("{$actualColumn}->start"); + $wrappedEndDateColumn = $grammar->wrap("{$actualColumn}->end"); + + if (str_contains(get_class($grammar), 'SQLiteGrammar')) { + $this->builder + ->orderByRaw("datetime({$wrappedStartDateColumn}) {$direction}") + ->orderByRaw("datetime({$wrappedEndDateColumn}) {$direction}"); + } else { + $this->builder + ->orderByRaw("cast({$wrappedStartDateColumn} as {$metaColumnCast}) {$direction}") + ->orderByRaw("cast({$wrappedEndDateColumn} as {$metaColumnCast}) {$direction}"); + } + + return $this; + } + + // SQLite casts dates to year, which is pretty unhelpful. + if ( + in_array($metaColumnCast, ['date', 'datetime']) + && Str::contains(get_class($grammar), 'SQLiteGrammar') + ) { + $this->builder->orderByRaw("datetime({$wrappedColumn}) {$direction}"); + + return $this; + } + + $this->builder->orderByRaw("cast({$wrappedColumn} as {$metaColumnCast}) {$direction}"); + + return $this; + } + + parent::orderBy($column, $direction); + + return $this; + } + + abstract protected function column($column): string; + + abstract protected function getMetaColumnCasts(): Collection; +} \ No newline at end of file From 1a52794f9646bc007ca79a4758cf538fcfd70465 Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Thu, 21 Aug 2025 15:29:16 +0000 Subject: [PATCH 10/17] Fix styling --- src/Assets/AssetQueryBuilder.php | 1 - src/Assets/QueriesJsonColumns.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index 4cb63c2a..2e3baeb1 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -3,7 +3,6 @@ namespace Statamic\Eloquent\Assets; use Illuminate\Support\Collection; -use Illuminate\Support\Str; use Statamic\Assets\AssetCollection; use Statamic\Contracts\Assets\AssetContainer; use Statamic\Contracts\Assets\QueryBuilder; diff --git a/src/Assets/QueriesJsonColumns.php b/src/Assets/QueriesJsonColumns.php index 25f736ed..b235074e 100644 --- a/src/Assets/QueriesJsonColumns.php +++ b/src/Assets/QueriesJsonColumns.php @@ -60,4 +60,4 @@ public function orderBy($column, $direction = 'asc') abstract protected function column($column): string; abstract protected function getMetaColumnCasts(): Collection; -} \ No newline at end of file +} From 0cd816b5a820c4eb2a1a50b47b0d24f092add11f Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:37:10 +0100 Subject: [PATCH 11/17] simplify --- src/Assets/AssetQueryBuilder.php | 28 ++++++------------------- src/{Assets => }/QueriesJsonColumns.php | 22 +++++++++++++++++-- 2 files changed, 26 insertions(+), 24 deletions(-) rename src/{Assets => }/QueriesJsonColumns.php (75%) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index 2e3baeb1..b7161c3b 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -6,6 +6,7 @@ use Statamic\Assets\AssetCollection; use Statamic\Contracts\Assets\AssetContainer; use Statamic\Contracts\Assets\QueryBuilder; +use Statamic\Eloquent\QueriesJsonColumns; use Statamic\Facades; use Statamic\Fields\Field; use Statamic\Query\EloquentQueryBuilder; @@ -60,30 +61,13 @@ protected function getMetaColumnCasts(): Collection ]; } - $container = $containerWhere['value']; - - if (! $container instanceof AssetContainer) { - $container = Facades\AssetContainer::find($container); - } + $container = $containerWhere['value'] instanceof AssetContainer + ? $containerWhere['value'] + : Facades\AssetContainer::find($containerWhere['value']); return $container->blueprint()->fields()->all() - ->filter(fn (Field $field) => in_array($field->type(), ['float', 'integer', 'date'])) - ->filter() - ->map(function (Field $field): ?string { - $cast = match (true) { - $field->type() === 'float' => 'float', - $field->type() === 'integer' => 'float', // A bit sneaky, but MySQL doesn't support casting as integer, it wants unsigned. - $field->type() === 'date' => $field->get('time_enabled') ? 'datetime' : 'date', - default => null, - }; - - // Date Ranges are dealt with a little bit differently. - if ($field->type() === 'date' && $field->get('mode') === 'range') { - $cast = "range_{$cast}"; - } - - return $cast; - }) + ->filter(fn (Field $field): bool => in_array($field->type(), ['float', 'integer', 'date'])) + ->map(fn (Field $field): string => $this->toCast($field)) ->filter() ->merge([ 'size' => 'float', diff --git a/src/Assets/QueriesJsonColumns.php b/src/QueriesJsonColumns.php similarity index 75% rename from src/Assets/QueriesJsonColumns.php rename to src/QueriesJsonColumns.php index b235074e..19094eea 100644 --- a/src/Assets/QueriesJsonColumns.php +++ b/src/QueriesJsonColumns.php @@ -1,9 +1,10 @@ type() === 'float' => 'float', + $field->type() === 'integer' => 'float', // A bit sneaky, but MySQL doesn't support casting as integer, it wants unsigned. + $field->type() === 'date' => $field->get('time_enabled') ? 'datetime' : 'date', + default => null, + }; + + // Date Ranges are dealt with a little bit differently. + if ($field->type() === 'date' && $field->get('mode') === 'range') { + $cast = "range_{$cast}"; + } + + return $cast; + } +} \ No newline at end of file From 613fa270e215c2e6fcb6abd9f752f38640d1cd8c Mon Sep 17 00:00:00 2001 From: duncanmcclean Date: Thu, 21 Aug 2025 15:38:00 +0000 Subject: [PATCH 12/17] Fix styling --- src/QueriesJsonColumns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QueriesJsonColumns.php b/src/QueriesJsonColumns.php index 19094eea..9b9c08e8 100644 --- a/src/QueriesJsonColumns.php +++ b/src/QueriesJsonColumns.php @@ -78,4 +78,4 @@ protected function toCast(Field $field): string return $cast; } -} \ No newline at end of file +} From 1dba7ab494527edd3fb4ad7b297c7865ebc56363 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:40:30 +0100 Subject: [PATCH 13/17] Rename some things. It's not always gonna be "meta" --- src/Assets/AssetQueryBuilder.php | 2 +- src/QueriesJsonColumns.php | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index b7161c3b..bb89fa12 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -47,7 +47,7 @@ public function with($relations, $callback = null) return $this; } - protected function getMetaColumnCasts(): Collection + protected function getJsonCasts(): Collection { $wheres = collect($this->builder->getQuery()->wheres); $containerWhere = $wheres->firstWhere('column', 'container'); diff --git a/src/QueriesJsonColumns.php b/src/QueriesJsonColumns.php index 9b9c08e8..9151b815 100644 --- a/src/QueriesJsonColumns.php +++ b/src/QueriesJsonColumns.php @@ -13,14 +13,14 @@ public function orderBy($column, $direction = 'asc') $actualColumn = $this->column($column); if ( - Str::contains($actualColumn, 'meta->') - && $metaColumnCast = $this->getMetaColumnCasts()->get($column) + Str::contains($actualColumn, ['data->', 'meta->']) + && $jsonCast = $this->getJsonCasts()->get($column) ) { $grammar = $this->builder->getConnection()->getQueryGrammar(); $wrappedColumn = $grammar->wrap($actualColumn); - if (Str::contains($metaColumnCast, 'range_')) { - $metaColumnCast = Str::after($metaColumnCast, 'range_'); + if (Str::contains($jsonCast, 'range_')) { + $jsonCast = Str::after($jsonCast, 'range_'); $wrappedStartDateColumn = $grammar->wrap("{$actualColumn}->start"); $wrappedEndDateColumn = $grammar->wrap("{$actualColumn}->end"); @@ -31,8 +31,8 @@ public function orderBy($column, $direction = 'asc') ->orderByRaw("datetime({$wrappedEndDateColumn}) {$direction}"); } else { $this->builder - ->orderByRaw("cast({$wrappedStartDateColumn} as {$metaColumnCast}) {$direction}") - ->orderByRaw("cast({$wrappedEndDateColumn} as {$metaColumnCast}) {$direction}"); + ->orderByRaw("cast({$wrappedStartDateColumn} as {$jsonCast}) {$direction}") + ->orderByRaw("cast({$wrappedEndDateColumn} as {$jsonCast}) {$direction}"); } return $this; @@ -40,7 +40,7 @@ public function orderBy($column, $direction = 'asc') // SQLite casts dates to year, which is pretty unhelpful. if ( - in_array($metaColumnCast, ['date', 'datetime']) + in_array($jsonCast, ['date', 'datetime']) && Str::contains(get_class($grammar), 'SQLiteGrammar') ) { $this->builder->orderByRaw("datetime({$wrappedColumn}) {$direction}"); @@ -48,7 +48,7 @@ public function orderBy($column, $direction = 'asc') return $this; } - $this->builder->orderByRaw("cast({$wrappedColumn} as {$metaColumnCast}) {$direction}"); + $this->builder->orderByRaw("cast({$wrappedColumn} as {$jsonCast}) {$direction}"); return $this; } @@ -60,7 +60,7 @@ public function orderBy($column, $direction = 'asc') abstract protected function column($column): string; - abstract protected function getMetaColumnCasts(): Collection; + abstract protected function getJsonCasts(): Collection; protected function toCast(Field $field): string { From 267b2a50a13b57e9f9cc110a9081969c7ccc0d1e Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:55:52 +0100 Subject: [PATCH 14/17] wip --- src/Assets/AssetQueryBuilder.php | 2 +- src/QueriesJsonColumns.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Assets/AssetQueryBuilder.php b/src/Assets/AssetQueryBuilder.php index bb89fa12..858d40ff 100644 --- a/src/Assets/AssetQueryBuilder.php +++ b/src/Assets/AssetQueryBuilder.php @@ -23,7 +23,7 @@ class AssetQueryBuilder extends EloquentQueryBuilder implements QueryBuilder 'size', 'width', 'height', 'duration', 'mime_type', 'last_modified', ]; - protected function column($column): string + protected function column($column) { if (in_array($column, self::META_COLUMNS)) { $column = 'meta->'.$column; diff --git a/src/QueriesJsonColumns.php b/src/QueriesJsonColumns.php index 9151b815..403bb237 100644 --- a/src/QueriesJsonColumns.php +++ b/src/QueriesJsonColumns.php @@ -58,7 +58,7 @@ public function orderBy($column, $direction = 'asc') return $this; } - abstract protected function column($column): string; + abstract protected function column($column); abstract protected function getJsonCasts(): Collection; From d3337f46f659b308071769f2dc230e2445f97fde Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:56:57 +0100 Subject: [PATCH 15/17] revert changes to test names --- 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 a895b553..c446107b 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_a_float_json_field() + public function entries_can_be_ordered_by_an_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_a_float_json_field() } #[Test] - public function entries_can_be_ordered_by_a_date_json_field() + public function entries_can_be_ordered_by_an_date_json_field() { $blueprint = Blueprint::makeFromFields(['date_field' => ['type' => 'date', 'time_enabled' => true]]); Blueprint::shouldReceive('in')->with('collections/posts')->andReturn(collect(['posts' => $blueprint])); From d1deb208888ac32490c55be3a2e0cf02469bea70 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Thu, 21 Aug 2025 16:59:33 +0100 Subject: [PATCH 16/17] don't want any changes for this file in the diff --- tests/Data/Entries/EntryQueryBuilderTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Data/Entries/EntryQueryBuilderTest.php b/tests/Data/Entries/EntryQueryBuilderTest.php index c446107b..980708a7 100644 --- a/tests/Data/Entries/EntryQueryBuilderTest.php +++ b/tests/Data/Entries/EntryQueryBuilderTest.php @@ -808,7 +808,6 @@ public function entries_can_be_ordered_by_a_datetime_range_json_field() EntryFactory::id('2')->slug('post-2')->collection('posts')->data(['title' => 'Post 2', 'date_field' => ['start' => '2021-01-13 20:31:04', 'end' => '2021-06-16 20:31:04']])->create(); EntryFactory::id('3')->slug('post-3')->collection('posts')->data(['title' => 'Post 3', 'date_field' => ['start' => '2021-11-17 20:31:04', 'end' => '2021-11-17 20:31:04']])->create(); EntryFactory::id('4')->slug('post-4')->collection('posts')->data(['title' => 'Post 4', 'date_field' => ['start' => '2021-06-15 20:31:04', 'end' => '2021-06-15 22:00:00']])->create(); - $entries = Entry::query()->where('collection', 'posts')->orderBy('date_field', 'asc')->get(); $this->assertCount(4, $entries); From f26b6dc54dffa25a2a8e924f7cef7fcdf2ae23a9 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Fri, 22 Aug 2025 11:11:17 +0100 Subject: [PATCH 17/17] `column` doesn't need to be in the trait It's required by the EloquentQueryBuilder in core. --- src/QueriesJsonColumns.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/QueriesJsonColumns.php b/src/QueriesJsonColumns.php index 403bb237..8a812f45 100644 --- a/src/QueriesJsonColumns.php +++ b/src/QueriesJsonColumns.php @@ -58,8 +58,6 @@ public function orderBy($column, $direction = 'asc') return $this; } - abstract protected function column($column); - abstract protected function getJsonCasts(): Collection; protected function toCast(Field $field): string