@@ -165,6 +165,136 @@ final class Notification
165165
166166<br>
167167
168+ # ## LivePropHydrationMethodsRule
169+
170+ Enforces that when a `#[LiveProp]` attribute specifies `hydrateWith` and `dehydrateWith` parameters:
171+ - Both parameters must be specified together
172+ - Both methods must exist in the component class and be declared as public
173+ - The types must be compatible throughout the hydration/dehydration cycle :
174+ - The property must have a type declaration
175+ - The hydrate method must return the same type as the property
176+ - The dehydrate method must accept the same type as the property as its first parameter
177+ - The dehydrate method's return type must match the hydrate method's parameter type
178+
179+ This ensures data flows correctly between frontend and backend representations.
180+
181+ ` ` ` yaml
182+ rules:
183+ - Kocal\P HPStanSymfonyUX\R ules\L iveComponent\L ivePropHydrationMethodsRule
184+ ` ` `
185+
186+ ` ` ` php
187+ // src/Twig/Components/ProductList.php
188+ namespace App\T wig\C omponents;
189+
190+ use Symfony\UX\LiveCo mponent\A ttribute\A sLiveComponent;
191+ use Symfony\UX\LiveCo mponent\A ttribute\L iveProp;
192+
193+ #[AsLiveComponent]
194+ final class ProductList
195+ {
196+ // Error: Missing dehydrateWith parameter
197+ #[LiveProp(hydrateWith: 'hydrateFilters')]
198+ public array $filters;
199+ }
200+ ` ` `
201+
202+ ` ` ` php
203+ // src/Twig/Components/ProductList.php
204+ namespace App\T wig\C omponents;
205+
206+ use Symfony\UX\LiveCo mponent\A ttribute\A sLiveComponent;
207+ use Symfony\UX\LiveCo mponent\A ttribute\L iveProp;
208+
209+ #[AsLiveComponent]
210+ final class ProductList
211+ {
212+ #[LiveProp(hydrateWith: 'hydrateFilters', dehydrateWith: 'dehydrateFilters')]
213+ public array $filters;
214+
215+ // Error: Methods are private/protected instead of public
216+ private function hydrateFilters(array $data): array
217+ {
218+ return $data;
219+ }
220+
221+ protected function dehydrateFilters(array $data): array
222+ {
223+ return $data;
224+ }
225+ }
226+ ` ` `
227+
228+ ` ` ` php
229+ // src/Twig/Components/ShoppingCart.php
230+ namespace App\T wig\C omponents;
231+
232+ use Symfony\UX\LiveCo mponent\A ttribute\A sLiveComponent;
233+ use Symfony\UX\LiveCo mponent\A ttribute\L iveProp;
234+
235+ class Product
236+ {
237+ public function __construct(public string $name, public float $price) {}
238+ }
239+
240+ #[AsLiveComponent]
241+ final class ShoppingCart
242+ {
243+ #[LiveProp(hydrateWith: 'hydrateProduct', dehydrateWith: 'dehydrateProduct')]
244+ public Product $product;
245+
246+ // Error: Return type doesn't match property type
247+ public function hydrateProduct(array $data): array
248+ {
249+ return $data;
250+ }
251+
252+ // Error: Parameter type doesn't match property type
253+ public function dehydrateProduct(string $product): array
254+ {
255+ return [];
256+ }
257+ }
258+ ` ` `
259+
260+ :x :
261+
262+ <br>
263+
264+ ` ` ` php
265+ // src/Twig/Components/ShoppingCart.php
266+ namespace App\T wig\C omponents;
267+
268+ use Symfony\UX\LiveCo mponent\A ttribute\A sLiveComponent;
269+ use Symfony\UX\LiveCo mponent\A ttribute\L iveProp;
270+
271+ class Product
272+ {
273+ public function __construct(public string $name, public float $price) {}
274+ }
275+
276+ #[AsLiveComponent]
277+ final class ShoppingCart
278+ {
279+ #[LiveProp(hydrateWith: 'hydrateProduct', dehydrateWith: 'dehydrateProduct')]
280+ public Product $product;
281+
282+ public function hydrateProduct(array $data): Product
283+ {
284+ return new Product($data['name'], $data['price']);
285+ }
286+
287+ public function dehydrateProduct(Product $product): array
288+ {
289+ return ['name' => $product->name, 'price' => $product->price];
290+ }
291+ }
292+ ` ` `
293+
294+ :+1 :
295+
296+ <br>
297+
168298# # TwigComponent Rules
169299
170300> [!NOTE]
0 commit comments