Symfony2如何将URI的参数传递给控制器​​Action方法?

时间:2014-11-27 06:50:46

标签: php symfony controller action

我已经开始学习Symfony2了。我遇到了一个疑问:如果我有这条路线:

# app/config/routing.yml
hello:
    path:      /hello/{name}
    defaults:  { _controller: AcmeHelloBundle:Hello:index }

这个控制器:

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
    public function indexAction($name)
    {
        return new Response('<html><body>Ciao '.$name.'!</body></html>');
    }
}

内部Symfony2(在app / bootstrap.php.cache内)调用调用user_func_array()PHP内置函数:

$arguments = $this->resolver->getArguments($request, $controller);
$response = call_user_func_array($controller, $arguments);

对getArguments()方法的调用返回一个参数数组以传递给action方法。但如果控制器是:

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
    public function indexAction($n)
    {
        return new Response('<html><body>Ciao '.$n.'!</body></html>');
    }
}

Symfony会抱怨RuntimeException,因为没有设置param $ n。

我的问题是:Symfony如何控制这种行为,我的意思是,如果一个路由有一个{name}参数,为什么控制器必须有一个带$ name参数的动作方法,参数必须被称为$ name?

因为在普通的PHP中,这可以工作:

$name = 'Alex';
function indexAction($n) {
   echo $n;
}
$f = 'indexAction';
$arguments = array($name);
call_user_func_array($f, $arguments);

即使函数签名接受名为$ n的param而不是$ name。

我希望这个问题是可以理解的,如果没有,请告诉我,我会进行编辑。

感谢您的关注!

3 个答案:

答案 0 :(得分:4)

所有这一切都在Controller Resolver HttpKernel/Controller/ControllerResolver.php中通过getArguments()&amp; doArguments()

为了更好地理解,您可以在Getting The Controller Arguments

中找到所需内容

修改:回答评论。

Symfony是否在内部使用ReflectionParameter类来跟踪方法签名的参数并将它们与路径参数匹配?

是的,ControllerResolver使用:

以下是:

 public function getArguments(Request $request, $controller)
    {
        if (is_array($controller)) {
            $r = new \ReflectionMethod($controller[0], $controller[1]);
        } elseif (is_object($controller) && !$controller instanceof \Closure) {
            $r = new \ReflectionObject($controller);
            $r = $r->getMethod('__invoke');
        } else {
            $r = new \ReflectionFunction($controller);
        }
        return $this->doGetArguments($request, $controller, $r->getParameters());
    }

答案 1 :(得分:1)

  1. 请求由Symfony前端控制器(例如app.php)处理;
  2. Symfony核心(即内核)要求路由器检查请求;
  3. 路由器将传入的URL与特定路由进行匹配,并返回有关路由的信息,包括应该执行的控制器;
  4. Symfony内核执行控制器,最终返回一个Response对象。
  5. enter image description here

    Resource

答案 2 :(得分:1)

在您的示例中,您的操作只有一个参数,因此我们很明显需要从路径中填充它。

要扩展您的示例,如果您向路线添加了另一个参数,例如:

# app/config/routing.yml
hello:
    path:      /hello/{name}/{surname}
    defaults:  { _controller: AcmeHelloBundle:Hello:index }

并将您的控制器更改为:

// src/Acme/HelloBundle/Controller/HelloController.php
namespace Acme\HelloBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
class HelloController
{
    public function indexAction($s, $n)
    {
        return new Response('<html><body>Ciao '.$n.' '.$s.'!</body></html>');
    }
}

Symfony不知道填充哪个路由参数的变量$s$n

如果您改变行动:

    public function indexAction($surname, $name)
    {
        return new Response('<html><body>Ciao '.$name.' '.$surname.'!</body></html>');
    }

现在,Symfony可以查看您的参数名称并将它们映射到路由参数。这也意味着只要名称与路由参数匹配,您就可以按任何顺序拥有indexAction个参数。我相信Symfony在内部使用Reflection API来解决所有问题。