Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions src/LiveComponent/src/Attribute/AsLiveComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,20 +99,6 @@ public static function preDehydrateMethods(object $component): \Traversable
yield from self::attributeMethodsFor(PreDehydrate::class, $component);
}

/**
* @param string|object $classOrObject
*
* @return \ReflectionMethod[]
*/
private static function attributeMethodsFor(string $attribute, object $component): \Traversable
{
foreach ((new \ReflectionClass($component))->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
if ($method->getAttributes($attribute)[0] ?? null) {
yield $method;
}
}
}

/**
* @return \ReflectionProperty[]
*/
Expand Down
34 changes: 34 additions & 0 deletions src/TwigComponent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,40 @@ If an option name matches an argument name in `mount()`, the
option is passed as that argument and the component system
will _not_ try to set it directly on a property.

### PreMount Hook

If you need to modify/validate data before it's _mounted_ on the
component use a `PreMount` hook:

```php
// src/Components/AlertComponent.php

use Symfony\UX\TwigComponent\Attribute\PreMount;
// ...

#[AsTwigComponent('alert')]
class AlertComponent
{
public string $message;
public string $type = 'success';

#[PreMount]
public function preMount(array $data): array
{
// validate data
$resolver = new OptionsResolver();
$resolver->setDefaults(['type' => 'success']);
$resolver->setAllowedValues('type', ['success', 'danger']);
$resolver->setRequired('message');
$resolver->setAllowedTypes('message', 'string');

return $resolver->resolve($data)
}

// ...
}
```

## Fetching Services

Let's create a more complex example: a "featured products" component.
Expand Down
24 changes: 24 additions & 0 deletions src/TwigComponent/src/Attribute/AsTwigComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,28 @@ public function __construct(string $name, ?string $template = null)
$this->name = $name;
$this->template = $template;
}

/**
* @internal
*
* @return \ReflectionMethod[]
*/
public static function preMountMethods(object $component): \Traversable
{
yield from self::attributeMethodsFor(PreMount::class, $component);
}

/**
* @internal
*
* @return \ReflectionMethod[]
*/
protected static function attributeMethodsFor(string $attribute, object $component): \Traversable
{
foreach ((new \ReflectionClass($component))->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
if ($method->getAttributes($attribute)[0] ?? null) {
yield $method;
}
}
}
}
22 changes: 22 additions & 0 deletions src/TwigComponent/src/Attribute/PreMount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Symfony\UX\TwigComponent\Attribute;

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* @author Kevin Bond <kevinbond@gmail.com>
*
* @experimental
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
final class PreMount
{
}
11 changes: 11 additions & 0 deletions src/TwigComponent/src/ComponentFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;

/**
* @author Kevin Bond <kevinbond@gmail.com>
Expand Down Expand Up @@ -80,6 +81,7 @@ public function configFor($component, string $name = null): array
public function create(string $name, array $data = []): object
{
$component = $this->getComponent($name);
$data = $this->preMount($component, $data);

$this->mount($component, $data);

Expand Down Expand Up @@ -140,4 +142,13 @@ private function getComponent(string $name): object

return $this->components->get($name);
}

private function preMount(object $component, array $data): array
{
foreach (AsTwigComponent::preMountMethods($component) as $method) {
$data = $component->{$method->name}($data);
}

return $data;
}
}
12 changes: 12 additions & 0 deletions src/TwigComponent/tests/Fixture/Component/ComponentB.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,23 @@
namespace Symfony\UX\TwigComponent\Tests\Fixture\Component;

use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
use Symfony\UX\TwigComponent\Attribute\PreMount;

/**
* @author Kevin Bond <kevinbond@gmail.com>
*/
#[AsTwigComponent('component_b', template: 'components/custom1.html.twig')]
final class ComponentB
{
public string $value;

#[PreMount]
public function preMount(array $data): array
{
if (isset($data['value'])) {
$data['value'] = 'pre-mount '.$data['value'];
}

return $data;
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Custom template 1
b value: {{ this.value }}
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{{ component('component_a', { propA: 'prop a value 1', propB: 'prop b value 1' }) }}
{{ component('component_a', { propA: 'prop a value 2', propB: 'prop b value 2' }) }}
{{ component('component_b') }}
{{ component('component_b', { value: 'b value 1' }) }}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public function testCanRenderTheSameComponentMultipleTimes(): void
$this->assertStringContainsString('propB: prop b value 1', $output);
$this->assertStringContainsString('propA: prop a value 2', $output);
$this->assertStringContainsString('propB: prop b value 2', $output);
$this->assertStringContainsString('b value: pre-mount b value 1', $output);
$this->assertStringContainsString('service: service a value', $output);
}

Expand Down