Skip to content

Reflect a model's relation methods once per class - #268

Closed
JonasPardon wants to merge 1 commit into
wintercms:wip/1.3from
JonasPardon:fix/memoize-relation-method-scan
Closed

JonasPardon wants to merge 1 commit into
wintercms:wip/1.3from
JonasPardon:fix/memoize-relation-method-scan

Conversation

@JonasPardon

Copy link
Copy Markdown

getRelationMethods() only reuses its result once $resolvedRelationMethods has an entry for the class. That entry is only written when a method resolves to a relation, so:

  • A model whose relations are all array-defined ($belongsTo, $hasMany, …) never gets an entry. Every call runs ReflectionClass::getMethods() again. getRelationType() calls it once per relation type, so this happens about ten times per lookup.
  • On a model with relation methods, resolving a single one first (e.g. getRelationType('messages')) writes a partial entry. After that, getRelationMethods() returns only ['messages'] instead of every relation method.
$author = new Author();
$author->getRelationType('messages');
$author->getRelationMethods(); // ['messages'], expected all 7 relation methods

The change keeps the methods found by the reflection scan per class in $scannedRelationMethods and returns those. Extension and dynamic methods are still checked on every call, as before.

Phone::getRelationType('author') ×1000 in the test suite goes from 139 ms to 1.2 ms. On a production Winter site on wip/1.3, this function took about 25% of the render time of a page with many models. That page went from 1040 ms to 676 ms with only this change.

  • Two tests: a single resolved relation no longer truncates getRelationMethods(), and a class without relation methods is reflected once. Both fail without the change.
  • Full suite, phpcs and phpstan pass.

🤖 Generated with Claude Code

getRelationMethods() only reused its result once a relation method had
been resolved, so a model whose relations are all array-defined was
reflected again on every relation lookup. Once a single method had been
resolved, it returned just that method.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 23, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b5626a65-2e10-4676-9ccd-9f6dafe08c1f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mjauvin

mjauvin commented Oct 26, 2026

Copy link
Copy Markdown
Member

Fixes wintercms/winter#1581

bennothommo added a commit that referenced this pull request Oct 26, 2026
Instead of repeating reflection on the model class each time a relation
is checked, this does one pass on the first model relation call, and
caches the result.

Dynamic methods are still checked live each time.

Resolves wintercms/winter#1581
Replaces #268

Thanks to @JonasPardon for initial fix.
@JonasPardon

Copy link
Copy Markdown
Author

Replaced by #270

LukeTowers pushed a commit that referenced this pull request Nov 4, 2026
Instead of repeating reflection on the model class each time a relation is checked, this does one pass on the first model relation call, and caches the result.

Dynamic methods are still checked live each time.

This also resolves a bug where an initial hasRelation call would cache a single relation and cause other relation methods (such as getDefinedRelations) to only return the single relation.

Resolves: wintercms/winter#1581
Replaces: #268
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.

3 participants