2013-04-27 36 views
3

我对Symfony2还是比较陌生,并且在解决以下问题时遇到困难。Symfony2动态路由到一个控制器带有多个可选参数的动作

我有一个控制器,具有(在控制器)中定义的以下路线的主浏览动作:

/** 
* @Route("/browse") 
* @Route("/browse/{page}") 
* @Route("/browse/c/{category}/{categoryName}") 
* @Route("/browse/c/{category}/{categoryName}/{page}") 
* @Route("/browse/c/{category}/b/{brand}/{page}") 
* @Route("/browse/b/{brand}") 
* @Route("/browse/b/{brand}/{page}") 
* @Template() 
*/ 
public function browseAction($category = 0, $page = 1, $brand = 0) { 

上述路由的工作原理,没有任何问题。

问题是从树枝视图或视图助手生成网址。

我本来希望能够做一个视图助手如下:

{{ url('browse', {'brand': '123'}) }} 

该工程确定在routing.yml中的以下内容:

browse: 
    pattern: /browse/b/{brand} 
    defaults: { _controller: MyCoreBundle:Browse:browse } 

我然后设法:

browse: 
    pattern: /browse/b/{brand} 
    pattern: /browse/c/{category}/b/{brand} 
    defaults: { _controller: MyCoreBundle:Browse:browse } 

但只有最后一个模式似乎适用,并尝试使用以下将引发错误:

{{ url('browse', {'brand': '123'}) }} 

我知道我可以创建多个routing.yml中个别路线被唯一命名。但这意味着取决于将使用的变量,我需要指定一个不同的路由名称,这将很快变得非常混乱。

我也试过:

browse: 
    pattern: /browse/c/{category}/b/{brand}/{page} 
    defaults: { _controller: MyCoreBundle:Browse:browse } 

有了:

{{ url('browse', {'brand': '123', 'category':'', 'page': '1'}) }} 

但抛出一个错误说类是不正确的格式..

我失去了一些东西在这里?有人能指出我正确的方向吗?我是否可能需要创建一个可以接受所有变量并根据输入构建URL的树枝扩展?

回答

3

我最终创建了一个树枝扩展。它很啰嗦,涉及传递route_params和query_params,但它的工作原理。

所以在我的控制,我需要得到航线和查询参数:

$routeParams = $this->get('request')->attributes->get('_route_params'); 
$queryParams = $this->get('request')->query->all(); 

然后传递给视图:

return array('products' => $products, 'mfdFacets' => $mfdFacets, 'routeParams' => $routeParams, 'queryParams' => $queryParams); 

然后在我的browse.html.twig我打电话视图助手和传递路径和查询参数:

{% render controller("MyCoreBundle:Helper:menumfd", {'mfdFacets': mfdFacets, 'routeParams': routeParams, 'queryParams': queryParams }) %} 

然后在辅助控制器:

/** 
    * @Route("/helper/menu/module/mfd") 
    * @Template() 
    */ 
    public function menumfdAction($mfdFacets, $routeParams, $queryParams) { 
    $manufacturers = $this->get("my.manufacturers")->makeNamedArray($mfdFacets); 
    return array('manufacturers' => $manufacturers, 'routeParams' => $routeParams, 'queryParams' => $queryParams); 
    } 

然后在助手视图:

<ul> 
{% for mfd in manufacturers %} 

    <li><a href="{{ mybrowseroute("brand", mfd.id, "brandName", mfd.name, routeParams, queryParams) }}">{{ mfd.name | raw }} ({{ mfd.count }})</a></li> 

{% endfor %} 
</ul> 

然后树枝扩展(服务?)类:

<? 

namespace My\Bundle\ServiceBundle\Twig\Extension; 

use Symfony\Component\HttpKernel\KernelInterface; 
use Symfony\Component\DependencyInjection\Container; 
use Symfony\Component\HttpFoundation\Request; 

class MyRouterExtension extends \Twig_Extension { 

    protected $container; 

    public function __construct(Container $container) { 
    $this->container = $container; 
    } 

    public function getFunctions() { 
    return array(
     'mybrowseroute' => new \Twig_Function_Method($this, 'myBrowseRoute') 
    ); 
    } 

    public function myBrowseRoute($label, $value, $label2, $value2, $routeParams, $queryParams) { 


    #print_r($routeParams); 
    $route_array = array("category", "categoryName", "brand", "brandName"); 

    ## Value 1 
    if (array_key_exists($label, $routeParams)) { 
     $routeParams["$label"] = $value; 
    } else { 
     if (in_array($label, $route_array)) { 
      $routeParams["$label"] = $value; 
     } 
    } 
    if (array_key_exists($label, $queryParams)) { 
     $queryParams["$label"] = $value; 
    } else { 
     if (!array_key_exists($label, $route_array) && !array_key_exists($label, $routeParams)) { 
      $queryParams["$label"] = $value; 
     } 
    } 

    ## Value 2 
    if (array_key_exists($label, $routeParams)) { 
     $routeParams["$label2"] = $value2; 
    } else { 
     if (in_array($label, $route_array)) { 
      $routeParams["$label2"] = $value2; 
     } 
    } 
    if (array_key_exists($label2, $queryParams)) { 
     $queryParams["$label2"] = $value2; 
    } else { 
     if (!array_key_exists($label2, $route_array) && !array_key_exists($label2, $routeParams)) { 
      $queryParams["$label2"] = $value2; 
     } 
    } 

    ## Generate URL string 

    $base_route = $this->container->get('router')->generate("browse"); 
    $routeString = $base_route; 


    if (array_key_exists("category", $routeParams)) { 
     $routeString .= "/c/" . $routeParams["category"]; 
    } 
    if (array_key_exists("categoryName", $routeParams)) { 
     $routeString .= "/" . urlencode($routeParams["categoryName"]); 
    } 
    if (array_key_exists("brand", $routeParams)) { 
     $routeString .= "/b/" . $routeParams["brand"]; 
    } 
    if (array_key_exists("brandName", $routeParams)) { 
     $routeString .= "/" . urlencode($routeParams["brandName"]); 
    } 

    # Page 
    $routeString .= '/1'; 

    $i = 1; 
    foreach($queryParams as $qLabel => $qValue){ 
     if($i == 1){ 
      $routeString .= "?$qLabel=$qValue"; 
     } else { 
      $routeString .= "&$qLabel=$qValue"; 
     } 
     $i++; 
    } 

    return $routeString; 
    } 

    public function getName() { 
    return 'my_router'; 
    } 

} 

这需要修改routing.yml如下:

browse: 
     pattern: /browse 
     defaults: { _controller: MyCoreBundle:Browse:browse } 

and in services.yml:

services: 
    my.router: 
    class: My\Bundle\ServiceBundle\Twig\Extension\MyRouterExtension 
    arguments: ['@service_container'] 
    tags: 
     - { name: twig.extension } 

如果你只是想通过在视图助手1键值对,你可以只使用:

所有的
<li><a href="{{ mybrowseroute("brand", mfd.id, '', '', routeParams, queryParams) }}">{{ mfd.name | raw }} ({{ mfd.count }})</a></li> 
+1

您可否对此进行清理并发布为捆绑包? ;) – 2013-04-27 18:12:01

