Лучший способ разработать приложение, совместимое с плагинами. Внедрение зависимости?

Мне интересно, как лучше всего создать полностью совместимое приложение с плагинами.

Я привык к концепции подключаемых модулей Wordpress, что вы можете определять действия и фильтры, а затем использовать их в своих подключаемых модулях. Таким образом, другие могут определять методы своих плагинов, которые выполняются при вызове действия (или фильтра).

Моя идея состоит в том, чтобы создать мое приложение с некоторыми действиями и фильтрами, а затем другие разработчики могут создать пакет, который будет мешать «нормальному» потоку приложения...

Я читал об внедрении зависимостей Symfony2, но не нашел подробного примера, чтобы сделать что-то похожее на то, что я хочу.

  • У кого-то есть реальный пример чего-то подобного, что я ищу?
  • Является ли Dependency Injection лучшим решением или мне следует создать собственный обработчик плагинов?

РЕДАКТИРОВАТЬ:

Что я сделал, чтобы другие пакеты могли добавлять элементы в мое меню knp-menu.

В моем базовом комплекте:

Определение фильтра, позволяющего подписчику получать и устанавливать данные меню:

# 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}

Таким образом, я могу использовать приоритет диспетчера событий для установки позиции каждой группы пунктов меню.


person Genar    schedule 30.01.2013    source источник
comment
привет, я бы присмотрелся к помеченным службам в контейнере зависимостей symfony.com/doc /2.0/components/dependency_injection/tags.html   -  person Julien Rollin    schedule 30.01.2013
comment
Спасибо, Жюльен, но я думаю, что EventDispatcher, как говорится в ответе ниже, это то, что я искал.   -  person Genar    schedule 04.02.2013


Ответы (1)


Хорошей отправной точкой в ​​предоставлении точек расширения для вашего приложения, в которых другие разработчики могут перехватывать свое собственное поведение, является использование EventDispatcher от Symfony — реализация шаблона Observer.

Symfony уже широко использует этот компонент в своем собственном ядре ( HttpKernel ), чтобы разрешить другие компоненты (или плагины, если хотите) для подключения различных точек в потоке HTTP-запроса -> ответа и обработки всего, от сопоставления запроса до генерации ответа.

Например, вы можете подключиться к событию kernel.request и немедленно вернуть ответ, если запрос недействителен, или к событию kernel.response и изменить содержимое ответа.

См. полный список по умолчанию KernelEvents.

Используя только их (есть много других, связанных с другими компонентами), вы можете создать систему плагинов, которая будет более функциональной, более тестируемой и более надежной, чем у «платформы» Wordpress.

Конечно, вы можете легко создавать и отправлять свои собственные события, соответствующие вашей бизнес-логике (например, создавать такие события, как post.created или comment.created) для приложения блога.

Теперь, в качестве примера, вот как вы настроите «плагин», который будет что-то делать с сгенерированным ответом, а затем инициирует другое событие (которое может использоваться другим плагином)

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'
        );
    }
}

Теперь вам просто нужно отметить свою службу как подписчика службы, и вы Выполнено. Вы только что подключили компонент HttpKernel:

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