简体   繁体   English

ZF3:如何根据方法和路由路由到特定的控制器/动作?

[英]ZF3: How to route to specific controller/action based on method and route?

In my module's module.config.php , I have something like this:在我的模块的module.config.php ,我有这样的东西:

namespace Application;

return [
    //...
    // myroute1 will route to IndexController fooAction if the route is matching '/index/foo' but regardless of request method
    'myroute1' => [
        'type' => Zend\Router\Http\Literal::class,
        'options' => [
            'route'    => '/index/foo',
            'defaults' => [
                'controller' => Controller\IndexController::class,
                'action'     => 'foo',
            ],
        ],
    ],

    // myroute2 will route to IndexController fooAction if the route is request method is GET but regardless of requested route
    'myroute2' => [
        'type'    => Zend\Router\Http\Method::class,
        'options' => [
            'verb'     => 'get',
            'defaults' => [
                'controller'    => Controller\IndexController::class,
                'action'        => 'foo',
            ],
        ],
    ],
    //...
];

What I'm trying to achieve:我正在努力实现的目标:

  • If route /index/foo is requested AND is requested by GET method, then it should be routed to IndexController fooAction如果路由/index/foo被请求并且被GET方法请求,那么它应该被路由到IndexController fooAction
  • If route /index/foo is requested AND is requested by POST method, then it should be routed to IndexController bar Action (notice it's barAction here not fooAction)如果路由/index/foo被请求并且是通过POST方法请求的,那么它应该被路由到IndexController bar Action (注意这里是 barAction 而不是 fooAction)

How to achieve that?如何做到这一点?

Try changing the literal to a Zend\\Mvc\\Router\\Http\\Part route, and then putting the HTTP routes in as CHILD routes! 尝试将文字更改为Zend\\Mvc\\Router\\Http\\Part路由,然后将HTTP路由作为CHILD路由放入!

See here https://docs.zendframework.com/zend-router/routing/#http-route-types 参见此处https://docs.zendframework.com/zend-router/routing/#http-route-types

A note to myself and anyone else looking, as additional note to @delboy1978uk's answer. 给自己和其他任何人的笔记,作为@ delboy1978uk答案的补充笔记。

The answer I was looking for is something like this: 我一直在寻找的答案是这样的:

  • GET /index/foo => IndexController fooAction GET /index/foo => IndexController fooAction
  • POST /index/foo => IndexController barAction POST /index/foo => IndexController barAction

So the code in module.config.php file can be like this: 所以module.config.php文件中的代码可以像这样:

return [
    //...
    'myroute1' => [// The parent route will match the route "/index/foo"
        'type' => Zend\Router\Http\Literal::class,
        'options' => [
            'route'    => '/index/foo',
            'defaults' => [
                'controller' => Controller\IndexController::class,
                'action'     => 'foo',
            ],
        ],
        'may_terminate' => false,
        'child_routes' => [
            'myroute1get' => [// This child route will match GET request
                'type' => Method::class,
                'options' => [
                    'verb' => 'get',
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action'     => 'foo'
                    ],
                ],
            ],
            'myroute1post' => [// This child route will match POST request
                'type' => Method::class,
                'options' => [
                    'verb' => 'post',
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action'     => 'bar'
                    ],
                ],
            ]
        ],
    ],
    //...
];

I know this is an old topic but I wanted to share my answer for anyone who comes across this and is struggling with zend or Laminas (I use Laminas) and the routing of method based AND localized routes.我知道这是一个古老的话题,但我想分享我的答案给任何遇到这个问题并且正在与 zend 或 Laminas(我使用 Laminas)以及基于方法和本地化路由的路由苦苦挣扎的人。 Basically you should be able to just replace "Laminas" by "Zend" for the namespaces.基本上,您应该能够将名称空间的“Laminas”替换为“Zend”。 The code base is very similar.代码库非常相似。

First of all: I was not able to use @evilReiko's solution because 'may_terminate' => false, always threw an exception for me.首先:我无法使用@evilReiko 的解决方案,因为'may_terminate' => false,总是为我抛出异常。 When I set it to true the child routes were ignored ... obviously :D当我将其设置为true ,子路由被忽略了......显然:D

But the note helped me to understand some stuff going on.但是这张便条帮助我了解了一些正在发生的事情。 I decided to just implement a custom class which handles both: URL localization and method/action routing.我决定只实现一个自定义类,它同时处理:URL 本地化和方法/操作路由。

I created a new folder Classes and added a new file MethodSegment into modules/Application .我创建了一个新文件夹Classes并在modules/Application添加了一个新文件MethodSegment So the file path would be modules/Application/Classes/MethodSegment.php .所以文件路径将是modules/Application/Classes/MethodSegment.php

<?php

namespace Application\Classes;

use Laminas\Router\Exception;
use Laminas\Stdlib\ArrayUtils;
use Laminas\Stdlib\RequestInterface as Request;
use Laminas\Router\Http\RouteMatch;
use Traversable;
    
/**
 * Method route.
 */
class MethodSegment extends \Laminas\Router\Http\Segment
{
    /**
     * associative array [method => action]
     *
     * @var array
     */
    protected $methodActions;

    /**
     * Default values - accessing $defaults from parent class Segment
     *
     * @var array
     */
    protected $defaults;

    /**
     * Create a new method route
     *
     * @param  string $route
     * @param  array  $constraints
     * @param  array  $defaults
     */
    public function __construct($route, array $constraints = [], array $defaults = [])
    {
        if(is_array($defaults['action']))
        {
            $this->methodActions = $defaults['action'];
            $defaults['action'] = array_values($defaults['action'])[0];
        }

        parent::__construct($route, $constraints, $defaults);
    }

    /**
     * factory(): defined by RouteInterface interface.
     *
     * @see    \Laminas\Router\RouteInterface::factory()
     *
     * @param  array|Traversable $options
     * @return Method
     * @throws Exception\InvalidArgumentException
     */
    public static function factory($options = [])
    {
        if ($options instanceof Traversable) {
            $options = ArrayUtils::iteratorToArray($options);
        } elseif (! is_array($options)) {
            throw new Exception\InvalidArgumentException(sprintf(
                '%s expects an array or Traversable set of options',
                __METHOD__
            ));
        }

        if (! isset($options['defaults'])) {
            $options['defaults'] = [];
        }

        return new static($options['route'] ?? null, $options['constraints'] ?? [], $options['defaults']);
    }

    /**
     * match(): defined by RouteInterface interface.
     *
     * @see    \Laminas\Router\RouteInterface::match()
     *
     * @return RouteMatch|null
     */
    public function match(Request $request, $pathOffset = null, array $options = [])
    {
        if (! method_exists($request, 'getMethod')) {
            return null;
        }

        $requestVerb = strtolower($request->getMethod());
        $verb = array_keys($this->methodActions);

        if (in_array($requestVerb, $verb)) {
            $this->defaults['action'] = $this->methodActions[$requestVerb];
            return parent::match($request, $pathOffset, $options);
        }

        return null;
    }
}

Basically I copied the code from the Laminas Method class and enhanced it so I can pass an array of actions.基本上,我从 Laminas Method 类中复制了代码并对其进行了增强,以便我可以传递一系列操作。

You can use the MethodSegment like so:您可以像这样使用 MethodSegment:

use App\Routing\MethodSegment;
return [
    'router' => [
        'routes' => [
            'home' => [
                'type'    => MethodSegment::class,
                'options' => [
                    'route'    => /[:language/],
                    'constraints' => [...],
                    'defaults' => [
                        'controller' => Controller\IndexController::class,
                        'action'     => [
                            'get' => 'index',
                            'post' => 'postIndex', // e.g. form
                        ],
                    ],
                ],
            ],
      [...]

Hope this helps anyone, IMO the child route approach is very clunky.希望这对任何人都有帮助,IMO 子路线方法非常笨拙。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM