Skip to content

Feat: Add Factory has()/for() to Magic Method Rector - #525

Open
MizouziE wants to merge 1 commit into
driftingly:mainfrom
MizouziE:factory-has-for-to-magic-method-rector
Open

Feat: Add Factory has()/for() to Magic Method Rector#525
MizouziE wants to merge 1 commit into
driftingly:mainfrom
MizouziE:factory-has-for-to-magic-method-rector

Conversation

@MizouziE

@MizouziE MizouziE commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

See (now removed, as per repo convention) description below:

/**
 * Rewrites factory ->has()/->for() calls into their magic method equivalents, folding any
 * count and literal state into the arguments (e.g. ->has(Variation::factory()->times(3)) becomes
 * ->hasVariations(3)). Genuine relationships with simple factories qualify, including polymorphic
 * has-side relations; only the inverse morphTo is excluded.
 *
 * @see FactoryHasForToMagicMethodRectorTest
 */

Hi, I work with an ancient (18 years old) PHP codebase which has been converted to use Laravel over the last 5+ years so a lot of it has very old ways of working still, but we are trying our hardest to be progressive and make use of the multitude of Laravel 13 conveniences. We had previously decided that, going forward, we would make more use of the eloquent magic methods for factory relationship definitions as we often find complex nested factories to be quite distracting when reading the setup for tests.

However, we all attended Laravel Live London recently and it was brought to our attention that we could use Rector (better than we already had been already) to help enforce it going forward and convert existing cases in one fell swoop.

This rule in the PR comes from the custom rule I got working with our 4000+ PHPUnit tests (+ a lot of Dusk tests) and we were all quite happy with the result, so I have tried to share it here and make it fit better with Rector-Laravel conventions.

Please do let me know if I can make further changes or take different approaches anywhere.

Possible concerns

  • There are an awful lot of fixtures, but it's due to there being an awful lot of edge cases that I found while developing this. I believe that I came to a sensible and acceptable position before finalising
  • I had to add some missing relationship stubs: I would be happy to add those in a separate PR if it's preferred?
  • I used British English for one of the self contained helper method names (cos I'm Brit-ish, innit)
  • I regenerated the docs as a final step as my understanding of the CI run is that it would not accept this without it (?): If I got that wrong I left it as the final commit so it could be easily reverted

Thanks for your time and a great package. It's value is immeasurable to teams like mine!

Converts Eloquent factory `has()` and `for()` calls into their
magic-method equivalents, e.g. `has(Comment::factory()->count(3))`
becomes `hasComments(3)` and `for(User::factory())` becomes
`forUser()`.

Relation names are resolved from the related model's factory and the
relation methods declared on the model, so polymorphic and singular
relations are skipped rather than rewritten incorrectly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MizouziE
MizouziE force-pushed the factory-has-for-to-magic-method-rector branch from 645eb8d to eea2fa5 Compare July 30, 2026 09:03
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.

1 participant