Eager loading for resources mapped to an entity through stateOptions (DTO output)
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 20/100
- Loại issue
- Tính năng
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- php
- Lĩnh vực
- api, performance
Hướng nghiên cứu
Start in EagerLoadingExtension::apply() and joinRelations(): apply() bails out without serialization groups/attributes, and joinRelations() builds metadata from $entityClass, so the DTO's #[ApiProperty(readableLink: true)] is ignored. Read #2002, #5062 and #8185 (fcfaf384) for the early-return and mapped-resource precedents. Done means a stateOptions: new Options(entityClass: ...) resource joins from its own readableLink/fetchEager properties, recursively through embedded resources, and the 30-item page no longer runs per-item queries; add eager-loading coverage.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Description
When a resource is a DTO backed by an entity through stateOptions: new Options(entityClass: ...), EagerLoadingExtension doesn't join any of the relations the DTO exposes. Each item then loads its relations lazily, which causes N+1 queries.
I understand that eager loading relies on serialization groups by design: apply() returns early without groups or attributes since #2002, to avoid recursive joins, and #5062 settled on documenting this rather than changing it. That makes sense for a resource that is the entity itself, where groups are the only way to tell which relations will be serialized.
A mapped resource is different: its class already describes the exact shape of the response. It usually needs no serialization groups, and adding some only to trigger eager loading duplicates that shape. Two things currently prevent the extension from using it:
- Without groups,
apply()returns early, so nothing is joined, even withforceEager: true(the default). - With groups or attributes,
joinRelations()reads the property metadata of the entity ($propertyMetadataFactory->create($entityClass, $association)), not that of the resource.#[ApiProperty(readableLink: true)]on the resource's properties is therefore ignored. The entity's to-one relations getreadableLink: null, so the extension stops after the first level.
The workarounds are:
- to put API Platform metadata on the entity itself, which is what
stateOptionsis meant to avoid; - to declare a
normalizationContextattributestree that duplicates the resource class. The serializer also applies that tree, so it silently drops any field missing from it.
On our project (API Platform 5.0.5, Doctrine ORM 3), a paginated sub-collection of 30 items like the one below runs 167 queries where 10 would be enough: one query per item for the to-one relation, plus the to-many collections of each item.
This looks like the same kind of gap as #8185, fixed in fcfaf384 for nested property filters on mapped resources.
Example
#[ApiResource(
operations: [
new GetCollection(
uriTemplate: '/libraries/{uuid}/books',
uriVariables: ['uuid' => new Link(toProperty: 'library', fromClass: LibraryOutput::class)],
provider: BookProvider::class, // Doctrine CollectionProvider + entity → DTO mapping
),
],
stateOptions: new Options(entityClass: Book::class),
)]
final readonly class BookOutput
{
public function __construct(
public string $uuid,
public string $title,
#[ApiProperty(readableLink: true)]
public AuthorOutput $author, // AuthorOutput embeds country (to-one) and awards (to-many)
/** @var list<ReviewOutput> */
#[ApiProperty(readableLink: true)]
public array $reviews,
) {
}
}
Current behavior: the page query selects only Book. Then author, its country and awards, and the reviews are loaded one book at a time.
Expected behavior: when the operation has stateOptions with an entityClass, the extension builds its join tree from the resource's property metadata instead of requiring groups. It follows the properties embedded with readableLink (or fetchEager), recursively through the embedded resources, and applies that tree to the entity's associations with the same names. The tree is bounded by what the resource embeds, which avoids the unbounded recursion that motivated #2002.
When the resource is mapped with the ObjectMapper (#[Map(source: ...)]), the same mapping could translate renamed properties into association names. We haven't checked whether ObjectMapperProvider has the same limitation, but it goes through the same Doctrine provider and extensions.
Would you accept such a change? If so, we can open a PR.
- Ngôn ngữ chính
- PHP
- Star
- 2.6k
- Fork
- 987
- Merge trung bình
- 1 ngày 22 giờ
- Pull request đã merge (30 ngày)
- 87
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của api-platform/core
-
API Platform ignores array shapes in the JSON SchemaCó thể đã có người làm @afraca đã nhận 3 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
api-platform/core#8766 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
SortFilter generates unsupported array form for parametersCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
api-platform/core#8744 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
JSON:API: resource schemas (`*.jsonapi`) have `properties` but no `type: object`Có thể đã có người làm @ousamabenyounes đã nhận 12 ngày trước. Đang mở
Độ khó 2/5 Dưới một giờ Mức phù hợp với người mới 80/100
api-platform/core#8701 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Doctrine ORM/ODM resource metadata collection factory runs outside the metadata cache since 4.2.16 (priority -50 vs `.cached` at -10)Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 68/100
api-platform/core#8769 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[Json-Schema] Nested resources lost externalDoc and description dataCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 68/100
api-platform/core#8721 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của api-platform/core
Issue tương tự
-
bug review needed
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 86/100
OpenMage/magento-lts#5816 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
priority:low type:docs
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
crazy-goat/rabbit-stream#958 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
nextcloud/richdocuments#6250 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
sync-en
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 83/100
Maintainer thường phản hồi trong vòng 1 ngày
-
sync-en
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 75/100
Maintainer thường phản hồi trong vòng 1 ngày