Skip to content

Commit 9704a88

Browse files
committed
add PostMount component hook
1 parent 55a7cf2 commit 9704a88

File tree

10 files changed

+138
-4
lines changed

10 files changed

+138
-4
lines changed

src/TwigComponent/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
- Add `PreMount` priority parameter.
88

9+
- Add `PostMount` hook.
10+
911
## 2.0.0
1012

1113
- Support for `stimulus` version 2 was removed and support for `@hotwired/stimulus`

src/TwigComponent/src/Attribute/AsTwigComponent.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ public static function preMountMethods(object $component): iterable
4444
return array_reverse($methods);
4545
}
4646

47+
public static function postMountMethods(object $component): iterable
48+
{
49+
$methods = iterator_to_array(self::attributeMethodsFor(PostMount::class, $component));
50+
51+
usort($methods, static function (\ReflectionMethod $a, \ReflectionMethod $b) {
52+
return $a->getAttributes(PostMount::class)[0]->newInstance()->priority <=> $b->getAttributes(PostMount::class)[0]->newInstance()->priority;
53+
});
54+
55+
return array_reverse($methods);
56+
}
57+
4758
/**
4859
* @internal
4960
*
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Symfony\UX\TwigComponent\Attribute;
4+
5+
/*
6+
* This file is part of the Symfony package.
7+
*
8+
* (c) Fabien Potencier <[email protected]>
9+
*
10+
* For the full copyright and license information, please view the LICENSE
11+
* file that was distributed with this source code.
12+
*/
13+
14+
/**
15+
* @author Kevin Bond <[email protected]>
16+
*
17+
* @experimental
18+
*/
19+
#[\Attribute(\Attribute::TARGET_METHOD)]
20+
final class PostMount
21+
{
22+
public int $priority;
23+
24+
/**
25+
* @param int $priority If multiple hooks are registered in a component, use to configure
26+
* the order in which they are called (higher called earlier)
27+
*/
28+
public function __construct(int $priority = 0)
29+
{
30+
$this->priority = $priority;
31+
}
32+
}

src/TwigComponent/src/ComponentFactory.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,17 @@ public function create(string $name, array $data = []): object
8787

8888
// set data that wasn't set in mount on the component directly
8989
foreach ($data as $property => $value) {
90-
if (!$this->propertyAccessor->isWritable($component, $property)) {
91-
throw new \LogicException(sprintf('Unable to write "%s" to component "%s". Make sure this is a writable property or create a mount() with a $%s argument.', $property, \get_class($component), $property));
90+
if ($this->propertyAccessor->isWritable($component, $property)) {
91+
$this->propertyAccessor->setValue($component, $property, $value);
92+
93+
unset($data[$property]);
9294
}
95+
}
96+
97+
$data = $this->postMount($component, $data);
9398

94-
$this->propertyAccessor->setValue($component, $property, $value);
99+
foreach ($data as $property => $value) {
100+
throw new \LogicException(sprintf('Unable to write "%s" to component "%s". Make sure this is a writable property or create a mount() with a $%s argument.', $property, \get_class($component), $property));
95101
}
96102

97103
return $component;
@@ -151,4 +157,13 @@ private function preMount(object $component, array $data): array
151157

152158
return $data;
153159
}
160+
161+
private function postMount(object $component, array $data): array
162+
{
163+
foreach (AsTwigComponent::postMountMethods($component) as $method) {
164+
$data = $component->{$method->name}($data);
165+
}
166+
167+
return $data;
168+
}
154169
}

src/TwigComponent/src/Resources/doc/index.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,40 @@ component use a ``PreMount`` hook::
264264
the order in which they're called, use the ``priority`` attribute parameter:
265265
``PreMount(priority: 10)`` (higher called earlier).
266266

