8000 feature #106 Twig/LiveComponent Attributes (kbond) · symfony/ux@4e7cd8c · GitHub
[go: up one dir, main page]

Skip to content

Commit 4e7cd8c

Browse files
committed
feature #106 Twig/LiveComponent Attributes (kbond)
This PR was squashed before being merged into the main branch. Discussion ---------- Twig/LiveComponent Attributes | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | Tickets | - | License | MIT Based on discussions with `@weaverryan`, the consensus from the core team seems to be to require PHP8+ and use attributes for defining components instead of the interface. The updated [readme](https://github.com/symfony/ux/blob/a80d8df64a41860d2fec407dfecac36ce34809a1/src/TwigComponent/README.md) shows how this would now work. Assuming this is the direction we want to go, I will update the `LiveComponent` package. Todo: - [x] Update `LiveComponent` - [x] Fix duplicated `LiveProp` [issue](#106 (comment)) on parent classes Commits ------- 46777c3 Twig/LiveComponent Attributes
2 parents 58e6427 + 46777c3 commit 4e7cd8c

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

@@ -113,19 +109,15 @@ Suppose you've already built a basic Twig component:
113109
// src/Components/RandomNumberComponent.php
114110
namespace App\Components;
115111

116-
use Symfony\UX\TwigComponent\ComponentInterface;
112+
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
117113

118-
class RandomNumberComponent implements ComponentInterface
114+
#[AsTwigComponent('random_number')]
115+
class RandomNumberComponent
119116
{
120117
public function getRandomNumber(): string
121118
{
122119
return rand(0, 1000);
123120
}
124-
125-
public static function getComponentName(): string
126-
{
127-
return 'random_number';
128-
}
129121
}
130122
```
131123

@@ -137,16 +129,18 @@ class RandomNumberComponent implements ComponentInterface
137129
```
138130

139131
To transform this into a "live" component (i.e. one that
140-
can be re-rendered live on the frontend), change your
141-
component's interface to `LiveComponentInterface`:
132+
can be re-rendered live on the frontend), replace the
133+
component's `AsTwigComponent` attribute with `AsLiveComponent`:
142134

143135
```diff
144136
// src/Components/RandomNumberComponent.php
145137

146-
+use Symfony\UX\LiveComponent\LiveComponentInterface;
138+
-use Symfony\UX\TwigComponent\Attribute\AsTwigComponent;
139+
+use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
147140

148-
-class RandomNumberComponent implements ComponentInterface
149-
+class RandomNumberComponent implements LiveComponentInterface
141+
-#[AsTwigComponent('random_number')]
142+
-#[AsLiveComponent('random_number')]
143+
class RandomNumberComponent
150144
{
151145
}
152146
```
@@ -193,11 +187,13 @@ namespace App\Components;
193187
// ...
194188
use Symfony\UX\LiveComponent\Attribute\LiveProp;
195189

