注册事件侦听器的位置

2022-08-31 00:50:05

我正在尝试在 CakePHP v2.1+ 中使用事件系统

它似乎非常强大,但文档有些模糊。触发事件似乎非常简单,但我不确定如何注册相应的侦听器来侦听事件。相关部分在这里,它提供了以下示例代码:

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

但它没有说明此代码应位于何处。在特定的控制器内部?在应用程序控制器内部?

如果相关,监听器将是我正在编写的插件的一部分。

更新:听起来一种流行的方法是将侦听器注册码放在插件的引导.php文件中。但是,我无法弄清楚如何从那里调用getEventManager(),因为应用程序的控制器类等不可用。

更新 2:我还被告知,听众可以住在模特里面。

更新 3:终于有一些牵引力了!以下代码将在我的 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');

我不知道为什么,但是当我尝试将两者组合成一行时,我不会得到错误。App::uses()


答案 1

事件

事件是与字符串关联的回调。对象(如 Model)将使用字符串触发事件,即使没有任何内容在侦听该事件。

CakePHP预先构建了模型等内部事件。可以将事件侦听器附加到模型并响应事件。Model.beforeSave

事件管理器

Cake中的每个模型都有自己的EventManager,还有一个gobal单例EventManager。这些并不是 EventManager 的同一个实例,它们的工作方式略有不同。

当模型触发事件时,它使用它具有的 EventManager 引用执行此操作。这意味着,您可以将事件侦听器附加到特定模型。优点是侦听器将仅接收来自该模型的事件。

全局侦听器是附加到 EventManager 的单例实例的侦听器。可以在代码中的任何位置访问。当您将侦听器附加到那里时,无论谁触发它,都会为发生的每个事件调用它。

当您在应用程序或插件中附加事件侦听器时,您可以使用全局管理器,否则您必须使用 获得对所需模型的引用。bootstrap.phpClassRegistry

要使用什么事件管理器?

如果要处理的事件是针对特定模型的,则将侦听器附加到该模型的事件管理器。要获取模型的参考,可以调用 .ClassRegistry::init(...)

如果要处理的事件可以在任何位置触发,则将侦听器附加到全局 EventManager。

只有您知道应该如何使用您的监听器。

听众内部

通常,您将业务逻辑放入模型中。不需要从事件侦听器访问控制器。模型在Cake中更容易访问和使用。

这是一个用于创建CakeEventListener的模板。侦听器负责监视某些事情发生时,然后将该信息传递给另一个模型。应将用于处理事件的业务逻辑放在模型中。

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

我喜欢做的是将所有事件放入文件夹中。这使得从源代码中的任何位置访问都非常容易。上面的代码将进入 。LibApp/Lib/Event/MyListener.php

附加事件列表

同样,这取决于您需要侦听的事件。您必须了解的第一件事是,必须创建一个对象才能触发事件。

例如;

当控制器显示索引时,模型无法触发事件,因为 Calendar 控制器从不使用该模型。是否需要在 保存时在 中添加侦听器以捕获?否,如果模型仅从控制器使用,则只需将侦听器附加到该控制器即可。DocumentModel.beforeSaveCalendarDocumentDocumentbootstrap.phpDocumentDocuments

另一方面,该模型几乎由组件使用。如果要处理正在删除的内容。您可能必须在 中附加一个事件侦听器,以确保不会偷偷删除。UserAuthUserbootstrap.php

在上面的示例中,我们可以像这样直接附加到模型。User

App::uses('MyListener','Lib');
$user = ClassRegistry::init('App.User');
$user->getEventManager()->attach(new MyListener());

此行将导入侦听器类。

App::uses('MyListener','Lib');

此行将获取用户模型的实例。

$user = ClassRegistry::init('App.User');

此行创建一个侦听器,并将其附加到用户模型。

$user->getEventManager()->attach(new MyListener());

如果模型在许多不同的地方使用。您可能必须在 中执行此操作,但如果它仅由一个控制器使用。您可以将该代码放在 PHP 文件中或 PHP 文件的顶部。Userbootstrap.phpbeforeFilter

全球事件管理器怎么样?

假设我们需要侦听一般事件。就像任何事情被拯救一样。我们希望附加到全局事件管理器。它将像这样,并放置在.bootstrap.php

App::uses('MyListener','Lib');
CakeEventManager::instance()->attach(new MyListener());

答案 2

如果你想在插件的bootstrap.php文件中附加一个事件侦听器,使用答案中发布的提示一切应该可以正常工作。这是我的代码(工作正常):

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 {
    ...
}

仅当加载插件时,才会注册与 MyPlugin 相关的事件侦听器。如果我不想使用插件,则不会附加事件侦听器。当您想使用插件在应用程序的各个位置添加一些功能时,我认为这是一个干净的解决方案。


推荐