-
-
Notifications
You must be signed in to change notification settings - Fork 936
Description
Currently, we must explicitly declare an #[ApiProperty(...)] attribute for every field just to assign serialization groups. This adds extra boilerplate, makes our models less clean, and those ApiProperty declarations aren’t used anywhere else in the codebase.
Current appraoch:
#[ApiResource(
normalizationContext: ['groups' => 'read'],
denormalizationContext: ['groups' => 'write'],
)]
#[ApiProperty(property: 'title', serialize: new Groups(['book']))]
class Book extends Model{}
Cons of this approach
1- Unnecessary boilerplate: Every property needs its own ApiProperty just for grouping.
2- Cluttered models: Mixing serialization annotations with domain logic reduces clarity.
3- No reuse: Those ApiProperty settings can’t be leveraged elsewhere.
4- Tight coupling: Because your models end up full of API-Platform annotations, it becomes harder to switch to a different serializer.
We could also attach #[Groups] to standalone getter/setter methods:
#[Groups(['read'])]
public function getTitle(): string
{
return $this->name;
}
#[Groups(['write'])]
public function setTitle(): string
{
return $this->name;
}
—but that requires writing two separate methods for each field (one for “read,” one for “write”), which is just as much boilerplate when you have dozens of properties.
I suggest allowing serialization groups to be declared directly on Laravel model attributes. By applying a custom #[Groups(...)] attribute to your accessors/mutators, you eliminate extra boilerplate and keep serialization metadata alongside your transformation logic.
class Book extends Model{
#[Groups(['read','write'])]
protected function name(): Attribute
{
return Attribute::make(
set: fn(string $v) => ucfirst(mb_strtolower(trim($v))),
get: fn(string $v) => $v,
);
}
}
Pros of this approach
1- Less code, more clarity: One #[Groups(...)] per attribute method replaces multiple ApiProperty or getter/setter annotations.
2- Better organization: Serialization metadata lives next to getter/setter logic, improving readability.
3- Reusability: Laravel’s attribute objects can be reflected on and reused by forms, docs, or custom serializers.
4- Laravel-idiomatic: Sticks to familiar Laravel patterns, avoiding heavy ties to API-Platform and simplifying future changes.
As a Symfony developer, I’m truly grateful to the API Platform team for creating such an outstanding framework—it has made building APIs a pleasure. And as a Lravel developer I hope we can bring the same level of simplicity and power to the Laravel ecosystem. Thank you in advance for considering this feature request!