267+
PostMount Hook
268+
~~~~~~~~~~~~~~
269+
270+
When a component is mounted with the passed data, if an item cannot be
271+
mounted on the component, an exception is thrown. You can intercept this
272+
behavior and "catch" this extra data with a ``PostMount`` hook method. This
273+
method accepts the extra data as an argument and must return an array. If
274+
the returned array is empty, the exception will be avoided::
275+
276+
// src/Components/AlertComponent.php
277+
278+
use Symfony\UX\TwigComponent\Attribute\PostMount;
279+
// ...
280+
281+
#[AsTwigComponent('alert')]
282+
class AlertComponent
283+
{
284+
#[PostMount]
285+
public function postMount(array $data): array
286+
{
287+
// do something with the "extra" data
288+
289+
return $data;
290+
}
291+
// ...
292+
}
293+
294+
.. note::
295+
296+
If your component has multiple ``PostMount`` hooks, and you'd like to control
297+
the order in which they're called, use the ``priority`` attribute parameter:
298+
``PostMount(priority: 10)`` (higher called earlier).
299+
>>>>>>> 0cd3131 (add `PostMount` component hook)
300+
267301
Fetching Services
268302
-----------------
269303

src/TwigComponent/tests/Fixture/Component/ComponentB.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\UX\TwigComponent\Tests\Fixture\Component;
1313

1414
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
15+
use Symfony\UX\TwigComponent\Attribute\PostMount;
1516
use Symfony\UX\TwigComponent\Attribute\PreMount;
1617

1718
/**
@@ -21,6 +22,7 @@
2122
final class ComponentB
2223
{
2324
public string $value;
25+
public string $postValue;
2426

2527
#[PreMount]
2628
public function preMount(array $data): array
@@ -31,4 +33,12 @@ public function preMount(array $data): array
3133

3234
return $data;
3335
}
36+
37+
#[PostMount]
38+
public function postMount(array $data): array
39+
{
40+
$this->postValue = $data['extra'] ?? 'default';
41+
42+
return [];
43+
}
3444
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
Custom template 1
22
b value: {{ value }}
3+
post value: {{ postValue }}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{{ component('component_a', { propA: 'prop a value 1', propB: 'prop b value 1' }) }}
22
{{ component('component_a', { propA: 'prop a value 2', propB: 'prop b value 2' }) }}
3-
{{ component('component_b', { value: 'b value 1' }) }}
3+
{{ component('component_b', { value: 'b value 1', extra: 'value' }) }}

src/TwigComponent/tests/Integration/ComponentExtensionTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public function testCanRenderTheSameComponentMultipleTimes(): void
3939
$this->assertStringContainsString('propA: prop a value 2', $output);
4040
$this->assertStringContainsString('propB: prop b value 2', $output);
4141
$this->assertStringContainsString('b value: pre-mount b value 1', $output);
42+
$this->assertStringContainsString('post value: value', $output);
4243
$this->assertStringContainsString('service: service a value', $output);
4344
}
4445

src/TwigComponent/tests/Unit/Attribute/AsTwigComponentTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
16+
use Symfony\UX\TwigComponent\Attribute\PostMount;
1617
use Symfony\UX\TwigComponent\Attribute\PreMount;
1718

1819
/**
@@ -46,4 +47,31 @@ public function hook3()
4647
$this->assertSame('hook3', $hooks[1]->name);
4748
$this->assertSame('hook1', $hooks[2]->name);
4849
}
50+
51+
public function testPostMountHooksAreOrderedByPriority(): void
52+
{
53+
$hooks = AsTwigComponent::postMountMethods(
54+
new class() {
55+
#[PostMount(priority: -10)]
56+
public function hook1()
57+
{
58+
}
59+
60+
#[PostMount(priority: 10)]
61+
public function hook2()
62+
{
63+
}
64+
65+
#[PostMount]
66+
public function hook3()
67+
{
68+
}
69+
}
70+
);
71+
72+
$this->assertCount(3, $hooks);
73+
$this->assertSame('hook2', $hooks[0]->name);
74+
$this->assertSame('hook3', $hooks[1]->name);
75+
$this->assertSame('hook1', $hooks[2]->name);
76+
}
4977
}

0 commit comments

Comments
 (0)