Repository navigation
addition: ContainerFactory & ImmutableFactory cycles - #41
Open
remipelhate wants to merge 19 commits into
Open
remipelhate wants to merge 19 commits into
remipelhate wants to merge 19 commits into
Conversation
remipelhate
marked this pull request as draft
November 13, 2026 11:38
remipelhate
marked this pull request as ready for review
November 13, 2026 15:48
…vider conventions
The DeferredAttribute interface allows to define a clear method signature to handle deferred attributes and eliminates the risk of accidentally breaking instance creation for factories expecting closure values.
Instead of supporting things like `state([‘attribute’ => fn() => ‘Resolve’])` directly, this Defer implementation acts as a simple wrapper to allow resolving custom closures: `state([‘attribute’ => new Defer(fn() => ‘Resolve’))`. The intent is also more explicit thiw way.
remipelhate
force-pushed
the
addition/immutable-factory-cycles
branch
from
November 13, 2026 16:06
879eb0d to
dfb62cf
Compare
This method does not exist on lower PhpUnit versions
times() previously accepted 0 as a valid argument and it makes sense to still support it whenever we’re dealing with dynamic input (e.g. through data providers).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR introduces support for cycles in
ImmutableFactoryinstances, similar to Laravel'sSequenceimplementation in Model Factories.Prior to this PR, creating instances for (e.g.) all cases of an enum required initialising
multiple factory instances:
[ new SomeFactory() ->state(['name' => 'First']) ->makeOne(), new SomeFactory() ->state(['name' => 'Middle']) ->makeOne(), new SomeFactory() ->state(['name' => 'Last']) ->makeOne(), ];While not inherently bad, this requires initialising multiple instances. More importantly, it wasn't possible to define cycling of values within an ImmutableFactory.
After this PR, the above can be rewritten as:
Changes
Faker Cycle provider
A new
CycleFaker provider is automatically registered (once) on both default and injected Faker instances of ImmutableFactory. It returns values from a list in order, wrapping back around to the first value once the end is reached:The queue is keyed by the serialized option list, so each distinct list cycles independently.
Cycle and Defer deferred attributes
cycle()provider when the factory resolves it, with acount()helper so the number of options can drivetimes().DeferredAttribute, so custom closures work in factory definitions and states without the factory resolving arbitrary Closure values.ImmutableFactoryinstance and returns the attribute value.ImmutableFactory rework
resolveValue()now only resolvesDeferredAttributeinstances. It also handles nested ContainerFactory and ImmutableFactory values.A new
ContainerFactoryhas been extracted fromImmutableFactorywith the container methods (rawMany(),rawCollection(),makeMany(),makeCollection()). This allowsImmutableFactoryto be focussed on definitions and states, whileContainerFactoryfocusses on the amount of instances to create.ImmutableFactory::times()returns an instance ofContainerFactory, which on its turn uses the originalImmutableFactoryinstance as mixin. This means that any method defined onImmutableFactorycan be used directly onContainerFactoryas well:Warning
Breaking changes
While these changes are technically breaking, the likelihood of impact is rather low. Container methods only had effect when
times()was called andContainerFactoryproxies any existing methods toImmutableFactory, keeping method call chains unaffected. Impact is mostly expected on:ImmutableFactory(or implementations of it) type hints where the provided instances calltimes(). These will have to be changed toContainerFactory.ImmutableFactory(or implementations of it) return types where the returned instances calltimes(). These will have to be changed toContainerFactory.ImmutableFactory::$countproperty.