简体   繁体   English

Symfony 路由:类前缀注释中的可选语言环境目录

[英]Symfony route: Optional locale directory in class prefix annotation

I need to be able to define routes where the /{_locale} part is optional.我需要能够定义/{_locale}部分是可选的路由。 This means I need these URLs to use the same controller:这意味着我需要这些 URL 来使用相同的控制器:

  1. my-domain.com/ (the locale will be set by a request listener) my-domain.com/ (区域设置将由请求侦听器设置)
  2. my-domain.com/[any-locale]/

Same goes with:同样适用于:

  1. my-domain.com/my-page (the locale will be set by a request listener) my-domain.com/my-page (区域设置将由请求侦听器设置)
  2. my-domain.com/[any-locale]/my-page

The problem is I can't define two class @Route annotations as this isn't valid/accepted by Symfony;问题是我不能定义两个类@Route注释,因为这不是有效的/被 Symfony 接受的; and I can't duplicate each and every route in my controller classes because there will be a large number and that would be really bad!而且我无法复制控制器类中的每条路由,因为会有大量路由,这真的很糟糕!

I've tried having only one class annotation but I couldn't get it to work.我试过只有一个类注释,但我无法让它工作。 Here are my attempts:这是我的尝试:

First:第一的:

/**
 * @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());
    }

}

Conclusions:结论:

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

Second:第二:

I thought the problem was the leading slash, so i tried this:我认为问题是前导斜杠,所以我试过这个:

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

Conclusions:结论:

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

I've seen this (old) question but no proper answer has been submitted yet :(我见过这个(旧)问题,但尚未提交正确答案:(

Anyone with a suggestion?有人有建议吗? Would be really helpful, thanks!真的会很有帮助,谢谢!

Here's what I eventually decided to do:这是我最终决定做的事情:

  1. despite what I wanted, I define two routes for each controller action (see example below);不管我想要什么,我为每个控制器动作定义了两条路线(见下面的例子);
  2. I extend Symfony's RoutingExtension to automatically pick the correct route when using path or url methods.我扩展了 Symfony 的RoutingExtension以在使用pathurl方法时自动选择正确的路由。

Here's what my double route configuration looks like (note the double route, the second having a +locale suffix):这是我的双路由配置的样子(注意双路由,第二个带有+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');
    }

}

And here's my RoutingExtension overload:这是我的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);
    }

}

I hope this helps!我希望这有帮助!

I do this in my project (yml)我在我的项目(yml)中这样做

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

And in the controller:在控制器中:

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

    $this->create404NotFound();
}

It is maybe a rudimentary way to solve the problem, but works.这可能是解决问题的基本方法,但有效。 All of your "default" routes works, and if you access to the site with any locale prefix, your listener change the language and this controller redirects to the correct route in the current language.您所有的“默认”路由都有效,如果您访问带有任何语言环境前缀的站点,您的侦听器会更改语言,并且此控制器会重定向到当前语言的正确路由。

I hope it helps.我希望它有帮助。

I found a way to customize the routingComponent by just extending it.我找到了一种通过扩展它来自定义路由组件的方法。 This way every url which matches no routes, will be retried prefixing url with default locale这样,每个不匹配路由的 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);
        }
    }
}

Set new urlMatcher in parameters:在参数中设置新的 urlMatcher:

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

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

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