Skip to content

Commit 80e7ba4

Browse files
committed
allow BelongsTo relations to return defaults
this implementation was copied and tweaked from the `HasOne` relationship.
1 parent 43bda84 commit 80e7ba4

File tree

2 files changed

+110
-12
lines changed

2 files changed

+110
-12
lines changed

src/Illuminate/Database/Eloquent/Relations/BelongsTo.php

+51-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88

99
class BelongsTo extends Relation
1010
{
11+
/**
12+
* Indicates if a default model instance should be used.
13+
*
14+
* Alternatively, may be a Closure or array.
15+
*
16+
* @var \Closure|array|bool
17+
*/
18+
protected $withDefault;
19+
1120
/**
1221
* The child model instance of the relation.
1322
*/
@@ -72,7 +81,7 @@ public function __construct(Builder $query, Model $child, $foreignKey, $ownerKey
7281
*/
7382
public function getResults()
7483
{
75-
return $this->query->first();
84+
return $this->query->first() ?: $this->getDefaultFor($this->parent);
7685
}
7786

7887
/**
@@ -149,12 +158,39 @@ protected function getEagerModelKeys(array $models)
149158
public function initRelation(array $models, $relation)
150159
{
151160
foreach ($models as $model) {
152-
$model->setRelation($relation, null);
161+
$model->setRelation($relation, $this->getDefaultFor($model));
153162
}
154163

155164
return $models;
156165
}
157166

167+
/**
168+
* Get the default value for this relation.
169+
*
170+
* @param \Illuminate\Database\Eloquent\Model $model
171+
* @return \Illuminate\Database\Eloquent\Model|null
172+
*/
173+
protected function getDefaultFor(Model $model)
174+
{
175+
//return nothing if the user doesn't say to
176+
if (! $this->withDefault) {
177+
return;
178+
}
179+
180+
//no need to set any attributes on this
181+
$instance = $this->related->newInstance();
182+
183+
if (is_callable($this->withDefault)) {
184+
return call_user_func($this->withDefault, $instance) ?: $instance;
185+
}
186+
187+
if (is_array($this->withDefault)) {
188+
$instance->forceFill($this->withDefault);
189+
}
190+
191+
return $instance;
192+
}
193+
158194
/**
159195
* Match the eagerly loaded results to their parents.
160196
*
@@ -342,4 +378,17 @@ public function getRelation()
342378
{
343379
return $this->relation;
344380
}
381+
382+
/**
383+
* Return a new model instance in case the relationship does not exist.
384+
*
385+
* @param \Closure|array|bool $callback
386+
* @return $this
387+
*/
388+
public function withDefault($callback = true)
389+
{
390+
$this->withDefault = $callback;
391+
392+
return $this;
393+
}
345394
}

tests/Database/DatabaseEloquentBelongsToTest.php

+59-10
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,60 @@
99

1010
class DatabaseEloquentBelongsToTest extends TestCase
1111
{
12+
protected $builder;
13+
14+
protected $related;
15+
1216
public function tearDown()
1317
{
1418
m::close();
1519
}
1620

21+
public function testBelongsToWithDefault()
22+
{
23+
$relation = $this->getRelation()->withDefault(); //belongsTo relationships
24+
25+
$this->builder->shouldReceive('first')->once()->andReturnNull();
26+
27+
$newModel = new EloquentBelongsToModelStub(); //ie Blog
28+
29+
$this->related->shouldReceive('newInstance')->once()->andReturn($newModel);
30+
31+
$this->assertSame($newModel, $relation->getResults());
32+
}
33+
34+
public function testBelongsToWithDynamicDefault()
35+
{
36+
$relation = $this->getRelation()->withDefault(function ($newModel) {
37+
$newModel->username = 'taylor';
38+
});
39+
40+
$this->builder->shouldReceive('first')->once()->andReturnNull();
41+
42+
$newModel = new EloquentBelongsToModelStub();
43+
44+
$this->related->shouldReceive('newInstance')->once()->andReturn($newModel);
45+
46+
$this->assertSame($newModel, $relation->getResults());
47+
48+
$this->assertSame('taylor', $newModel->username);
49+
}
50+
51+
public function testBelongsToWithArrayDefault()
52+
{
53+
$relation = $this->getRelation()->withDefault(['username' => 'taylor']);
54+
55+
$this->builder->shouldReceive('first')->once()->andReturnNull();
56+
57+
$newModel = new EloquentBelongsToModelStub();
58+
59+
$this->related->shouldReceive('newInstance')->once()->andReturn($newModel);
60+
61+
$this->assertSame($newModel, $relation->getResults());
62+
63+
$this->assertSame('taylor', $newModel->username);
64+
}
65+
1766
public function testUpdateMethodRetrievesModelAndUpdates()
1867
{
1968
$relation = $this->getRelation();
@@ -126,18 +175,18 @@ public function testDefaultEagerConstraintsWhenNotIncrementing()
126175

127176
protected function getRelation($parent = null, $incrementing = true, $keyType = 'int')
128177
{
129-
$builder = m::mock('Illuminate\Database\Eloquent\Builder');
130-
$builder->shouldReceive('where')->with('relation.id', '=', 'foreign.value');
131-
$related = m::mock('Illuminate\Database\Eloquent\Model');
132-
$related->incrementing = $incrementing;
133-
$related->shouldReceive('getKeyType')->andReturn($keyType);
134-
$related->shouldReceive('getIncrementing')->andReturn($incrementing);
135-
$related->shouldReceive('getKeyName')->andReturn('id');
136-
$related->shouldReceive('getTable')->andReturn('relation');
137-
$builder->shouldReceive('getModel')->andReturn($related);
178+
$this->builder = m::mock('Illuminate\Database\Eloquent\Builder');
179+
$this->builder->shouldReceive('where')->with('relation.id', '=', 'foreign.value');
180+
$this->related = m::mock('Illuminate\Database\Eloquent\Model');
181+
$this->related->incrementing = $incrementing;
182+
$this->related->shouldReceive('getKeyType')->andReturn($keyType);
183+
$this->related->shouldReceive('getIncrementing')->andReturn($incrementing);
184+
$this->related->shouldReceive('getKeyName')->andReturn('id');
185+
$this->related->shouldReceive('getTable')->andReturn('relation');
186+
$this->builder->shouldReceive('getModel')->andReturn($this->related);
138187
$parent = $parent ?: new EloquentBelongsToModelStub;
139188

140-
return new BelongsTo($builder, $parent, 'foreign_key', 'id', 'relation');
189+
return new BelongsTo($this->builder, $parent, 'foreign_key', 'id', 'relation');
141190
}
142191
}
143192

0 commit comments

Comments
 (0)