php - Dónde registrar oyentes de eventos
events cakephp (3)
Eventos
Los eventos son devoluciones de llamada que están asociadas a una cadena. Un objeto, como un Modelo, activará un evento utilizando una cadena incluso si no hay nada escuchando para ese evento.
CakePHP viene preconstruido con eventos internos para cosas como Modelos. Puede adjuntar un detector de eventos a un Modelo y responder a un evento Model.beforeSave
.
El EventManager
Todos los modelos de Cake tienen su propio EventManager, y además hay un administrador de eventos único de gobal. Estas no son todas la misma instancia de EventManager, y funcionan de forma ligeramente diferente.
Cuando un Modelo desencadena un evento, lo hace utilizando la referencia de EventManager que tiene. Esto significa que puede adjuntar un detector de eventos a un Modelo específico. Las ventajas son que su oyente solo recibirá eventos de ese Modelo.
Los oyentes globales son los que están conectados a la instancia única de EventManager. Que se puede acceder desde cualquier parte de tu código. Cuando adjuntas un oyente allí se llama para cada evento que ocurre sin importar quién lo desencadena.
Cuando conecta el detector de eventos en bootstrap.php
de una aplicación o complemento, puede usar el administrador global, de lo contrario debe obtener una referencia al Modelo que necesita utilizando ClassRegistry
.
¿Qué EventManager usar?
Si el evento que desea manejar corresponde a un Modelo específico, adjunte el oyente al EventManager de ese Modelo. Para obtener una referencia del modelo, puede llamar a ClassRegistry::init(...)
.
Si el evento que desea manejar puede activarse en cualquier lugar, adjunte el oyente al EventManager global.
Solo tú sabes cómo debe usarse tu oyente.
Dentro de un oyente
Generalmente, pones tu lógica de negocios en modelos. No debería necesitar acceder a un Controlador desde un detector de eventos. Los modelos son mucho más fáciles de acceder y usar en Cake.
Aquí hay una plantilla para crear un CakeEventListener. El oyente es responsable de monitorear cuando sucede algo, y luego pasar esa información a otro Modelo. Debe colocar su lógica comercial para procesar el evento en Modelos.
<?php
App::uses(''CakeEventListener'', ''Event'');
class MyListener implements CakeEventListener
{
/**
*
* @var Document The model.
*/
protected $Document;
/**
* Constructor
*/
public function __construct()
{
// get a reference to a Model that we''ll use
$this->Document = ClassRegistry::init(''Agg.Document'');
}
/**
* Register the handlers.
*
* @see CakeEventListener::implementedEvents()
*/
public function implementedEvents()
{
return array(
''Model.User.afterSave''=>''UserChanged''
);
}
/**
* Use the Event to dispatch the work to a Model.
*
* @param CakeEvent $event
* The event object and data.
*/
public function UserChanged(CakeEvent $event)
{
$data = $event->data;
$subject = $event->subject();
$this->Document->SomethingImportantHappened($data,$subject);
}
}
Lo que me gusta hacer es colocar todos mis eventos en la carpeta Lib
. Esto hace que sea muy fácil acceder desde cualquier lugar en el código fuente. El código anterior iría a App/Lib/Event/MyListener.php
.
Adjuntando los EventListeners
Nuevamente, depende de qué eventos necesita escuchar. Lo primero que debe entender es que se debe crear un objeto para activar el evento.
Por ejemplo;
No es posible que el modelo de Document
Model.beforeSave
evento Model.beforeSave
cuando el controlador de Calendar
muestra un índice, porque el controlador de Calendar nunca usa el modelo de Document
. ¿Necesita agregar un oyente a Document
en el bootstrap.php
para ver cuándo se guarda? No, si el modelo de Document
solo se usa desde el controlador de Documents
, entonces solo necesita adjuntar el oyente allí.
Por otro lado, el modelo de User
es utilizado por el componente Auth
casi cada. Si quieres manejar a un User
siendo eliminado. Es posible que tenga que adjuntar un detector de eventos en bootstrap.php
para asegurarse de que ningún delegado lo cierre.
En el ejemplo anterior, podemos adjuntar directamente al modelo de User
como tal.
App::uses(''MyListener'',''Lib'');
$user = ClassRegistry::init(''App.User'');
$user->getEventManager()->attach(new MyListener());
Esta línea importará tu clase de oyente.
App::uses(''MyListener'',''Lib'');
Esta línea obtendrá una instancia del Modelo de Usuario.
$user = ClassRegistry::init(''App.User'');
Esta línea crea un oyente y lo asocia al modelo de Usuario.
$user->getEventManager()->attach(new MyListener());
Si el Modelo de User
se usa en muchos lugares diferentes. Puede que tenga que hacer esto en bootstrap.php
, pero si solo lo usa un controlador. Puede colocar ese código en beforeFilter
o en la parte superior del archivo PHP.
¿Qué pasa con Global EventManager?
Asumiendo que necesitamos escuchar eventos generales. Como cuando alguna vez se guarda. Quisiéramos adjuntarnos al EventManager global. Sería algo como esto, y se colocaría en bootstrap.php
.
App::uses(''MyListener'',''Lib'');
CakeEventManager::instance()->attach(new MyListener());
Estoy tratando de usar el sistema de eventos en CakePHP v2.1 +
Parece ser bastante poderoso, pero la documentación es algo vaga. Disparar el evento parece bastante directo, pero no estoy seguro de cómo registrar el oyente (s) correspondiente (s) para escuchar el evento. La sección relevante está aquí y ofrece el siguiente código de ejemplo:
App::uses(''CakeEventListener'', ''Event'');
class UserStatistic implements CakeEventListener {
public function implementedEvents() {
return array(
''Model.Order.afterPlace'' => ''updateBuyStatistic'',
);
}
public function updateBuyStatistic($event) {
// Code to update statistics
}
}
// Attach the UserStatistic object to the Order''s event manager
$statistics = new UserStatistic();
$this->Order->getEventManager()->attach($statistics);
Pero no dice dónde debería residir este código. Dentro de un controlador específico? Dentro del controlador de la aplicación?
En caso de que sea relevante, el oyente será parte de un complemento que estoy escribiendo.
Actualización: Parece que una forma popular de hacerlo es colocar el código de registro del oyente en el archivo bootstrap.php del complemento. Sin embargo, no puedo averiguar cómo llamar a getEventManager () desde allí porque las clases de controlador de la aplicación, etc. no están disponibles.
Actualización 2: También me dijeron que los oyentes pueden vivir dentro de los Modelos.
Actualización 3: ¡ Finalmente algo de tracción! El siguiente código registrará correctamente un evento cuando esté dentro de MyPlugin / Config / bootstrap.php
App::uses(''CakeEventManager'', ''Event'');
App::uses(''CakeEventListener'', ''Event'');
class LegacyWsatListener implements CakeEventListener {
public function implementedEvents() {
return array(
''Controller.Attempt.complete'' => ''handleLegacyWsat'',
);
}
public static function handleLegacyWsat($event) { //method must be static if used by global EventManager
// Code to update statistics
error_log(''event from bootstrap'');
}
}
CakeEventManager::instance()->attach(array(''LegacyWsatListener'', ''handleLegacyWsat''), ''Controller.Attempt.complete'');
No estoy seguro de por qué, pero no puedo obtener errores cuando intento combinar los dos App::uses()
en una sola línea.
No es importante, donde reside el código. Solo asegúrate de que se ejecute y de que tus eventos estén registrados y adjuntos correctamente.
Estamos utilizando un archivo único donde se adjuntan todos los eventos y lo incluimos desde bootstrap.php, esto asegura que todos los eventos estén disponibles desde todas las ubicaciones de la aplicación.
La magia ocurre cuando envías un evento, como desde una acción de controlador.
$event = new CakeEvent(''Model.Order.afterPlace'', $this, array(''some''=>''data'') ));
$this->getEventManager()->dispatch($event);
Sin embargo, puede enviar eventos desde cualquier lugar donde pueda acceder al EventManager (en Modelos, Controlador y Vistas de forma predeterminada)
Si desea adjuntar un detector de eventos dentro del archivo bootstrap.php de su complemento, todo debería funcionar bien utilizando los consejos publicados en las respuestas. Aquí está mi código (que funciona correctamente):
MyPlugin / Config / bootstrap.php:
App::uses(''CakeEventManager'', ''Event'');
App::uses(''MyEventListener'', ''MyPlugin.Lib/Event'');
CakeEventManager::instance()->attach(new MyEventListener());
MyPlugin / Lib / Event / MyEventListener.php:
App::uses(''CakeEventListener'', ''Event'');
class MyEventListener implements CakeEventListener {
...
}
Los escuchas de eventos relacionados con MyPlugin se registran solo cuando se carga el complemento. Si no quiero usar el complemento, los detectores de eventos no están adjuntos. Creo que esta es una solución limpia cuando desea agregar alguna funcionalidad en varios lugares de su aplicación utilizando un complemento.