196-
class RandomNumberComponent implements LiveComponentInterface
190+
#[AsLiveComponent('random_number')]
191+
class RandomNumberComponent
197192
{
198-
/** @LiveProp */
193+
#[LiveProp]
199194
public int $min = 0;
200-
/** @LiveProp */
195+
196+
#[LiveProp]
201197
public int $max = 1000;
202198

203199
public function getRandomNumber(): string
@@ -216,14 +212,14 @@ when rendering the component:
216212
{{ component('random_number', { min: 5, max: 500 }) }}
217213
```
218214

219-
But what's up with those `@LiveProp` annotations? A property with
220-
the `@LiveProp` annotation (or `LiveProp` PHP 8 attribute) becomes
221-
a "stateful" property for this component. In other words, each time
222-
we click the "Generate a new number!" button, when the component
223-
re-renders, it will _remember_ the original values for the `$min` and
224-
`$max` properties and generate a random number between 5 and 500.
225-
If you forgot to add `@LiveProp`, when the component re-rendered,
226-
those two values would _not_ be set on the object.
215+
But what's up with those `LiveProp` attributes? A property with
216+
the `LiveProp` attribute becomes a "stateful" property for this
217+
component. In other words, each time we click the "Generate a
218+
new number!" button, when the component re-renders, it will
219+
_remember_ the original values for the `$min` and `$max` properties
220+
and generate a random number between 5 and 500. If you forgot to
221+
add `LiveProp`, when the component re-rendered, those two values
222+
would _not_ be set on the object.
227223

228224
In short: LiveProps are "stateful properties": they will always
229225
be set when rendering. Most properties will be LiveProps, with
@@ -277,13 +273,14 @@ the `writable=true` option:
277273
// src/Components/RandomNumberComponent.php
278274
// ...
279275

280-
class RandomNumberComponent implements LiveComponentInterface
276+
class RandomNumberComponent
281277
{
282-
- /** @LiveProp() */
283-
+ /** @LiveProp(writable=true) */
278+
- #[LiveProp]
279+
+ #[LiveProp(writable: true)]
284280
public int $min = 0;
285-
- /** @LiveProp() */
286-
+ /** @LiveProp(writable=true) */
281+
282+
- #[LiveProp]
283+
+ #[LiveProp(writable: true)]
287284
public int $max = 1000;
288285

289286
// ...
@@ -438,8 +435,8 @@ want to add a "Reset Min/Max" button to our "random number"
438435
component that, when clicked, sets the min/max numbers back
439436
to a default value.
440437

441-
First, add a method with a `LiveAction` annotation (or PHP 8 attribute)
442-
above it that does the work:
438+
First, add a method with a `LiveAction` attribute above it that
439+
does the work:
443440

444441
```php
445442
// src/Components/RandomNumberComponent.php
@@ -448,13 +445,11 @@ namespace App\Components;
448445
// ...
449446
use Symfony\UX\LiveComponent\Attribute\LiveAction;
450447

451-
class RandomNumberComponent implements LiveComponentInterface
448+
class RandomNumberComponent
452449
{
453450
// ...
454451

455-
/**
456-
* @LiveAction
457-
*/
452+
#[LiveAction]
458453
public function resetMinMax()
459454
{
460455
$this->min = 0;
@@ -513,13 +508,11 @@ namespace App\Components;
513508
// ...
514509
use Psr\Log\LoggerInterface;
515510

516-
class RandomNumberComponent implements LiveComponentInterface
511+
class RandomNumberComponent
517512
{
518513
// ...
519514

520-
/**
521-
* @LiveAction
522-
*/
515+
#[LiveAction]
523516
public function resetMinMax(LoggerInterface $logger)
524517
{
525518
$this->min = 0;
@@ -558,13 +551,11 @@ namespace App\Components;
558551
// ...
559552
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
560553

561-
class RandomNumberComponent extends AbstractController implements LiveComponentInterface
554+
class RandomNumberComponent extends AbstractController
562555
{
563556
// ...
564557

565-
/**
566-
* @LiveAction
567-
*/
558+
#[LiveAction]
568559
public function resetMinMax()
569560
{
570561
// ...
@@ -694,11 +685,12 @@ use App\Entity\Post;
694685
use App\Form\PostType;
695686
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
696687
use Symfony\Component\Form\FormInterface;
688+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
697689
use Symfony\UX\LiveComponent\Attribute\LiveProp;
698-
use Symfony\UX\LiveComponent\LiveComponentInterface;
699690
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
700691

701-
class PostFormComponent extends AbstractController implements LiveComponentInterface
692+
#[AsLiveComponent('post_form')]
693+
class PostFormComponent extends AbstractController
702694
{
703695
use ComponentWithFormTrait;
704696

@@ -708,13 +700,12 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
708700
* Needed so the same form can be re-created
709701
* when the component is re-rendered via Ajax.
710702
*
711-
* The fieldName="" option is needed in this situation because
703+
* The `fieldName` option is needed in this situation because
712704
* the form renders fields with names like `name="post[title]"`.
713-
* We set fieldName="" so that this live prop doesn't collide
705+
* We set `fieldName: ''` so that this live prop doesn't collide
714706
* with that data. The value - initialFormData - could be anything.
715-
*
716-
* @LiveProp(fieldName="initialFormData")
717707
*/
708+
#[LiveProp(fieldName: 'initialFormData')]
718709
public ?Post $post = null;
719710

720711
/**
@@ -725,11 +716,6 @@ class PostFormComponent extends AbstractController implements LiveComponentInter
725716
// we can extend AbstractController to get the normal shortcuts
726717
return $this->createForm(PostType::class, $this->post);
727718
}
728-
729-
public static function getComponentName(): string
730-
{
731-
return 'post_form';
732-
}
733719
}
734720
```
735721

@@ -885,13 +871,11 @@ action to the component:
885871
use Doctrine\ORM\EntityManagerInterface;
886872
use Symfony\UX\LiveComponent\Attribute\LiveAction;
887873

888-
class PostFormComponent extends AbstractController implements LiveComponentInterface
874+
class PostFormComponent extends AbstractController
889875
{
890876
// ...
891877

892-
/**
893-
* @LiveAction()
894-
*/
878+
#[LiveAction]
895879
public function save(EntityManagerInterface $entityManager)
896880
{
897881
// shortcut to submit the form with form values
@@ -942,20 +926,14 @@ that is being edited:
942926
namespace App\Twig\Components;
943927

944928
use App\Entity\Post;
929+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
945930
use Symfony\UX\LiveComponent\Attribute\LiveProp;
946-
use Symfony\UX\LiveComponent\LiveComponentInterface;
947931

948-
class EditPostComponent implements LiveComponentInterface
932+
#[AsLiveComponent('edit_post')]
933+
class EditPostComponent
949934
{
950-
/**
951-
* @LiveProp()
952-
*/
935+
#[LiveProp]
953936
public Post $post;
954-
955-
public static function getComponentName(): string
956-
{
957-
return 'edit_post';
958-
}
959937
}
960938
```
961939

@@ -995,12 +973,10 @@ you can enable it via the `exposed` option:
995973
```diff
996974
// ...
997975

998-
class EditPostComponent implements LiveComponentInterface
976+
class EditPostComponent
999977
{
1000-
/**
1001-
- * @LiveProp(exposed={})
1002-
+ * @LiveProp(exposed={"title", "content"})
1003-
*/
978+
- #[LiveProp]
979+
+ #[LiveProp(exposed: ['title', 'content'])]
1004980
public Post $post;
1005981

1006982
// ...
@@ -1030,36 +1006,28 @@ First use the `ValidatableComponentTrait` and add any constraints you need:
10301006

10311007
```php
10321008
use App\Entity\User;
1009+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
10331010
use Symfony\UX\LiveComponent\Attribute\LiveProp;
1034-
use Symfony\UX\LiveComponent\LiveComponentInterface;
10351011
use Symfony\UX\LiveComponent\ValidatableComponentTrait;
10361012
use Symfony\Component\Validator\Constraints as Assert;
10371013

1038-
class EditUserComponent implements LiveComponentInterface
1014+
#[AsLiveComponent('edit_user')]
1015+
class EditUserComponent
10391016
{
10401017
use ValidatableComponentTrait;
10411018

1042-
/**
1043-
* @LiveProp(exposed={"email", "plainPassword"})
1044-
* @Assert\Valid()
1045-
*/
1019+
#[LiveProp(exposed: ['email', 'plainPassword'])]
1020+
#[Assert\Valid]
10461021
public User $user;
10471022

1048-
/**
1049-
* @LiveProp()
1050-
* @Assert\IsTrue()
1051-
*/
1023+
#[LiveProp]
1024+
#[Assert\IsTrue]
10521025
public bool $agreeToTerms = false;
1053-
1054-
public static function getComponentName() : string
1055-
{
1056-
return 'edit_user';
1057-
}
10581026
}
10591027
```
10601028

1061-
Be sure to add the `@Assert\IsValid` to any property where you want
1062-
the object on that property to also be validated.
1029+
Be sure to add the `IsValid` attribute/annotation to any property where
1030+
you want the object on that property to also be validated.
10631031

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

1076-
class EditUserComponent implements LiveComponentInterface
1044+
#[AsLiveComponent('edit_user')]
1045+
class EditUserComponent
10771046
{
10781047
// ...
10791048

1080-
/**
1081-
* @LiveAction()
1082-
*/
1049+
#[LiveAction]
10831050
public function save()
10841051
{
10851052
// this will throw an exception if validation fails

0 commit comments

Comments
 (0)
0