feature #30501 [FrameworkBundle][Routing] added Configurators to handle template and redirect controllers (HeahDude)
This PR was merged into the 5.1-dev branch.
Discussion
----------
[FrameworkBundle][Routing] added Configurators to handle template and redirect controllers
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | let's see
| Fixed tickets | partially #24640, #25145
| License | MIT
| Doc PR | symfony/symfony-docs#11120
While working on https://github.com/symfony/symfony-docs/pull/11085, I felt bad about the long notations required for simple [redirects](https://symfony.com/doc/current/routing/redirect_in_config.html) and [templates rendering](https://symfony.com/doc/current/templating/render_without_controller.html) template actions, but I love and use those features since always. Then I gave it a try yesterday night and now I realised I missed #24640 and that #25145 has been closed x).
So here we go, here's my WIP. WDYT of this implementation? ping @javiereguiluz?
I'm going to open the PR in the docs so we can discuss the DX changes there too, and keep focus on the code here.
Cheers!
EDIT
----
This PR now only update PHP-DSL configurators.
______________
TODO:
- [x] gather reviews
- ~[x] fix xml schema~
- [x] add some tests
- ~[ ] handle xsd auto discovery~
- [x] rebase on top of #30507
- [x] ~add shortcuts for #30514~
Commits
-------
de74794acf
[FrameworkBundle][Routing] added Configurators to handle template and redirect controllers
This commit is contained in:
commit
477ee19778
@ -4,6 +4,7 @@ CHANGELOG
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* Added `Routing\Loader` and `Routing\Loader\Configurator` namespaces to ease defining routes with default controllers
|
||||
* Added the `framework.router.context` configuration node to configure the `RequestContext`
|
||||
* Made `MicroKernelTrait::configureContainer()` compatible with `ContainerConfigurator`
|
||||
* Added a new `mailer.message_bus` option to configure or disable the message bus to use to send mails.
|
||||
@ -29,7 +30,7 @@ CHANGELOG
|
||||
* Removed the `translator.selector` and `session.save_listener` services
|
||||
* Removed `SecurityUserValueResolver`, use `UserValueResolver` instead
|
||||
* Removed `routing.loader.service`.
|
||||
* Service route loaders must be tagged with `routing.route_loader`.
|
||||
* Service route loaders must be tagged with `routing.route_loader`.
|
||||
* Added `slugger` service and `SluggerInterface` alias
|
||||
* Removed the `lock.store.flock`, `lock.store.semaphore`, `lock.store.memcached.abstract` and `lock.store.redis.abstract` services.
|
||||
* Removed the `router.cache_class_prefix` parameter.
|
||||
@ -81,8 +82,8 @@ CHANGELOG
|
||||
options if you're using Symfony's serializer.
|
||||
* [BC Break] Removed the `framework.messenger.routing.send_and_handle` configuration.
|
||||
Instead of setting it to true, configure a `SyncTransport` and route messages to it.
|
||||
* Added information about deprecated aliases in `debug:autowiring`
|
||||
* Added php ini session options `sid_length` and `sid_bits_per_character`
|
||||
* Added information about deprecated aliases in `debug:autowiring`
|
||||
* Added php ini session options `sid_length` and `sid_bits_per_character`
|
||||
to the `session` section of the configuration
|
||||
* Added support for Translator paths, Twig paths in translation commands.
|
||||
* Added support for PHP files with translations in translation commands.
|
||||
|
@ -11,14 +11,14 @@
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Kernel;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\RoutingConfigurator;
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\PhpFileLoader as RoutingPhpFileLoader;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\AbstractConfigurator;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader as ContainerPhpFileLoader;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||
use Symfony\Component\Routing\Loader\PhpFileLoader as RoutingPhpFileLoader;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Routing\RouteCollectionBuilder;
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
<argument type="service" id="file_locator" />
|
||||
</service>
|
||||
|
||||
<service id="routing.loader.php" class="Symfony\Component\Routing\Loader\PhpFileLoader">
|
||||
<service id="routing.loader.php" class="Symfony\Bundle\FrameworkBundle\Routing\Loader\PhpFileLoader">
|
||||
<tag name="routing.loader" />
|
||||
<argument type="service" id="file_locator" />
|
||||
</service>
|
||||
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\Traits\AddTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class GoneRouteConfigurator extends RouteConfigurator
|
||||
{
|
||||
use AddTrait;
|
||||
|
||||
/**
|
||||
* @param bool $permanent Whether the redirection is permanent
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function permanent(bool $permanent = true)
|
||||
{
|
||||
return $this->defaults(['permanent' => $permanent]);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\Traits\AddTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
|
||||
|
||||
/**
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
class RedirectRouteConfigurator extends RouteConfigurator
|
||||
{
|
||||
use AddTrait;
|
||||
|
||||
/**
|
||||
* @param bool $permanent Whether the redirection is permanent
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function permanent(bool $permanent = true)
|
||||
{
|
||||
return $this->defaults(['permanent' => $permanent]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|array $ignoreAttributes Whether to ignore attributes or an array of attributes to ignore
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function ignoreAttributes($ignoreAttributes = true)
|
||||
{
|
||||
return $this->defaults(['ignoreAttributes' => $ignoreAttributes]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $keepRequestMethod Whether redirect action should keep HTTP request method
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function keepRequestMethod(bool $keepRequestMethod = true)
|
||||
{
|
||||
return $this->defaults(['keepRequestMethod' => $keepRequestMethod]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $keepQueryParams Whether redirect action should keep query parameters
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function keepQueryParams(bool $keepQueryParams = true)
|
||||
{
|
||||
return $this->defaults(['keepQueryParams' => $keepQueryParams]);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\RedirectController;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\TemplateController;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator as BaseRouteConfigurator;
|
||||
|
||||
/**
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
class RouteConfigurator extends BaseRouteConfigurator
|
||||
{
|
||||
/**
|
||||
* @param string $template The template name
|
||||
* @param array $context The template variables
|
||||
*/
|
||||
final public function template(string $template, array $context = []): TemplateRouteConfigurator
|
||||
{
|
||||
return (new TemplateRouteConfigurator($this->collection, $this->route, $this->name, $this->parentConfigurator, $this->prefixes))
|
||||
->defaults([
|
||||
'_controller' => TemplateController::class,
|
||||
'template' => $template,
|
||||
'context' => $context,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $route The route name to redirect to
|
||||
*/
|
||||
final public function redirectToRoute(string $route): RedirectRouteConfigurator
|
||||
{
|
||||
return (new RedirectRouteConfigurator($this->collection, $this->route, $this->name, $this->parentConfigurator, $this->prefixes))
|
||||
->defaults([
|
||||
'_controller' => RedirectController::class.'::redirectAction',
|
||||
'route' => $route,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url The relative path or URL to redirect to
|
||||
*/
|
||||
final public function redirectToUrl(string $url): UrlRedirectRouteConfigurator
|
||||
{
|
||||
return (new UrlRedirectRouteConfigurator($this->collection, $this->route, $this->name, $this->parentConfigurator, $this->prefixes))
|
||||
->defaults([
|
||||
'_controller' => RedirectController::class.'::urlRedirectAction',
|
||||
'path' => $url,
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
final public function gone(): GoneRouteConfigurator
|
||||
{
|
||||
return (new GoneRouteConfigurator($this->collection, $this->route, $this->name, $this->parentConfigurator, $this->prefixes))
|
||||
->defaults([
|
||||
'_controller' => RedirectController::class.'::redirectAction',
|
||||
'route' => '',
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\Traits\AddTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator as BaseRoutingConfigurator;
|
||||
|
||||
class RoutingConfigurator extends BaseRoutingConfigurator
|
||||
{
|
||||
use AddTrait;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\Traits\AddTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
|
||||
|
||||
/**
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
class TemplateRouteConfigurator extends RouteConfigurator
|
||||
{
|
||||
use AddTrait;
|
||||
|
||||
/**
|
||||
* @param int|null $maxAge Max age for client caching
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function maxAge(?int $maxAge)
|
||||
{
|
||||
return $this->defaults(['maxAge' => $maxAge]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|null $sharedMaxAge Max age for shared (proxy) caching
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function sharedMaxAge(?int $sharedMaxAge)
|
||||
{
|
||||
return $this->defaults(['sharedAge' => $sharedMaxAge]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool|null $private Whether or not caching should apply for client caches only
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function private(?bool $private = true)
|
||||
{
|
||||
return $this->defaults(['private' => $private]);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\RouteConfigurator;
|
||||
use Symfony\Component\Routing\Loader\Configurator\CollectionConfigurator;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator as BaseRouteConfigurator;
|
||||
|
||||
trait AddTrait
|
||||
{
|
||||
/**
|
||||
* Adds a route.
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*
|
||||
* @return RouteConfigurator
|
||||
*/
|
||||
public function add(string $name, $path): BaseRouteConfigurator
|
||||
{
|
||||
$parentConfigurator = $this instanceof CollectionConfigurator ? $this : ($this instanceof RouteConfigurator ? $this->parentConfigurator : null);
|
||||
$route = $this->createLocalizedRoute($this->collection, $name, $path, $this->name, $this->prefixes);
|
||||
|
||||
return new RouteConfigurator($this->collection, $route, $this->name, $parentConfigurator, $this->prefixes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a route.
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*
|
||||
* @return RouteConfigurator
|
||||
*/
|
||||
final public function __invoke(string $name, $path): BaseRouteConfigurator
|
||||
{
|
||||
return $this->add($name, $path);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\Traits\AddTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
|
||||
|
||||
/**
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
class UrlRedirectRouteConfigurator extends RouteConfigurator
|
||||
{
|
||||
use AddTrait;
|
||||
|
||||
/**
|
||||
* @param bool $permanent Whether the redirection is permanent
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function permanent(bool $permanent = true)
|
||||
{
|
||||
return $this->defaults(['permanent' => $permanent]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $scheme The URL scheme (null to keep the current one)
|
||||
* @param int|null $port The HTTP or HTTPS port (null to keep the current one for the same scheme or the default configured port)
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function scheme(?string $scheme, int $port = null)
|
||||
{
|
||||
$this->defaults(['scheme' => $scheme]);
|
||||
|
||||
if ('http' === $scheme) {
|
||||
$this->defaults(['httpPort' => $port]);
|
||||
} elseif ('https' === $scheme) {
|
||||
$this->defaults(['httpsPort' => $port]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $keepRequestMethod Whether redirect action should keep HTTP request method
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function keepRequestMethod(bool $keepRequestMethod = true)
|
||||
{
|
||||
return $this->defaults(['keepRequestMethod' => $keepRequestMethod]);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\RoutingConfigurator;
|
||||
use Symfony\Component\Routing\Loader\PhpFileLoader as BasePhpFileLoader;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
class PhpFileLoader extends BasePhpFileLoader
|
||||
{
|
||||
protected function callConfigurator(callable $result, string $path, string $file): RouteCollection
|
||||
{
|
||||
$collection = new RouteCollection();
|
||||
|
||||
$result(new RoutingConfigurator($collection, $this, $path, $file));
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator;
|
||||
|
||||
return function (RoutingConfigurator $routes) {
|
||||
$routes->add('classic_route', '/classic');
|
||||
|
||||
$routes->add('template_route', '/static')
|
||||
->template('static.html.twig', ['foo' => 'bar'])
|
||||
->maxAge(300)
|
||||
->sharedMaxAge(100)
|
||||
->private()
|
||||
->methods(['GET'])
|
||||
->utf8()
|
||||
->condition('abc')
|
||||
;
|
||||
$routes->add('redirect_route', '/redirect')
|
||||
->redirectToRoute('target_route')
|
||||
->permanent()
|
||||
->ignoreAttributes(['attr', 'ibutes'])
|
||||
->keepRequestMethod()
|
||||
->keepQueryParams()
|
||||
->schemes(['http'])
|
||||
->host('legacy')
|
||||
->utf8()
|
||||
;
|
||||
$routes->add('url_redirect_route', '/redirect-url')
|
||||
->redirectToUrl('/url-target')
|
||||
->permanent()
|
||||
->scheme('http', 1)
|
||||
->keepRequestMethod()
|
||||
->host('legacy')
|
||||
->utf8()
|
||||
;
|
||||
$routes->add('not_a_route', '/not-a-path')
|
||||
->gone()
|
||||
->host('legacy')
|
||||
->utf8()
|
||||
;
|
||||
$routes->add('gone_route', '/gone-path')
|
||||
->gone()
|
||||
->permanent()
|
||||
->utf8()
|
||||
;
|
||||
};
|
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests\Routing\Loader;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\RedirectController;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\TemplateController;
|
||||
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\Config\FileLocatorInterface;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
abstract class AbstractLoaderTest extends TestCase
|
||||
{
|
||||
/** @var LoaderInterface */
|
||||
protected $loader;
|
||||
|
||||
abstract protected function getLoader(): LoaderInterface;
|
||||
|
||||
abstract protected function getType(): string;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->loader = $this->getLoader();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$this->loader = null;
|
||||
}
|
||||
|
||||
public function getLocator(): FileLocatorInterface
|
||||
{
|
||||
return new FileLocator([__DIR__.'/../../Fixtures/Resources/config/routing']);
|
||||
}
|
||||
|
||||
public function testRoutesAreLoaded()
|
||||
{
|
||||
$routeCollection = $this->loader->load('routes.'.$this->getType());
|
||||
|
||||
$expectedCollection = new RouteCollection();
|
||||
|
||||
$expectedCollection->add('classic_route', (new Route('/classic')));
|
||||
|
||||
$expectedCollection->add('template_route', (new Route('/static'))
|
||||
->setDefaults([
|
||||
'_controller' => TemplateController::class,
|
||||
'context' => ['foo' => 'bar'],
|
||||
'template' => 'static.html.twig',
|
||||
'maxAge' => 300,
|
||||
'sharedAge' => 100,
|
||||
'private' => true,
|
||||
])
|
||||
->setMethods(['GET'])
|
||||
->setOptions(['utf8' => true])
|
||||
->setCondition('abc')
|
||||
);
|
||||
$expectedCollection->add('redirect_route', (new Route('/redirect'))
|
||||
->setDefaults([
|
||||
'_controller' => RedirectController::class.'::redirectAction',
|
||||
'route' => 'target_route',
|
||||
'permanent' => true,
|
||||
'ignoreAttributes' => ['attr', 'ibutes'],
|
||||
'keepRequestMethod' => true,
|
||||
'keepQueryParams' => true,
|
||||
])
|
||||
->setSchemes(['http'])
|
||||
->setHost('legacy')
|
||||
->setOptions(['utf8' => true])
|
||||
);
|
||||
$expectedCollection->add('url_redirect_route', (new Route('/redirect-url'))
|
||||
->setDefaults([
|
||||
'_controller' => RedirectController::class.'::urlRedirectAction',
|
||||
'path' => '/url-target',
|
||||
'permanent' => true,
|
||||
'scheme' => 'http',
|
||||
'httpPort' => 1,
|
||||
'keepRequestMethod' => true,
|
||||
])
|
||||
->setHost('legacy')
|
||||
->setOptions(['utf8' => true])
|
||||
);
|
||||
$expectedCollection->add('not_a_route', (new Route('/not-a-path'))
|
||||
->setDefaults([
|
||||
'_controller' => RedirectController::class.'::redirectAction',
|
||||
'route' => '',
|
||||
])
|
||||
->setHost('legacy')
|
||||
->setOptions(['utf8' => true])
|
||||
);
|
||||
$expectedCollection->add('gone_route', (new Route('/gone-path'))
|
||||
->setDefaults([
|
||||
'_controller' => RedirectController::class.'::redirectAction',
|
||||
'route' => '',
|
||||
'permanent' => true,
|
||||
])
|
||||
->setOptions(['utf8' => true])
|
||||
);
|
||||
$expectedCollection->addResource(new FileResource(realpath(
|
||||
__DIR__.'/../../Fixtures/Resources/config/routing/routes.'.$this->getType()
|
||||
)));
|
||||
|
||||
$this->assertEquals($expectedCollection, $routeCollection);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests\Routing\Loader;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\Loader\PhpFileLoader;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
|
||||
class PhpFileLoaderTest extends AbstractLoaderTest
|
||||
{
|
||||
protected function getLoader(): LoaderInterface
|
||||
{
|
||||
return new PhpFileLoader($this->getLocator());
|
||||
}
|
||||
|
||||
protected function getType(): string
|
||||
{
|
||||
return 'php';
|
||||
}
|
||||
}
|
@ -80,6 +80,7 @@
|
||||
"symfony/messenger": "<4.4",
|
||||
"symfony/mime": "<4.4",
|
||||
"symfony/property-info": "<4.4",
|
||||
"symfony/routing": "<5.1",
|
||||
"symfony/serializer": "<4.4",
|
||||
"symfony/stopwatch": "<4.4",
|
||||
"symfony/translation": "<5.0",
|
||||
|
@ -4,6 +4,7 @@ CHANGELOG
|
||||
5.1.0
|
||||
-----
|
||||
|
||||
* added the protected method `PhpFileLoader::callConfigurator()` as extension point to ease custom routing configuration
|
||||
* deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
|
||||
* added "priority" option to annotated routes
|
||||
* added argument `$priority` to `RouteCollection::add()`
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
@ -19,6 +18,7 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
*/
|
||||
class ImportConfigurator
|
||||
{
|
||||
use Traits\PrefixTrait;
|
||||
use Traits\RouteTrait;
|
||||
|
||||
private $parent;
|
||||
@ -43,38 +43,7 @@ class ImportConfigurator
|
||||
*/
|
||||
final public function prefix($prefix, bool $trailingSlashOnRoot = true): self
|
||||
{
|
||||
if (!\is_array($prefix)) {
|
||||
$this->route->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($this->route->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$prefix[$locale] = trim(trim($localePrefix), '/');
|
||||
}
|
||||
foreach ($this->route->all() as $name => $route) {
|
||||
if (null === $locale = $route->getDefault('_locale')) {
|
||||
$this->route->remove($name);
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$this->route->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($prefix[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
|
||||
} else {
|
||||
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$this->route->add($name, $route);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->addPrefix($this->route, $prefix, $trailingSlashOnRoot);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ class RouteConfigurator
|
||||
use Traits\AddTrait;
|
||||
use Traits\RouteTrait;
|
||||
|
||||
private $parentConfigurator;
|
||||
protected $parentConfigurator;
|
||||
|
||||
public function __construct(RouteCollection $collection, $route, string $name = '', CollectionConfigurator $parentConfigurator = null, array $prefixes = null)
|
||||
{
|
||||
|
@ -13,64 +13,33 @@ namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\Loader\Configurator\CollectionConfigurator;
|
||||
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait AddTrait
|
||||
{
|
||||
use LocalizedRouteTrait;
|
||||
|
||||
/**
|
||||
* @var RouteCollection
|
||||
*/
|
||||
private $collection;
|
||||
|
||||
private $name = '';
|
||||
|
||||
private $prefixes;
|
||||
protected $collection;
|
||||
protected $name = '';
|
||||
protected $prefixes;
|
||||
|
||||
/**
|
||||
* Adds a route.
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*/
|
||||
final public function add(string $name, $path): RouteConfigurator
|
||||
public function add(string $name, $path): RouteConfigurator
|
||||
{
|
||||
$paths = [];
|
||||
$parentConfigurator = $this instanceof CollectionConfigurator ? $this : ($this instanceof RouteConfigurator ? $this->parentConfigurator : null);
|
||||
$route = $this->createLocalizedRoute($this->collection, $name, $path, $this->name, $this->prefixes);
|
||||
|
||||
if (\is_array($path)) {
|
||||
if (null === $this->prefixes) {
|
||||
$paths = $path;
|
||||
} elseif ($missing = array_diff_key($this->prefixes, $path)) {
|
||||
throw new \LogicException(sprintf('Route "%s" is missing routes for locale(s) "%s".', $name, implode('", "', array_keys($missing))));
|
||||
} else {
|
||||
foreach ($path as $locale => $localePath) {
|
||||
if (!isset($this->prefixes[$locale])) {
|
||||
throw new \LogicException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
|
||||
}
|
||||
|
||||
$paths[$locale] = $this->prefixes[$locale].$localePath;
|
||||
}
|
||||
}
|
||||
} elseif (null !== $this->prefixes) {
|
||||
foreach ($this->prefixes as $locale => $prefix) {
|
||||
$paths[$locale] = $prefix.$path;
|
||||
}
|
||||
} else {
|
||||
$this->collection->add($this->name.$name, $route = $this->createRoute($path));
|
||||
|
||||
return new RouteConfigurator($this->collection, $route, $this->name, $parentConfigurator, $this->prefixes);
|
||||
}
|
||||
|
||||
$routes = new RouteCollection();
|
||||
|
||||
foreach ($paths as $locale => $path) {
|
||||
$routes->add($name.'.'.$locale, $route = $this->createRoute($path));
|
||||
$this->collection->add($this->name.$name.'.'.$locale, $route);
|
||||
$route->setDefault('_locale', $locale);
|
||||
$route->setDefault('_canonical_route', $this->name.$name);
|
||||
}
|
||||
|
||||
return new RouteConfigurator($this->collection, $routes, $this->name, $parentConfigurator, $this->prefixes);
|
||||
return new RouteConfigurator($this->collection, $route, $this->name, $parentConfigurator, $this->prefixes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,13 +47,8 @@ trait AddTrait
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*/
|
||||
final public function __invoke(string $name, $path): RouteConfigurator
|
||||
public function __invoke(string $name, $path): RouteConfigurator
|
||||
{
|
||||
return $this->add($name, $path);
|
||||
}
|
||||
|
||||
private function createRoute(string $path): Route
|
||||
{
|
||||
return new Route($path);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
trait LocalizedRouteTrait
|
||||
{
|
||||
/**
|
||||
* Creates one or many routes.
|
||||
*
|
||||
* @param string|array $path the path, or the localized paths of the route
|
||||
*
|
||||
* @return Route|RouteCollection
|
||||
*/
|
||||
final protected function createLocalizedRoute(RouteCollection $collection, string $name, $path, string $namePrefix = '', array $prefixes = null)
|
||||
{
|
||||
$paths = [];
|
||||
|
||||
if (\is_array($path)) {
|
||||
if (null === $prefixes) {
|
||||
$paths = $path;
|
||||
} elseif ($missing = array_diff_key($prefixes, $path)) {
|
||||
throw new \LogicException(sprintf('Route "%s" is missing routes for locale(s) "%s".', $name, implode('", "', array_keys($missing))));
|
||||
} else {
|
||||
foreach ($path as $locale => $localePath) {
|
||||
if (!isset($prefixes[$locale])) {
|
||||
throw new \LogicException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
|
||||
}
|
||||
|
||||
$paths[$locale] = $prefixes[$locale].$localePath;
|
||||
}
|
||||
}
|
||||
} elseif (null !== $prefixes) {
|
||||
foreach ($prefixes as $locale => $prefix) {
|
||||
$paths[$locale] = $prefix.$path;
|
||||
}
|
||||
} else {
|
||||
$collection->add($namePrefix.$name, $route = $this->createRoute($path));
|
||||
|
||||
return $route;
|
||||
}
|
||||
|
||||
$routes = new RouteCollection();
|
||||
|
||||
foreach ($paths as $locale => $path) {
|
||||
$routes->add($name.'.'.$locale, $route = $this->createRoute($path));
|
||||
$collection->add($namePrefix.$name.'.'.$locale, $route);
|
||||
$route->setDefault('_locale', $locale);
|
||||
$route->setDefault('_canonical_route', $namePrefix.$name);
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
|
||||
private function createRoute(string $path): Route
|
||||
{
|
||||
return new Route($path);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait PrefixTrait
|
||||
{
|
||||
final protected function addPrefix(RouteCollection $routes, $prefix, bool $trailingSlashOnRoot)
|
||||
{
|
||||
if (\is_array($prefix)) {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$prefix[$locale] = trim(trim($localePrefix), '/');
|
||||
}
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
if (null === $locale = $route->getDefault('_locale')) {
|
||||
$routes->remove($name);
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$routes->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($prefix[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
|
||||
} else {
|
||||
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$routes->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($routes->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ trait RouteTrait
|
||||
/**
|
||||
* @var RouteCollection|Route
|
||||
*/
|
||||
private $route;
|
||||
protected $route;
|
||||
|
||||
/**
|
||||
* Adds defaults.
|
||||
|
@ -22,6 +22,8 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
* The file must return a RouteCollection instance.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Nicolas grekas <p@tchwork.com>
|
||||
* @author Jules Pietri <jules@heahprod.com>
|
||||
*/
|
||||
class PhpFileLoader extends FileLoader
|
||||
{
|
||||
@ -47,8 +49,7 @@ class PhpFileLoader extends FileLoader
|
||||
$result = $load($path);
|
||||
|
||||
if (\is_object($result) && \is_callable($result)) {
|
||||
$collection = new RouteCollection();
|
||||
$result(new RoutingConfigurator($collection, $this, $path, $file));
|
||||
$collection = $this->callConfigurator($result, $path, $file);
|
||||
} else {
|
||||
$collection = $result;
|
||||
}
|
||||
@ -65,6 +66,15 @@ class PhpFileLoader extends FileLoader
|
||||
{
|
||||
return \is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'php' === $type);
|
||||
}
|
||||
|
||||
protected function callConfigurator(callable $result, string $path, string $file): RouteCollection
|
||||
{
|
||||
$collection = new RouteCollection();
|
||||
|
||||
$result(new RoutingConfigurator($collection, $this, $path, $file));
|
||||
|
||||
return $collection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,7 +14,8 @@ namespace Symfony\Component\Routing\Loader;
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Config\Util\XmlUtils;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
@ -25,6 +26,9 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
*/
|
||||
class XmlFileLoader extends FileLoader
|
||||
{
|
||||
use LocalizedRouteTrait;
|
||||
use PrefixTrait;
|
||||
|
||||
const NAMESPACE_URI = 'http://symfony.com/schema/routing';
|
||||
const SCHEME_PATH = '/schema/routing/routing-1.0.xsd';
|
||||
|
||||
@ -98,41 +102,40 @@ class XmlFileLoader extends FileLoader
|
||||
/**
|
||||
* Parses a route and adds it to the RouteCollection.
|
||||
*
|
||||
* @param \DOMElement $node Element to parse that represents a Route
|
||||
* @param string $path Full path of the XML file being processed
|
||||
* @param \DOMElement $node Element to parse that represents a Route
|
||||
* @param string $filepath Full path of the XML file being processed
|
||||
*
|
||||
* @throws \InvalidArgumentException When the XML is invalid
|
||||
*/
|
||||
protected function parseRoute(RouteCollection $collection, \DOMElement $node, string $path)
|
||||
protected function parseRoute(RouteCollection $collection, \DOMElement $node, string $filepath)
|
||||
{
|
||||
if ('' === $id = $node->getAttribute('id')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" attribute.', $path));
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" attribute.', $filepath));
|
||||
}
|
||||
|
||||
$schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
|
||||
$methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
list($defaults, $requirements, $options, $condition, $paths) = $this->parseConfigs($node, $path);
|
||||
list($defaults, $requirements, $options, $condition, $paths) = $this->parseConfigs($node, $filepath);
|
||||
|
||||
if (!$paths && '' === $node->getAttribute('path')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have a "path" attribute or <path> child nodes.', $path));
|
||||
$path = $node->getAttribute('path');
|
||||
|
||||
if (!$paths && '' === $path) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have a "path" attribute or <path> child nodes.', $filepath));
|
||||
}
|
||||
|
||||
if ($paths && '' !== $node->getAttribute('path')) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "path" attribute and <path> child nodes.', $path));
|
||||
if ($paths && '' !== $path) {
|
||||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "path" attribute and <path> child nodes.', $filepath));
|
||||
}
|
||||
|
||||
if (!$paths) {
|
||||
$route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
|
||||
$collection->add($id, $route);
|
||||
} else {
|
||||
foreach ($paths as $locale => $p) {
|
||||
$defaults['_locale'] = $locale;
|
||||
$defaults['_canonical_route'] = $id;
|
||||
$route = new Route($p, $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
|
||||
$collection->add($id.'.'.$locale, $route);
|
||||
}
|
||||
}
|
||||
$route = $this->createLocalizedRoute($collection, $id, $paths ?: $path);
|
||||
$route->addDefaults($defaults);
|
||||
$route->addRequirements($requirements);
|
||||
$route->addOptions($options);
|
||||
$route->setHost($node->getAttribute('host'));
|
||||
$route->setSchemes($schemes);
|
||||
$route->setMethods($methods);
|
||||
$route->setCondition($condition);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,6 +159,7 @@ class XmlFileLoader extends FileLoader
|
||||
$schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
|
||||
$methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
|
||||
$trailingSlashOnRoot = $node->hasAttribute('trailing-slash-on-root') ? XmlUtils::phpize($node->getAttribute('trailing-slash-on-root')) : true;
|
||||
$namePrefix = $node->getAttribute('name-prefix') ?: null;
|
||||
|
||||
list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes) = $this->parseConfigs($node, $path);
|
||||
|
||||
@ -187,39 +191,7 @@ class XmlFileLoader extends FileLoader
|
||||
}
|
||||
|
||||
foreach ($imported as $subCollection) {
|
||||
/* @var $subCollection RouteCollection */
|
||||
if ('' !== $prefix || !$prefixes) {
|
||||
$subCollection->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($subCollection->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($prefixes as $locale => $localePrefix) {
|
||||
$prefixes[$locale] = trim(trim($localePrefix), '/');
|
||||
}
|
||||
foreach ($subCollection->all() as $name => $route) {
|
||||
if (null === $locale = $route->getDefault('_locale')) {
|
||||
$subCollection->remove($name);
|
||||
foreach ($prefixes as $locale => $localePrefix) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$subCollection->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($prefixes[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $path));
|
||||
} else {
|
||||
$route->setPath($prefixes[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$subCollection->add($name, $route);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->addPrefix($subCollection, $prefixes ?: $prefix, $trailingSlashOnRoot);
|
||||
|
||||
if (null !== $host) {
|
||||
$subCollection->setHost($host);
|
||||
@ -233,14 +205,13 @@ class XmlFileLoader extends FileLoader
|
||||
if (null !== $methods) {
|
||||
$subCollection->setMethods($methods);
|
||||
}
|
||||
if (null !== $namePrefix) {
|
||||
$subCollection->addNamePrefix($namePrefix);
|
||||
}
|
||||
$subCollection->addDefaults($defaults);
|
||||
$subCollection->addRequirements($requirements);
|
||||
$subCollection->addOptions($options);
|
||||
|
||||
if ($namePrefix = $node->getAttribute('name-prefix')) {
|
||||
$subCollection->addNamePrefix($namePrefix);
|
||||
}
|
||||
|
||||
$collection->addCollection($subCollection);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ namespace Symfony\Component\Routing\Loader;
|
||||
|
||||
use Symfony\Component\Config\Loader\FileLoader;
|
||||
use Symfony\Component\Config\Resource\FileResource;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
|
||||
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
use Symfony\Component\Yaml\Exception\ParseException;
|
||||
use Symfony\Component\Yaml\Parser as YamlParser;
|
||||
@ -27,6 +28,9 @@ use Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class YamlFileLoader extends FileLoader
|
||||
{
|
||||
use LocalizedRouteTrait;
|
||||
use PrefixTrait;
|
||||
|
||||
private static $availableKeys = [
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root', 'locale', 'format', 'utf8', 'exclude',
|
||||
];
|
||||
@ -110,10 +114,6 @@ class YamlFileLoader extends FileLoader
|
||||
$defaults = isset($config['defaults']) ? $config['defaults'] : [];
|
||||
$requirements = isset($config['requirements']) ? $config['requirements'] : [];
|
||||
$options = isset($config['options']) ? $config['options'] : [];
|
||||
$host = isset($config['host']) ? $config['host'] : '';
|
||||
$schemes = isset($config['schemes']) ? $config['schemes'] : [];
|
||||
$methods = isset($config['methods']) ? $config['methods'] : [];
|
||||
$condition = isset($config['condition']) ? $config['condition'] : null;
|
||||
|
||||
foreach ($requirements as $placeholder => $requirement) {
|
||||
if (\is_int($placeholder)) {
|
||||
@ -134,20 +134,14 @@ class YamlFileLoader extends FileLoader
|
||||
$options['utf8'] = $config['utf8'];
|
||||
}
|
||||
|
||||
if (\is_array($config['path'])) {
|
||||
$route = new Route('', $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
|
||||
|
||||
foreach ($config['path'] as $locale => $path) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setPath($path);
|
||||
$collection->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} else {
|
||||
$route = new Route($config['path'], $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
|
||||
$collection->add($name, $route);
|
||||
}
|
||||
$route = $this->createLocalizedRoute($collection, $name, $config['path']);
|
||||
$route->addDefaults($defaults);
|
||||
$route->addRequirements($requirements);
|
||||
$route->addOptions($options);
|
||||
$route->setHost($config['host'] ?? '');
|
||||
$route->setSchemes($config['schemes'] ?? []);
|
||||
$route->setMethods($config['methods'] ?? []);
|
||||
$route->setCondition($config['condition'] ?? null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,6 +163,7 @@ class YamlFileLoader extends FileLoader
|
||||
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
|
||||
$methods = isset($config['methods']) ? $config['methods'] : null;
|
||||
$trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
|
||||
$namePrefix = $config['name_prefix'] ?? '';
|
||||
$exclude = $config['exclude'] ?? null;
|
||||
|
||||
if (isset($config['controller'])) {
|
||||
@ -186,6 +181,7 @@ class YamlFileLoader extends FileLoader
|
||||
|
||||
$this->setCurrentDir(\dirname($path));
|
||||
|
||||
/** @var RouteCollection[] $imported */
|
||||
$imported = $this->import($config['resource'], $type, false, $file, $exclude) ?: [];
|
||||
|
||||
if (!\is_array($imported)) {
|
||||
@ -193,39 +189,7 @@ class YamlFileLoader extends FileLoader
|
||||
}
|
||||
|
||||
foreach ($imported as $subCollection) {
|
||||
/* @var $subCollection RouteCollection */
|
||||
if (!\is_array($prefix)) {
|
||||
$subCollection->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($subCollection->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$prefix[$locale] = trim(trim($localePrefix), '/');
|
||||
}
|
||||
foreach ($subCollection->all() as $name => $route) {
|
||||
if (null === $locale = $route->getDefault('_locale')) {
|
||||
$subCollection->remove($name);
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$subCollection->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($prefix[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $file));
|
||||
} else {
|
||||
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$subCollection->add($name, $route);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->addPrefix($subCollection, $prefix, $trailingSlashOnRoot);
|
||||
|
||||
if (null !== $host) {
|
||||
$subCollection->setHost($host);
|
||||
@ -239,14 +203,13 @@ class YamlFileLoader extends FileLoader
|
||||
if (null !== $methods) {
|
||||
$subCollection->setMethods($methods);
|
||||
}
|
||||
if (null !== $namePrefix) {
|
||||
$subCollection->addNamePrefix($namePrefix);
|
||||
}
|
||||
$subCollection->addDefaults($defaults);
|
||||
$subCollection->addRequirements($requirements);
|
||||
$subCollection->addOptions($options);
|
||||
|
||||
if (isset($config['name_prefix'])) {
|
||||
$subCollection->addNamePrefix($config['name_prefix']);
|
||||
}
|
||||
|
||||
$collection->addCollection($subCollection);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user