Как получить совпадающее имя маршрута в представлении - Zend Expressive

Я знаю, что могу сгенерировать URL-адрес, передав имя маршрута

<?php echo $this->url('route-name') #in view file ?>

Но могу ли я получить информацию в обратном направлении? Из текущего URL/URI мне нужно получить имя маршрута.

Реальный случай: у меня есть layout.phtml, где находится верхнее меню (html). Текущая ссылка в меню должна быть помечена классом css. Итак, например, что мне нужно:

<?php // in layout.phtml file
  $index_css   = $this->getRouteName() == 'home-page' ? 'active' : 'none'; 
  $about_css   = $this->getRouteName() == 'about'     ? 'active' : 'none'; 
  $contact_css = $this->getRouteName() == 'contact'   ? 'active' : 'none';  
?>

Я использую быстрый маршрут, но мне интересно любое решение. Решение не обязательно должно быть в файле просмотра.


person tasmaniski    schedule 28.06.2016    source источник
comment
Вероятно, вам понадобится помощник, чтобы получить роутер, у которого есть метод match. Непроверенная предполагаемая реализация.   -  person bishop    schedule 28.06.2016


Ответы (3)


Из моего исследования такая информация есть в RouteResult в общедоступном методе getMatchedRouteName(). Проблема в том, как добраться до этого экземпляра из View.

Мы знаем, что можем получить RouteResult, но из объекта Request, который находится в методе __invoke() промежуточного ПО.

public function __invoke($request, $response, $next){
    # instance of RouteResult
    $routeResult = $request->getAttribute('Zend\Expressive\Router\RouteResult');
    $routeName   = $routeResult->getMatchedRouteName();
    // ... 
}

Как рекомендовал @timdev, мы будем черпать вдохновение в существующем помощнике UrlHelper. и сделайте почти такую ​​же реализацию в пользовательском View Helper.

Короче говоря, мы создадим 2 класса.

  1. CurrentUrlHelper с методом setRouteResult() и
  2. CurrentUrlMiddleware с __invoke($req, $res, $next)

Мы добавим CurrentUrlHelper в CurrentUrlMiddleware и в методе __invoke() вызовем CurrentUrlHelper::setRouteResult() с соответствующим экземпляром RouteResult. Позже мы можем использовать наш CurrentUrlHelper с экземпляром RouteResult. Оба класса также должны иметь Фабрику.

class CurrentUrlMiddlewareFactory {
    public function __invoke(ContainerInterface $container) {
        return new CurrentUrlMiddleware(
            $container->get(CurrentUrlHelper::class)
        );
    }
}

class CurrentUrlMiddleware {
    private $currentUrlHelper;

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

    public function __invoke($request, $response, $next = null) {
        $result = $request->getAttribute('Zend\Expressive\Router\RouteResult');
        $this->currentUrlHelper->setRouteResult($result);

        return $next($request, $response); # continue with execution
    }
}

И наш новый помощник:

class CurrentUrlHelper {
    private $routeResult;

    public function __invoke($name) {
        return $this->routeResult->getMatchedRouteName() === $name;
    }

    public function setRouteResult(RouteResult $result) {
        $this->routeResult = $result;
    }
}


class CurrentUrlHelperFactory{
    public function __invoke(ContainerInterface $container){
        # pull out CurrentUrlHelper from container!
        return $container->get(CurrentUrlHelper::class);
    }
}

Теперь нам нужно только прописать в конфигах наш новый View Helper и Middleware:

dependencies.global.php

'dependencies' => [
    'invokables' => [
        # dont have any constructor! 
        CurrentUrlHelper::class => CurrentUrlHelper::class, 
    ],
]

промежуточное ПО-pipeline.global.php

'factories' => [
    CurrentUrlMiddleware::class => CurrentUrlMiddlewareFactory::class,
], 
'middleware' => [
    Zend\Expressive\Container\ApplicationFactory::ROUTING_MIDDLEWARE,
    Zend\Expressive\Helper\UrlHelperMiddleware::class,
    CurrentUrlMiddleware::class,         # Our new Middleware
    Zend\Expressive\Container\ApplicationFactory::DISPATCH_MIDDLEWARE,
],

И, наконец, мы можем зарегистрировать наш View Helper в templates.global.php.

'view_helpers' => [
    'factories' => [
        # use factory to grab an instance of CurrentUrlHelper
        'currentRoute' => CurrentUrlHelperFactory::class 
    ]
],
  • важно зарегистрировать наше промежуточное ПО после ROUTING_MIDDLEWARE и до DISPATCH_MIDDLEWARE!

  • Кроме того, у нас есть CurrentUrlHelperFactory только для того, чтобы назначить его ключу «currentRoute».

Теперь вы можете использовать хелпер в любом файле шаблона :)

<?php // in layout.phtml file
  $index_css   = $this->currentRoute('home-page') ? 'active' : 'none'; 
  $about_css   = $this->currentRoute('about') ? 'active' : 'none'; 
  $contact_css = $this->currentRoute('contact') ? 'active' : 'none';  
?>
person tasmaniski    schedule 29.06.2016
comment
Конечно, вы можете использовать Reflection, но этот метод защищен по какой-то причине. Этот трюк может больше не работать в будущем. - person xtreamwayz; 30.06.2016
comment
Да, Reflection были быстрыми, но не очень хорошими решениями. Я меняю. - person tasmaniski; 30.06.2016
comment
$routeResult = $request->getAttribute('Zend\Expressive\Router\RouteResult'); $routeName = $routeResult->getMatchedRouteName(); Я думаю, было бы лучше заменить это на: $router = $container-›get(RouterInterface::class); $router-›match($request)-›getMatchedRouteName(); - person Andriy; 20.11.2017

Как вы заметили в своем ответе, UrlHelper полезно заметить. Однако создание нового помощника, зависящего от UrlHelper (и отражения), не идеально.

Вам лучше написать свой собственный помощник, вдохновленный UrlHelper, но не зависящий от него.

Вы можете посмотреть код для UrlHelper, UrlHelperFactory и UrlHelperMiddleware, чтобы сообщить о своей собственной реализации.

person timdev    schedule 29.06.2016
comment
Спасибо! Я действительно нашел хорошее вдохновение в существующем UrlHelper :) - person tasmaniski; 30.06.2016

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

Промежуточное ПО действий:

class Dashboard implements MiddlewareInterface
{
    private $responseRenderer;

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

    public function __invoke(Request $request, Response $response, callable $out = null) : Response
    {
        return $this->responseRenderer->render($request, $response, 'common::dashboard');
    }
}

Новый класс-оболочка:

<?php

declare(strict_types = 1);

namespace Infrastructure\View;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Zend\Diactoros\Stream;
use Zend\Expressive\Router\RouteResult;
use Zend\Expressive\Template\TemplateRendererInterface;

class ResponseRenderer
{
    private $templateRenderer;

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

    public function render(Request $request, Response $response, string $templateName, array $data = []) : Response
    {
        $routeResult       = $request->getAttribute(RouteResult::class);
        $data['routeName'] = $routeResult->getMatchedRouteName();

        $body = new Stream('php://temp', 'wb+');
        $body->write($this->templateRenderer->render($templateName, $data));
        $body->rewind();

        return $response->withBody($body);
    }
}

Код заимствован из GitHub.

person xtreamwayz    schedule 30.06.2016