Skip to content

Commit 9080791

Browse files
committed
Address PR feedback
- Highlight advantages over arrays (type safety, IDE support, decoupled serialization) - Add API response example showing DTOs used outside ORM context
1 parent 5b27169 commit 9080791

1 file changed

Lines changed: 57 additions & 6 deletions

File tree

en/orm/query-builder.rst

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -659,9 +659,18 @@ Projecting Results Into DTOs
659659
----------------------------
660660

661661
In addition to fetching results as Entity objects or arrays, you can project
662-
query results directly into Data Transfer Objects (DTOs). DTOs are useful when
663-
you need a memory-efficient, read-only representation of your data, or when you
664-
want to decouple your data layer from the ORM's Entity objects.
662+
query results directly into Data Transfer Objects (DTOs). DTOs offer several
663+
advantages:
664+
665+
- **Memory efficiency** - DTOs consume approximately 3x less memory than Entity
666+
objects, making them ideal for large result sets.
667+
- **Type safety** - DTOs provide strong typing and IDE autocompletion support,
668+
unlike plain arrays.
669+
- **Decoupled serialization** - DTOs let you separate your API response
670+
structure from your database schema, making it easier to version APIs or
671+
expose only specific fields.
672+
- **Read-only data** - Using ``readonly`` classes ensures data integrity and
673+
makes your intent clear.
665674

666675
The ``projectAs()`` method allows you to specify a DTO class that results will
667676
be hydrated into::
@@ -683,9 +692,6 @@ be hydrated into::
683692
->projectAs(ArticleDto::class)
684693
->toArray();
685694

686-
DTOs typically consume about 3x less memory than Entity objects, making them
687-
ideal for read-heavy operations or when processing large result sets.
688-
689695
DTO Creation Methods
690696
^^^^^^^^^^^^^^^^^^^^
691697

@@ -771,6 +777,51 @@ attribute to specify the type of elements in array properties::
771777
->projectAs(ArticleDto::class)
772778
->toArray();
773779

780+
Using DTOs for API Responses
781+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
782+
783+
DTOs are particularly useful for building API responses where you want to
784+
control the output structure independently from your database schema. You can
785+
define a DTO that represents your API contract and include custom serialization
786+
logic::
787+
788+
readonly class ArticleApiResponse
789+
{
790+
public function __construct(
791+
public int $id,
792+
public string $title,
793+
public string $slug,
794+
public string $authorName,
795+
public string $publishedAt,
796+
) {
797+
}
798+
799+
public static function createFromArray(
800+
array $data,
801+
bool $ignoreMissing = false
802+
): self {
803+
return new self(
804+
id: $data['id'],
805+
title: $data['title'],
806+
slug: Inflector::slug($data['title']),
807+
authorName: $data['author']['name'] ?? 'Unknown',
808+
publishedAt: $data['created']->format('c'),
809+
);
810+
}
811+
}
812+
813+
// In your controller
814+
$articles = $this->Articles->find()
815+
->contain(['Authors'])
816+
->projectAs(ArticleApiResponse::class)
817+
->toArray();
818+
819+
return $this->response->withType('application/json')
820+
->withStringBody(json_encode(['articles' => $articles]));
821+
822+
This approach keeps your API response format decoupled from your database
823+
schema, making it easier to evolve your API without changing your data model.
824+
774825
.. note::
775826

776827
DTO projection is applied as the final formatting step, after all other

0 commit comments

Comments
 (0)