Skip to content

Commit

Permalink
Allow Eloquent has one relations to return a default new model
Browse files Browse the repository at this point in the history
  • Loading branch information
sileence committed Nov 10, 2016
1 parent 47480d7 commit 87ff047
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 15 deletions.
41 changes: 38 additions & 3 deletions src/Illuminate/Database/Eloquent/Relations/HasOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,38 @@

namespace Illuminate\Database\Eloquent\Relations;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;

class HasOne extends HasOneOrMany
{
/**
* Determine whether getResults should return a default new model instance or not.
*
* @var bool
*/
protected $withDefault = false;

/**
* Return a new model instance in case the relationship does not exist.
*
* @return $this
*/
public function withDefault()
{
$this->withDefault = true;

return $this;
}

/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->query->first();
return $this->query->first() ?: $this->getDefaultFor($this->parent);
}

/**
Expand All @@ -26,7 +46,7 @@ public function getResults()
public function initRelation(array $models, $relation)
{
foreach ($models as $model) {
$model->setRelation($relation, null);
$model->setRelation($relation, $this->getDefaultFor($model));
}

return $models;
Expand All @@ -35,7 +55,7 @@ public function initRelation(array $models, $relation)
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param array $models
* @param \Illuminate\Database\Eloquent\Collection $results
* @param string $relation
* @return array
Expand All @@ -44,4 +64,19 @@ public function match(array $models, Collection $results, $relation)
{
return $this->matchOne($models, $results, $relation);
}

/**
* Get the default value for this relation.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Database\Eloquent\Model|null
*/
protected function getDefaultFor(Model $model)
{
if ($this->withDefault) {
return $this->related->newInstance()->setAttribute(
$this->getPlainForeignKey(), $model->getAttribute($this->localKey)
);
}
}
}
45 changes: 33 additions & 12 deletions tests/Database/DatabaseEloquentHasOneTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,32 @@

class DatabaseEloquentHasOneTest extends PHPUnit_Framework_TestCase
{
protected $builder;

protected $related;

protected $parent;

public function tearDown()
{
m::close();
}

public function testHasOneWithDefault()
{
$relation = $this->getRelation()->withDefault();

$this->builder->shouldReceive('first')->once()->andReturnNull();

$newModel = m::mock('Illuminate\Database\Eloquent\Model');

$newModel->shouldReceive('setAttribute')->once()->with('foreign_key', 1)->andReturn($newModel);

$this->related->shouldReceive('newInstance')->once()->andReturn($newModel);

$this->assertInstanceOf('Illuminate\Database\Eloquent\Model', $relation->getResults());
}

public function testSaveMethodSetsForeignKeyOnModel()
{
$relation = $this->getRelation();
Expand Down Expand Up @@ -113,18 +134,18 @@ public function testRelationCountQueryCanBeBuilt()

protected function getRelation()
{
$builder = m::mock('Illuminate\Database\Eloquent\Builder');
$builder->shouldReceive('whereNotNull')->with('table.foreign_key');
$builder->shouldReceive('where')->with('table.foreign_key', '=', 1);
$related = m::mock('Illuminate\Database\Eloquent\Model');
$builder->shouldReceive('getModel')->andReturn($related);
$parent = m::mock('Illuminate\Database\Eloquent\Model');
$parent->shouldReceive('getAttribute')->with('id')->andReturn(1);
$parent->shouldReceive('getCreatedAtColumn')->andReturn('created_at');
$parent->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at');
$parent->shouldReceive('newQueryWithoutScopes')->andReturn($builder);

return new HasOne($builder, $parent, 'table.foreign_key', 'id');
$this->builder = m::mock('Illuminate\Database\Eloquent\Builder');
$this->builder->shouldReceive('whereNotNull')->with('table.foreign_key');
$this->builder->shouldReceive('where')->with('table.foreign_key', '=', 1);
$this->related = m::mock('Illuminate\Database\Eloquent\Model');
$this->builder->shouldReceive('getModel')->andReturn($this->related);
$this->parent = m::mock('Illuminate\Database\Eloquent\Model');
$this->parent->shouldReceive('getAttribute')->with('id')->andReturn(1);
$this->parent->shouldReceive('getCreatedAtColumn')->andReturn('created_at');
$this->parent->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at');
$this->parent->shouldReceive('newQueryWithoutScopes')->andReturn($this->builder);

return new HasOne($this->builder, $this->parent, 'table.foreign_key', 'id');
}
}

Expand Down

0 comments on commit 87ff047

Please sign in to comment.