diff --git a/src/Illuminate/Database/Eloquent/Model.php b/src/Illuminate/Database/Eloquent/Model.php index 5fe4bb82da82..19ed641a9b53 100644 --- a/src/Illuminate/Database/Eloquent/Model.php +++ b/src/Illuminate/Database/Eloquent/Model.php @@ -168,6 +168,15 @@ abstract class Model implements ArrayAccess, Arrayable, Jsonable, JsonSerializab */ protected $touches = []; + /** + * The event map for the model. + * + * Allows for object-based events for native Eloquent events. + * + * @var array + */ + protected $events = []; + /** * User exposed observable events. * @@ -1655,13 +1664,22 @@ protected function fireModelEvent($event, $halt = true) return true; } + $method = $halt ? 'until' : 'fire'; + + // If a custom event type has been configured for this event we'll fire that + // instead of the string version of the event. This provides for a custom + // "object-based" event more consistent with the rest of the framework. + if (isset($this->events[$event])) { + return static::$dispatcher->$method( + new $this->events[$event]($this) + ); + } + // 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}: ".static::class; - $method = $halt ? 'until' : 'fire'; - return static::$dispatcher->$method($event, $this); } diff --git a/tests/Database/DatabaseEloquentModelTest.php b/tests/Database/DatabaseEloquentModelTest.php index 315c31108055..3bc0e1806668 100755 --- a/tests/Database/DatabaseEloquentModelTest.php +++ b/tests/Database/DatabaseEloquentModelTest.php @@ -238,9 +238,21 @@ public function testUpdateIsCancelledIfUpdatingEventReturnsFalse() $this->assertFalse($model->save()); } + public function testEventsCanBeFiredWithCustomEventObjects() + { + $model = $this->getMockBuilder('EloquentModelEventObjectStub')->setMethods(['newQueryWithoutScopes'])->getMock(); + $query = m::mock('Illuminate\Database\Eloquent\Builder'); + $model->expects($this->once())->method('newQueryWithoutScopes')->will($this->returnValue($query)); + $model->setEventDispatcher($events = m::mock('Illuminate\Contracts\Events\Dispatcher')); + $events->shouldReceive('until')->once()->with(m::type(EloquentModelSavingEventStub::class))->andReturn(false); + $model->exists = true; + + $this->assertFalse($model->save()); + } + public function testUpdateProcessWithoutTimestamps() { - $model = $this->getMockBuilder('EloquentModelStub')->setMethods(['newQueryWithoutScopes', 'updateTimestamps', 'fireModelEvent'])->getMock(); + $model = $this->getMockBuilder('EloquentModelEventObjectStub')->setMethods(['newQueryWithoutScopes', 'updateTimestamps', 'fireModelEvent'])->getMock(); $model->timestamps = false; $query = m::mock('Illuminate\Database\Eloquent\Builder'); $query->shouldReceive('where')->once()->with('id', '=', 1); @@ -1804,3 +1816,12 @@ class EloquentModelNonIncrementingStub extends Illuminate\Database\Eloquent\Mode protected $guarded = []; public $incrementing = false; } + +class EloquentModelSavingEventStub {} + +class EloquentModelEventObjectStub extends Illuminate\Database\Eloquent\Model +{ + protected $events = [ + 'saving' => EloquentModelSavingEventStub::class + ]; +}