在几乎所有情况下,扩展 October CMS 都在插件注册文件中进行,它本质上是一个 Laravel 服务提供者。 该注册文件名为 Plugin.php 并位于插件的根目录中。
以下扩展方法可在插件注册类中重写:
| Method | Description |
|---|---|
| register() | called when the plugin is first registered, called before boot. |
| boot() | called right before the request route, called after register. |
| registerMarkupTags() | registers additional markup tags that can be used in the CMS. |
| registerComponents() | registers any CMS components used by this plugin. |
| registerNavigation() | registers backend navigation menu items for this plugin. |
| registerPermissions() | registers any backend permissions used by this plugin. |
| registerSettings() | registers any backend configuration links used by this plugin. |
| registerFormWidgets() | registers any backend form widgets supplied by this plugin. |
| registerReportWidgets() | registers any backend report widgets, including the dashboard widgets. |
| registerListColumnTypes() | registers any custom list column types supplied by this plugin. |
| registerMailTemplates() | registers any mail view templates supplied by this plugin. |
| registerMailLayouts() | registers any mail view layouts supplied by this plugin. |
| registerMailPartials() | registers any mail view partials supplied by this plugin. |
| registerSchedule() | registers scheduled tasks that are executed on a regular basis. |
| registerContentFields() | registers content fields that are used by Tailor blueprints. |
事件服务 是注入或修改核心类或其他插件功能的主要方式。该服务可以通过在 PHP 文件顶部(在命名空间声明之后)添加 use Event 来导入 Event facade,从而在任何类中使用。
订阅事件最常见的位置是插件注册文件的 boot 方法。例如,当用户首次注册时,你可能希望将他们添加到第三方邮件列表,这可以通过订阅一个 rainlab.user.register 全局事件来实现。
class Plugin extends PluginBase
{
// ...
public function boot()
{
Event::listen('rainlab.user.register', function ($user) {
// Code to register $user->email to mailing list
});
}
}同样可以通过扩展模型的构造函数并使用本地事件来实现。
User::extend(function ($model) {
$model->bindEvent('user.register', function () use ($model) {
// Code to register $model->email to mailing list
});
});在本地触发事件 通过在实现了 October\Rain\Support\Traits\Emitter 的对象实例上调用 fireEvent(). 由于本地事件只在一个特定的对象实例上触发, 因此不需要为它们设置命名空间 因为一个给定项目不太可能在本地上下文中在相同的对象上触发多个同名事件.
$this->fireEvent('post.beforePost', [$firstParam, $secondParam]);全局事件通过调用Event::fire()来触发。 由于这些事件在整个应用程序中是全局的,因此最佳实践是通过在事件名称中包含供应商信息来对它们进行命名空间划分。 如果您的插件作者是ACME,且插件名称是Blog,那么由ACME.Blog插件提供的任何全局事件都应以acme.blog作为前缀。
Event::fire('acme.blog.post.beforePost', [$firstParam, $secondParam]);如果全局事件和本地事件在同一位置提供,最佳实践是在全局事件之前触发本地事件,以便本地事件具有优先权。此外,全局事件应将本地事件在其上触发的对象实例作为第一个参数提供。
$this->fireEvent('post.beforePost', [$firstParam, $secondParam]);
Event::fire('rainlab.blog.beforePost', [$this, $firstParam, $secondParam]);一旦订阅了此事件,参数就在处理程序方法中可用了。例如:
// Global
Event::listen('acme.blog.post.beforePost', function ($post, $param1, $param2) {
Log::info($post->name . 'posted. Parameters: ' . $param1 . ' ' . $param2);
});
// Local
$post->bindEvent('post.beforePost', function ($param1, $param2) use ($post) {
Log::info($post->name . 'posted. Parameters: ' . $param1 . ' ' . $param2);
});有时你可能希望允许后端视图文件或局部文件被扩展,例如一个工具栏。这可以通过使用在所有后端控制器中找到的 fireViewEvent 方法实现。
将此代码放置在您的视图文件中:
<div class="footer-area-extension">
<?= $this->fireViewEvent('backend.auth.extendSigninView', [$firstParam]) ?>
</div>这将允许其他插件通过挂钩该事件并返回所需的标记,将 HTML 注入到这个区域。
Event::listen('backend.auth.extendSigninView', function ($controller, $firstParam) {
return '<a href="#">Sign in with Google!</a>';
});事件处理程序中的第一个参数始终是调用对象(控制器)。
:::
上述示例将输出以下标记:
<div class="footer-area-extension">
<a href="#">Sign in with Google!</a>
</div>这些是关于事件如何使用的一些实用示例。
此示例将通过绑定到其本地事件来修改 User 模型的 model.getAttribute 事件。这在插件注册文件的 boot 方法中进行。在这两种情况下,当 $model->foo 属性被访问时,它将返回值 bar。
// Local event hook that affects all users
User::extend(function ($model) {
$model->bindEvent('model.getAttribute', function ($attribute, $value) {
if ($attribute === 'foo') {
return 'bar';
}
});
});
// Double event hook that affects user #2 only
User::extend(function ($model) {
$model->bindEvent('model.afterFetch', function () use ($model) {
if ($model->id !== 2) {
return;
}
$model->bindEvent('model.getAttribute', function ($attribute, $value) {
if ($attribute === 'foo') {
return 'bar';
}
});
});
});为了给引入的字段添加模型验证,请挂接到 beforeValidate 事件并抛出一个 ValidationException 异常。
User::extend(function ($model) {
$model->bindEvent('model.beforeValidate', function () use ($model) {
if (!$model->billing_first_name) {
throw new \ValidationException(['billing_first_name' => 'First name is required']);
}
});
});有多种方式可以扩展后台表单,参阅后台控制器文章以了解更多信息。
这个示例将监听 Backend\Widget\Form 组件的 backend.form.extendFields 全局事件,并在表单组件被用于修改用户时注入一些额外字段。此事件也在插件注册文件的 boot 方法内部订阅。
// Extend all backend form usage
Event::listen('backend.form.extendFields', function($widget) {
// Only apply this listener when the Users controller is being used
if (!$widget->getController() instanceof \RainLab\User\Controllers\Users) {
return;
}
// Only apply this listener when the User model is being modified
if (!$widget->model instanceof \RainLab\User\Models\User) {
return;
}
// Only apply this listener when the Form widget in question is a root-level
// Form widget (not a repeater, nestedform, etc)
if ($widget->isNested) {
return;
}
// Add an extra birthday field
$widget->addFields([
'birthday' => [
'label' => 'Birthday',
'comment' => 'Select the users birthday',
'type' => 'datepicker'
]
]);
// Remove a Surname field
$widget->removeField('surname');
});您也可以使用
backend.form.extendFieldsBefore事件来添加字段。
此示例将修改 Backend\Widget\Lists 类的 backend.list.extendColumns 全局事件并注入一些额外的列值,条件是该列表正用于修改用户。此事件也已在插件注册文件的 boot 方法内部订阅。
// Extend all backend list usage
Event::listen('backend.list.extendColumns', function ($widget) {
// Only for the User controller
if (!$widget->getController() instanceof \RainLab\User\Controllers\Users) {
return;
}
// Only for the User model
if (!$widget->model instanceof \RainLab\User\Models\User) {
return;
}
// Add an extra birthday column
$widget->addColumns([
'birthday' => [
'label' => 'Birthday'
],
]);
// Remove a Surname column
$widget->removeColumn('surname');
});此示例将声明一个新的全局事件 rainlab.forum.topic.post 以及一个名为 topic.post 的局部事件位于 Topic 组件内部。这在 组件类定义 中进行。
class Topic extends ComponentBase
{
public function onPost()
{
// ...
$this->fireEvent('topic.post', [$post, $postUrl]);
Event::fire('rainlab.forum.topic.post', [$this, $post, $postUrl]);
}
}接下来,这将演示如何从内部挂接此新事件布局执行生命周期。当onPost事件处理程序在Topic组件(上方)内部被调用时,将写入跟踪日志。
[topic]
slug = "{{ :slug }}"<?
function onInit()
{
$this->topic->bindEvent('topic.post', function($post, $postUrl) {
trace_log('A post has been submitted at '.$postUrl);
});
}
?>::: 参阅