Spec-Zone.ru › Yii 2.0

События

  1. Обработчики событий
  2. Прикрепление обработчиков событий
  3. Порядок обработки событий
  4. Вызов событий
  5. Открепление обработчиков событий
  6. Обработчики событий на уровне класса
  7. События с использованием интерфейсов
  8. Глобальные события

События позволяют вставить пользовательский код в существующий код в определённые моменты выполнения. Вы можете прикрепить пользовательский код к событию, и когда событие будет вызвано, код выполнится автоматически. Например, объект почтового сервера может вызвать событие messageSent при успешной отправке сообщения. Если вы хотите отслеживать сообщения, которые были успешно отправлены, вы можете просто прикрепить код отслеживания к событию messageSent.

Yii вводит базовый класс под названием yii\base\Component для поддержки событий. Если классу требуется вызывать события, он должен наследоваться от yii\base\Component или от его потомка.

Обработчики событий

Обработчик событий — это PHP обратный вызов, который выполняется при вызове события, к которому он прикреплён. Вы можете использовать любой из следующих обратных вызовов:

  • глобальную PHP функцию, указанную как строка (без скобок), например, 'trim';
  • метод объекта, указанный как массив из объекта и имени метода как строки (без скобок), например, [$object, 'methodName'];
  • статический метод класса, указанный как массив из имени класса и имени метода как строки (без скобок), например, ['ClassName', 'methodName'];
  • анонимную функцию, например, function ($event) { ... }.

Подпись обработчика событий:

function ($event) {
    // $event is an object of yii\base\Event or a child class
}

Через параметр $event, обработчик событий может получить следующую информацию о произошедшем событии:

  • имя события;
  • отправитель события: объект, метод которого trigger() был вызван;
  • пользовательские данные: данные, предоставленные при прикреплении обработчика событий (объясняется далее).

Прикрепление обработчиков событий

Вы можете прикрепить обработчик к событию, вызвав метод yii\base\Component::on(). Например:

$foo = new Foo;

// this handler is a global function
$foo->on(Foo::EVENT_HELLO, 'function_name');

// this handler is an object method
$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']);

// this handler is a static class method
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// this handler is an anonymous function
$foo->on(Foo::EVENT_HELLO, function ($event) {
    // event handling logic
});

Вы также можете прикрепить обработчики событий через настройки. Для получения дополнительной информации обратитесь к разделу Настройки.

При прикреплении обработчика событий вы можете предоставить дополнительные данные в качестве третьего параметра к yii\base\Component::on(). Данные будут доступны обработчику при вызове события. Например:

// The following code will display "abc" when the event is triggered
// because $event->data contains the data passed as the 3rd argument to "on"
$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc');

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

Порядок обработки событий

Вы можете прикрепить один или несколько обработчиков к одному событию. Когда событие вызывается, прикреплённые обработчики вызываются в том порядке, в котором они были прикреплены к событию. Если обработчик должен остановить вызов последующих обработчиков, он может установить свойство yii\base\Event::$handled параметра $event в значение true.

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

По умолчанию новый прикрепленный обработчик добавляется в конец очереди обработчиков события. В результате обработчик будет вызван последним при вызове события. Чтобы вставить новый обработчик в начало очереди обработчиков, чтобы он был вызван первым, вы можете вызвать yii\base\Component::on(), передав false в качестве четвёртого параметра $append.

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

Вызов событий

События вызываются путём вызова метода yii\base\Component::trigger(). Метод требует имя события и необязательно объект события, описывающий параметры, которые будут переданы обработчикам событий. Например:

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);
    }
}

С помощью вышеприведённого кода любые вызовы bar() будут вызывать событие с именем hello.

Подсказка: Рекомендуется использовать константы класса для представления имён событий. В приведённом выше примере константа EVENT_HELLO представляет событие hello. Этот подход имеет три преимущества. Во-первых, он предотвращает ошибки ввода. Во-вторых, он может сделать события распознаваемыми для поддержки автозаполнения IDE. В-третьих, вы можете определить поддерживаемые события в классе, просто проверив объявления констант.

Иногда при вызове события вам может потребоваться передать дополнительную информацию обработчикам событий. Например, почтовый сервер может передать информацию о сообщении обработчикам события messageSent для того, чтобы обработчики могли получить подробности отправленных сообщений. Для этого вы можете передать объект события в качестве второго параметра методу yii\base\Component::trigger(). Объект события должен быть экземпляром класса yii\base\Event или его потомка. Например:

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)
    {
        // ...sending $message...

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

Когда вызывается метод yii\base\Component::trigger(), он вызовет все обработчики, прикреплённые к событию с заданным именем.

Открепление обработчиков событий

Чтобы открепить обработчик от события, вызовите метод yii\base\Component::off(). Например:

// the handler is a global function
$foo->off(Foo::EVENT_HELLO, 'function_name');

// the handler is an object method
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']);

// the handler is a static class method
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']);

// the handler is an anonymous function
$foo->off(Foo::EVENT_HELLO, $anonymousFunction);

Обратите внимание, что, как правило, не следует пытаться открепить анонимную функцию, если вы не сохранили её где-то при прикреплении к событию. В приведенном выше примере предполагается, что анонимная функция хранится в переменной $anonymousFunction.

Чтобы открепить все обработчики от события, просто вызовите yii\base\Component::off() без второго параметра:

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

Обработчики событий на уровне класса

В предыдущих разделах описано, как прикрепить обработчик к событию на уровне экземпляра. Иногда вам может потребоваться реагировать на событие, вызванное любым экземпляром класса, а не только конкретным экземпляром. Вместо того, чтобы прикреплять обработчик событий к каждому экземпляру, вы можете прикрепить обработчик на уровне класса, вызвав статический метод yii\base\Event::on().

Например, объект Active Record будет вызывать событие EVENT_AFTER_INSERT всякий раз, когда он вставляет новую запись в базу данных. Чтобы отслеживать вставки, выполненные любым объектом Active Record, вы можете использовать следующий код:

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');
});

