Implement cqrs handlers
Prestashop Developer Skills
npx -y skills add jeffsenso/prestashop-skills --skill implement-cqrs-handlersAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Implement all command and query handlers in the Adapter layer. Each handler delegates to the repository for persistence — no direct SQL or ObjectModel access. Handlers use #[AsCommandHandler] / #[AsQueryHandler] attributes for auto-registration. Trigger: "implement handlers for {Domain}".
SKILL.md
4.6 KB, as published. Nobody here has run it
implement-cqrs-handlers
Handlers live in src/Adapter/{Domain}/CommandHandler/ and QueryHandler/. See CQRS/CONTEXT.md for conventions (#[AsCommandHandler]/#[AsQueryHandler] attributes, no cross-handler calls, return types).
1. Add handler
Add{Domain}Handler.php implementing Add{Domain}HandlerInterface:
- Inject
{Domain}Repository handle(Add{Domain}Command $command): {Domain}Id:- Construct the ObjectModel entity from command data
- For multilingual fields, map
$command->getLocalizedNames()to the lang array - Call
$this->repository->create(...) - Return the new
{Domain}Id
- Catch persistence exceptions, rethrow as domain exceptions
Reference: src/Adapter/Tax/CommandHandler/AddTaxHandler.php (simple), src/Adapter/Manufacturer/CommandHandler/AddManufacturerHandler.php (with image)
2. Edit handler (partial-update)
Edit{Domain}Handler.php implementing Edit{Domain}HandlerInterface:
- Load entity:
$entity = $this->repository->get{Domain}($command->getId()) - For each field:
if ($command->getName() !== null) { $entity->name = $command->getName(); } - Apply only non-null fields — never overwrite with null
- Call
$this->repository->update($entity) - Sub-resource commands are dispatched independently, not composed here
Reference: src/Adapter/Tax/CommandHandler/EditTaxHandler.php (simple), src/Adapter/Carrier/CommandHandler/EditCarrierHandler.php (many fields)
3. Delete handler
Delete{Domain}Handler.php implementing Delete{Domain}HandlerInterface:
- Load entity to verify existence (throws
{Domain}NotFoundException) - Check business constraints: if entity is referenced by active orders/other entities, throw
CannotDelete{Domain}Exception - Call
$this->repository->delete($command->getId())
4. Toggle status handler
Toggle{Domain}StatusHandler.php:
- Load entity by ID
- Flip:
$entity->active = !$entity->active - Call repository update
- Return void — the controller reads back state from the grid
5. Sub-resource handler (if sub-resources exist)
Set{Domain}{SubResource}sHandler.php:
Two strategies depending on complexity:
Strategy A: Atomic replace (simpler, quicker)
Used when sub-resources have no identity of their own or when maintaining existing rows isn't important:
- Begin a DB transaction
- Delete all existing sub-resource rows for the entity
- Insert new rows from
$command->getItems() - Commit; on failure, rollback and throw domain exception
- Empty array = delete all (valid use case)
Reference: src/Adapter/Carrier/CommandHandler/SetCarrierRangesHandler.php
Strategy B: Incremental update (cleaner, preserves existing sub-resources)
Preferred when sub-resources have their own identity or when preserving existing rows matters (e.g., to keep audit trails, auto-increment IDs, or related data):
- Load existing sub-resources for the entity
- Compare with the new collection: identify additions, updates, and removals
- Apply only the necessary changes (insert new, update changed, delete removed)
- Wrap in a transaction
Choose Strategy B when possible for cleaner data management. Use Strategy A when sub-resources are simple value-like collections without individual identity.
6. Get-for-editing handler
Get{Domain}ForEditingHandler.php in QueryHandler/:
- Load entity via repository
- Map ALL fields to the return DTO: scalars, multilingual (array keyed by langId), related IDs
- Return typed
Editable{Domain}DTO (with scalar types only — no VOs in the DTO) - No write side effects
Reference: src/Adapter/Tax/QueryHandler/GetTaxForEditingHandler.php (simple)
7. List query handler (if explicit query exists)
Most domains use the grid QueryBuilder pattern — no handler needed. Only create if the domain
uses an explicit Get{Domain}sForListing query class.
Rules
Conventions (attributes, no SQL, no cross-handler calls, return types) are in CQRS/CONTEXT.md. Skill-specific reminders:
- Check null before every field update in Edit handler (partial-update pattern)
- Always verify existence before deletion — never delete blindly