Commit 6c60542a authored by Jens Segers's avatar Jens Segers

Improve embedded relations, and allow nested embedded relations

parent 0e923bca
......@@ -32,8 +32,16 @@ class EmbedsMany extends EmbedsOneOrMany {
$model->setAttribute('_id', new MongoId);
}
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested())
{
$this->associate($model);
return $this->parent->save();
}
// Push the new model to the database.
$result = $this->query->push($this->localKey, $model->getAttributes(), true);
$result = $this->getBaseQuery()->push($this->localKey, $model->getAttributes(), true);
// Attach the model to its parent.
if ($result) $this->associate($model);
......@@ -49,12 +57,23 @@ class EmbedsMany extends EmbedsOneOrMany {
*/
public function performUpdate(Model $model, array $values)
{
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested())
{
$this->associate($model);
return $this->parent->save();
}
// Get the correct foreign key value.
$foreignKey = $this->getForeignKeyValue($model);
// Use array dot notation for better update behavior.
$values = array_dot($model->getDirty(), $this->localKey . '.$.');
// Update document in database.
$result = $this->query->where($this->localKey . '.' . $model->getKeyName(), $foreignKey)
->update(array($this->localKey . '.$' => $model->getAttributes()));
$result = $this->getBaseQuery()->where($this->localKey . '.' . $model->getKeyName(), $foreignKey)
->update($values);
// Attach the model to its parent.
if ($result) $this->associate($model);
......
......@@ -32,7 +32,15 @@ class EmbedsOne extends EmbedsOneOrMany {
$model->setAttribute('_id', new MongoId);
}
$result = $this->query->update(array($this->localKey => $model->getAttributes()));
// For deeply nested documents, let the parent handle the changes.
if ($this->isNested())
{
$this->associate($model);
return $this->parent->save();
}
$result = $this->getBaseQuery()->update(array($this->localKey => $model->getAttributes()));
// Attach the model to its parent.
if ($result) $this->associate($model);
......@@ -48,7 +56,17 @@ class EmbedsOne extends EmbedsOneOrMany {
*/
public function performUpdate(Model $model, array $values)
{
$result = $this->query->update(array($this->localKey => $model->getAttributes()));
if ($this->isNested())
{
$this->associate($model);
return $this->parent->save();
}
// Use array dot notation for better update behavior.
$values = array_dot($model->getDirty(), $this->localKey . '.');
$result = $this->getBaseQuery()->update($values);
// Attach the model to its parent.
if ($result) $this->associate($model);
......
......@@ -51,6 +51,7 @@ abstract class EmbedsOneOrMany extends Relation {
// If this is a nested relation, we need to get the parent query instead.
if ($parentRelation = $this->getParentRelation())
{
//$this->query = $parentRelation->parent->newQuery();
$this->query = $parentRelation->getQuery();
}
......@@ -66,7 +67,7 @@ abstract class EmbedsOneOrMany extends Relation {
{
if (static::$constraints)
{
$this->query->where($this->parent->getKeyName(), '=', $this->parent->getKey());
$this->query->where($this->getQualifiedParentKeyName(), '=', $this->getParentKey());
}
}
......@@ -328,4 +329,54 @@ abstract class EmbedsOneOrMany extends Relation {
return $this->parent->getParent();
}
/**
* Check if this relation is nested in another relation.
*
* @return boolean
*/
protected function isNested()
{
return $this->getParentRelation() != null;
}
/**
* Get the fully qualified local key name.
*
* @return string
*/
protected function getPathHierarchy($glue = '.')
{
if ($parentRelation = $this->getParentRelation())
{
return $parentRelation->getPathHierarchy($glue) . $glue . $this->localKey;
}
return $this->localKey;
}
/**
* Get the parent's fully qualified key name.
*
* @return string
*/
protected function getQualifiedParentKeyName()
{
if ($parentRelation = $this->getParentRelation())
{
return $parentRelation->getPathHierarchy() . '.' . $this->parent->getKeyName();
}
return $this->parent->getKeyName();
}
/**
* Get the primary key value of the parent.
*
* @return string
*/
protected function getParentKey()
{
return $this->parent->getKey();
}
}
......@@ -74,6 +74,7 @@ class EmbeddedRelationsTest extends TestCase {
$user = User::find($user->_id);
$user->addresses()->save(new Address(array('city' => 'Bruxelles')));
$this->assertEquals(array('London', 'New York', 'Bruxelles'), $user->addresses->lists('city'));
$address = $user->addresses[1];
$address->city = "Manhattan";
$user->addresses()->save($address);
......@@ -83,6 +84,29 @@ class EmbeddedRelationsTest extends TestCase {
$this->assertEquals(array('London', 'Manhattan', 'Bruxelles'), $freshUser->addresses->lists('city'));
}
public function testEmbedsManySaveModel()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London'));
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.creating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.created: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address->save();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('until')->once()->with('eloquent.updating: '.get_class($address), $address)->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.updated: '.get_class($address), $address);
$events->shouldReceive('fire')->once()->with('eloquent.saved: '.get_class($address), $address);
$address->city = 'Paris';
$address->save();
}
public function testEmbedsToArray()
{
$user = User::create(array('name' => 'John Doe'));
......@@ -217,6 +241,32 @@ class EmbeddedRelationsTest extends TestCase {
$this->assertEquals(array('Bristol'), $user->addresses->lists('city'));
}
public function testEmbedsManyDelete()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::type('Address'))->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::type('Address'));
$address->delete();
$this->assertEquals(2, $user->addresses()->count());
$this->assertEquals(2, $user->addresses->count());
$address->unsetEventDispatcher();
$address = $user->addresses->first();
$address->delete();
$user = User::where('name', 'John Doe')->first();
$this->assertEquals(1, $user->addresses()->count());
$this->assertEquals(1, $user->addresses->count());
}
public function testEmbedsManyDissociate()
{
$user = User::create(array());
......@@ -339,7 +389,7 @@ class EmbeddedRelationsTest extends TestCase {
$this->assertTrue(is_array($user->toArray()['addresses']));
}
public function testEmbedsManyDelete()
public function testEmbedsManyDeleteAll()
{
$user1 = User::create(array('name' => 'John Doe'));
$user1->addresses()->save(new Address(array('city' => 'New York')));
......@@ -500,4 +550,77 @@ class EmbeddedRelationsTest extends TestCase {
$this->assertEquals('Mark Doe', $user->father->name);
}
public function testNestedEmbedsOne()
{
$user = User::create(array('name' => 'John Doe'));
$father = $user->father()->create(array('name' => 'Mark Doe'));
$grandfather = $father->father()->create(array('name' => 'Steve Doe'));
$greatgrandfather = $grandfather->father()->create(array('name' => 'Tom Doe'));
$user->name = 'Tim Doe';
$user->save();
$father->name = 'Sven Doe';
$father->save();
$greatgrandfather->name = 'Ron Doe';
$greatgrandfather->save();
$this->assertEquals('Tim Doe', $user->name);
$this->assertEquals('Sven Doe', $user->father->name);
$this->assertEquals('Steve Doe', $user->father->father->name);
$this->assertEquals('Ron Doe', $user->father->father->father->name);
$user = User::where('name', 'Tim Doe')->first();
$this->assertEquals('Tim Doe', $user->name);
$this->assertEquals('Sven Doe', $user->father->name);
$this->assertEquals('Steve Doe', $user->father->father->name);
$this->assertEquals('Ron Doe', $user->father->father->father->name);
}
public function testNestedEmbedsMany()
{
$user = User::create(array('name' => 'John Doe'));
$country1 = $user->addresses()->create(array('country' => 'France'));
$country2 = $user->addresses()->create(array('country' => 'Belgium'));
$city1 = $country1->addresses()->create(array('city' => 'Paris'));
$city2 = $country2->addresses()->create(array('city' => 'Ghent'));
$city3 = $country2->addresses()->create(array('city' => 'Brussels'));
$city3->city = 'Bruges';
$city3->save();
$this->assertEquals(2, $user->addresses()->count());
$this->assertEquals(1, $user->addresses()->first()->addresses()->count());
$this->assertEquals(2, $user->addresses()->last()->addresses()->count());
$user = User::where('name', 'John Doe')->first();
$this->assertEquals(2, $user->addresses()->count());
$this->assertEquals(1, $user->addresses()->first()->addresses()->count());
$this->assertEquals(2, $user->addresses()->last()->addresses()->count());
}
public function testNestedMixedEmbeds()
{
$user = User::create(array('name' => 'John Doe'));
$father = $user->father()->create(array('name' => 'Mark Doe'));
$country1 = $father->addresses()->create(array('country' => 'France'));
$country2 = $father->addresses()->create(array('country' => 'Belgium'));
$country2->country = 'England';
$country2->save();
$father->name = 'Steve Doe';
$father->save();
$this->assertEquals('France', $user->father->addresses()->first()->country);
$this->assertEquals('England', $user->father->addresses()->last()->country);
$this->assertEquals('Steve Doe', $user->father->name);
$user = User::where('name', 'John Doe')->first();
$this->assertEquals('France', $user->father->addresses()->first()->country);
$this->assertEquals('England', $user->father->addresses()->last()->country);
$this->assertEquals('Steve Doe', $user->father->name);
}
}
......@@ -6,4 +6,9 @@ class Address extends Eloquent {
protected static $unguarded = true;
public function addresses()
{
return $this->embedsMany('Address');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment