扩展插件

根据事件扩展

插件主要使用事件服务进行扩展,以注入或修改核心类和其他插件的功能。

订阅事件

订阅事件最常见的地方是插件注册文件boot方法。 例如,当用户首次注册时,您可能希望将它们添加到第三方邮件列表,这可以通过订阅rainlab.user.register全局事件来实现。

  1. public function boot()
  2. {
  3. Event::listen('rainlab.user.register', function($user) {
  4. // Code to register $user->email to mailing list
  5. });
  6. }

通过扩展模型的构造函数和使用本地事件可以实现相同的目的。

  1. User::extend(function($model) {
  2. $model->bindEvent('user.register', function() use ($model) {
  3. // Code to register $model->email to mailing list
  4. });
  5. });

声明事件

您可以在全局或本地声明事件,下面是声明全局事件的示例:

  1. Event::fire('acme.blog.beforePost', ['first parameter', 'second parameter']);

本地等效项要求代码位于调用对象的上下文中。

  1. $this->fireEvent('blog.beforePost', ['first parameter', 'second parameter']);

注意: 将本地事件放在全局事件之前是一种好习惯,所以本地事件优先。

订阅此事件后,参数在处理程序方法中可用。 例如:

  1. // Global
  2. Event::listen('acme.blog.beforePost', function($param1, $param2) {
  3. echo 'Parameters: ' . $param1 . ' ' . $param2;
  4. });
  5. // Local
  6. $this->bindEvent('blog.beforePost', function($param1, $param2) {
  7. echo 'Parameters: ' . $param1 . ' ' . $param2;
  8. });

扩展后端视图

有时您可能希望允许后端视图文件或部分扩展,例如工具栏。 这可以使用所有后端控制器中的fireViewEvent方法。

将此代码放在您的视图文件中:

  1. <div class="footer-area-extension">
  2. <?= $this->fireViewEvent('backend.auth.extendSigninView') ?>
  3. </div>

这将允许其他插件通过挂钩事件并返回所需的标记来将HTML注入此区域。

  1. Event::listen('backend.auth.extendSigninView', function($controller) {
  2. return '<a href="#">Sign in with Google!</a>';
  3. });

注意: 事件处理程序中的第一个参数将始终是调用对象(控制器)。

上面的示例将输出以下标记:

  1. <div class="footer-area-extension">
  2. <a href="#">Sign in with Google!</a>
  3. </div>

用法示例

这些是如何使用事件的一些实际示例。

扩展用户模型

此示例将通过绑定到其本地事件来修改User模型的model.getAttribute事件。 这是在插件注册文件boot方法内执行的。 在两种情况下,当访问$model-> foo属性时,它将返回值bar

  1. class Plugin extends PluginBase
  2. {
  3. [...]
  4. public function boot()
  5. {
  6. // Local event hook that affects all users
  7. User::extend(function($model) {
  8. $model->bindEvent('model.getAttribute', function($attribute, $value) {
  9. if ($attribute == 'foo') {
  10. return 'bar';
  11. }
  12. });
  13. });
  14. // Double event hook that affects user #2 only
  15. User::extend(function($model) {
  16. $model->bindEvent('model.afterFetch', function() use ($model) {
  17. if ($model->id != 2) {
  18. return;
  19. }
  20. $model->bindEvent('model.getAttribute', function($attribute, $value) {
  21. if ($attribute == 'foo') {
  22. return 'bar';
  23. }
  24. });
  25. });
  26. });
  27. }
  28. }

扩展后端表单

此示例将修改Backend\Widget\Form类的backend.form.extendFields全局事件,并在表单用于修改用户的条件下注入一些额外的字段值。 此事件也在插件注册文件boot方法内订阅。

  1. class Plugin extends PluginBase
  2. {
  3. [...]
  4. public function boot()
  5. {
  6. // Extend all backend form usage
  7. Event::listen('backend.form.extendFields', function($widget) {
  8. // Only for the User controller
  9. if (!$widget->getController() instanceof \RainLab\User\Controllers\Users) {
  10. return;
  11. }
  12. // Only for the User model
  13. if (!$widget->model instanceof \RainLab\User\Models\User) {
  14. return;
  15. }
  16. // Add an extra birthday field
  17. $widget->addFields([
  18. 'birthday' => [
  19. 'label' => 'Birthday',
  20. 'comment' => 'Select the users birthday',
  21. 'type' => 'datepicker'
  22. ]
  23. ]);
  24. // Remove a Surname field
  25. $widget->removeField('surname');
  26. });
  27. }
  28. }

