concept-events.md 11.5 KB
Newer Older
larnu committed
1 2 3
Eventos
=======

4 5
Los eventos permiten inyectar código dentro de otro código existente en ciertos puntos de ejecución. Se pueden adjuntar
código personalizado a un evento, cuando se lance (triggered), el código se ejecutará automáticamente. Por ejemplo, un
6
objeto mailer puede lanzar el evento `messageSent` cuando se envía un mensaje correctamente. Si se quiere rastrear
larnu committed
7 8
el correcto envío del mensaje, se puede, simplemente, añadir un código de seguimiento al evento `messageSent`.

9
Yii introduce una clase base [[yii\base\Component]] para soportar eventos. Si una clase necesita lanzar un evento,
larnu committed
10 11 12 13 14
este debe extender a [[yii\base\Component]] o a una clase hija.

Gestor de Eventos <a name="event-handlers"></a>
-----------------

15 16
Un gestor de eventos es una
[llamada de retorno PHP (PHP callback)](http://php.net/manual/es/language.types.callable.php) que se ejecuta cuando se
larnu committed
17 18
lanza el evento al que corresponde. Se puede usar cualquier llamada de retorno de las enumeradas a continuación:

19 20 21 22 23 24
- una función de PHP global especificada como una cadena de texto (sin paréntesis), ej. `'trim'`;
- un método de objeto especificado como un array de un objeto y un nombre de método como una cadena de texto
  (sin paréntesis), ej. `[$object, 'methodNAme']`;
- un método de clase estático especificado como un array de un nombre de clase y un método como una cadena de texto
  (sin paréntesis), ej. `[$class, 'methodName']`;
- una función anónima, ej. `function ($event) { ... }`.
larnu committed
25 26 27 28 29 30 31 32 33

La firma de un gestor de eventos es:

```php
function ($event) {
    // $event es un objeto de yii\base\Event o de una clase hija
}
```

34
Un gestor de eventos puede obtener la siguiente información acerca de un evento ya sucedido mediante el parámetro
larnu committed
35 36
`$event`:

37 38
- [[yii\base\Event::name|nombre del evento]]
- [[yii\base\Event::sender|evento enviando]]: el objeto desde el que se ha ejecutado `trigger()`
39
- [[yii\base\Event::data|custom data]]: los datos que se proporcionan al adjuntar el gestor de eventos
larnu committed
40 41
  (se explicará más adelante)

42

larnu committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
Añadir Gestores de Eventos <a name="attaching-event-handlers"></a>
--------------------------

Se puede añadir un gestor a un evento llamando al método [[yii\base\Component::on()]]. Por ejemplo:

```php
$foo = new Foo;

// este gestor es una función global
$foo->on(Foo::EVENT_HELLO, 'function_name');

// este gestor es un método de objeto
$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);

// este gestor es un método de clase estática
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// este gestor es una función anónima
$foo->on(Foo::EVENT_HELLO, function ($event) {
    // event handling logic
});
```

66
También se pueden adjuntar gestores de eventos mediante [configuraciones](concept-configurations.md). Se pueden
larnu committed
67 68
encontrar más de talles en la sección [Configuraciones](concept-configurations.md#configuration-format).

69 70
Cuando se adjunta un gestor de eventos, se pueden proporcionar datos adicionales como tercer parámetro de
[[yii\base\Component::on()]]. El gestor podrá acceder a los datos cuando se lance el evento y se ejecute el gestor.
larnu committed
71 72
Por ejemplo:

73
```php
larnu committed
74
// El siguiente código muestra "abc" cuando se lanza el evento
75
// ya que $event->data contiene los datos enviados en el tercer parámetro de "on"
larnu committed
76 77 78 79 80 81 82 83 84 85
$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');

function function_name($event) {
    echo $event->data;
}
```

Ordenación de Gestores de Eventos
---------------------------------

86 87
Se puede adjuntar uno o más gestores a un único evento. Cuando se lanza un evento, se ejecutarán los gestores adjuntos
en el orden que se hayan añadido al evento. Si un gestor necesita parar la invocación de los gestores que le siguen,
larnu committed
88 89 90 91 92 93 94 95
se puede establecer la propiedad [[yii\base\Event::handled]] del parámetro `$event` para que sea `true`:

```php
$foo->on(Foo::EVENT_HELLO, function ($event) {
    $event->handled = true;
});
```

96 97 98
De forma predeterminada, cada nuevo gestor añadido se pone a la cola de la lista de gestores del evento. Por lo tanto,
el gestor se ejecutará en el último lugar cuando se lance el evento. Para insertar un nuevo gestor al principio de la
cola de gestores para que sea ejecutado primero, se debe llamar a [[yii\base\Component::on()]], pasando al cuarto
99
parámetro `$append` el valor false:
larnu committed
100 101 102 103 104 105 106 107 108 109

```php
$foo->on(Foo::EVENT_HELLO, function ($event) {
    // ...
}, $data, false);
```

Lanzamiento de Eventos <a name="triggering-events"></a>
----------------------

110 111
Los eventos se lanzan llamando al método [[yii\base\Component::trigger()]]. El método requiere un *nombre de evento*,
y de forma opcional un objeto de evento que describa los parámetros que se enviarán a los gestores de eventos. Por
larnu committed
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
ejemplo:

```php
namespace app\components;

use yii\base\Component;
use yii\base\Event;

class Foo extends Component
{
    const EVENT_HELLO = 'hello';

    public function bar()
    {
        $this->trigger(self::EVENT_HELLO);
    }
}
```

Con el código anterior, cada llamada a `bar()` lanzará un evento llamado `hello`

133 134 135
> Consejo: Se recomienda usar las constantes de clase para representar nombres de eventos. En el anterior ejemplo, la
  constante `EVENT_HELLO` representa el evento `hello`. Este enfoque proporciona tres beneficios. Primero, previene
  errores tipográficos. Segundo, puede hacer que los IDEs reconozcan los eventos en las funciones de auto-completado.
larnu committed
136 137
  Tercero, se puede ver que eventos soporta una clase simplemente revisando la declaración de constantes.

138
A veces cuando se lanza un evento se puede querer pasar información adicional al gestor de eventos. Por ejemplo, un
139
mailer puede querer enviar la información del mensaje para que los gestores del evento `messageSent` para que los
140 141
gestores puedan saber las particularidades del mensaje enviado. Para hacerlo, se puede proporcionar un objeto de tipo
evento como segundo parámetro al método [[yii\base\Component::trigger()]]. El objeto de tipo evento debe ser una
142
instancia de la clase [[yii\base\Event]] o de su clase hija. Por ejemplo:
larnu committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190

```php
namespace app\components;

use yii\base\Component;
use yii\base\Event;

class MessageEvent extends Event
{
    public $message;
}

class Mailer extends Component
{
    const EVENT_MESSAGE_SENT = 'messageSent';

    public function send($message)
    {
        // ...enviando $message...

        $event = new MessageEvent;
        $event->message = $message;
        $this->trigger(self::EVENT_MESSAGE_SENT, $event);
    }
}
```

Cuando se lanza el método [[yii\base\Component::trigger()]], se ejecutarán todos los gestores adjuntos al evento.

Desadjuntar Gestores de Evento <a name="detaching-event-handlers"></a>
------------------------------

Para desadjuntar un gestor de un evento, se puede ejecutar el método [[yii\base\Component::off()]]. Por ejemplo:

```php
// el gestor es una función global
$foo->off(Foo::EVENT_HELLO, 'function_name');

// el gestor es un método de objeto
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);

// el gestor es un método estático de clase
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// el gestor es una función anónima
$foo->off(Foo::EVENT_HELLO, $anonymousFunction);
```

191 192
Tenga en cuenta que en general no se debe intentar desadjuntar las funciones anónimas a no ser que se almacene donde
se ha adjuntado al evento. En el anterior ejemplo, se asume que la función anónima se almacena como variable
larnu committed
193 194
`$anonymousFunction`.

195
Para desadjuntar TODOS los gestores de un evento, se puede llamar [[yii\base\Component::off()]] sin el segundo
larnu committed
196 197 198 199 200 201 202 203 204
parámetro:

```php
$foo->off(Foo::EVENT_HELLO);
```

Nivel de Clase (Class-Level) Gestores de Eventos <a name="class-level-event-handlers"></a>
------------------------------------------------

205 206 207
En las subsecciones anteriores se ha descrito como adjuntar un gestor a un evento a *nivel de instancia*. A veces, se
puede querer que un gestor responda todos los eventos de *todos* las instancias de una clase en lugar de una instancia
especifica. En lugar de adjuntar un gestor de eventos a una instancia, se puede adjuntar un gestor a *nivel de clase*
larnu committed
208 209
llamando al método estático [[yii\base\Event::on()]].

210 211 212
Por ejemplo, un objeto de tipo [Active Record](db-active-record.md) lanzará un evento
[[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] cada vez que inserte un nuevo registro en la base
de datos. Para poder registrar las inserciones efectuadas por *todos* los objetos
larnu committed
213 214 215 216 217 218 219 220 221 222 223 224
[Active Record](db-active-record.md), se puede usar el siguiente código:

```php
use Yii;
use yii\base\Event;
use yii\db\ActiveRecord;

Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) {
    Yii::trace(get_class($event->sender) . ' is inserted');
});
```

225 226
Se invocará al gestor de eventos cada vez que una instancia de [[yii\db\ActiveRecord|ActiveRecord]], o de uno de sus
clases hijas, lance un evento de tipo [[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]. Se puede
larnu committed
227 228
obtener el objeto que ha lanzado el evento mediante `$event->sender` en el gestor.

229
Cuando un objeto lanza un evento, primero llamará los gestores a nivel de instancia, y a continuación los gestores a
larnu committed
230 231
nivel de clase.

232 233
Se puede lanzar un evento de tipo *nivel de clase* llamando al método estático [[yii\base\Event::trigger()]]. Un
evento de nivel de clase no se asocia a un objeto en particular. Como resultado, esto provocará solamente la
larnu committed
234 235 236 237 238 239 240 241 242 243 244 245
invocación de los gestores de eventos a nivel de clase.

```php
use yii\base\Event;

Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) {
    echo $event->sender;  // displays "app\models\Foo"
});

Event::trigger(Foo::className(), Foo::EVENT_HELLO);
```

246
Tenga en cuenta que en este caso, el `$event->sender` hace referencia al nombre de la clase que lanza el evento en
larnu committed
247 248
lugar de a la instancia del objeto.

249 250
> Nota: Debido a que los gestores a nivel de clase responderán a los eventos lanzados por cualquier instancia de la
clase, o cualquier clase hija, se debe usar con cuidado, especialmente en las clases de bajo nivel (low-level), tales
larnu committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
como [[yii\base\Object]].

Para desadjuntar un gestor de eventos a nivel de clase, se tiene que llamar a [[yii\base\Event::off()]]. Por ejemplo:

```php
// desadjunta $handler
Event::off(Foo::className(), Foo::EVENT_HELLO, $handler);

// desadjunta todos los gestores de Foo::EVENT_HELLO
Event::off(Foo::className(), Foo::EVENT_HELLO);
```

Eventos Globales <a name="global-events"></a>
----------------

266 267
Yii soporta los llamados *eventos globales*, que en realidad es un truco basado en el gestor de eventos descrito
anteriormente. El evento global requiere un Singleton globalmente accesible, tal como la instancia de
larnu committed
268 269
[aplicación](structure-applications.md) en si misma.

270 271
Para crear un evento global, un evento remitente (event sender) llama al método `trigger()` del Singleton para lanzar
el evento, en lugar de llamar al propio método `trigger()` del remitente. De forma similar, los gestores de eventos se
larnu committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285
adjuntan al evento del Singleton. Por ejemplo:

```php
use Yii;
use yii\base\Event;
use app\components\Foo;

Yii::$app->on('bar', function ($event) {
    echo get_class($event->sender);  // muestra "app\components\Foo"
});

Yii::$app->trigger('bar', new Event(['sender' => new Foo]));
```

286 287 288
Un beneficio de usar eventos globales es que no se necesita un objeto cuando se adjuntan gestores a un evento para que
sean lanzados por el objeto. En su lugar, los gestores adjuntos y el lanzamiento de eventos se efectúan en el
Singleton (ej. la instancia de la aplicación).
larnu committed
289

290 291 292
Sin embargo, debido a que los `namespaces` de los eventos globales son compartidos por todas partes, se les deben
asignar nombres bien pensados, como puede ser la introducción de algún `namespace`
(ej. "frontend.mail.sent", "backend.mail.sent").