2.0.0-rc.3
Pre-release
Pre-release
Fixes
- Fix
recycle()silently rebuilding a mid-chain required parent. Recycling a node composed via the newwithRequiredParents()no longer lands in the "explicit" association bucket, soDataCompiler::mergeWithToOne()substitutes the recycled row instead of rebuilding it (#89). - Fix
BaseFactory::collectForeignKeyColumns()FK to alias cache going stale on runtime association changes. The cache is now invalidated when abelongsTois added, removed, or replaced, so both thestrictDefinitiondetector and the auto-skip inDataCompilersee the current schema (#91). - Fix
foreignKey => falsebelongsTocomposition crashing at persist time with "Cannot set an empty field". Cake'sBelongsTo::saveAssociated()can't cascade through a relation with no FK column;DataCompilernow routes that one edge through an independent save that joins the active test transaction, applies nested IS_ASSOCIATED semantics, and replays the parent factory'safterSavecallbacks (#93). - Fix two transaction-lifecycle defects in
FactoryTransactionStrategy: a failing per-connection rollback no longer aborts the cascade or leaves the strategy pointing at dead connections;BaseFactory::finalizePersistedEntities()now runs unconditionally so an associated entity saved via a parent's connection still getsclean()/setNew(false)/setSource()(#94). - Fix
ScenarioAwareTrait::loadFixtureScenario()swallowing PHPUnitAssertionFailedErrorand turning a test failure into a test error. Framework exceptions now pass through untouched while PHPErrors and plumbing exceptions keep wrapping asFixtureScenarioException(#96). - Fix
has()silently dropping pivot data when the alias is not abelongsToMany.hasOne/hasManymarshallers have no junction to populate, so the pivot was lost on save;has()now throws when$pivotis supplied for a non-BTM alias (#97). - Fix
->with('Alias', F1)->with('Alias', F2)losing F1's marshaller options on to-many edges. A parallel$associationHistorynow merges marshaller options across every duplicate add for the same to-many alias; to-one keeps the existing last-wins semantics (#98). - Fix detection of custom-condition (uuid) join columns for
belongsTodeclared with'foreignKey' => false. The FK-in-definition()detector andautoSkipComposeOnExplicitForeignKeynow recognize legacyconditions-based joins (#82). - Fix
with('Alias[N]', SomeFactory::new())silently dropping the bracket count and building 1 row instead of N. The leaf-factory branch now honors the bracket count just like the array-data path (#103). - Fix a
configure()-timewithRequiredParents()regression where the composed parents survivedconfigure()but their ids never propagated into the child's FK columns, crashing on save with NOT NULL. The demoted-to-defaults bucket fromwithRequiredParents()was silently overwritten bycollectAssociationsFromDefaultTemplate(); now merged with the correct precedence (#105). - Fix three pinned-FK precedence bugs in
withRequiredParents(): (a) an array-instantiation pin overridden bysequenceField()no longer skips parent composition; (b) the additiverequiredParentAssociations()hook now honors the pinned-FK skip uniformly with auto-detected aliases; (c) the entity-instantiation path (Factory::new($entity)) now also composes the parent when sequence may overwrite the entity-side FK pin, and the auto-resolved parent no longer gets silently demoted-into-invisibility on entity-injected builds (#106).
Improvements
- Add
BaseFactory::withRequiredParents()— the ergonomic counterpart toFixtureFactories.strictDefinition. Composes exactly the parents a schema requires (NOT NULLbelongsToFKs) in one call, with optional shared-ancestor reuse viarecycle()(#87). - Add
excludedRequiredParentAssociations()— symmetric exclude hook at the factory class level, complementing the additiverequiredParentAssociations()and the per-call$exceptargument. Exclude wins over both. Composite-key opt-in via the additive hook is now also covered end-to-end (#92, #102). - Add visibility for auto-skipped
configure()associations (#86). - Bake: skip columns managed by
TimestampBehavioron insert. Detection is by class (so aliased'className' => 'Timestamp'loads are caught too) and only'Model.beforeSave'writers configured with theneworalwaysqualifier are skipped (#95). - Add a target type-check on the explicit-alias form of
for()/has(). The previous direction guard only checked cardinality, so passing a factory whose target table didn't match the alias silently mis-wired the entity into the wrong slot. The guard now also rejects a target mismatch (#97). - Warn at
E_USER_WARNINGwhenFactoryTransactionStrategy::setupTest()cannot resolve its primary connection (typo in a subclass, missing test connection config). Previously the eager begin disappeared silently and mixed-style tests leaked direct$table->save()rows across methods — exactly the bug the eager default was reintroduced to fix in v2. Set$primaryConnection = ''to opt out without the warning (#99). recycle($a, $b)now rejects two entities for the same source table in one call — that shape silently dropped the first entity and was almost always a typo for "I want one of these but I'm not sure which". Across separate->recycle($a)->recycle($b)calls last-wins is preserved as intentional map update (#100).TableAssertionsTrait::assertEntityMissing()now refuses entities that were never persisted — both the null-PK case and the application-assigned-PK (UUID / string) case whereisNew()is still true. Previously it short-circuited to false on null PK and silently passed, proving nothing (#103).
Full Changelog: 2.0.0-rc.2...2.0.0-rc.3