Commit f249b3be authored by Jens Segers's avatar Jens Segers

Merge pull request #164 from duxet/embedded-events

Trigger events in embedded models
parents d7ac7b07 21d973ad
...@@ -18,6 +18,7 @@ before_script: ...@@ -18,6 +18,7 @@ before_script:
- mysql -e 'create database unittest;' - mysql -e 'create database unittest;'
- composer self-update - composer self-update
- composer require satooshi/php-coveralls:dev-master - composer require satooshi/php-coveralls:dev-master
- composer require mockery/mockery:dev-master
- composer install --dev --no-interaction - composer install --dev --no-interaction
script: script:
......
...@@ -173,6 +173,16 @@ class Connection extends \Illuminate\Database\Connection { ...@@ -173,6 +173,16 @@ class Connection extends \Illuminate\Database\Connection {
return parent::getElapsedTime($start); return parent::getElapsedTime($start);
} }
/**
* Get the PDO driver name.
*
* @return string
*/
public function getDriverName()
{
return '';
}
/** /**
* Dynamically pass methods to the connection. * Dynamically pass methods to the connection.
* *
......
...@@ -133,6 +133,24 @@ class EmbedsMany extends Relation { ...@@ -133,6 +133,24 @@ class EmbedsMany extends Relation {
return $this->getResults(); return $this->getResults();
} }
/**
* Get the results with given ids.
*
* @param array $ids
* @return \Illuminate\Database\Eloquent\Collection
*/
public function find(array $ids)
{
$documents = $this->getEmbeddedRecords();
$primaryKey = $this->related->getKeyName();
$documents = array_filter($documents, function ($document) use($primaryKey, $ids) {
return in_array($document[$primaryKey], $ids);
});
return $this->toCollection($documents);
}
/** /**
* Attach a model instance to the parent model. * Attach a model instance to the parent model.
* *
...@@ -141,19 +159,29 @@ class EmbedsMany extends Relation { ...@@ -141,19 +159,29 @@ class EmbedsMany extends Relation {
*/ */
public function save(Model $model) public function save(Model $model)
{ {
if ($this->fireModelEvent($model, 'saving') === false) return false;
$this->updateTimestamps($model); $this->updateTimestamps($model);
// Insert a new document. // Insert a new document.
if ( ! $model->exists) if ( ! $model->exists)
{ {
return $this->performInsert($model); $result = $this->performInsert($model);
} }
// Update an existing document. // Update an existing document.
else else
{ {
return $this->performUpdate($model); $result = $this->performUpdate($model);
} }
if ($result)
{
$this->fireModelEvent($result, 'saved', false);
return $result;
}
return false;
} }
/** /**
...@@ -186,13 +214,21 @@ class EmbedsMany extends Relation { ...@@ -186,13 +214,21 @@ class EmbedsMany extends Relation {
*/ */
protected function performInsert(Model $model) protected function performInsert(Model $model)
{ {
if ($this->fireModelEvent($model, 'creating') === false) return false;
// Insert the related model in the parent instance // Insert the related model in the parent instance
$this->associateNew($model); $this->associateNew($model);
// Push the document to the database. // Push the document to the database.
$result = $this->query->push($this->localKey, $model->getAttributes(), true); $result = $this->query->push($this->localKey, $model->getAttributes(), true);
return $result ? $model : false; if ($result)
{
$this->fireModelEvent($model, 'created', false);
return $model;
}
return false;
} }
/** /**
...@@ -203,6 +239,8 @@ class EmbedsMany extends Relation { ...@@ -203,6 +239,8 @@ class EmbedsMany extends Relation {
*/ */
protected function performUpdate(Model $model) protected function performUpdate(Model $model)
{ {
if ($this->fireModelEvent($model, 'updating') === false) return false;
// Update the related model in the parent instance // Update the related model in the parent instance
$this->associateExisting($model); $this->associateExisting($model);
...@@ -213,7 +251,13 @@ class EmbedsMany extends Relation { ...@@ -213,7 +251,13 @@ class EmbedsMany extends Relation {
$result = $this->query->where($this->localKey . '.' . $model->getKeyName(), $id) $result = $this->query->where($this->localKey . '.' . $model->getKeyName(), $id)
->update(array($this->localKey . '.$' => $model->getAttributes())); ->update(array($this->localKey . '.$' => $model->getAttributes()));
return $result ? $model : false; if ($result)
{
$this->fireModelEvent($model, 'updated', false);
return $model;
}
return false;
} }
/** /**
...@@ -327,12 +371,23 @@ class EmbedsMany extends Relation { ...@@ -327,12 +371,23 @@ class EmbedsMany extends Relation {
{ {
$ids = $this->getIdsArrayFrom($ids); $ids = $this->getIdsArrayFrom($ids);
$models = $this->find($ids);
$ids = array();
$primaryKey = $this->related->getKeyName(); $primaryKey = $this->related->getKeyName();
// Pull the documents from the database. // Pull the documents from the database.
foreach ($ids as $id) foreach ($models as $model)
{ {
if ($this->fireModelEvent($model, 'deleting') === false) continue;
$id = $model->getKey();
$this->query->pull($this->localKey, array($primaryKey => $this->getForeignKeyValue($id))); $this->query->pull($this->localKey, array($primaryKey => $this->getForeignKeyValue($id)));
$ids[] = $id;
$this->fireModelEvent($model, 'deleted', false);
} }
return $this->dissociate($ids); return $this->dissociate($ids);
...@@ -511,4 +566,27 @@ class EmbedsMany extends Relation { ...@@ -511,4 +566,27 @@ class EmbedsMany extends Relation {
return $this->getBaseQuery()->convertKey($id); return $this->getBaseQuery()->convertKey($id);
} }
/**
* Fire the given event for the given model.
*
* @param string $event
* @param bool $halt
* @return mixed
*/
protected function fireModelEvent(Model $model, $event, $halt = true)
{
$dispatcher = $model->getEventDispatcher();
if ( is_null($dispatcher)) return true;
// We will append the names of the class to the event to distinguish it from
// other model events that are fired, allowing us to listen on each model
// event set individually instead of catching event for all the models.
$event = "eloquent.{$event}: ".get_class($model);
$method = $halt ? 'until' : 'fire';
return $dispatcher->$method($event, $model);
}
} }
...@@ -9,6 +9,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -9,6 +9,8 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
public function tearDown() public function tearDown()
{ {
Mockery::close();
User::truncate(); User::truncate();
Book::truncate(); Book::truncate();
Item::truncate(); Item::truncate();
...@@ -297,7 +299,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -297,7 +299,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user = User::create(array('name' => 'John Doe')); $user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'London')); $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 = $user->addresses()->save($address); $address = $user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertNotNull($user->_addresses); $this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city')); $this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertInstanceOf('DateTime', $address->created_at); $this->assertInstanceOf('DateTime', $address->created_at);
...@@ -309,8 +319,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -309,8 +319,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user = User::find($user->_id); $user = User::find($user->_id);
$this->assertEquals(array('London', 'Paris'), $user->addresses->lists('city')); $this->assertEquals(array('London', 'Paris'), $user->addresses->lists('city'));
$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 = 'New York'; $address->city = 'New York';
$user->addresses()->save($address); $user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertEquals(2, count($user->addresses)); $this->assertEquals(2, count($user->addresses));
$this->assertEquals(2, count($user->addresses()->get())); $this->assertEquals(2, count($user->addresses()->get()));
...@@ -403,9 +420,16 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -403,9 +420,16 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles')))); $user->addresses()->saveMany(array(new Address(array('city' => 'London')), new Address(array('city' => 'Bristol')), new Address(array('city' => 'Bruxelles'))));
$address = $user->addresses->first(); $address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(true);
$events->shouldReceive('fire')->once()->with('eloquent.deleted: '.get_class($address), Mockery::mustBe($address));
$user->addresses()->destroy($address->_id); $user->addresses()->destroy($address->_id);
$this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city')); $this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
$address = $user->addresses->first(); $address = $user->addresses->first();
$user->addresses()->destroy($address); $user->addresses()->destroy($address);
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city')); $this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
...@@ -452,4 +476,62 @@ class RelationsTest extends PHPUnit_Framework_TestCase { ...@@ -452,4 +476,62 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(array(), $user->addresses->lists('city')); $this->assertEquals(array(), $user->addresses->lists('city'));
} }
public function testEmbedsManyCreatingEventReturnsFalse()
{
$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(false);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManySavingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'Paris'));
$address->exists = true;
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.saving: '.get_class($address), $address)->andReturn(false);
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManyUpdatingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$address = new Address(array('city' => 'New York'));
$address->exists = true;
$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(false);
$address->city = 'Warsaw';
$this->assertFalse($user->addresses()->save($address));
$address->unsetEventDispatcher();
}
public function testEmbedsManyDeletingEventReturnsFalse()
{
$user = User::create(array('name' => 'John Doe'));
$user->addresses()->save(new Address(array('city' => 'New York')));
$address = $user->addresses->first();
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), Mockery::mustBe($address))->andReturn(false);
$this->assertEquals(0, $user->addresses()->destroy($address));
$this->assertEquals(array('New York'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
}
} }
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