Skip to content

Commit 9f99a12

Browse files
Twig/LiveComponent Attributes
1 parent 49bc395 commit 9f99a12

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+872
-700
lines changed

.github/workflows/test.yaml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,19 @@ jobs:
5454
cd src/LazyImage
5555
composer update --prefer-lowest --prefer-dist --no-interaction --no-ansi --no-progress
5656
php vendor/bin/simple-phpunit
57-
- name: TwigComponent
58-
run: |
59-
cd src/TwigComponent
60-
composer update --prefer-lowest --prefer-dist --no-interaction --no-ansi --no-progress
61-
php vendor/bin/simple-phpunit
6257
63-
tests-php-low-deps-74:
58+
tests-php8-low-deps:
6459
runs-on: ubuntu-latest
6560
steps:
6661
- uses: actions/checkout@master
6762
- uses: shivammathur/setup-php@v2
6863
with:
69-
php-version: '7.4'
64+
php-version: '8.0'
65+
- name: TwigComponent
66+
run: |
67+
cd src/TwigComponent
68+
composer update --prefer-lowest --prefer-dist --no-interaction --no-ansi --no-progress
69+
php vendor/bin/simple-phpunit
7070
- name: LiveComponent
7171
run: |
7272
cd src/LiveComponent
@@ -108,14 +108,12 @@ jobs:
108108
- name: TwigComponent
109109
run: |
110110
cd src/TwigComponent
111-
composer config platform.php 7.4.99
112111
composer update --prefer-dist --no-interaction --no-ansi --no-progress
113112
php vendor/bin/simple-phpunit
114113
- name: LiveComponent
115114
run: |
116115
cd src/LiveComponent
117116
php ../../.github/build-packages.php
118-
composer config platform.php 7.4.99
119117
composer update --prefer-dist --no-interaction --no-ansi --no-progress
120118
php vendor/bin/simple-phpunit
121119

src/LiveComponent/README.md

Lines changed: 67 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ A real-time product search component might look like this:
1515
// src/Components/ProductSearchComponent.php
1616
namespace App\Components;
1717

18-
use Symfony\UX\LiveComponent\LiveComponentInterface;
18+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
1919

20-
class ProductSearchComponent implements LiveComponentInterface
20+
#[AsLiveComponent('product_search')]
21+
class ProductSearchComponent
2122
{
2223
public string $query = '';
2324

@@ -33,11 +34,6 @@ class ProductSearchComponent implements LiveComponentInterface
3334
// example method that returns an array of Products
3435
return $this->productRepository->search($this->query);
3536
}
36-
37-
public static function getComponentName(): string
38-
{
39-
return 'product_search';
40-
}
4137
}
4238
```
4339

@@ -103,19 +99,15 @@ Suppose you've already built a basic Twig component:
10399
// src/Components/RandomNumberComponent.php
104100
namespace App\Components;
105101

106-
use Symfony\UX\TwigComponent\ComponentInterface;
102+
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
107103

108-
class RandomNumberComponent implements ComponentInterface
104+
#[AsTwigComponent('random_number')]
105+
class RandomNumberComponent
109106
{
110107
public function getRandomNumber(): string
111108
{
112109
return rand(0, 1000);
113110
}
114-
115-
public static function getComponentName(): string
116-
{
117-
return 'random_number';
118-
}
119111
}
120112
```
121113

@@ -127,16 +119,18 @@ class RandomNumberComponent implements ComponentInterface
127119
```
128120

129121
To transform this into a "live" component (i.e. one that
130-
can be re-rendered live on the frontend), change your
131-
component's interface to `LiveComponentInterface`:
122+
can be re-rendered live on the frontend), replace the
123+
component's `AsTwigComponent` attribute with `AsLiveComponent`:
132124

133125
```diff
134126
// src/Components/RandomNumberComponent.php
135127

136-
+use Symfony\UX\LiveComponent\LiveComponentInterface;
128+
-use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
129+
+use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
137130

138-
-class RandomNumberComponent implements ComponentInterface
139-
+class RandomNumberComponent implements LiveComponentInterface
131+
-#[AsTwigComponent('random_number')]
132+
-#[AsLiveComponent('random_number')]
133+
class RandomNumberComponent
140134
{
141135
}
142136
```
@@ -183,11 +177,13 @@ namespace App\Components;
183177
// ...
184178
use Symfony\UX\LiveComponent\Attribute\LiveProp;
185179

186-
class RandomNumberComponent implements LiveComponentInterface
180+
#[AsLiveComponent('random_number')]
181+
class RandomNumberComponent
187182
{
188-
/** @LiveProp */
183+
#[LiveProp]
189184
public int $min = 0;
190-
/** @LiveProp */
185+
186+
#[LiveProp]
191187
public int $max = 1000;
192188

193189
public function getRandomNumber(): string
@@ -206,14 +202,14 @@ when rendering the component:
206202
{{ component('random_number', { min: 5, max: 500 }) }}
207203
```
208204

209-
But what's up with those `@LiveProp` annotations? A property with
210-
the `@LiveProp` annotation (or `LiveProp` PHP 8 attribute) becomes
211-
a "stateful" property for this component. In other words, each time
212-
we click the "Generate a new number!" button, when the component
213-
re-renders, it will _remember_ the original values for the `$min` and
214-
`$max` properties and generate a random number between 5 and 500.
215-
If you forgot to add `@LiveProp`, when the component re-rendered,
216-
those two values would _not_ be set on the object.
205+
But what's up with those `LiveProp` attributes? A property with
206+
the `LiveProp` attribute becomes a "stateful" property for this
207+
component. In other words, each time we click the "Generate a
208+
new number!" button, when the component re-renders, it will
209+
_remember_ the original values for the `$min` and `$max` properties
210+
and generate a random number between 5 and 500. If you forgot to
211+
add `LiveProp`, when the component re-rendered, those two values
212+
would _not_ be set on the object.
217213

218214
In short: LiveProps are "stateful properties": they will always
219215
be set when rendering. Most properties will be LiveProps, with
@@ -267,13 +263,14 @@ the `writable=true` option:
267263
// src/Components/RandomNumberComponent.php
268264
// ...
269265

270-
class RandomNumberComponent implements LiveComponentInterface
266+
class RandomNumberComponent
271267
{
272-
- /** @LiveProp() */
273-
+ /** @LiveProp(writable=true) */
268+
- #[LiveProp]
269+
+ #[LiveProp(writable: true)]
274270
public int $min = 0;
275-
- /** @LiveProp() */
276-
+ /** @LiveProp(writable=true) */
271+
272+
- #[LiveProp]
273+
+ #[LiveProp(writable: true)]
277274
public int $max = 1000;
278275

279276
// ...
@@ -428,8 +425,8 @@ want to add a "Reset Min/Max" button to our "random number"
428425
component that, when clicked, sets the min/max numbers back
429426
to a default value.
430427

431-
First, add a method with a `LiveAction` annotation (or PHP 8 attribute)
432-
above it that does the work:
428+
First, add a method with a `LiveAction` attribute above it that
429+
does the work:
433430

434431
```php
435432
// src/Components/RandomNumberComponent.php
@@ -438,13 +435,11 @@ namespace App\Components;
438435
// ...
439436
use Symfony\UX\LiveComponent\Attribute\LiveAction;
440437

441-
class RandomNumberComponent implements LiveComponentInterface
438+
class RandomNumberComponent
442439
{
443440
// ...
444441

445-
/**
446-
* @LiveAction
447-
*/
442+
#[LiveAction]
448443
public function resetMinMax()
449444
{
450445
$this->min = 0;
@@ -503,13 +498,11 @@ namespace App\Components;
503498
// ...
504499
use Psr\Log\LoggerInterface;
505500

506-
class RandomNumberComponent implements LiveComponentInterface
501+
class RandomNumberComponent
507502
{
508503
// ...
509504

510-
/**
511-
* @LiveAction
512-
*/
505+
#[LiveAction]
513506
public function resetMinMax(LoggerInterface $logger)
514507
{
515508
$this->min = 0;
@@ -548,13 +541,11 @@ namespace App\Components;
548541
// ...
549542
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
550543

551-
class RandomNumberComponent extends AbstractController implements LiveComponentInterface
544+
class RandomNumberComponent extends AbstractController
552545
{
553546
// ...
554547

555-
/**
556-
* @LiveAction
557-
*/
548+
#[LiveAction]
558549
public function resetMinMax()
559550
{
560551
// ...
@@ -684,11 +675,12 @@ use App\Entity\Post;
684675
use App\Form\PostType;
685676
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
686677
use Symfony\Component\Form\FormInterface;
678+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
687679
use Symfony\UX\LiveComponent\Attribute\LiveProp;
688-
use Symfony\UX\LiveComponent\LiveComponentInterface;
689680
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
690681

691-
class PostFormComponent extends AbstractController implements LiveComponentInterface
682+
#[AsLiveComponent('post_form')]
683+
class PostFormComponent extends AbstractController
692684
{
693685
use ComponentWithFormTrait;
694686

@@ -698,13 +690,12 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
698690
* Needed so the same form can be re-created
699691
* when the component is re-rendered via Ajax.
700692
*
701-
* The fieldName="" option is needed in this situation because
693+
* The `fieldName` option is needed in this situation because
702694
* the form renders fields with names like `name="post[title]"`.
703-
* We set fieldName="" so that this live prop doesn't collide
695+
* We set `fieldName: ''` so that this live prop doesn't collide
704696
* with that data. The value - initialFormData - could be anything.
705-
*
706-
* @LiveProp(fieldName="initialFormData")
707697
*/
698+
#[LiveProp(fieldName: 'initialFormData')]
708699
public ?Post $post = null;
709700

710701
/**
@@ -715,11 +706,6 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
715706
// we can extend AbstractController to get the normal shortcuts
716707
return $this->createForm(PostType::class, $this->post);
717708
}
718-
719-
public static function getComponentName(): string
720-
{
721-
return 'post_form';
722-
}
723709
}
724710
```
725711

@@ -875,13 +861,11 @@ action to the component:
875861
use Doctrine\ORM\EntityManagerInterface;
876862
use Symfony\UX\LiveComponent\Attribute\LiveAction;
877863

878-
class PostFormComponent extends AbstractController implements LiveComponentInterface
864+
class PostFormComponent extends AbstractController
879865
{
880866
// ...
881867

882-
/**
883-
* @LiveAction()
884-
*/
868+
#[LiveAction]
885869
public function save(EntityManagerInterface $entityManager)
886870
{
887871
// shortcut to submit the form with form values
@@ -932,20 +916,14 @@ that is being edited:
932916
namespace App\Twig\Components;
933917

934918
use App\Entity\Post;
919+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
935920
use Symfony\UX\LiveComponent\Attribute\LiveProp;
936-
use Symfony\UX\LiveComponent\LiveComponentInterface;
937921

938-
class EditPostComponent implements LiveComponentInterface
922+
#[AsLiveComponent('edit_post')]
923+
class EditPostComponent
939924
{
940-
/**
941-
* @LiveProp()
942-
*/
925+
#[LiveProp]
943926
public Post $post;
944-
945-
public static function getComponentName(): string
946-
{
947-
return 'edit_post';
948-
}
949927
}
950928
```
951929

@@ -985,12 +963,10 @@ you can enable it via the `exposed` option:
985963
```diff
986964
// ...
987965

988-
class EditPostComponent implements LiveComponentInterface
966+
class EditPostComponent
989967
{
990-
/**
991-
- * @LiveProp(exposed={})
992-
+ * @LiveProp(exposed={"title", "content"})
993-
*/
968+
- #[LiveProp]
969+
+ #[LiveProp(exposed: ['title', 'content'])]
994970
public Post $post;
995971

996972
// ...
@@ -1020,36 +996,28 @@ First use the `ValidatableComponentTrait` and add any constraints you need:
1020996

1021997
```php
1022998
use App\Entity\User;
999+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
10231000
use Symfony\UX\LiveComponent\Attribute\LiveProp;
1024-
use Symfony\UX\LiveComponent\LiveComponentInterface;
10251001
use Symfony\UX\LiveComponent\ValidatableComponentTrait;
10261002
use Symfony\Component\Validator\Constraints as Assert;
10271003

1028-
class EditUserComponent implements LiveComponentInterface
1004+
#[AsLiveComponent('edit_user')]
1005+
class EditUserComponent
10291006
{
10301007
use ValidatableComponentTrait;
10311008

1032-
/**
1033-
* @LiveProp(exposed={"email", "plainPassword"})
1034-
* @Assert\Valid()
1035-
*/
1009+
#[LiveProp(exposed: ['email', 'plainPassword'])]
1010+
#[Assert\Valid]
10361011
public User $user;
10371012

1038-
/**
1039-
* @LiveProp()
1040-
* @Assert\IsTrue()
1041-
*/
1013+
#[LiveProp]
1014+
#[Assert\IsTrue]
10421015
public bool $agreeToTerms = false;
1043-
1044-
public static function getComponentName() : string
1045-
{
1046-
return 'edit_user';
1047-
}
10481016
}
10491017
```
10501018

1051-
Be sure to add the `@Assert\IsValid` to any property where you want
1052-
the object on that property to also be validated.
1019+
Be sure to add the `IsValid` attribute/annotation to any property where
1020+
you want the object on that property to also be validated.
10531021

10541022
Thanks to this setup, the component will now be automatically validated
10551023
on each render, but in a smart way: a property will only be validated
@@ -1063,13 +1031,12 @@ in an action:
10631031
```php
10641032
use Symfony\UX\LiveComponent\Attribute\LiveAction;
10651033

1066-
class EditUserComponent implements LiveComponentInterface
1034+
#[AsLiveComponent('edit_user')]
1035+
class EditUserComponent
10671036
{
10681037
// ...
10691038

1070-
/**
1071-
* @LiveAction()
1072-
*/
1039+
#[LiveAction]
10731040
public function save()
10741041
{
10751042
// this will throw an exception if validation fails

0 commit comments

Comments
 (0)