Skip to content

Add DTO (Data Transfer Object) support to Queue plugin - #192

Open
skie wants to merge 8 commits into
cakephp:3.xfrom
skie:feature/dto-support
Open

Add DTO (Data Transfer Object) support to Queue plugin#192
skie wants to merge 8 commits into
cakephp:3.xfrom
skie:feature/dto-support

Conversation

@skie

@skie skie commented Aug 8, 2026

Copy link
Copy Markdown
Member

Allows job payloads to be dispatched and received as typed DTO objects instead of plain arrays, while maintaining full backward compatibility with existing array-based jobs.

Key Features:

  • QueueManager::push() now accepts a DTO object directly, or a plain array paired with an explicit dtoClass option (metadata for uniqueness hashing / debugging)
  • New Message::getDto(ExpectedDto::class) hydrates the payload into the class the job asks for — the expected type comes from application code, not from the message body, so a tampered queue message cannot choose which class is instantiated. getArgument() still returns the raw array unchanged. getDtoClass() exposes any dtoClass recorded at dispatch time as metadata only
  • Two hydration styles, matching CakePHP 5.4's own DTO conventions (#[RequestToDto], SelectQuery::projectAs()): constructor reflection (with nested DTOs and #[CollectionOf]), and a static createFromArray() factory
  • shouldBeUnique dedupe hashing now factors in dtoClass, so two different DTO types with coincidentally identical data are never treated as duplicates of each other
  • Fully backward compatible for dispatch — legacy array-only pushes produce byte-identical message bodies. On receive, getDto() throws when the expected class is missing or the payload cannot be hydrated; jobs that still accept legacy arrays can catch that or keep using getArgument()

Usage:

$order = new OrderDto(id: 7, customer: 'Acme Corp', items: [
    new OrderItemDto(sku: 'SKU-1', quantity: 2),
]);

QueueManager::push(ProcessOrderJob::class, $order);

// Or, array payload + optional dtoClass metadata:
QueueManager::push(ProcessOrderJob::class, $data, [
    'dtoClass' => OrderDto::class,
]);
class ProcessOrderJob implements JobInterface
{
    public function execute(Message $message): ?string
    {
        $order = $message->getDto(OrderDto::class);
        return Processor::ACK;
    }
}

Note: requires bumping cakephp/cakephp from ^5.1.0 to ^5.4 (needed for ResultSetFactory::hydrateDto() / DtoMapper), plus php from >=8.1 to >=8.2 to match. Since this raises the floor for every existing installation — not just DTO users — this should ship as 3.0.0 off a new 3.x branch rather than a 2.x minor/patch release, with the version bump called out explicitly in the changelog/release notes.

skie added 4 commits August 8, 2026 19:07
Allows job payloads to be dispatched and received as typed DTO objects instead of plain arrays, while maintaining full backward compatibility with existing array-based jobs.

- `QueueManager::push()` now accepts a DTO object directly, or a plain array paired with an explicit `dtoClass` option
- New `Message::getDto()` / `getDtoClass()` to hydrate the payload back into the DTO on the receiving side — `getArgument()` still returns the raw array unchanged
- Two hydration styles, matching CakePHP 5.4's own DTO conventions (`#[RequestToDto]`, `SelectQuery::projectAs()`): constructor reflection (with nested DTOs and `#[CollectionOf]`), and a static `createFromArray()` factory
- `shouldBeUnique` dedupe hashing now factors in `dtoClass`, so two different DTO types with coincidentally identical data are never treated as duplicates of each other
- Fully backward compatible — legacy array-only pushes produce byte-identical message bodies; `getDto()` gracefully returns `null` (never throws) when no DTO was dispatched or the recorded `dtoClass` can no longer be autoloaded
Comment thread src/QueueManager.php
Comment thread src/QueueManager.php
Comment thread composer.json
Comment thread src/Dto/DtoManager.php Outdated
@LordSimal
LordSimal requested a review from dereuromark August 8, 2026 16:38
@LordSimal
LordSimal changed the base branch from 2.x to 3.x August 8, 2026 16:40
@LordSimal
LordSimal requested review from ADmad and markstory August 8, 2026 16:44
@skie
skie requested a review from LordSimal August 8, 2026 19:23
Comment thread composer.json
Comment thread docs/en/jobs.md Outdated
Comment thread docs/en/jobs.md Outdated
Comment thread docs/en/jobs.md
Comment thread src/Job/Message.php Outdated

$dtoClass = $this->getDtoClass();
if ($dtoClass === null) {
return null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like an error condition to me.

Comment thread src/Job/Message.php Outdated
Comment thread src/QueueManager.php
Hydrate only the type the job asks for so a tampered queue body cannot choose which class is instantiated.
Throw on failure instead of returning null.
@skie
skie requested review from ADmad and markstory August 10, 2026 11:41
Comment thread src/Dto/DtoManager.php
Co-authored-by: Mark Story <mark@mark-story.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants