From 5f1f6a26ea1edb2a6996308f8ef3739bed721877 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 5 Dec 2024 11:31:37 -0500 Subject: [PATCH] Bring back the reflection stuff - that'll happen for tag pairs. --- .../Language/Runtime/PathDataManager.php | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/View/Antlers/Language/Runtime/PathDataManager.php b/src/View/Antlers/Language/Runtime/PathDataManager.php index ed407c3e15..cec692806e 100644 --- a/src/View/Antlers/Language/Runtime/PathDataManager.php +++ b/src/View/Antlers/Language/Runtime/PathDataManager.php @@ -4,6 +4,7 @@ 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; @@ -642,10 +643,12 @@ public function getData(VariableReference $path, $data, $isForArrayIndex = false } } - if (count($path->pathParts) > 1 && $this->isPair == false) { + if (count($path->pathParts) > 1 && $this->isPair == false && ! $this->reducedVar instanceof Model) { // If we have more steps in the path to take, but we are // not a tag pair, we need to reduce anyway so we // can descend further into the nested values. + // We skip this step for Models to prevent + // some of the reflection stuff below. $this->lockData(); $this->reducedVar = self::reduce($this->reducedVar, true, $this->shouldDoValueIntercept); $this->unlockData(); @@ -779,6 +782,10 @@ public function getData(VariableReference $path, $data, $isForArrayIndex = false $this->compact(true); } + if ($this->reducedVar instanceof Model && $this->isPair) { + $this->reducedVar = self::reduce($this->reducedVar, true, true, false); + } + $this->namedSlotsInScope = false; $this->resetInternalState(); @@ -914,6 +921,10 @@ private function reduceVar($path, $processorData = []) */ private function compact($isFinal) { + if (! $isFinal && $this->reducedVar instanceof Model) { + return; + } + if ($this->isForArrayIndex && $isFinal && is_object($this->reducedVar) && method_exists($this->reducedVar, '__toString')) { $this->reducedVar = (string) $this->reducedVar; @@ -956,14 +967,15 @@ protected static function guardRuntimeReturnValue($value) * @param mixed $value The value to reduce. * @param bool $isPair Indicates if the path belongs to a node pair. * @param bool $reduceBuildersAndAugmentables Indicates if Builder and Augmentable instances should be resolved. + * @param bool $leaveModelsAlone * @return array|string */ - public static function reduce($value, $isPair = true, $reduceBuildersAndAugmentables = true) + public static function reduce($value, $isPair = true, $reduceBuildersAndAugmentables = true, $leaveModelsAlone = true) { $reductionStack = [$value]; $returnValue = $value; - if ($value instanceof Model) { + if ($value instanceof Model && $leaveModelsAlone) { return $value; } @@ -1023,6 +1035,26 @@ 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; + } + + 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; + GlobalRuntimeState::$isEvaluatingData = false; + continue; } elseif ($reductionValue instanceof Arrayable) { GlobalRuntimeState::$isEvaluatingData = true; @@ -1058,6 +1090,10 @@ public static function reduceForAntlers($value, Parser $parser, $data, $isPair = GlobalRuntimeState::$isEvaluatingUserData = true; GlobalRuntimeState::$isEvaluatingData = true; + if ($value instanceof Model) { + return $value; + } + if ($value instanceof Collection) { $value = $value->all(); }