【问题标题】:Symfony route: Optional locale directory in class prefix annotationSymfony 路由:类前缀注释中的可选语言环境目录
【发布时间】:2015-09-29 09:39:28
【问题描述】:

我需要能够定义/{_locale} 部分是可选的路由。这意味着我需要这些 URL 来使用相同的控制器:

  1. my-domain.com/(区域设置将由请求侦听器设置)
  2. my-domain.com/[any-locale]/

同理:

  1. my-domain.com/my-page(区域设置将由请求侦听器设置)
  2. my-domain.com/[any-locale]/my-page

问题是我无法定义两个类 @Route 注释,因为这不是 Symfony 有效/接受的;而且我不能复制控制器类中的每条路由,因为会有大量路由,这真的很糟糕!

我试过只使用一个类注释,但我无法让它工作。以下是我的尝试:

第一:

/**
 * @Route("/{_locale}", requirements={"_locale": "(\w{2,3})?"})
 */
class DefaultController extends Controller {

    /**
     * @Route("/")
     * @param Request $request
     * @return Response
     */
    public function indexAction(Request $request) {
        return new Response('index '.$request->getLocale());
    }

    /**
     * @Route("/my-page")
     * @param Request $request
     * @return Response
     */
    public function testAction(Request $request) {
        return new Response('test '.$request->getLocale());
    }

}

结论:

  1. 作品
  2. 作品
  3. 失败(因为只有 my-domain.com//my-page 可以工作)
  4. 作品

第二:

我认为问题出在前面的斜杠上,所以我尝试了这个:

/**
 * @Route("{slash_and_locale}", requirements={"slash_and_locale": "\/?(\w{2,3})?"}, defaults={"slash_and_locale": ""})
 */
class DefaultController extends Controller { // ...

结论:

  1. 作品
  2. 作品
  3. 失败(同样的原因)
  4. 作品

我已经看到了这个(old) question,但还没有提交正确的答案:(

有人有建议吗? 真的很有帮助,谢谢!

【问题讨论】:

标签: php symfony routes


【解决方案1】:

这是我最终决定要做的事情:

  1. 不管我想要什么,我为每个控制器操作定义了两条路由(参见下面的示例);
  2. 我扩展了 Symfony 的 RoutingExtension 以在使用 pathurl 方法时自动选择正确的路线。

这是我的双路由配置的样子(注意双路由,第二个有+locale 后缀):

<?php

// src\AppBundle\Controller\DefaultController.php

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller {

    /**
     * @Route("/", name="front_index")
     * @Route("/{_locale}/", name="front_index+locale", requirements={"_locale": "\w{2,3}"})
     * @return Response
     */
    public function indexAction() {
        return $this->render('test/domain-translation.html.twig');
    }

}

这是我的RoutingExtension 重载:

<?php

// src\AppBundle\Twig\LocaleRoutingExtension.php

namespace AppBundle\Twig;

use AppBundle\Document\Domain;
use Symfony\Bridge\Twig\Extension\RoutingExtension;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;

class LocaleRoutingExtension extends RoutingExtension {

    /** @var ContainerInterface */
    protected $container;

    /** @var Domain */
    protected $domain;

    /** @var Request */
    protected $request;

    public function setContainer(ContainerInterface $container) {
        $this->container = $container;
        $this->domain = $this->container->get('app.domain_service')->getDomain();
        $this->request = $this->container->get('request_stack')->getMasterRequest();
    }

    protected function processNameAndParameters($name, $parameters) {

        $nameWithLocale = $name . '+locale';
        $routes = $this->container->get('router')->getRouteCollection();
        $route = $routes->get($nameWithLocale);

        if (array_key_exists('no-locale-filter', $parameters) && $parameters['no-locale-filter']) {
            unset($parameters['no-locale-filter']);
            return array('name' => $name, 'parameters' => $parameters);
        }

        if (isset($this->domain) && $this->domain->hasMultipleLocales() && isset($route)) {
            $name = $nameWithLocale;

            $locale = null;
            if (!array_key_exists('_locale', $parameters)) {
                if (isset($this->request)) {
                    $locale = $this->request->getLocale();
                } else {
                    $locale = $this->domain->getDefaultLocale();
                }
            }

            $parameters['_locale'] = $locale;
        }

        return array('name' => $name, 'parameters' => $parameters);
    }

    public function getPath($name, $parameters = array(), $relative = false) {
        $processed = $this->processNameAndParameters($name, $parameters);
        return parent::getPath($processed['name'], $processed['parameters'], $relative);
    }

    public function getUrl($name, $parameters = array(), $schemeRelative = false) {
        $processed = $this->processNameAndParameters($name, $parameters);
        return parent::getUrl($processed['name'], $processed['parameters'], $schemeRelative);
    }

}

我希望这会有所帮助!

【讨论】:

    【解决方案2】:

    我在我的项目(yml)中这样做

    language_selection:
        path: /{locale}/{path}
        defaults: { _controller: PurchaserBundle:Home:langSelection, path: "" }
        requirements:
            path:  .+
            locale: es|ca|en
    

    在控制器中:

    public function langSelectionAction($locale, $path)
    {
        if (in_array($locale, array('es', 'en', 'ca'))) {
            return $this->redirect('/' . $path);
        }
    
        $this->create404NotFound();
    }
    

    这可能是解决问题的基本方法,但有效。您的所有“默认”路由都有效,如果您使用任何语言环境前缀访问站点,您的侦听器会更改语言,并且此控制器会重定向到当前语言的正确路由。

    希望对你有帮助。

    【讨论】:

    • 不幸的是,这不是我想要的,因为它使用重定向并强制在 URL 中具有区域设置。
    • 不需要在 url 中包含语言环境。这个路由是在我的路由文件末尾定义的,所以只有在所有其他路由都没有使用时才匹配
    • 我了解用户不必访问其中包含语言环境的 URL,但生成的 URL(重定向后)将有一个。
    • 恰恰相反。用户访问domain.com/es/home(例如),重定向后的结果是domain.com/home,locale改为“es”
    【解决方案3】:

    我找到了一种通过扩展来自定义 routingComponent 的方法。这样,每个不匹配路由的 url 都将被重试使用默认语言环境作为前缀的 url

    <?php
    
    namespace App\Component;
    
    use Symfony\Component\Routing\Exception\ResourceNotFoundException;
    use Symfony\Component\Routing\Matcher\UrlMatcher as BaseUrlMatcher;
    
    class UrlMatcher extends BaseUrlMatcher
    {
        public function match($pathinfo)
        {
            try {
                return parent::match($pathinfo);
            } catch (ResourceNotFoundException $exception) {
                $url = sprintf('/%s%s', 'en', $pathinfo);
    
                return parent::match($url);
            }
        }
    }
    

    在参数中设置新的urlMatcher:

    parameters:
            # UrlMatcher
            router.options.matcher.cache_class: ~
            router.options.matcher_class: App\Component\UrlMatcher
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-02-09
      • 2022-01-15
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      • 2017-03-02
      • 1970-01-01
      相关资源
      最近更新 更多