从一个单独的模块连接到用户保存
Hook into User Save from a separate module
我想在大型单体 Yii 应用程序中使用模块来分隔代码。假设有一个 core
模块有一个 User
模型,我正在创建一个新的 audit
模块来跟踪用户更新。
审计模块有一个 AuditLog
模型,它存储有关 User
模型更改的信息。
如果我不想将审计上下文分开,我可以将审计逻辑添加到 User->afterSave()
。
在不向用户模型添加特定于审计的逻辑的情况下,Yii 中对保存的用户做出反应的最惯用的方式是什么?
正如 Insane Skull 在评论中所建议的那样,您应该在用户更改时使用事件来处理审计任务。
已经存在 yii\db\BaseActiveRecord::EVENT_AFTER_INSERT
和 yii\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() 回调的方式编辑用户记录,则不会触发这些事件。如果您也使用这些方法更新用户,您可能想要添加自己的事件并在更改发生时触发它。
我想在大型单体 Yii 应用程序中使用模块来分隔代码。假设有一个 core
模块有一个 User
模型,我正在创建一个新的 audit
模块来跟踪用户更新。
审计模块有一个 AuditLog
模型,它存储有关 User
模型更改的信息。
如果我不想将审计上下文分开,我可以将审计逻辑添加到 User->afterSave()
。
在不向用户模型添加特定于审计的逻辑的情况下,Yii 中对保存的用户做出反应的最惯用的方式是什么?
正如 Insane Skull 在评论中所建议的那样,您应该在用户更改时使用事件来处理审计任务。
已经存在 yii\db\BaseActiveRecord::EVENT_AFTER_INSERT
和 yii\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() 回调的方式编辑用户记录,则不会触发这些事件。如果您也使用这些方法更新用户,您可能想要添加自己的事件并在更改发生时触发它。