Обработчик событий будет вызываться всякий раз, когда экземпляр ActiveRecord или один из его подклассов вызывают событие EVENT_AFTER_INSERT. В обработчике вы можете получить объект, который вызвал событие, через $event->sender.

Когда объект вызывает событие, он сначала вызывает обработчики на уровне экземпляра, а затем обработчики на уровне класса.

Вы можете вызвать событие на уровне класса, вызвав статический метод yii\base\Event::trigger(). Событие на уровне класса не связано с конкретным объектом. В результате оно вызовет только обработчики событий на уровне класса. Например:

use yii\base\Event;

Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) {
    var_dump($event->sender);  // displays "null"
});

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

Обратите внимание, что в этом случае $event->sender является null, а не экземпляром объекта.

Примечание: Поскольку обработчик на уровне класса будет реагировать на событие, вызванное любым экземпляром этого класса или любыми подклассами, его следует использовать с осторожностью, особенно если класс является базовым классом низкого уровня, например, yii\base\Object.

Чтобы открепить обработчик события на уровне класса, вызовите yii\base\Event::off(). Например:

// detach $handler
Event::off(Foo::className(), Foo::EVENT_HELLO, $handler);

// detach all handlers of Foo::EVENT_HELLO
Event::off(Foo::className(), Foo::EVENT_HELLO);

События с использованием интерфейсов

Существует ещё более абстрактный способ работы со событиями. Вы можете создать отдельный интерфейс для специального события и реализовать его в классах, где вам это нужно.

Например, мы можем создать следующий интерфейс:

namespace app\interfaces;

interface DanceEventInterface
{
    const EVENT_DANCE = 'dance';
}

И два класса, которые его реализуют:

class Dog extends Component implements DanceEventInterface
{
    public function meetBuddy()
    {
        echo "Woof!";
        $this->trigger(DanceEventInterface::EVENT_DANCE);
    }
}

class Developer extends Component implements DanceEventInterface
{
    public function testsPassed()
    {
        echo "Yay!";
        $this->trigger(DanceEventInterface::EVENT_DANCE);
    }
}

Чтобы обработать событие EVENT_DANCE, вызванное любым из этих классов, вызовите Event::on() и передайте имя класса интерфейса в качестве первого аргумента:

Event::on('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, function ($event) {
    Yii::trace(get_class($event->sender) . ' just danced'); // Will log that Dog or Developer danced
});

Вы можете вызвать событие этих классов:

// trigger event for Dog class
Event::trigger(Dog::className(), DanceEventInterface::EVENT_DANCE);

// trigger event for Developer class
Event::trigger(Developer::className(), DanceEventInterface::EVENT_DANCE);

Но обратите внимание, что вы не можете вызвать все классы, которые реализуют интерфейс:

// DOES NOT WORK. Classes that implement this interface will NOT be triggered.
Event::trigger('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE);

Чтобы открепить обработчик события, вызовите Event::off(). Например:

// detaches $handler
Event::off('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE, $handler);

// detaches all handlers of DanceEventInterface::EVENT_DANCE
Event::off('app\interfaces\DanceEventInterface', DanceEventInterface::EVENT_DANCE);

Глобальные события

Yii поддерживает так называемое глобальное событие, которое фактически является уловкой, основанной на механизме событий, описанном выше. Глобальное событие требует глобально доступного синглетона, например, самого экземпляра приложения application.

Чтобы создать глобальное событие, отправитель события вызывает метод синглетона trigger() для вызова события вместо вызова собственного метода отправителя trigger(). Аналогичным образом, обработчики событий прикрепляются к событию в синглетоне. Например:

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

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

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

Преимущество использования глобальных событий заключается в том, что вам не нужен объект при прикреплении обработчика к событию, которое будет вызываться объектом. Вместо этого прикрепление обработчика и вызов события выполняется через синглетон (например, экземпляр приложения).

Однако, поскольку пространство имён глобальных событий используется всеми участниками, следует разумно называть глобальные события, например, вводя какое-либо пространство имён (например, "frontend.mail.sent", "backend.mail.sent").

© 2008–2017 by Yii Software LLC
Licensed under the three clause BSD license.
http://www.yiiframework.com/doc-2.0/guide-concept-events.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API