While working on the endpoints tracked by PrestaShop/PrestaShop#39630, I hit a case that the current `ps_apiresources` conventions don't cover:
`AddBusinessEntityCommand` (on PrestaShop `develop`) has this shape:
```php
public function __construct(
// ...scalars + enum...
/** @PARAM array $billingAddresses /
private readonly array $billingAddresses = [],
/* @PARAM array $shippingAddresses */
private readonly array $shippingAddresses = [],
)
```
The constructor calls VO methods on the array items (`$address->isDefault()`), so the CQRS bus expects them already hydrated as `BusinessEntityBillingAddress` / `BusinessEntityShippingAddress` instances.
What I tried
A plain `CQRSCreate` operation with a resource DTO exposing `public array $billingAddresses;` and the request body carrying associative arrays for each address (`{alias, address1, address2, city, postcode, countryId, stateId, phone, phoneMobile, default}`). Full draft: PrestaEdit#3.
What happens
```
500 - Call to a member function isDefault() on array
at AddBusinessEntityCommand.php:153
```
`CQRSApiSerializer` doesn't hydrate the array items into VOs — Symfony's Serializer, as wired, does not read the `@param array` docblock on the target command constructor to type-cast collection elements. The addresses reach the command as raw associative arrays and the ctor blows up.
Why I can't fix it under current rules
`CONTEXT.md` forbids:
- custom normalizers,
- custom processors,
- Value Objects as resource properties.
`CQRSCommandMapping` (`[apiField] => [commandParam]`) only routes keys; it cannot instantiate nested types.
Question
What's the intended module-side pattern when a CQRS command param is a typed array of VOs?
Possibilities I can see:
- A blessed exception: allow per-resource `Denormalizer` implementations under a specific namespace, listed as an approved escape hatch in `CONTEXT.md`.
- A framework enhancement: teach `CQRSApiSerializer` (or a decorator) to read `@param array` docblocks on the target command's constructor and hydrate items via reflection — no per-resource code needed.
- A convention on core: recommend a scalar-friendly companion command (e.g. `AddBusinessEntityFromScalarsCommand`) that accepts `array` and constructs the VOs internally, letting the module stay simple.
- A dedicated `CQRSCreate` option: something like `nestedTypes: ['[billingAddresses]' => BusinessEntityBillingAddress::class]` that the processor honors.
Happy to prototype whichever direction the maintainers want. This isn't just `BusinessEntity` — it will hit any future endpoint whose write command takes VO collections.
While working on the endpoints tracked by PrestaShop/PrestaShop#39630, I hit a case that the current `ps_apiresources` conventions don't cover:
`AddBusinessEntityCommand` (on PrestaShop `develop`) has this shape:
```php
public function __construct(
// ...scalars + enum...
/** @PARAM array $billingAddresses /
private readonly array $billingAddresses = [],
/* @PARAM array $shippingAddresses */
private readonly array $shippingAddresses = [],
)
```
The constructor calls VO methods on the array items (`$address->isDefault()`), so the CQRS bus expects them already hydrated as `BusinessEntityBillingAddress` / `BusinessEntityShippingAddress` instances.
What I tried
A plain `CQRSCreate` operation with a resource DTO exposing `public array $billingAddresses;` and the request body carrying associative arrays for each address (`{alias, address1, address2, city, postcode, countryId, stateId, phone, phoneMobile, default}`). Full draft: PrestaEdit#3.
What happens
```
500 - Call to a member function isDefault() on array
at AddBusinessEntityCommand.php:153
```
`CQRSApiSerializer` doesn't hydrate the array items into VOs — Symfony's Serializer, as wired, does not read the `@param array` docblock on the target command constructor to type-cast collection elements. The addresses reach the command as raw associative arrays and the ctor blows up.
Why I can't fix it under current rules
`CONTEXT.md` forbids:
`CQRSCommandMapping` (`[apiField] => [commandParam]`) only routes keys; it cannot instantiate nested types.
Question
What's the intended module-side pattern when a CQRS command param is a typed array of VOs?
Possibilities I can see:
Happy to prototype whichever direction the maintainers want. This isn't just `BusinessEntity` — it will hit any future endpoint whose write command takes VO collections.