diff --git a/src/Illuminate/Database/Eloquent/Factories/BelongsToManyRelationship.php b/src/Illuminate/Database/Eloquent/Factories/BelongsToManyRelationship.php index da004c83bc74..fd350e6fce6c 100644 --- a/src/Illuminate/Database/Eloquent/Factories/BelongsToManyRelationship.php +++ b/src/Illuminate/Database/Eloquent/Factories/BelongsToManyRelationship.php @@ -50,7 +50,9 @@ public function __construct($factory, $pivot, $relationship) */ public function createFor(Model $model) { - Collection::wrap($this->factory instanceof Factory ? $this->factory->create([], $model) : $this->factory)->each(function ($attachable) use ($model) { + $relationship = $model->{$this->relationship}(); + + Collection::wrap($this->factory instanceof Factory ? $this->factory->prependState($relationship->getQuery()->pendingAttributes)->create([], $model) : $this->factory)->each(function ($attachable) use ($model) { $model->{$this->relationship}()->attach( $attachable, is_callable($this->pivot) ? call_user_func($this->pivot, $model) : $this->pivot diff --git a/src/Illuminate/Database/Eloquent/Factories/Factory.php b/src/Illuminate/Database/Eloquent/Factories/Factory.php index ffe9018e67f0..a52d840f421e 100644 --- a/src/Illuminate/Database/Eloquent/Factories/Factory.php +++ b/src/Illuminate/Database/Eloquent/Factories/Factory.php @@ -530,6 +530,21 @@ public function state($state) ]); } + /** + * Prepend a new state transformation to the model definition. + * + * @param (callable(array, TModel|null): array)|array $state + * @return static + */ + public function prependState($state) + { + return $this->newInstance([ + 'states' => $this->states->prepend( + is_callable($state) ? $state : fn () => $state, + ), + ]); + } + /** * Set a single model attribute. * diff --git a/src/Illuminate/Database/Eloquent/Factories/Relationship.php b/src/Illuminate/Database/Eloquent/Factories/Relationship.php index 4024f1c929c0..e23bc99d78b0 100644 --- a/src/Illuminate/Database/Eloquent/Factories/Relationship.php +++ b/src/Illuminate/Database/Eloquent/Factories/Relationship.php @@ -49,13 +49,15 @@ public function createFor(Model $parent) $this->factory->state([ $relationship->getMorphType() => $relationship->getMorphClass(), $relationship->getForeignKeyName() => $relationship->getParentKey(), - ])->create([], $parent); + ])->prependState($relationship->getQuery()->pendingAttributes)->create([], $parent); } elseif ($relationship instanceof HasOneOrMany) { $this->factory->state([ $relationship->getForeignKeyName() => $relationship->getParentKey(), - ])->create([], $parent); + ])->prependState($relationship->getQuery()->pendingAttributes)->create([], $parent); } elseif ($relationship instanceof BelongsToMany) { - $relationship->attach($this->factory->create([], $parent)); + $relationship->attach( + $this->factory->prependState($relationship->getQuery()->pendingAttributes)->create([], $parent) + ); } } diff --git a/tests/Database/DatabaseEloquentFactoryTest.php b/tests/Database/DatabaseEloquentFactoryTest.php index b0860d236b03..da5467794d77 100644 --- a/tests/Database/DatabaseEloquentFactoryTest.php +++ b/tests/Database/DatabaseEloquentFactoryTest.php @@ -850,6 +850,62 @@ public function test_factory_global_model_resolver() $this->assertEquals(FactoryTestGuessModelFactory::new()->modelName(), FactoryTestGuessModel::class); } + public function test_factory_model_has_many_relationship_has_pending_attributes() + { + FactoryTestUser::factory()->has(new FactoryTestPostFactory(), 'postsWithFooBarBazAsTitle')->create(); + + $this->assertEquals('foo bar baz', FactoryTestPost::first()->title); + } + + public function test_factory_model_has_many_relationship_has_pending_attributes_override() + { + FactoryTestUser::factory()->has((new FactoryTestPostFactory())->state(['title' => 'other title']), 'postsWithFooBarBazAsTitle')->create(); + + $this->assertEquals('other title', FactoryTestPost::first()->title); + } + + public function test_factory_model_has_one_relationship_has_pending_attributes() + { + FactoryTestUser::factory()->has(new FactoryTestPostFactory(), 'postWithFooBarBazAsTitle')->create(); + + $this->assertEquals('foo bar baz', FactoryTestPost::first()->title); + } + + public function test_factory_model_has_one_relationship_has_pending_attributes_override() + { + FactoryTestUser::factory()->has((new FactoryTestPostFactory())->state(['title' => 'other title']), 'postWithFooBarBazAsTitle')->create(); + + $this->assertEquals('other title', FactoryTestPost::first()->title); + } + + public function test_factory_model_belongs_to_many_relationship_has_pending_attributes() + { + FactoryTestUser::factory()->has(new FactoryTestRoleFactory(), 'rolesWithFooBarBazAsName')->create(); + + $this->assertEquals('foo bar baz', FactoryTestRole::first()->name); + } + + public function test_factory_model_belongs_to_many_relationship_has_pending_attributes_override() + { + FactoryTestUser::factory()->has((new FactoryTestRoleFactory())->state(['name' => 'other name']), 'rolesWithFooBarBazAsName')->create(); + + $this->assertEquals('other name', FactoryTestRole::first()->name); + } + + public function test_factory_model_morph_many_relationship_has_pending_attributes() + { + (new FactoryTestPostFactory())->has(new FactoryTestCommentFactory(), 'commentsWithFooBarBazAsBody')->create(); + + $this->assertEquals('foo bar baz', FactoryTestComment::first()->body); + } + + public function test_factory_model_morph_many_relationship_has_pending_attributes_override() + { + (new FactoryTestPostFactory())->has((new FactoryTestCommentFactory())->state(['body' => 'other body']), 'commentsWithFooBarBazAsBody')->create(); + + $this->assertEquals('other body', FactoryTestComment::first()->body); + } + /** * Get a database connection instance. * @@ -895,11 +951,26 @@ public function posts() return $this->hasMany(FactoryTestPost::class, 'user_id'); } + public function postsWithFooBarBazAsTitle() + { + return $this->hasMany(FactoryTestPost::class, 'user_id')->withAttributes(['title' => 'foo bar baz']); + } + + public function postWithFooBarBazAsTitle() + { + return $this->hasOne(FactoryTestPost::class, 'user_id')->withAttributes(['title' => 'foo bar baz']); + } + public function roles() { return $this->belongsToMany(FactoryTestRole::class, 'role_user', 'user_id', 'role_id')->withPivot('admin'); } + public function rolesWithFooBarBazAsName() + { + return $this->belongsToMany(FactoryTestRole::class, 'role_user', 'user_id', 'role_id')->withPivot('admin')->withAttributes(['name' => 'foo bar baz']); + } + public function factoryTestRoles() { return $this->belongsToMany(FactoryTestRole::class, 'role_user', 'user_id', 'role_id')->withPivot('admin'); @@ -944,6 +1015,11 @@ public function comments() { return $this->morphMany(FactoryTestComment::class, 'commentable'); } + + public function commentsWithFooBarBazAsBody() + { + return $this->morphMany(FactoryTestComment::class, 'commentable')->withAttributes(['body' => 'foo bar baz']); + } } class FactoryTestCommentFactory extends Factory