Skip to content

Commit

Permalink
[5.4] Add make method to HasOneOrMany and MorphOneOrMany relati…
Browse files Browse the repository at this point in the history
…ons (#19307)

* [5.4] Add `make` method to `HasOneOrMany` and `MorphOneOrMany` relations

Implement a `make` method for instanciating eloquent models
on `hasOne`, `hasMany`, `morphOne`, and `morphMany` relationships
without performing inserts.

* Fix CS
  • Loading branch information
sebdesign authored and taylorotwell committed May 24, 2017
1 parent 85fed0e commit 10e15da
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Illuminate/Database/Eloquent/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public function __construct(QueryBuilder $query)
}

/**
* Create and return and un-saved model instance.
* Create and return an un-saved model instance.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
Expand Down
13 changes: 13 additions & 0 deletions src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,19 @@ public function __construct(Builder $query, Model $parent, $foreignKey, $localKe
parent::__construct($query, $parent);
}

/**
* Create and return an un-saved instance of the related model.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function make(array $attributes = [])
{
return tap($this->related->newInstance($attributes), function ($instance) {
$instance->setAttribute($this->getForeignKeyName(), $this->getParentKey());
});
}

/**
* Set the base constraints on the relation query.
*
Expand Down
16 changes: 16 additions & 0 deletions src/Illuminate/Database/Eloquent/Relations/MorphOneOrMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,22 @@ public function __construct(Builder $query, Model $parent, $type, $id, $localKey
parent::__construct($query, $parent, $id, $localKey);
}

/**
* Create and return an un-saved instance of the related model.
*
* @param array $attributes
* @return \Illuminate\Database\Eloquent\Model
*/
public function make(array $attributes = [])
{
return tap($this->related->newInstance($attributes), function ($instance) {
// When saving a polymorphic relationship, we need to set not only the foreign
// key, but also the foreign key type, which is typically the class name of
// the parent model. This makes the polymorphic item unique in the table.
$this->setForeignAttributesForCreate($instance);
});
}

/**
* Set the base constraints on the relation query.
*
Expand Down
9 changes: 9 additions & 0 deletions tests/Database/DatabaseEloquentHasManyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ public function tearDown()
m::close();
}

public function testMakeMethodDoesNotSaveNewModel()
{
$relation = $this->getRelation();
$instance = $this->expectNewModel($relation, ['name' => 'taylor']);
$instance->expects($this->never())->method('save');

$this->assertEquals($instance, $relation->make(['name' => 'taylor']));
}

public function testCreateMethodProperlyCreatesNewModel()
{
$relation = $this->getRelation();
Expand Down
11 changes: 11 additions & 0 deletions tests/Database/DatabaseEloquentHasOneTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ public function testHasOneWithArrayDefault()
$this->assertSame(1, $newModel->getAttribute('foreign_key'));
}

public function testMakeMethodDoesNotSaveNewModel()
{
$relation = $this->getRelation();
$instance = $this->getMockBuilder('Illuminate\Database\Eloquent\Model')->setMethods(['newInstance', 'setAttribute'])->getMock();
$relation->getRelated()->shouldReceive('newInstance')->with(['name' => 'taylor'])->andReturn($instance);
$instance->expects($this->once())->method('setAttribute')->with('foreign_key', 1);
$instance->expects($this->never())->method('save');

$this->assertEquals($instance, $relation->make(['name' => 'taylor']));
}

public function testSaveMethodSetsForeignKeyOnModel()
{
$relation = $this->getRelation();
Expand Down
14 changes: 14 additions & 0 deletions tests/Database/DatabaseEloquentMorphTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,20 @@ public function testMorphManyEagerConstraintsAreProperlyAdded()
$relation->addEagerConstraints([$model1, $model2]);
}

public function testMakeFunctionOnMorph()
{
$_SERVER['__eloquent.saved'] = false;

This comment has been minimized.

Copy link
@sebdesign

sebdesign May 24, 2017

Author Contributor

This line also needs to go. Copy-pasting is hard.

// Doesn't matter which relation type we use since they share the code...
$relation = $this->getOneRelation();
$instance = m::mock('Illuminate\Database\Eloquent\Model');
$instance->shouldReceive('setAttribute')->once()->with('morph_id', 1);
$instance->shouldReceive('setAttribute')->once()->with('morph_type', get_class($relation->getParent()));
$instance->shouldReceive('save')->never();
$relation->getRelated()->shouldReceive('newInstance')->once()->with(['name' => 'taylor'])->andReturn($instance);

$this->assertEquals($instance, $relation->make(['name' => 'taylor']));
}

public function testCreateFunctionOnMorph()
{
// Doesn't matter which relation type we use since they share the code...
Expand Down

0 comments on commit 10e15da

Please sign in to comment.