From 70d13589ac32a1330bd40bbc761b70955d36b4b3 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 1 Oct 2024 08:02:04 +0100 Subject: [PATCH 1/3] Support rendering model attributes in Antlers --- .../Language/Runtime/PathDataManager.php | 17 +++++++ tests/Antlers/Runtime/ModelTest.php | 44 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tests/Antlers/Runtime/ModelTest.php diff --git a/src/View/Antlers/Language/Runtime/PathDataManager.php b/src/View/Antlers/Language/Runtime/PathDataManager.php index 050083e87a..1d1eac1658 100644 --- a/src/View/Antlers/Language/Runtime/PathDataManager.php +++ b/src/View/Antlers/Language/Runtime/PathDataManager.php @@ -4,6 +4,8 @@ use Exception; use Illuminate\Contracts\Support\Arrayable; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; @@ -1014,6 +1016,21 @@ public static function reduce($value, $isPair = true, $reduceBuildersAndAugmenta $reductionStack[] = $reductionValue->all(); GlobalRuntimeState::$isEvaluatingData = false; + continue; + } elseif ($reductionValue instanceof Model) { + GlobalRuntimeState::$isEvaluatingData = true; + $data = $reductionValue->toArray(); + + foreach (get_class_methods($reductionValue) as $method) { + if ((new \ReflectionMethod($reductionValue, $method))->getReturnType()?->getName() === Attribute::class) { + $method = Str::snake($method); + $data[$method] = $reductionValue->$method; + } + } + + $reductionStack[] = $data; + GlobalRuntimeState::$isEvaluatingData = false; + continue; } elseif ($reductionValue instanceof Arrayable) { GlobalRuntimeState::$isEvaluatingData = true; diff --git a/tests/Antlers/Runtime/ModelTest.php b/tests/Antlers/Runtime/ModelTest.php new file mode 100644 index 0000000000..9e992a692f --- /dev/null +++ b/tests/Antlers/Runtime/ModelTest.php @@ -0,0 +1,44 @@ +title = 'Title'; + + $data = [ + 'model' => $model, + ]; + + $template = <<<'EOT' +{{ model:title }}{{ model:foo_bar }} +EOT; + + $this->assertSame('TitleFooBar', $this->renderString($template, $data)); + } +} + +class FakeModel extends \Illuminate\Database\Eloquent\Model +{ + public function fooBar(): Attribute + { + return Attribute::make( + get: fn() => 'FooBar', + ); + } +} From e4dad4a3dc6c6639952116d081aa87fb84ae18b5 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 1 Oct 2024 08:06:20 +0100 Subject: [PATCH 2/3] :beer: --- tests/Antlers/Runtime/ModelTest.php | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/Antlers/Runtime/ModelTest.php b/tests/Antlers/Runtime/ModelTest.php index 9e992a692f..f5754bbcda 100644 --- a/tests/Antlers/Runtime/ModelTest.php +++ b/tests/Antlers/Runtime/ModelTest.php @@ -3,15 +3,6 @@ namespace Tests\Antlers\Runtime; use Illuminate\Database\Eloquent\Casts\Attribute; -use Illuminate\Support\Collection; -use Mockery; -use PHPUnit\Framework\Attributes\Test; -use Statamic\Contracts\Query\Builder; -use Statamic\Tags\Tags; -use Statamic\View\Antlers\Language\Runtime\GlobalRuntimeState; -use Statamic\View\Antlers\Language\Runtime\NodeProcessor; -use Tests\Antlers\Fixtures\Addon\Modifiers\IsBuilder; -use Tests\Antlers\Fixtures\Addon\Tags\VarTestTags as VarTest; use Tests\Antlers\ParserTestCase; class ModelTest extends ParserTestCase @@ -38,7 +29,7 @@ class FakeModel extends \Illuminate\Database\Eloquent\Model public function fooBar(): Attribute { return Attribute::make( - get: fn() => 'FooBar', + get: fn () => 'FooBar', ); } } From fed1430148e4e0121ec2602dd66e4d8cb1862c40 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Tue, 1 Oct 2024 17:03:52 +0100 Subject: [PATCH 3/3] Add support for the legacy style of model accessors --- .../Language/Runtime/PathDataManager.php | 5 +++++ tests/Antlers/Runtime/ModelTest.php | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/View/Antlers/Language/Runtime/PathDataManager.php b/src/View/Antlers/Language/Runtime/PathDataManager.php index 1d1eac1658..5e726ea541 100644 --- a/src/View/Antlers/Language/Runtime/PathDataManager.php +++ b/src/View/Antlers/Language/Runtime/PathDataManager.php @@ -1026,6 +1026,11 @@ public static function reduce($value, $isPair = true, $reduceBuildersAndAugmenta $method = Str::snake($method); $data[$method] = $reductionValue->$method; } + + if (Str::startsWith($method, 'get') && Str::endsWith($method, 'Attribute')) { + $method = Str::of($method)->after('get')->before('Attribute')->snake()->__toString(); + $data[$method] = $reductionValue->getAttribute($method); + } } $reductionStack[] = $data; diff --git a/tests/Antlers/Runtime/ModelTest.php b/tests/Antlers/Runtime/ModelTest.php index f5754bbcda..5d11a40566 100644 --- a/tests/Antlers/Runtime/ModelTest.php +++ b/tests/Antlers/Runtime/ModelTest.php @@ -22,6 +22,22 @@ public function test_model_attributes_are_returned() $this->assertSame('TitleFooBar', $this->renderString($template, $data)); } + + public function test_legacy_model_attributes_are_returned() + { + $model = FakeModel::make(); + $model->title = 'Title'; + + $data = [ + 'model' => $model, + ]; + + $template = <<<'EOT' +{{ model:title }}{{ model:bar_baz }} +EOT; + + $this->assertSame('TitleBarBaz', $this->renderString($template, $data)); + } } class FakeModel extends \Illuminate\Database\Eloquent\Model @@ -32,4 +48,9 @@ public function fooBar(): Attribute get: fn () => 'FooBar', ); } + + public function getBarBazAttribute() + { + return 'BarBaz'; + } }