ZF3:如何根据方法和路由路由到特定的controller/action?
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 barAction (注意这里是 barAction 而不是 fooAction)
如何实现?
尝试将文字更改为 Zend\Mvc\Router\Http\Part
路由,然后将 HTTP 路由作为子路由放入!
看这里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
但是这张便条帮助我理解了一些正在发生的事情。我决定只实现一个自定义 class 来处理:URL 本地化和 method/action 路由。
我创建了一个新文件夹 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 方法 class 复制了代码并对其进行了增强,以便我可以传递一系列操作。
您可以像这样使用 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 子路由方法非常笨重。
在我模块的 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 barAction (注意这里是 barAction 而不是 fooAction)
如何实现?
尝试将文字更改为 Zend\Mvc\Router\Http\Part
路由,然后将 HTTP 路由作为子路由放入!
看这里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
但是这张便条帮助我理解了一些正在发生的事情。我决定只实现一个自定义 class 来处理:URL 本地化和 method/action 路由。
我创建了一个新文件夹 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 方法 class 复制了代码并对其进行了增强,以便我可以传递一系列操作。
您可以像这样使用 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 子路由方法非常笨重。