diff --git a/src/Data/DataCollection.php b/src/Data/DataCollection.php index f6ce3d81311..76a5434aa41 100644 --- a/src/Data/DataCollection.php +++ b/src/Data/DataCollection.php @@ -7,9 +7,9 @@ use Illuminate\Support\Collection as IlluminateCollection; use Statamic\Exceptions\MethodNotFoundException; use Statamic\Facades\Compare; +use Statamic\Query\ResolveValue; use Statamic\Search\PlainResult; use Statamic\Search\Result; -use Statamic\Support\Str; /** * An abstract collection of data types. @@ -100,15 +100,7 @@ protected function getSortableValue($sort, $item) $item = $item->getSearchable() ?? $item; } - if (method_exists($item, $method = Str::camel($sort))) { - return $this->normalizeSortableValue(call_user_func([$item, $method])); - } - - if (method_exists($item, 'value')) { - return $this->normalizeSortableValue($item->value($sort)); - } - - return $this->normalizeSortableValue($item->get($sort)); + return $this->normalizeSortableValue((new ResolveValue)($item, $sort)); } /** diff --git a/tests/Data/DataCollectionTest.php b/tests/Data/DataCollectionTest.php index 1ee5795f7ba..de697a8882a 100644 --- a/tests/Data/DataCollectionTest.php +++ b/tests/Data/DataCollectionTest.php @@ -3,7 +3,9 @@ namespace Tests\Data; use PHPUnit\Framework\Attributes\Test; +use Statamic\Contracts\Search\Searchable; use Statamic\Data\DataCollection; +use Statamic\Search\Result; use Tests\TestCase; class DataCollectionTest extends TestCase @@ -31,4 +33,69 @@ public function it_sorts_by_first_item_in_arrays() $this->assertEquals([1, 3, 2], $collection->multisort('foos')->pluck('id')->all()); } + + #[Test] + public function sorting_by_unsafe_method_does_not_invoke_it() + { + $a = new DataCollectionTestObject('alfa'); + $b = new DataCollectionTestObject('bravo'); + + $collection = new DataCollection([$a, $b]); + + $collection->multisort('delete'); + + $this->assertFalse($a->deleted); + $this->assertFalse($b->deleted); + } + + #[Test] + public function sorting_search_results_by_unsafe_method_does_not_invoke_it() + { + $a = new DataCollectionTestObject('alfa'); + $b = new DataCollectionTestObject('bravo'); + + $collection = new DataCollection([ + new Result($a, 'test'), + new Result($b, 'test'), + ]); + + $collection->multisort('delete'); + + $this->assertFalse($a->deleted); + $this->assertFalse($b->deleted); + } +} + +class DataCollectionTestObject implements Searchable +{ + public bool $deleted = false; + + public function __construct(public string $title) + { + } + + public function delete() + { + $this->deleted = true; + } + + public function get($key) + { + return $this->{$key} ?? null; + } + + public function getSearchValue(string $field) + { + return $this->{$field} ?? null; + } + + public function getSearchReference(): string + { + return 'test::'.$this->title; + } + + public function toSearchResult(): Result + { + return new Result($this, 'test'); + } }