Skip to content

Commit b26e37c

Browse files
committed
Add LivePropHydrationMethodsRule rule
1 parent c3ecd70 commit b26e37c

21 files changed

+997
-0
lines changed

README.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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\PHPStanSymfonyUX\Rules\LiveComponent\LivePropHydrationMethodsRule
184+
```
185+
186+
```php
187+
// src/Twig/Components/ProductList.php
188+
namespace App\Twig\Components;
189+
190+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
191+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
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\Twig\Components;
205+
206+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
207+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
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\Twig\Components;
231+
232+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
233+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
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\Twig\Components;
267+
268+
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
269+
use Symfony\UX\LiveComponent\Attribute\LiveProp;
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]

phpstan.dist.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ parameters:
1010
paths:
1111
- tests/**/Fixture/*
1212
identifiers:
13+
- argument.type
1314
- method.unused
1415
- missingType.iterableValue
16+
- missingType.parameter
17+
- missingType.property
18+
- missingType.return
1519
- property.unused
20+
- return.type

phpunit.dist.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<php>
1414
<ini name="display_errors" value="1" />
1515
<ini name="error_reporting" value="-1" />
16+
<ini name="memory_limit" value="-1" />
1617
</php>
1718

1819
<testsuites>

0 commit comments

Comments
 (0)