繁体   English   中英

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

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

在我的模块的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',
            ],
        ],
    ],
    //...
];

我正在努力实现的目标:

  • 如果路由/index/foo被请求并且被GET方法请求,那么它应该被路由到IndexController fooAction
  • 如果路由/index/foo被请求并且是通过POST方法请求的,那么它应该被路由到IndexController bar Action (注意这里是 barAction 而不是 fooAction)

如何做到这一点?

尝试将文字更改为Zend\\Mvc\\Router\\Http\\Part路由,然后将HTTP路由作为CHILD路由放入!

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

给自己和其他任何人的笔记,作为@ delboy1978uk答案的补充笔记。

我一直在寻找的答案是这样的:

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

所以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'
                    ],
                ],
            ]
        ],
    ],
    //...
];

我知道这是一个古老的话题,但我想分享我的答案给任何遇到这个问题并且正在与 zend 或 Laminas(我使用 Laminas)以及基于方法和本地化路由的路由苦苦挣扎的人。 基本上,您应该能够将名称空间的“Laminas”替换为“Zend”。 代码库非常相似。

首先:我无法使用@evilReiko 的解决方案,因为'may_terminate' => false,总是为我抛出异常。 当我将其设置为true ,子路由被忽略了......显然:D

但是这张便条帮助我了解了一些正在发生的事情。 我决定只实现一个自定义类,它同时处理:URL 本地化和方法/操作路由。

我创建了一个新文件夹Classes并在modules/Application添加了一个新文件MethodSegment 所以文件路径将是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;
    }
}

基本上,我从 Laminas Method 类中复制了代码并对其进行了增强,以便我可以传递一系列操作。

您可以像这样使用 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
                        ],
                    ],
                ],
            ],
      [...]

希望这对任何人都有帮助,IMO 子路线方法非常笨拙。

暂无
暂无

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

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