如果您需要扩展后端表单并同时添加对翻译这些新字段的支持,则必须在呈现实际表单之前添加字段。 这可以通过backend.form.extendFieldsBefore事件来完成。

  1. public function boot()
  2. {
  3. Event::listen('backend.form.extendFieldsBefore', function($widget) {
  4. // You should always check to see if you're extending correct model/controller
  5. if(!$widget->model instanceof \Foo\Example\Models\Bar) {
  6. return;
  7. }
  8. // Here you can't use addFields() because it will throw you an exception because form is not yet created
  9. // and it does not have tabs and fields
  10. // For this example we will pretend that we want to add a new field named example_field
  11. $widget->fields['example_field'] = [
  12. 'label' => 'Example field',
  13. 'comment' => 'Your example field',
  14. 'type' => 'text',
  15. ];
  16. // Ok that's it about adding field inside form, now we need to tell our model that this field is translatable
  17. });
  18. // We will pretend that our model already implements RainLab Translatable behavior and $translatable property,
  19. // if it does not you'll have to add it with addDynamicProperty()
  20. \Foo\Example\Models\Bar::extend(function($model) {
  21. $model->translatable[] = 'example_field';
  22. });
  23. }

请记住在类文件的顶部添加use Event以使用全局事件。

扩展后端列表

此示例将修改Backend\Widget\Lists类的backend.list.extendColumns全局事件,并在使用列表修改用户的条件下注入一些额外的列值。 此事件也在插件注册文件boot方法内订阅。

  1. class Plugin extends PluginBase
  2. {
  3. [...]
  4. public function boot()
  5. {
  6. // Extend all backend list usage
  7. Event::listen('backend.list.extendColumns', function($widget) {
  8. // Only for the User controller
  9. if (!$widget->getController() instanceof \RainLab\User\Controllers\Users) {
  10. return;
  11. }
  12. // Only for the User model
  13. if (!$widget->model instanceof \RainLab\User\Models\User) {
  14. return;
  15. }
  16. // Add an extra birthday column
  17. $widget->addColumns([
  18. 'birthday' => [
  19. 'label' => 'Birthday'
  20. ]
  21. ]);
  22. // Remove a Surname column
  23. $widget->removeColumn('surname');
  24. });
  25. }
  26. }

请记住在类文件的顶部添加use Event以使用全局事件。

扩展组件

这个例子将在Topic组件中声明一个新的全局事件rainlab.forum.topic.post和名为topic.post的本地事件。 这在Component class definition中执行。

  1. class Topic extends ComponentBase
  2. {
  3. public function onPost()
  4. {
  5. [...]
  6. /*
  7. * Extensibility
  8. */
  9. Event::fire('rainlab.forum.topic.post', [$this, $post, $postUrl]);
  10. $this->fireEvent('topic.post', [$post, $postUrl]);
  11. }
  12. }

接下来,这将演示如何从页面执行生命周期中挂钩这个新事件。 当在Topic组件(上面)中调用onPost事件处理程序时,这将写入跟踪日志。

  1. [topic]
  2. slug = "{{ :slug }}"
  3. ==
  4. function onInit()
  5. {
  6. $this['topic']->bindEvent('topic.post', function($post, $postUrl) {
  7. trace_log('A post has been submitted at '.$postUrl);
  8. });
  9. }

扩展后端菜单

此示例将使用 替换后端中CMS和Pages的标签。

  1. class Plugin extends PluginBase
  2. {
  3. [...]
  4. public function boot()
  5. {
  6. Event::listen('backend.menu.extendItems', function($manager) {
  7. $manager->addMainMenuItems('October.Cms', [
  8. 'cms' => [
  9. 'label' => '...'
  10. ]
  11. ]);
  12. $manager->addSideMenuItems('October.Cms', 'cms', [
  13. 'pages' => [
  14. 'label' => '...'
  15. ]
  16. ]);
  17. });
  18. }
  19. }

同样,我们可以删除具有相同事件的菜单项:

  1. Event::listen('backend.menu.extendItems', function($manager) {
  2. $manager->removeMainMenuItem('October.Cms', 'cms');
  3. $manager->removeSideMenuItem('October.Cms', 'cms', 'pages');
  4. });