From 0b0e7bb94d7cc70f80f317ed8460e3d3940e4bac Mon Sep 17 00:00:00 2001 From: Kevin <kevinsliedrecht2000@gmail.com> Date: Tue, 30 Jul 2024 14:11:16 +0200 Subject: [PATCH 1/3] Fix issue with onUpdated not triggered on nested array proprety --- src/LiveComponent/src/LiveComponentHydrator.php | 3 ++- src/LiveComponent/src/Util/DehydratedProps.php | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/LiveComponent/src/LiveComponentHydrator.php b/src/LiveComponent/src/LiveComponentHydrator.php index 547106fadc3..3cdb2ceda91 100644 --- a/src/LiveComponent/src/LiveComponentHydrator.php +++ b/src/LiveComponent/src/LiveComponentHydrator.php @@ -663,7 +663,8 @@ private function processOnUpdatedHook(object $component, string $frontendName, L foreach ($onUpdated as $propName => $funcName) { if (LiveProp::IDENTITY === $propName) { - if (!$dehydratedUpdatedProps->hasPropValue($frontendName)) { + if (!$dehydratedUpdatedProps->hasPropValue($frontendName) && + !$dehydratedUpdatedProps->hasNestedPathsForProperty($frontendName)) { continue; } diff --git a/src/LiveComponent/src/Util/DehydratedProps.php b/src/LiveComponent/src/Util/DehydratedProps.php index 64d50cea770..693c24b2369 100644 --- a/src/LiveComponent/src/Util/DehydratedProps.php +++ b/src/LiveComponent/src/Util/DehydratedProps.php @@ -113,6 +113,17 @@ public function getNestedPathsForProperty(string $prop): array return $nestedPaths; } + public function hasNestedPathsForProperty(string $prop): bool + { + foreach ($this->propValues as $fullPath => $value) { + if (str_starts_with($fullPath, $prop.'.')) { + return true; + } + } + + return false; + } + public function calculateUnexpectedNestedPathsForProperty(string $prop, array $expectedNestedPaths): array { $clone = clone $this; From b968810566d51395b7fcc1a07b422fb23ca4a8c8 Mon Sep 17 00:00:00 2001 From: Kevin <kevinsliedrecht2000@gmail.com> Date: Wed, 31 Jul 2024 09:08:22 +0200 Subject: [PATCH 2/3] Add onUpdated with array test to LiveComponentHydratorTest --- .../src/LiveComponentHydrator.php | 4 ++-- .../Integration/LiveComponentHydratorTest.php | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/LiveComponent/src/LiveComponentHydrator.php b/src/LiveComponent/src/LiveComponentHydrator.php index 3cdb2ceda91..bd022ccb577 100644 --- a/src/LiveComponent/src/LiveComponentHydrator.php +++ b/src/LiveComponent/src/LiveComponentHydrator.php @@ -663,8 +663,8 @@ private function processOnUpdatedHook(object $component, string $frontendName, L foreach ($onUpdated as $propName => $funcName) { if (LiveProp::IDENTITY === $propName) { - if (!$dehydratedUpdatedProps->hasPropValue($frontendName) && - !$dehydratedUpdatedProps->hasNestedPathsForProperty($frontendName)) { + if (!$dehydratedUpdatedProps->hasPropValue($frontendName) + && !$dehydratedUpdatedProps->hasNestedPathsForProperty($frontendName)) { continue; } diff --git a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php index 2441146fccd..119cbfd9beb 100644 --- a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php +++ b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php @@ -234,6 +234,25 @@ public function onEntireEntityUpdated($oldValue) }); }]; + yield 'onUpdated: with array' => [function () { + return HydrationTest::create(new class() { + #[LiveProp(writable: true, onUpdated: 'onNestedArrayUpdated')] + public array $array = []; + + public function onNestedArrayUpdated($oldValue) + { + if ('Kevin' === $this->array['name']) { + $this->array['name'] = 'Simon'; + } + } + }) + ->mountWith(['array' => ['name' => 'Ryan']]) + ->userUpdatesProps(['array.name' => 'Kevin']) + ->assertObjectAfterHydration(function (object $object) { + self::assertSame('Simon', $object->array['name']); + }); + }]; + yield 'string: (de)hydrates correctly' => [function () { return HydrationTest::create(new class() { #[LiveProp()] From 6c46e0c284fdcd4edc65bb89e2adbd41dd91b1a2 Mon Sep 17 00:00:00 2001 From: Kevin <kevinsliedrecht2000@gmail.com> Date: Thu, 1 Aug 2024 14:45:24 +0200 Subject: [PATCH 3/3] Add support for wildcard to onUpdated --- src/LiveComponent/src/LiveComponentHydrator.php | 15 +++++++-------- src/LiveComponent/src/Util/DehydratedProps.php | 10 +++------- .../Integration/LiveComponentHydratorTest.php | 10 ++++++---- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/LiveComponent/src/LiveComponentHydrator.php b/src/LiveComponent/src/LiveComponentHydrator.php index 061f7b7afd4..88a788ba6b0 100644 --- a/src/LiveComponent/src/LiveComponentHydrator.php +++ b/src/LiveComponent/src/LiveComponentHydrator.php @@ -670,8 +670,7 @@ private function processOnUpdatedHook(object $component, string $frontendName, L foreach ($onUpdated as $propName => $funcName) { if (LiveProp::IDENTITY === $propName) { - if (!$dehydratedUpdatedProps->hasPropValue($frontendName) - && !$dehydratedUpdatedProps->hasNestedPathsForProperty($frontendName)) { + if (!$dehydratedUpdatedProps->hasPropValue($frontendName)) { continue; } @@ -687,13 +686,13 @@ private function processOnUpdatedHook(object $component, string $frontendName, L } $key = \sprintf('%s.%s', $frontendName, $propName); - if (!$dehydratedUpdatedProps->hasPropValue($key)) { - continue; - } + $fullPaths = $dehydratedUpdatedProps->searchFullPathsForProperty($key); - $this->ensureOnUpdatedMethodExists($component, $funcName); - $propertyOldValue = $dehydratedOriginalProps->getPropValue($key); - $component->{$funcName}($propertyOldValue); + foreach ($fullPaths as $fullPath) { + $this->ensureOnUpdatedMethodExists($component, $funcName); + $propertyOldValue = $dehydratedOriginalProps->getPropValue($fullPath); + $component->{$funcName}($propertyOldValue); + } } } } diff --git a/src/LiveComponent/src/Util/DehydratedProps.php b/src/LiveComponent/src/Util/DehydratedProps.php index 693c24b2369..9c8f63a20ab 100644 --- a/src/LiveComponent/src/Util/DehydratedProps.php +++ b/src/LiveComponent/src/Util/DehydratedProps.php @@ -113,15 +113,11 @@ public function getNestedPathsForProperty(string $prop): array return $nestedPaths; } - public function hasNestedPathsForProperty(string $prop): bool + public function searchFullPathsForProperty(string $prop): array { - foreach ($this->propValues as $fullPath => $value) { - if (str_starts_with($fullPath, $prop.'.')) { - return true; - } - } + $regex = '/^'.preg_replace(['/\\\\\*$/i', '/\\\\\*/i'], ['', '.*?'], preg_quote($prop, '/')).'/i'; - return false; + return preg_grep($regex, array_keys($this->propValues)); } public function calculateUnexpectedNestedPathsForProperty(string $prop, array $expectedNestedPaths): array diff --git a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php index 119cbfd9beb..1f057c693d9 100644 --- a/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php +++ b/src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php @@ -234,12 +234,12 @@ public function onEntireEntityUpdated($oldValue) }); }]; - yield 'onUpdated: with array' => [function () { + yield 'onUpdated: with wildcard' => [function () { return HydrationTest::create(new class() { - #[LiveProp(writable: true, onUpdated: 'onNestedArrayUpdated')] + #[LiveProp(writable: true, onUpdated: ['*' => 'onArrayUpdated'])] public array $array = []; - public function onNestedArrayUpdated($oldValue) + public function onArrayUpdated($oldValue) { if ('Kevin' === $this->array['name']) { $this->array['name'] = 'Simon'; @@ -247,7 +247,9 @@ public function onNestedArrayUpdated($oldValue) } }) ->mountWith(['array' => ['name' => 'Ryan']]) - ->userUpdatesProps(['array.name' => 'Kevin']) + ->userUpdatesProps([ + 'array.name' => 'Kevin', + ]) ->assertObjectAfterHydration(function (object $object) { self::assertSame('Simon', $object->array['name']); });