Skip to content

Commit 3609c3a

Browse files
Add embedsMany relationship to Eloquent Model
1 parent 10c6a9c commit 3609c3a

File tree

6 files changed

+332
-0
lines changed

6 files changed

+332
-0
lines changed

src/Jenssegers/Eloquent/Model.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
<?php namespace Jenssegers\Eloquent;
22

3+
use LogicException;
34
use Illuminate\Database\Eloquent\Relations\HasOne;
45
use Illuminate\Database\Eloquent\Relations\HasMany;
56
use Illuminate\Database\Eloquent\Relations\MorphOne;
67
use Illuminate\Database\Eloquent\Relations\MorphMany;
8+
use Illuminate\Database\Eloquent\Relations\Relation;
79
use Jenssegers\Mongodb\Relations\BelongsTo;
810
use Jenssegers\Mongodb\Relations\BelongsToMany;
11+
use Jenssegers\Mongodb\Relations\EmbedsMany;
12+
use Jenssegers\Mongodb\Relations\EmbeddedRelation;
913
use Jenssegers\Mongodb\Query\Builder as QueryBuilder;
1014

1115
abstract class Model extends \Illuminate\Database\Eloquent\Model {
@@ -219,6 +223,45 @@ public function belongsToMany($related, $collection = null, $foreignKey = null,
219223
return new BelongsToMany($query, $this, $collection, $foreignKey, $otherKey, $relation);
220224
}
221225

226+
/**
227+
* Define an embedded one-to-many relationship.
228+
*
229+
* @param string $related
230+
* @param string $collection
231+
* @return \Illuminate\Database\Eloquent\Relations\EmbedsMany
232+
*/
233+
protected function embedsMany($related, $collection = null)
234+
{
235+
// If no collection name was provided, we assume that the standard is the
236+
// related model camel_cased, pluralized and suffixed with '_ids'
237+
if (is_null($collection))
238+
{
239+
$collection = str_plural(snake_case($related)) . '_ids';
240+
}
241+
242+
return new EmbedsMany($this, $related, $collection);
243+
}
244+
245+
/**
246+
* Get a relationship value from a method.
247+
*
248+
* @param string $key
249+
* @param string $camelKey
250+
* @return mixed
251+
*/
252+
protected function getRelationshipFromMethod($key, $camelKey)
253+
{
254+
$relations = $this->$camelKey();
255+
256+
if ( ! $relations instanceof Relation and ! $relations instanceof EmbeddedRelation)
257+
{
258+
throw new LogicException('Relationship method must return an object of type '
259+
. 'Illuminate\Database\Eloquent\Relations\Relation or Jenssegers\Mongodb\Relations\EmbeddedRelation');
260+
}
261+
262+
return $this->relations[$key] = $relations->getResults();
263+
}
264+
222265
/**
223266
* Get a new query builder instance for the connection.
224267
*
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php namespace Jenssegers\Mongodb\Relations;
2+
3+
use Illuminate\Database\Eloquent\Model;
4+
5+
abstract class EmbeddedRelation {
6+
7+
/**
8+
* The parent model instance.
9+
*
10+
* @var \Illuminate\Database\Eloquent\Model
11+
*/
12+
protected $parent;
13+
14+
/**
15+
* The related model class name.
16+
*
17+
* @var string
18+
*/
19+
protected $related;
20+
21+
/**
22+
* Create a new has many relationship instance.
23+
*
24+
* @param \Illuminate\Database\Eloquent\Model $parent
25+
* @param string $related
26+
* @param string $collection
27+
* @return void
28+
*/
29+
public function __construct(Model $parent, $related)
30+
{
31+
$this->parent = $parent;
32+
$this->related = $related;
33+
}
34+
35+
/**
36+
* Get the results of the relationship.
37+
*
38+
* @return mixed
39+
*/
40+
abstract public function getResults();
41+
42+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<?php namespace Jenssegers\Mongodb\Relations;
2+
3+
use Illuminate\Database\Eloquent\Model;
4+
use Illuminate\Database\Eloquent\Collection;
5+
6+
use MongoId;
7+
8+
class EmbedsMany extends EmbeddedRelation {
9+
10+
/**
11+
* The parent collection attribute where the related are stored.
12+
*
13+
* @var string
14+
*/
15+
protected $collection;
16+
17+
/**
18+
* Create a new has many relationship instance.
19+
*
20+
* @param \Illuminate\Database\Eloquent\Model $parent
21+
* @param string $related
22+
* @param string $collection
23+
* @return void
24+
*/
25+
public function __construct(Model $parent, $related, $collection)
26+
{
27+
$this->collection = $collection;
28+
29+
parent::__construct($parent, $related);
30+
}
31+
32+
/**
33+
* Get the results of the relationship.
34+
*
35+
* @return Illuminate\Database\Eloquent\Collection
36+
*/
37+
public function getResults()
38+
{
39+
$models = new Collection();
40+
41+
$modelsAttributes = $this->parent->getAttribute($this->collection);
42+
43+
if (is_array($modelsAttributes))
44+
{
45+
foreach ($modelsAttributes as $attributes)
46+
{
47+
$models->push(new $this->related($attributes));
48+
}
49+
}
50+
51+
return $models;
52+
}
53+
54+
/**
55+
* Create a new instance and attach it to the parent model.
56+
*
57+
* @param array $attributes
58+
* @return \Illuminate\Database\Eloquent\Model
59+
*/
60+
public function build(array $attributes)
61+
{
62+
if ( ! isset($attributes['_id'])) $attributes['_id'] = new MongoId;
63+
64+
$collection = $this->parent->getAttribute($this->collection);
65+
$collection[''.$attributes['_id']] = $attributes;
66+
$this->parent->setAttribute($this->collection, $collection);
67+
68+
return new $this->related($attributes);
69+
}
70+
71+
/**
72+
* Attach an instance to the parent model.
73+
*
74+
* @param \Illuminate\Database\Eloquent\Model $model
75+
* @return \Illuminate\Database\Eloquent\Model
76+
*/
77+
public function add($model)
78+
{
79+
return $this->build($model->toArray());
80+
}
81+
82+
/**
83+
* Create a new instance, attach it to the parent model and save this model.
84+
*
85+
* @param array $attributes
86+
* @return \Illuminate\Database\Eloquent\Model
87+
*/
88+
public function create(array $attributes)
89+
{
90+
$instance = $this->build($attributes);
91+
92+
$this->parent->save();
93+
94+
return $instance;
95+
}
96+
97+
/**
98+
* Attach a model instance to the parent model and save this model.
99+
*
100+
* @param \Illuminate\Database\Eloquent\Model $model
101+
* @return \Illuminate\Database\Eloquent\Model
102+
*/
103+
public function push($model)
104+
{
105+
return $this->create($model->toArray());
106+
}
107+
108+
/**
109+
* Create an array of new instances and attach them to the parent model.
110+
*
111+
* @param array|Traversable $modelsAttributes
112+
* @return array
113+
*/
114+
public function buildMany($modelsAttributes)
115+
{
116+
$instances = array();
117+
118+
foreach ($modelsAttributes as $attributes)
119+
{
120+
$instances[] = $this->build($attributes);
121+
}
122+
123+
return $instances;
124+
}
125+
126+
/**
127+
* Attach an array of new instances to the parent model.
128+
*
129+
* @param array|Traversable $models
130+
* @return array
131+
*/
132+
public function addMany($models)
133+
{
134+
$modelsAttributes = $this->getAttributesOf($models);
135+
136+
return $this->buildMany($modelsAttributes);
137+
}
138+
139+
/**
140+
* Create an array of new instances, attach them to the parent model and save this model.
141+
*
142+
* @param array|Traversable $modelsAttributes
143+
* @return array
144+
*/
145+
public function createMany($modelsAttributes)
146+
{
147+
$instances = $this->buildMany($modelsAttributes);
148+
149+
$this->parent->save();
150+
151+
return $instances;
152+
}
153+
154+
/**
155+
* Attach an array of new instances to the parent model and save this model.
156+
*
157+
* @param array|Traversable $models
158+
* @return array
159+
*/
160+
public function pushMany($models)
161+
{
162+
$modelsAttributes = $this->getAttributesOf($models);
163+
164+
return $this->createMany($modelsAttributes);
165+
}
166+
167+
/**
168+
* Transform a list of models to a list of models' attributes
169+
*
170+
* @param array|Traversable $models
171+
* @return array
172+
*/
173+
protected function getAttributesOf($models)
174+
{
175+
$modelsAttributes = array();
176+
177+
foreach ($models as $key => $model)
178+
{
179+
$modelsAttributes[$key] = $model->getAttributes();
180+
}
181+
182+
return $modelsAttributes;
183+
}
184+
185+
}

tests/RelationsTest.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
use Illuminate\Database\Eloquent\Collection;
4+
35
class RelationsTest extends PHPUnit_Framework_TestCase {
46

57
public function setUp() {
@@ -279,4 +281,50 @@ public function testMorph()
279281
$photo = Photo::first();
280282
$this->assertEquals($photo->imageable->name, $user->name);
281283
}
284+
285+
public function testEmbedsManySaveOneRelated()
286+
{
287+
$user = User::create(array('name' => 'John Doe'));
288+
289+
$user->addresses()->create(array('city' => 'Paris'));
290+
$user->addresses()->push(new Address(array('city' => 'London')));
291+
$user->addresses()->build(array('city' => 'Bruxelles'));
292+
$user->addresses()->add(new Address(array('city' => 'New-York')));
293+
294+
$freshUser = User::find($user->id);
295+
$this->assertEquals(array('Paris', 'London', 'Bruxelles', 'New-York'), $user->addresses->lists('city'));
296+
$this->assertEquals(array('Paris', 'London'), $freshUser->addresses->lists('city'));
297+
298+
$user->save();
299+
$freshUser = User::find($user->id);
300+
$this->assertEquals(array('Paris', 'London', 'Bruxelles', 'New-York'), $freshUser->addresses->lists('city'));
301+
302+
}
303+
304+
public function testEmbedsManySaveManyRelated()
305+
{
306+
$user = User::create(array('name' => 'John Doe'));
307+
308+
$user->addresses()->createMany(array(array('city' => 'Paris'), array('city' => 'Rouen')));
309+
$user->addresses()->pushMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol'))));
310+
$user->addresses()->buildMany(array(array('city' => 'Bruxelles')));
311+
$user->addresses()->addMany(new Collection(array(new Address(array('city' => 'New-York')))));
312+
313+
$freshUser = User::find($user->id);
314+
$this->assertEquals(array('Paris', 'Rouen', 'London', 'Bristol', 'Bruxelles', 'New-York'), $user->addresses->lists('city'));
315+
$this->assertEquals(array('Paris', 'Rouen', 'London', 'Bristol'), $freshUser->addresses->lists('city'));
316+
317+
$user->save();
318+
$freshUser = User::find($user->id);
319+
$this->assertEquals(array('Paris', 'Rouen', 'London', 'Bristol', 'Bruxelles', 'New-York'), $freshUser->addresses->lists('city'));
320+
321+
}
322+
323+
public function testEmbedsManyCreateId()
324+
{
325+
$user = new User(array('name' => 'John Doe'));
326+
$user->addresses()->build(array('city' => 'Bruxelles'));
327+
$this->assertInstanceOf('MongoId', $user->addresses->first()->_id);
328+
}
329+
282330
}

tests/models/Address.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
use Jenssegers\Mongodb\Model as Eloquent;
4+
5+
class Address extends Eloquent {
6+
7+
protected static $unguarded = true;
8+
9+
}

tests/models/User.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public function photos()
5151
return $this->morphMany('Photo', 'imageable');
5252
}
5353

54+
public function addresses()
55+
{
56+
return $this->embedsMany('Address');
57+
}
58+
5459
/**
5560
* Get the unique identifier for the user.
5661
*

0 commit comments

Comments
 (0)