Skip to content

Commit

Permalink
[8.x] Add support for attaching existing model instances in factories (
Browse files Browse the repository at this point in the history
…#35494)

* Add support for associating existing models using the 'for' factory method

* Add support for 'hasAttached'

* Fix support for BelongsTo with existing model

* Fix support for BelongsToMany with existing model

* Update docblock

* Formatting

* Formatting

Co-authored-by: John T. Bonaccorsi <jtbonaccorsi@gmail.com>
  • Loading branch information
bakerkretzmar and imjohnbon committed Dec 7, 2020
1 parent c721d5e commit c9dcaa2
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 13 deletions.
Expand Up @@ -10,7 +10,7 @@ class BelongsToManyRelationship
/**
* The related factory instance.
*
* @var \Illuminate\Database\Eloquent\Factories\Factory
* @var \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model
*/
protected $factory;

Expand All @@ -31,12 +31,12 @@ class BelongsToManyRelationship
/**
* Create a new attached relationship definition.
*
* @param \Illuminate\Database\Eloquent\Factories\Factory $factory
* @param \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model $factory
* @param callable|array $pivot
* @param string $relationship
* @return void
*/
public function __construct(Factory $factory, $pivot, $relationship)
public function __construct($factory, $pivot, $relationship)
{
$this->factory = $factory;
$this->pivot = $pivot;
Expand All @@ -51,7 +51,7 @@ public function __construct(Factory $factory, $pivot, $relationship)
*/
public function createFor(Model $model)
{
Collection::wrap($this->factory->create([], $model))->each(function ($attachable) use ($model) {
Collection::wrap($this->factory instanceof Factory ? $this->factory->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
Expand Down
Expand Up @@ -10,7 +10,7 @@ class BelongsToRelationship
/**
* The related factory instance.
*
* @var \Illuminate\Database\Eloquent\Factories\Factory
* @var \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Database\Eloquent\Model
*/
protected $factory;

Expand All @@ -31,11 +31,11 @@ class BelongsToRelationship
/**
* Create a new "belongs to" relationship definition.
*
* @param \Illuminate\Database\Eloquent\Factories\Factory $factory
* @param \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Database\Eloquent\Model $factory
* @param string $relationship
* @return void
*/
public function __construct(Factory $factory, $relationship)
public function __construct($factory, $relationship)
{
$this->factory = $factory;
$this->relationship = $relationship;
Expand All @@ -52,7 +52,7 @@ public function attributesFor(Model $model)
$relationship = $model->{$this->relationship}();

return $relationship instanceof MorphTo ? [
$relationship->getMorphType() => $this->factory->newModel()->getMorphClass(),
$relationship->getMorphType() => $this->factory instanceof Factory ? $this->factory->newModel()->getMorphClass() : $this->factory->getMorphClass(),
$relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()),
] : [
$relationship->getForeignKeyName() => $this->resolver($relationship->getOwnerKeyName()),
Expand All @@ -69,7 +69,7 @@ protected function resolver($key)
{
return function () use ($key) {
if (! $this->resolved) {
$instance = $this->factory->create();
$instance = $this->factory instanceof Factory ? $this->factory->create() : $this->factory;

return $this->resolved = $key ? $instance->{$key} : $instance->getKey();
}
Expand Down
8 changes: 4 additions & 4 deletions src/Illuminate/Database/Eloquent/Factories/Factory.php
Expand Up @@ -481,12 +481,12 @@ protected function guessRelationship(string $related)
/**
* Define an attached relationship for the model.
*
* @param \Illuminate\Database\Eloquent\Factories\Factory $factory
* @param \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Model $factory
* @param callable|array $pivot
* @param string|null $relationship
* @return static
*/
public function hasAttached(self $factory, $pivot = [], $relationship = null)
public function hasAttached($factory, $pivot = [], $relationship = null)
{
return $this->newInstance([
'has' => $this->has->concat([new BelongsToManyRelationship(
Expand All @@ -500,11 +500,11 @@ public function hasAttached(self $factory, $pivot = [], $relationship = null)
/**
* Define a parent relationship for the model.
*
* @param \Illuminate\Database\Eloquent\Factories\Factory $factory
* @param \Illuminate\Database\Eloquent\Factories\Factory|\Illuminate\Database\Eloquent\Model $factory
* @param string|null $relationship
* @return static
*/
public function for(self $factory, $relationship = null)
public function for($factory, $relationship = null)
{
return $this->newInstance(['for' => $this->for->concat([new BelongsToRelationship(
$factory,
Expand Down
52 changes: 52 additions & 0 deletions tests/Database/DatabaseEloquentFactoryTest.php
Expand Up @@ -250,6 +250,21 @@ public function test_belongs_to_relationship()
$this->assertCount(3, FactoryTestPost::all());
}

public function test_belongs_to_relationship_with_existing_model_instance()
{
$user = FactoryTestUserFactory::new(['name' => 'Taylor Otwell'])->create();
$posts = FactoryTestPostFactory::times(3)
->for($user, 'user')
->create();

$this->assertCount(3, $posts->filter(function ($post) use ($user) {
return $post->user->is($user);
}));

$this->assertCount(1, FactoryTestUser::all());
$this->assertCount(3, FactoryTestPost::all());
}

public function test_morph_to_relationship()
{
$posts = FactoryTestCommentFactory::times(3)
Expand All @@ -263,6 +278,20 @@ public function test_morph_to_relationship()
$this->assertCount(3, FactoryTestComment::all());
}

public function test_morph_to_relationship_with_existing_model_instance()
{
$post = FactoryTestPostFactory::new(['title' => 'Test Title'])->create();
$posts = FactoryTestCommentFactory::times(3)
->for($post, 'commentable')
->create();

$this->assertSame('Test Title', FactoryTestPost::first()->title);
$this->assertCount(3, FactoryTestPost::first()->comments);

$this->assertCount(1, FactoryTestPost::all());
$this->assertCount(3, FactoryTestComment::all());
}

public function test_belongs_to_many_relationship()
{
$users = FactoryTestUserFactory::times(3)
Expand Down Expand Up @@ -290,6 +319,29 @@ public function test_belongs_to_many_relationship()
unset($_SERVER['__test.role.creating-user']);
}

public function test_belongs_to_many_relationship_with_existing_model_instances()
{
$roles = FactoryTestRoleFactory::times(3)
->afterCreating(function ($role) {
$_SERVER['__test.role.creating-role'] = $role;
})
->create();
FactoryTestUserFactory::times(3)
->hasAttached($roles, ['admin' => 'Y'], 'roles')
->create();

$this->assertCount(3, FactoryTestRole::all());

$user = FactoryTestUser::latest()->first();

$this->assertCount(3, $user->roles);
$this->assertSame('Y', $user->roles->first()->pivot->admin);

$this->assertInstanceOf(Eloquent::class, $_SERVER['__test.role.creating-role']);

unset($_SERVER['__test.role.creating-role']);
}

public function test_sequences()
{
$users = FactoryTestUserFactory::times(2)->sequence(
Expand Down

0 comments on commit c9dcaa2

Please sign in to comment.