开发插件兼容应用程序的最佳方式。依赖注入?

时间:2013-01-30 13:17:10

标签: symfony dependency-injection event-dispatching pluggable

我想知道为插件创建完全兼容的应用程序的最佳方法。

我习惯使用Wordpress插件概念,您可以定义操作和过滤器,然后在插件中使用。所以其他人可以在他们的插件上定义在调用动作(或过滤器)时执行的方法。

我的想法是使用一些操作和过滤器创建我的应用程序,然后其他开发人员可以构建一个干扰“正常”应用程序流的Bundle ...

我正在阅读有关Symfony2依赖注入的内容,但我没有找到一些全面的例子来做我想要的类似事情。

  • 有人有一个我正在寻找类似事物的真实例子吗?
  • 依赖注入是最好的解决方案,还是我应该构建自己的插件处理程序?

编辑:

我做了什么让其他捆绑包将项目添加到我的菜单菜单。

在我的基础包中:

定义允许订阅者获取和设置菜单数据的过滤器:

# BaseBundle/Event/FilterMenuEvent.php

class FilterMenuEvent extends Event
{
    protected $menu;

    public function __construct($menu)
    {
        $this->menu = $menu;
    }

    public function getMenu()
    {
        return $this->menu;
    }
}

定义菜单的事件:

# Event/MenuEvents.php
final class MenuEvents
{
    const BEFORE_ITEMS = 'menu.before.items';
    const AFTER_ITEMS = 'menu.after.items';
}

设置订户:

# Event/MenuSubscriber.php
class MenuSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'menu.after.items'     => array(
                array('homeItems', 9000),
                array('quickactionsItems', 80),
                array('adminItems', 70),
             ...
                array('logoutItems', -9000),
            )
        );
    }
    public function homeItems(FilterMenuEvent $menu_filter)
    {
        $menu = $menu_filter->getMenu();
        $menu->addChild('Home', array('route' => 'zashost_zaspanel_homepage'));
    }

    public function quickactionsItems(FilterMenuEvent $menu_filter)
    {
        $menu = $menu_filter->getMenu();
        $menu->addChild('Quick actions', array( 'route' => null));
        $menu['Quick actions']->addChild('Add hosting', array( 'route' => 'zashost_zaspanel_register_host'));
    }
}

在生成菜单中调度事件:

# Menu\Builder.php

class Builder extends ContainerAware
{
    public function userMenu(FactoryInterface $factory, array $options)
    {
        $menu = $factory->createItem('root');

        $this->container->get('event_dispatcher')->dispatch(MenuEvents::AFTER_ITEMS , new FilterMenuEvent($menu));

        return $menu;
    }
}

将订阅者附加到内核事件订阅者:

# services.yml
    services:
        # Menu items added with event listener
        base_menu_subscriber:
            class: Acme\BaseBundle\Event\MenuSubscriber
            arguments: ['@event_dispatcher']
            tags:
                - {name: kernel.event_subscriber}

然后在第三方包中:

设置我的第三方活动订阅者:

class MenuSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return array(
            'menu.after.items'     => array('afterItems', 55)
        );
    }

    public function afterItems(FilterMenuEvent $menu_filter)
    {
        $menu = $menu_filter->getMenu();
        $menu->addChild('Backups', array( 'route' => null));
        $menu['Backups']->addChild('Create new backup', array( 'route' => null));
        return $menu;
    }
}

附加到内核事件订阅者:

# srevices.yml
services:
    menu_subscriber:
        class: Acme\ThirdPartyBundle\Event\MenuSubscriber
        arguments: ['@event_dispatcher']
        tags:
            - {name: kernel.event_subscriber}

通过这种方式,我可以使用Event Dispatcher的优先级来设置菜单中每组项目的位置。

1 个答案:

答案 0 :(得分:0)

为应用程序提供扩展点的一个很好的起点是,其他开发人员可以挂钩他们的自定义行为,就是使用Symfony的EventDispatcher组件 - Observer Pattern的实现。

Symfony已经在其自己的核心(HttpKernel)中广泛使用该组件,以允许其他组件(或插件,如果您愿意)挂钩http请求中的各个点 - >响应流并处理从请求匹配到响应生成的所有内容。

例如,您可以挂钩kernel.request事件并在请求无效时立即返回响应,或者返回kernel.response事件并更改响应内容。

请参阅default KernelEvents的完整列表。

通过仅使用这些(还有许多与其他组件相关的内容),您可以创建一个比Wordpress“平台”更强大,更可测试且更强大的插件系统。

当然,您可以轻松地创建和发送适合您的业务逻辑的事件(例如,为post.createdcomment.created创建事件)。

现在,为了举个例子,下面是你将如何配置一个“插件”,它将对生成的Response执行某些操作,然后触发另一个事件(可以由另一个插件使用)

namespace Vendor;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;

class ResponseAlter implements EventSubscriberInterface
{

    private $dispatcher;

    public function __construct(EventDispatcher $dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function doSomethingWithResponse(FilterResponseEvent $event)
    {
        $response = $event->getResponse();

        /**
         * let other plugins hook to the provide.footer event and
         * add the result to the response
         */
        $footer = new ProvideFooterEvent();
        $this->dispatcher->dispatch('provide.footer', $footer);

        $this->addFooterProvidedByPluginToResponse($response, $footer->getProvidedFooter());

        $event->setResponse($response);
    }

    static function getSubscribedEvents() 
    {
        return array(
            'kernel.response' => 'doSomethingWithResponse'
        );
    }
}

现在您只需要tag作为服务订阅者的服务即可完成。您只是在HttpKernel组件中插入

services:
    my_subscriber:
        class: Vendor\ResponseAlter
        arguments: ['@event_dispatcher']
        tags:
            - {name: kernel.event_subscriber}