1

调试的路线,你会看到每一个路线标注得到一个唯一的名称

php app/console router:debug 

您可以在注释中的名称参数设置个人的名字。看看documentation

@Route("/browse/{page}", name="browse_with_page") 

为您的案例创建一个专用的树枝扩展可能是另一种解决方案。或者,这个提到的blog post可能会很有趣。

编辑:你可以最小化你的路线,因为页面参数可以是可选的。

@Route("/browse/c/{category}/{categoryName}") 
@Route("/browse/c/{category}/{categoryName}/{page}") 

是一样的,可以称为两种方式。

@Route("/browse/c/{category}/{categoryName}/{page}", defaults={"page" = 1}) 

在控制台上测试它,同样的路线应该匹配。

php app:console router:match /browse/c/1/foobar 
php app:console router:match /browse/c/1/foobar/2 
+0

感谢您的建议 - 我已经添加了上述解决方案 - 我认为允许非常动态的参数组合。 – someuser 2013-04-27 15:57:39

-2

首先,这不是动态路由。这是具有多种模式的静态路由。有一个明显的区别。

其次,您需要为每个模式创建一个专门定义的路由,并为每个模式定义唯一的名称。但是,您可以将页面参数默认为1,以使其可选,从而减少定义的模式数量。第三,当需要在树枝中生成这些链接时,只需使用上面定义的唯一名称以及所需的参数即可。

最后,在最后一个例子中,类别是一个必需的参数,并且您将一个空白值传递给它。这会创建“/ browse/c // b/123/1”,这是Symfony2无法处理的路径。