Skip to content

[6.x] Cache Entry::descendants() per request - #15645

Merged
jasonvarga merged 1 commit into
6.xfrom
fix/memoize-entry-descendants
Oct 15, 2026
Merged

jasonvarga merged 1 commit into
6.xfrom
fix/memoize-entry-descendants

Conversation

@joshuablum

Copy link
Copy Markdown
Member

Since #14773, Entry::descendants() loads the localization tree with one batched query per level but only the first level (directDescendants()) is cached. Entry::in() resolves through $this->root()->descendants(), so every in() call queries the deeper levels again. Code that calls in() once per site, like the locales tag or addons building hreflang tags, runs O(sites*depth) queries on every page view.
Using the Eloquent driver, 10 in() calls on an entry with nested localizations ran 24 queries, and 30 calls ran 74.

This PR caches the whole tree (entry-all-descendants-{id}) so the descendant queries run once per entry per request. The batched loading from #14773 is unchanged.

  • descendants() and directDescendants() return copies, so callers that modify the result can't corrupt the cache. Previously, descendants() returned the cached first-level collection itself when there were no deeper levels.
  • A new private flushDescendantsCache() clears both cache keys for an entry and its ancestors. It's called on save, delete, deleteDescendants() and detachLocalizations().
  • When a save changes an entry's origin, the previous origin's tree is cleared too, using getOriginal('origin').
  • delete() didn't clear any cache before, so an origin kept listing a deleted localization for the rest of the request. That's fixed as part of this.
  • Like the existing first-level cache, saving or deleting through the repository directly (Facades\Entry::save($entry)) skips the clearing.

Related: statamic/seo-pro#694 works around the same issue in SEO Pro by resolving an entry's localizations once per cascade. This PR fixes it in core so every caller of in() benefits.

@jasonvarga
jasonvarga merged commit f607946 into 6.x Oct 15, 2026
65 checks passed
@jasonvarga
jasonvarga deleted the fix/memoize-entry-descendants branch October 15, 2026 15:08
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.

2 participants