[5.x]: Link fields can't be eager-loaded, and each linked element costs a query
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ó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 25/100
Hướng nghiên cứu
Read BaseElementLinkType::element(), Link, and LinkData to trace how linked elements are fetched and how eager loading changes the field value. The issue describes regression tests and proposed changes for both 5.12 and 6.x; review those tests and the eager-loading map handling. Done means links load in batches with and without with(), preserve LinkData, and the 6.x worker case does not reuse elements from earlier jobs.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
What happened?
Description
Link fields fetch each linked element with its own query, and they can't be eager-loaded. Adding a Link field to with() makes things worse: the field's value becomes an empty ElementCollection instead of a LinkData object.
The cause is that BaseElementLinkType::element() runs $query->one() once per distinct link value. Results are memoised, but never batched. Link doesn't implement EagerLoadingFieldInterface, so there is no way to batch them.
On a content site, every entry or asset link in a Matrix or Content Block field (buttons, teasers, steps) adds one element query per page. On the Craft 6 site where we found this, that was one extra query per distinct link. A page with 20 entry links runs 20 single-row element queries, which nothing can batch.
The Craft 5 docs say that "it is not currently possible to eager-load link field relationships, as their element type is not known until the field data is loaded" (link.md). Since Craft 5, though, the field data is loaded with the source elements. An EagerLoadingFieldInterface map can also return one set per element type, so the element type is known when the map is built.
Craft 6 has the same per-link query. It also gained LinkElementLoader / BaseElementLinkType::preloadElements() for Table link cells in #19998, but Link fields don't use them.
Steps to reproduce
- Add a Link field
linkFieldthat allows entry links to an entry type. - Create three entries that each link to a different entry.
- Run
Entry::find()->id($ids)->all()and read$entry->linkField->getElement()(orgetUrl()/getLabel()) on each of them. That runs one element query per entry. - Run
Entry::find()->id($ids)->with(['linkField'])->all()and read$entry->linkField. It is an emptyElementCollection, not aLinkData.
Expected behavior
with(['linkField']), including nested paths such aslinkField.image, eager-loads the linked elements, and the field value stays aLinkData.- Without
with(), reading the first link loads the linked elements of every element from the same query together. Entry authors (Entry::getAuthors()) and Content Block fields already work this way.
Actual behavior
- Each
getElement(),getUrl()orgetLabel()call on a link to an element runs its own element query (one per distinct link value). - With
with(['linkField']),$entry->linkFieldis an emptyElementCollectioninstead of aLinkData.
Proposed fix (PRs ready)
LinkimplementsEagerLoadingFieldInterface. The map is built from the field values that are already loaded, with one map per link type and site. It uses the same criteria asBaseElementLinkType::element()(status(null),drafts(null),revisions(null), and eithersiteIdorsite('*')->unique()->preferSites()), so the resolved elements don't change.- The eager-loaded elements are handed to the
LinkDataobjects through the existingSetEagerLoadedElementsevent, withhandledset, sogetFieldValue()keeps returning links. LinkDatafalls back toElements::eagerLoadElements()acrosselementQueryResultwhen nowith()was given, the same way asEntry::getAuthors().- Links to different sites or link types each need their own map, which uncovered a separate bug:
normalizeEagerLoadingMaps()merges a list of maps with+=, so only the first map is used. The PRs append them instead. - On 6.x, a separate small fix:
BaseElementLinkType::$fetchedElementsis a static array that outlives the request. Queue workers and Octane return elements fetched by earlier jobs, andTestCaseresets it with reflection. It moves to a#[Scoped]holder.
I've prepared PRs against 5.12 and 6.x, with regression tests that fail without the change, and will open them once you're OK with the approach. Let me know if you'd prefer different target branches.
Craft CMS version
5.12 (branch 5.12) and 6.x (branch 6.x, and 6.0.0-alpha.20)
PHP version
8.5
Operating system and version
Linux (Docker)
Database type and version
PostgreSQL 16 (also reproduced with SQLite in the 6.x test suite)
Image driver and version
n/a
Installed plugins and versions
- Ngôn ngữ chính
- PHP
- Star
- 3.6k
- Fork
- 710
- Merge trung bình
- 11 giờ 26 phút
- Pull request đã merge (30 ngày)
- 324
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không 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 craftcms/cms
-
[5.x]: Moving an asset with “Replace it” throws TypeError: volumeUid must be of type string, null givenCó thể đã có người làm @i-just đã nhận 2 ngày trước. Đang mởbug repo:cms
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
craftcms/cms#20128 · 2 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug repo:cms
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 55/100
craftcms/cms#20171 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[5.x]: Block added through a draft gets an empty first revision on multi-site, so reverting deletes its nested contentCó thể đã có người làm @i-just đã nhận 1 ngày trước. Đang mởbug repo:cms
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
craftcms/cms#20151 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[5.x]: Passkeys registered with webauthn-lib v4 still fail with an uncaught InvalidUserHandleException (HTTP 500) after the #19530 fixCó thể đã có người làm @i-just đã nhận 9 ngày trước. Đang mởrepo:cms
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 45/100
craftcms/cms#20052 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[6.x]: Colored chips: link blue has low contrast on tinted backgroundsCó thể đã có người làm @brandonkelly đã nhận 15 ngày trước. Đang mởbug repo:cms
craftcms/cms#19984 · 3 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
unopim/shopify-connector#55 ·
-
14.3.x
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
-
CMS video element: "CmsVideoPlugin" sets `volume = 0`, so videos cannot be unmuted on Android ChromeĐang mởneeds-triage Platform(Default)
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
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 76/100
PrestaShop/ps_googleanalytics#187 ·