I was wondering how events will work in 2.0, the current implementation requires quite a lot of boiler plate code (beforeSave(), onBeforeSave()). It might be better to approach events partly in the same way jQuery does
$model = new User;
$model->on("beforeSave", function($event) { $event->isValid = false; } );
// or
$model->on("beforeSave", array("myClass", "myMethod"));
// in the save method
if (!$this->trigger("beforeSave", array("someParam" => "someValueToBePassedToEachEventHandler")) {
return false
}
This approach considerably reduces the boiler plate, and i don’t really see any disadvantages, it also means that you can create new events and listen for them on the fly, without having to change the component’s code. The only problem is how to document the events. Perhaps we could use a php doc tag in the class definition:
/**
* @event beforeSave invoked before a record is saved
* @event afterSave invoked after a record is saved
*/
One of the main reasons for the current event design (i.e. using an on-method to declare an event) is to make event declaration more explicit and better documented (such as what the event parameter would be). And because of this explicit declaration, it also allows events to be bound like a property (i.e. you can use configuration array to initialize a component).
Event binding should be similar here:
// your version
$model->on("beforeSave", function($event) { $event->isValid = false; } );
// current Yii version
$model->onBeforeSave = function($event) { $event->isValid = false; } ;
@PoL: What is the benefit of having namespaced events?
@Qiang, I believe he’s talking about signal-slot model where you can use events w/o declaring these and just calling Event::raise(‘some.name’, $arguments) and components are using Event::subscribe(‘some.name’, function($arguments){…}). So it’s quite different.
Via a behavior, or from another event (beforeSave could raise beforeRegister on a user model if the user model is new), or from elsewhere in your application. Behaviors are the main use case I think.
@samdark not backbone specifically, they follow a similar convention to jQuery (or jQuery follows them, I’m not sure which came first), but I think this approach, and the syntax: on(), off(), trigger() are quite common, and self explanatory.
@qiang : it 's very useful , like jquery 's global ajax event . we can listen the global event to make sure some functionality(similar functionality) handling go together but scatter to everywhere .
by the way i think it 's useful too introduce another event mechanism as symfony ’ event dispatcher (don’t need declare the event first(pseudo code ) : $eventDispatcher->raiseEvent(‘anyStringAsEventId’,array(../*eventObj or some customer data */)); you should register your eventListener before the raiseEvent : $eventDispatcher->listen(‘someEvent’,array(/*php callable here */)) ; another way is search the event listener in raiseEvent method ,thus we can raise any event in any module ,listening event is the same .when search the event listener we should obey some convention eg: events dir for listening event from anther modules ).
I have implemented the proposed new event system in 2.0. Below are some usage examples:
// attach an anonymous function to the 'click' event
$button->on('click', function($event) { ... });
// trigger the 'click' event
$button->trigger('click', new Event($this));
// detaches $callback from 'click'
$button->off('click', $callback);
// returns event handlers of 'click' as a vector object so that we can manipulate it
$handlers = $button->getEventHandlers('click');
It is also possible to attach an event handler in a configuration array:
That won’t work if you have properties with the same name as the event, i think qiang’s implementation is absolutely fine. One thing though, the second parameter to trigger() should be optional, it should just raise a new default event with the object as the sender if it isn’t specified
Just curious: is “trigger” really public? Or was this just for demonstration purpose? I implemented it as protected, because I can’t see a reason why events should be triggered from outside.
Can you please include the event name in the "Event" object when it gets triggered? Would be helpful in cases where one event handler is attached to several events.
Third, I think the second parameter of trigger can be made optional:
class Button extends Component
{
public function foo()
{
$this->trigger('fooEvent');
}
public function bar()
{
$this->trigger('barEvent', new DerivedEvent);
}
public function baz()
{
$this->trigger('bazEvent', array(
'eventParamKey' => 'eventParamValue',
));
}
}
class Component
{
protected function trigger( $eventName, $event=null )
{
if ($event === null)
{
$event = new Event();
}
else if (is_array($event))
{
$params = $event;
$event = new Event();
$event->params = $params;
}
if ($event instanceof Event)
{
$event->sender = $this;
$event->name = $eventName;
}
else
{
throw new Exception( "Invalid param" );
}
// Event prepared, start your event dispatching logic.
}
}
Maybe not even hard code the "Event" class, but provide a way to configure the default event class that will be created (must derive from Event).