Расширение Zend\View\Helper\Url в Zend Framework 2

Я написал простой помощник вида URL, который расширяет Zend\View\Helper\Url и прикрепил его к ViewHelperManager:

MyNamespace\View\Helper\Url

namespace MyNamespace\View\Helper;

use Zend\View\Helper\Url as ZendUrl;

class Url extends ZendUrl {

    public function __invoke($name = null, array $params = array(), $options = array(), $reuseMatchedParams = false) {
        $link = parent::__invoke($name, $params, $options, $reuseMatchedParams);
        ...
        return $link;
    }

}

Application\Module

namespace Application;

use ...

class Module {

    public function onBootstrap(MvcEvent $mvcEvent) {
        $application = $mvcEvent->getApplication();
        $serviceManager = $application->getServiceManager();
        $viewHelperManager = $serviceManager->get('ViewHelperManager');
        $viewHelperManager->setInvokableClass('url', 'MyNamespace\View\Helper\Url');
        ...
    }

}

Теперь приложение выдает исключение:

Неустранимая ошибка: необработанное исключение "Zend\View\Exception\RuntimeException" с сообщением "Не предоставлен экземпляр RouteStackInterface" в /var/www/foo/bar/vendor/zendframework/zendframework/library/Zend/View/Helper/Url.php on линия 76

Zend\View\Exception\RuntimeException: экземпляр / RouteStackInterface не предоставлен в /var/www/foo/bar/vendor/zendframework/zendframework/library/Zend/View/Helper/Url.php в строке 76

Я отлаживал оба Url классы. Венна MyNamespace\View\Helper\Url используется метод Zend\View\Helper\Url#setRouter(...) не вызывается и роутер не установлен. Не понимаю почему...

Как заставить это работать?

4 ответа

Решение

Не проверял это, поэтому я не знаю, работает ли это, я просто догадываюсь:

Заменить:

$viewHelperManager->setInvokableClass('url', 'MyNamespace\View\Helper\Url');

с:

$viewHelperManager->setFactory('url', function ($sm) use($serviceLocator) {
    $helper = new \MyNamespace\View\Helper\Url;
    $router = Console::isConsole() ? 'HttpRouter' : 'Router';
    $helper->setRouter($serviceLocator->get($router));

    $match = $serviceLocator->get('application')
    ->getMvcEvent()
    ->getRouteMatch();

    if ($match instanceof RouteMatch) {
        $helper->setRouteMatch($match);
    }

    return $helper;
});

Вы также можете использовать инициализаторы!

'view_helpers' => array(
    'invokables' => array(
        'MyUrl' => 'MyModule\View\Helper\MyUrl'
    ),
    'initializers' => array(
        function ($instance, $sm) {
            if ($instance instanceof \Zend\View\Helper\Url) {
                $serviceLocator = $sm->getServiceLocator();

                $router = \Zend\Console\Console::isConsole() ? 'HttpRouter' : 'Router';
                $instance->setRouter($serviceLocator->get($router));

                $match = $serviceLocator->get('application')
                    ->getMvcEvent()
                    ->getRouteMatch();

                if ($match instanceof RouteMatch) {
                    $instance->setRouteMatch($match);
                }
            }
        }
    )
)

И вы можете расширить \Zend\View\Helper\Url, как вы хотите

namespace MyModule\View\Helper;

use Zend\View\Helper\Url as ZendUrl;

class MyUrl extends ZendUrl {

    public function __invoke() {
        return parent::__invoke('MyRoute');
    }

}

Хотя этот вопрос довольно старый, он решил, что здесь стоит добавить переопределение на основе конфигурации, а также использовать фабрику, зарегистрированную в HelperPluginManager:

Внутри module.config.php:

'view_helpers' => array(
    'factories'=> array(
        'url' => 'Application\View\Helper\UrlFactory',
    )
),

А сам плагин view helper factory:

<?php

namespace Application\View\Helper;

use Zend\Console\Console;
use Zend\Mvc\Router\RouteMatch;
use Zend\Mvc\Router\RouteStackInterface;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\View\Helper\Url as BaseUrlHelper;
use Zend\View\HelperPluginManager;

class UrlFactory implements FactoryInterface
{
    /**
     * Create service
     *
     * @param ServiceLocatorInterface $helperPluginManager
     * @return mixed
     */
    public function createService(ServiceLocatorInterface $helperPluginManager)
    {
        /**
         * @var $helperPluginManager HelperPluginManager
         * @var $router RouteStackInterface
         */
        $serviceLocator = $helperPluginManager->getServiceLocator();
        $helper = new Url();

        $router = Console::isConsole() ? 'HttpRouter' : 'Router';
        $router = $serviceLocator->get($router);
        $helper->setRouter($router);

        $match = $serviceLocator->get('application')
            ->getMvcEvent()
            ->getRouteMatch()
        ;

        if ($match instanceof RouteMatch) {
            $helper->setRouteMatch($match);
        }

        return $helper;
    }
}

Дело в том, что на этом заводе можно получить только HelperPluginManager Сервисный локатор. Чтобы получить доступ к другим службам, сначала нужно получить глобальный локатор службы (который выполняется useиспользование глобального локатора услуг извне замыкания во всех других решениях).

Решение Айдына сработало для меня, я поместил код, более или менее нетронутый в getViewHelperConfig файла module.php

public function getViewHelperConfig()
{
    return array(
        'invokables' => array(
            .....
        ),
        'factories' => array(
            'modalurl'            => function ($helperPluginManager) {
                $serviceLocator = $helperPluginManager->getServiceLocator();
                $view_helper =  new \Application\View\Helper\ModalUrl();
                $router = \Zend\Console\Console::isConsole() ? 'HttpRouter' : 'Router';
                $view_helper->setRouter($serviceLocator->get($router));

                $match = $serviceLocator->get('application')
                    ->getMvcEvent()
                    ->getRouteMatch();

                if ($match instanceof RouteMatch) {
                    $view_helper->setRouteMatch($match);
                }

                return $view_helper;
            }
        ),
    );
}
Другие вопросы по тегам