从一个单独的模块连接到用户保存

Hook into User Save from a separate module

我想在大型单体 Yii 应用程序中使用模块来分隔代码。假设有一个 core 模块有一个 User 模型,我正在创建一个新的 audit 模块来跟踪用户更新。

审计模块有一个 AuditLog 模型,它存储有关 User 模型更改的信息。

如果我不想将审计上下文分开,我可以将审计逻辑添加到 User->afterSave()

在不向用户模型添加特定于审计的逻辑的情况下,Yii 中对保存的用户做出反应的最惯用的方式是什么?

正如 Insane Skull 在评论中所建议的那样,您应该在用户更改时使用事件来处理审计任务。

已经存在 yii\db\BaseActiveRecord::EVENT_AFTER_INSERTyii\db\BaseActiveRecord::EVENT_AFTER_UPDATE 事件,这些事件在保存从 yii\db\ActiveRecord 继承的任何对象时触发。

您可以在审计模块中为此事件创建处理程序,例如:

namespace app\modules\audit\components;

class UserChangeHandler extends \yii\base\Component
{
    public function handleInsert(\yii\db\AfterSaveEvent $event)
    {
        // ... your audit logic when new user is created ...
        // original User model is available in $event->sender
    }

    public function handleUpdate(\yii\db\AfterSaveEvent $event)
    {
         // ... your audit logic when user is updated ...
    }
}

然后你可以像这样绑定审计模块bootstrap中的事件:

namespace app\modules\audit;

use app\modules\audit\components\UserChangeHandler;
use app\models\User;
use yii\base\Event;
use yii\db\BaseActiveRecord;

class Module extends \yii\base\Module implements \yii\base\BootstrapInterface
{
    private UserChangeHandler $handler;

    public function __construct($id, $parent, UserChangeHandler $handler, $config = [])
    {
        parent::__construct($id, $parent, $config);
        $this->handler = $handler;
    }

    public function bootstrap($app)
    {
        Event::on(
            User::class,
            BaseActiveRecord::EVENT_AFTER_INSERT,
            [$this->handler, 'handleInsert']
        );
        Event::on(
           User::class,
           BaseActiveRecord::EVENT_AFTER_UPDATE,
           [$this->handler, 'handleUpdate']
        );
    }

    // ... other definitions in module class ...
}

然后在您的应用程序配置中添加审核模块并告诉应用程序它应该 bootstrap 像这样:

return [
    'bootstrap' => [
        //... other bootstraped components/modules ...
        'audit',
    ],
    'modules' => [
        // ... other modules ...
        'audit' => \app\modules\audit\Module::class,
    ],
    // ... other configurations ...
];

这样每次在用户模型中调用 after insert 或 after update 事件时,都会执行您的处理程序。您的核心应用程序独立于审计插件,仅在应用程序配置中引用。

注意:EVENT_AFTER_UPDATE和EVENT_AFTER_INSERT仅在使用save()update()insert()方法时触发。如果使用静态 updateAll() 方法或其他不触发 afterSave() 回调的方式编辑用户记录,则不会触发这些事件。如果您也使用这些方法更新用户,您可能想要添加自己的事件并在更改发生时触发它。