Commit 9dad7666 authored by duxet's avatar duxet

Trigger events in embedded models

parent d7ac7b07
......@@ -18,6 +18,7 @@ before_script:
- mysql -e 'create database unittest;'
- composer self-update
- composer require satooshi/php-coveralls:dev-master
- composer require mockery/mockery:dev-master
- composer install --dev --no-interaction
script:
......
......@@ -173,6 +173,16 @@ class Connection extends \Illuminate\Database\Connection {
return parent::getElapsedTime($start);
}
/**
* Get the PDO driver name.
*
* @return string
*/
public function getDriverName()
{
return '';
}
/**
* Dynamically pass methods to the connection.
*
......
......@@ -133,6 +133,24 @@ class EmbedsMany extends Relation {
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.
*
......@@ -141,19 +159,29 @@ class EmbedsMany extends Relation {
*/
public function save(Model $model)
{
if ($this->fireModelEvent($model, 'saving') === false) return false;
$this->updateTimestamps($model);
// Insert a new document.
if ( ! $model->exists)
{
return $this->performInsert($model);
$result = $this->performInsert($model);
}
// Update an existing document.
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 {
*/
protected function performInsert(Model $model)
{
if ($this->fireModelEvent($model, 'creating') === false) return false;
// Insert the related model in the parent instance
$this->associateNew($model);
// Push the document to the database.
$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 {
*/
protected function performUpdate(Model $model)
{
if ($this->fireModelEvent($model, 'updating') === false) return false;
// Update the related model in the parent instance
$this->associateExisting($model);
......@@ -213,7 +251,13 @@ class EmbedsMany extends Relation {
$result = $this->query->where($this->localKey . '.' . $model->getKeyName(), $id)
->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 {
{
$ids = $this->getIdsArrayFrom($ids);
$models = $this->find($ids);
$ids = array();
$primaryKey = $this->related->getKeyName();
// 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)));
$ids[] = $id;
$this->fireModelEvent($model, 'deleted', false);
}
return $this->dissociate($ids);
......@@ -511,4 +566,27 @@ class EmbedsMany extends Relation {
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 {
public function tearDown()
{
Mockery::close();
User::truncate();
Book::truncate();
Item::truncate();
......@@ -297,7 +299,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$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 = $user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertNotNull($user->_addresses);
$this->assertEquals(array('London'), $user->addresses->lists('city'));
$this->assertInstanceOf('DateTime', $address->created_at);
......@@ -309,8 +319,15 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$user = User::find($user->_id);
$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';
$user->addresses()->save($address);
$address->unsetEventDispatcher();
$this->assertEquals(2, count($user->addresses));
$this->assertEquals(2, count($user->addresses()->get()));
......@@ -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'))));
$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);
$this->assertEquals(array('Bristol', 'Bruxelles'), $user->addresses->lists('city'));
$address->unsetEventDispatcher();
$address = $user->addresses->first();
$user->addresses()->destroy($address);
$this->assertEquals(array('Bruxelles'), $user->addresses->lists('city'));
......@@ -452,4 +476,61 @@ class RelationsTest extends PHPUnit_Framework_TestCase {
$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'));
$address = new Address(array('city' => 'New York'));
$user->addresses()->save($address);
$address->setEventDispatcher($events = Mockery::mock('Illuminate\Events\Dispatcher'));
$events->shouldReceive('until')->once()->with('eloquent.deleting: '.get_class($address), $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