【问题标题】:How to get matched route name in View - Zend Expressive如何在视图中获取匹配的路由名称 - Zend Expressive
【发布时间】:2016-10-30 22:23:06
【问题描述】:

我知道我可以通过路由名称生成 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';  
?>

我正在使用快速路线,但我对任何解决方案都很感兴趣。解决方案不必在查看文件中。

【问题讨论】:

标签: php zend-framework zend-framework3 mezzio fastroute


【解决方案1】:

根据我的研究,在公共方法 getMatchedRouteName() 中的RouteResult 实例中有这样的信息。问题是如何从视图中访问此实例。

我们知道我们可以得到 RouteResult,但是来自中间件的 __invoke() 方法中的 Request 对象。

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

正如@timdev 建议的那样,我们将在现有的助手UrlHelper 中找到灵感,并在自定义视图助手中制作几乎相同的implementation

简而言之,我们将创建 2 个类。

  1. CurrentUrlHelper 与方法 setRouteResult()
  2. CurrentUrlMiddleware__invoke($req, $res, $next)

我们将在 CurrentUrlMiddleware 中注入 CurrentUrlHelper 和 在 __invoke() 方法中,使用适当的 RouteResult 实例调用 CurrentUrlHelper::setRouteResult()。 稍后我们可以在其中使用带有 RouteResult 实例的 CurrentUrlHelper。这两个类也应该有一个工厂。

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, 
    ],
]

middleware-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,
],

最后我们可以在 templates.global.php 中注册我们的 View Helper

'view_helpers' => [
    'factories' => [
        # use factory to grab an instance of CurrentUrlHelper
        'currentRoute' => CurrentUrlHelperFactory::class 
    ]
],
  • 在 ROUTING_MIDDLEWARE 之后和 DISPATCH_MIDDLEWARE 之前注册我们的中间件很重要!

  • 此外,我们有 CurrentUrlHelperFactory 仅用于将其分配给键 'currentRoute'。

现在你可以在任何模板文件中使用 helper :)

<?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';  
?>

【讨论】:

  • 当然可以使用反射,但该方法受到保护是有原因的。这个技巧将来可能不再有效。
  • 是的,Reflection 很快,但不是那么好的决定。我改变它。
  • $routeResult = $request->getAttribute('Zend\Expressive\Router\RouteResult'); $routeName = $routeResult->getMatchedRouteName();我认为最好将其替换为: $router = $container->get(RouterInterface::class); $router->match($request)->getMatchedRouteName();
【解决方案2】:

正如您在自我回答中所指出的,UrlHelper 是一个有用的东西。但是,创建一个依赖于 UrlHelper(和反射)的新助手并不理想。

你最好编写自己的助手,启发 UrlHelper 但不要依赖它。

您可以查看 UrlHelper、UrlHelperFactory 和 UrlHelperMiddleware 的代码以了解您自己的实现。

【讨论】:

  • 谢谢!我真的在现有的 UrlHelper 中找到了很好的灵感:)
【解决方案3】:

您可以将模板渲染器包装在另一个类中并将请求传递给那里,减去您需要的内容并将其注入真正的模板渲染器。

动作中间件:

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借来的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-04
    • 2015-11-26
    • 1970-01-01
    • 2014-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多