[Routing] Implement i18n routing

This commit is contained in:
Frank de Jonge 2018-02-11 10:36:32 +01:00 committed by Nicolas Grekas
parent a1b241473d
commit e32c414b04
54 changed files with 1121 additions and 247 deletions

View File

@ -22,6 +22,7 @@ namespace Symfony\Component\Routing\Annotation;
class Route
{
private $path;
private $locales = array();
private $name;
private $requirements = array();
private $options = array();
@ -38,11 +39,20 @@ class Route
*/
public function __construct(array $data)
{
if (isset($data['locales'])) {
throw new \BadMethodCallException(sprintf('Unknown property "locales" on annotation "%s".', get_class($this)));
}
if (isset($data['value'])) {
$data['path'] = $data['value'];
$data[is_array($data['value']) ? 'locales' : 'path'] = $data['value'];
unset($data['value']);
}
if (isset($data['path']) && is_array($data['path'])) {
$data['locales'] = $data['path'];
unset($data['path']);
}
foreach ($data as $key => $value) {
$method = 'set'.str_replace('_', '', $key);
if (!method_exists($this, $method)) {
@ -62,6 +72,16 @@ class Route
return $this->path;
}
public function setLocales(array $locales)
{
$this->locales = $locales;
}
public function getLocales(): array
{
return $this->locales;
}
public function setHost($pattern)
{
$this->host = $pattern;

View File

@ -54,11 +54,13 @@ use Psr\Log\LoggerInterface;
class {$options['class']} extends {$options['base_class']}
{
private static \$declaredRoutes;
private \$defaultLocale;
public function __construct(RequestContext \$context, LoggerInterface \$logger = null)
public function __construct(RequestContext \$context, LoggerInterface \$logger = null, string \$defaultLocale = null)
{
\$this->context = \$context;
\$this->logger = \$logger;
\$this->defaultLocale = \$defaultLocale;
if (null === self::\$declaredRoutes) {
self::\$declaredRoutes = {$this->generateDeclaredRoutes()};
}
@ -107,7 +109,14 @@ EOF;
return <<<'EOF'
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
{
if (!isset(self::$declaredRoutes[$name])) {
$locale = $parameters['_locale']
?? $this->context->getParameter('_locale')
?: $this->defaultLocale;
if (null !== $locale && isset(self::$declaredRoutes[$name.'.'.$locale])) {
unset($parameters['_locale']);
$name = $name.'.'.$locale;
} elseif (!isset(self::$declaredRoutes[$name])) {
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
}

View File

@ -37,6 +37,8 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
protected $logger;
private $defaultLocale;
/**
* This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL.
*
@ -65,11 +67,12 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
'%7C' => '|',
);
public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null)
public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null, string $defaultLocale = null)
{
$this->routes = $routes;
$this->context = $context;
$this->logger = $logger;
$this->defaultLocale = $defaultLocale;
}
/**
@ -109,7 +112,13 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
*/
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
{
if (null === $route = $this->routes->get($name)) {
$locale = $parameters['_locale']
?? $this->context->getParameter('_locale')
?: $this->defaultLocale;
if (null !== $locale && null !== $route = $this->routes->get($name.'.'.$locale)) {
unset($parameters['_locale']);
} elseif (null === $route = $this->routes->get($name)) {
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
}

View File

@ -13,6 +13,7 @@ namespace Symfony\Component\Routing\Loader;
use Doctrine\Common\Annotations\Reader;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Config\Loader\LoaderInterface;
@ -119,9 +120,11 @@ abstract class AnnotationClassLoader implements LoaderInterface
}
}
/** @var $annot RouteAnnotation */
if (0 === $collection->count() && $class->hasMethod('__invoke') && $annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) {
$globals['path'] = '';
$globals['path'] = null;
$globals['name'] = '';
$globals['locales'] = array();
$this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke'));
}
@ -137,11 +140,6 @@ abstract class AnnotationClassLoader implements LoaderInterface
$name = $globals['name'].$name;
$defaults = array_replace($globals['defaults'], $annot->getDefaults());
foreach ($method->getParameters() as $param) {
if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) {
$defaults[$param->getName()] = $param->getDefaultValue();
}
}
$requirements = array_replace($globals['requirements'], $annot->getRequirements());
$options = array_replace($globals['options'], $annot->getOptions());
$schemes = array_merge($globals['schemes'], $annot->getSchemes());
@ -157,11 +155,56 @@ abstract class AnnotationClassLoader implements LoaderInterface
$condition = $globals['condition'];
}
$route = $this->createRoute($globals['path'].$annot->getPath(), $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
$path = $annot->getLocales() ?: $annot->getPath();
$prefix = $globals['locales'] ?: $globals['path'];
$paths = array();
$this->configureRoute($route, $class, $method, $annot);
if (\is_array($path)) {
if (!\is_array($prefix)) {
foreach ($path as $locale => $localePath) {
$paths[$locale] = $prefix.$localePath;
}
} elseif ($missing = array_diff_key($prefix, $path)) {
throw new \LogicException(sprintf('Route to "%s" is missing paths for locale(s) "%s".', $class->name.'::'.$method->name, implode('", "', array_keys($missing))));
} else {
foreach ($path as $locale => $localePath) {
if (!isset($prefix[$locale])) {
throw new \LogicException(sprintf('Route to "%s" with locale "%s" is missing a corresponding prefix in class "%s".', $method->name, $locale, $class->name));
}
$collection->add($name, $route);
$paths[$locale] = $prefix[$locale].$localePath;
}
}
} elseif (\is_array($prefix)) {
foreach ($prefix as $locale => $localePrefix) {
$paths[$locale] = $localePrefix.$path;
}
} else {
$paths[] = $prefix.$path;
}
foreach ($method->getParameters() as $param) {
if (isset($defaults[$param->name]) || !$param->isDefaultValueAvailable()) {
continue;
}
foreach ($paths as $locale => $path) {
if (false !== strpos($path, sprintf('{%s}', $param->name))) {
$defaults[$param->name] = $param->getDefaultValue();
break;
}
}
}
foreach ($paths as $locale => $path) {
$route = $this->createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
$this->configureRoute($route, $class, $method, $annot);
if (0 !== $locale) {
$route->setDefault('_locale', $locale);
$collection->add($name.'.'.$locale, $route);
} else {
$collection->add($name, $route);
}
}
}
/**
@ -208,7 +251,8 @@ abstract class AnnotationClassLoader implements LoaderInterface
protected function getGlobals(\ReflectionClass $class)
{
$globals = array(
'path' => '',
'path' => null,
'locales' => array(),
'requirements' => array(),
'options' => array(),
'defaults' => array(),
@ -228,6 +272,8 @@ abstract class AnnotationClassLoader implements LoaderInterface
$globals['path'] = $annot->getPath();
}
$globals['locales'] = $annot->getLocales();
if (null !== $annot->getRequirements()) {
$globals['requirements'] = $annot->getRequirements();
}

View File

@ -24,32 +24,27 @@ class CollectionConfigurator
private $parent;
private $parentConfigurator;
private $parentPrefixes;
public function __construct(RouteCollection $parent, string $name, self $parentConfigurator = null)
public function __construct(RouteCollection $parent, string $name, self $parentConfigurator = null, array $parentPrefixes = null)
{
$this->parent = $parent;
$this->name = $name;
$this->collection = new RouteCollection();
$this->route = new Route('');
$this->parentConfigurator = $parentConfigurator; // for GC control
$this->parentPrefixes = $parentPrefixes;
}
public function __destruct()
{
$this->collection->addPrefix(rtrim($this->route->getPath(), '/'));
if (null === $this->prefixes) {
$this->collection->addPrefix($this->route->getPath());
}
$this->parent->addCollection($this->collection);
}
/**
* Adds a route.
*/
final public function add(string $name, string $path): RouteConfigurator
{
$this->collection->add($this->name.$name, $route = clone $this->route);
return new RouteConfigurator($this->collection, $route->setPath($path), $this->name, $this);
}
/**
* Creates a sub-collection.
*
@ -57,18 +52,44 @@ class CollectionConfigurator
*/
final public function collection($name = '')
{
return new self($this->collection, $this->name.$name, $this);
return new self($this->collection, $this->name.$name, $this, $this->prefixes);
}
/**
* Sets the prefix to add to the path of all child routes.
*
* @param string|array $prefix the prefix, or the localized prefixes
*
* @return $this
*/
final public function prefix(string $prefix)
final public function prefix($prefix)
{
$this->route->setPath($prefix);
if (\is_array($prefix)) {
if (null === $this->parentPrefixes) {
// no-op
} elseif ($missing = array_diff_key($this->parentPrefixes, $prefix)) {
throw new \LogicException(sprintf('Collection "%s" is missing prefixes for locale(s) "%s".', $this->name, implode('", "', array_keys($missing))));
} else {
foreach ($prefix as $locale => $localePrefix) {
if (!isset($this->parentPrefixes[$locale])) {
throw new \LogicException(sprintf('Collection "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $this->name, $locale));
}
$prefix[$locale] = $this->parentPrefixes[$locale].$localePrefix;
}
}
$this->prefixes = $prefix;
$this->route->setPath('/');
} else {
$this->prefixes = null;
$this->route->setPath($prefix);
}
return $this;
}
private function createRoute($path): Route
{
return (clone $this->route)->setPath($path);
}
}

View File

@ -36,11 +36,35 @@ class ImportConfigurator
/**
* Sets the prefix to add to the path of all child routes.
*
* @param string|array $prefix the prefix, or the localized prefixes
*
* @return $this
*/
final public function prefix(string $prefix)
final public function prefix($prefix)
{
$this->route->addPrefix($prefix);
if (!\is_array($prefix)) {
$this->route->addPrefix($prefix);
} 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->setPath($localePrefix.$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].$route->getPath());
$this->route->add($name, $route);
}
}
}
return $this;
}

View File

@ -11,7 +11,6 @@
namespace Symfony\Component\Routing\Loader\Configurator;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
/**
@ -24,11 +23,12 @@ class RouteConfigurator
private $parentConfigurator;
public function __construct(RouteCollection $collection, Route $route, string $name = '', CollectionConfigurator $parentConfigurator = null)
public function __construct(RouteCollection $collection, $route, string $name = '', CollectionConfigurator $parentConfigurator = null, array $prefixes = null)
{
$this->collection = $collection;
$this->route = $route;
$this->name = $name;
$this->parentConfigurator = $parentConfigurator; // for GC control
$this->prefixes = $prefixes;
}
}

View File

@ -11,6 +11,7 @@
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;
@ -24,22 +25,65 @@ trait AddTrait
private $name = '';
private $prefixes;
/**
* Adds a route.
*
* @param string|array $path the path, or the localized paths of the route
*/
final public function add(string $name, string $path): RouteConfigurator
final public function add(string $name, $path): RouteConfigurator
{
$parentConfigurator = $this instanceof RouteConfigurator ? $this->parentConfigurator : null;
$this->collection->add($this->name.$name, $route = new Route($path));
$paths = array();
$parentConfigurator = $this instanceof CollectionConfigurator ? $this : ($this instanceof RouteConfigurator ? $this->parentConfigurator : null);
return new RouteConfigurator($this->collection, $route, '', $parentConfigurator);
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);
}
return new RouteConfigurator($this->collection, $routes, $this->name, $parentConfigurator, $this->prefixes);
}
/**
* Adds a route.
*
* @param string|array $path the path, or the localized paths of the route
*/
final public function __invoke(string $name, string $path): RouteConfigurator
final public function __invoke(string $name, $path): RouteConfigurator
{
return $this->add($name, $path);
}
private function createRoute($path): Route
{
return new Route($path);
}
}

View File

@ -107,17 +107,34 @@ class XmlFileLoader extends FileLoader
*/
protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path)
{
if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('path')) {
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path));
if ('' === $id = $node->getAttribute('id')) {
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" attribute.', $path));
}
$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) = $this->parseConfigs($node, $path);
list($defaults, $requirements, $options, $condition, $paths) = $this->parseConfigs($node, $path);
$route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
$collection->add($id, $route);
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));
}
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) {
$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;
$routeName = $id.'.'.$locale;
$route = new Route($p, $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition);
$collection->add($routeName, $route);
}
}
}
/**
@ -142,13 +159,41 @@ 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;
list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path);
list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes) = $this->parseConfigs($node, $path);
if ('' !== $prefix && $prefixes) {
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "prefix" attribute and <prefix> child nodes.', $path));
}
$this->setCurrentDir(dirname($path));
$subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
/* @var $subCollection RouteCollection */
$subCollection->addPrefix($prefix);
$subCollection = $this->import($resource, ('' !== $type ? $type : null), false, $file);
if ('' !== $prefix || !$prefixes) {
$subCollection->addPrefix($prefix);
} 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.$route->getPath());
$localizedRoute->setDefault('_locale', $locale);
$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].$route->getPath());
$subCollection->add($name, $route);
}
}
}
if (null !== $host) {
$subCollection->setHost($host);
}
@ -204,6 +249,8 @@ class XmlFileLoader extends FileLoader
$requirements = array();
$options = array();
$condition = null;
$prefixes = array();
$paths = array();
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
if ($node !== $n->parentNode) {
@ -211,6 +258,12 @@ class XmlFileLoader extends FileLoader
}
switch ($n->localName) {
case 'path':
$paths[$n->getAttribute('locale')] = trim($n->textContent);
break;
case 'prefix':
$prefixes[$n->getAttribute('locale')] = trim($n->textContent);
break;
case 'default':
if ($this->isElementValueNull($n)) {
$defaults[$n->getAttribute('key')] = null;
@ -243,7 +296,7 @@ class XmlFileLoader extends FileLoader
$defaults['_controller'] = $controller;
}
return array($defaults, $requirements, $options, $condition);
return array($defaults, $requirements, $options, $condition, $paths, $prefixes);
}
/**

View File

@ -120,9 +120,19 @@ class YamlFileLoader extends FileLoader
$defaults['_controller'] = $config['controller'];
}
$route = new Route($config['path'], $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
if (is_array($config['path'])) {
$route = new Route('', $defaults, $requirements, $options, $host, $schemes, $methods, $condition);
$collection->add($name, $route);
foreach ($config['path'] as $locale => $path) {
$localizedRoute = clone $route;
$localizedRoute->setDefault('_locale', $locale);
$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);
}
}
/**
@ -151,9 +161,33 @@ class YamlFileLoader extends FileLoader
$this->setCurrentDir(dirname($path));
/** @var RouteCollection $subCollection */
$subCollection = $this->import($config['resource'], $type, false, $file);
/* @var $subCollection RouteCollection */
$subCollection->addPrefix($prefix);
if (!\is_array($prefix)) {
$subCollection->addPrefix($prefix);
} 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->setPath($localePrefix.$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].$route->getPath());
$subCollection->add($name, $route);
}
}
}
if (null !== $host) {
$subCollection->setHost($host);
}

View File

@ -24,6 +24,14 @@
</xsd:choice>
</xsd:complexType>
<xsd:complexType name="localised-path">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="locale" type="xsd:string" use="required" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:group name="configs">
<xsd:choice>
<xsd:element name="default" nillable="true" type="default" />
@ -34,10 +42,12 @@
</xsd:group>
<xsd:complexType name="route">
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
<xsd:sequence>
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="path" type="localised-path" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" use="required" />
<xsd:attribute name="path" type="xsd:string" use="required" />
<xsd:attribute name="path" type="xsd:string" />
<xsd:attribute name="host" type="xsd:string" />
<xsd:attribute name="schemes" type="xsd:string" />
<xsd:attribute name="methods" type="xsd:string" />
@ -45,8 +55,10 @@
</xsd:complexType>
<xsd:complexType name="import">
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
<xsd:sequence maxOccurs="unbounded" minOccurs="0">
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="prefix" type="localised-path" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="resource" type="xsd:string" use="required" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="prefix" type="xsd:string" />

View File

@ -24,6 +24,14 @@ class RouteTest extends TestCase
$route = new Route(array('foo' => 'bar'));
}
/**
* @expectedException \BadMethodCallException
*/
public function testTryingToSetLocalesDirectly()
{
$route = new Route(array('locales' => array('nl' => 'bar')));
}
/**
* @dataProvider getValidParameters
*/
@ -45,6 +53,7 @@ class RouteTest extends TestCase
array('methods', array('GET', 'POST'), 'getMethods'),
array('host', '{locale}.example.com', 'getHost'),
array('condition', 'context.getMethod() == "GET"', 'getCondition'),
array('value', array('nl' => '/hier', 'en' => '/here'), 'getLocales'),
);
}
}

View File

@ -0,0 +1,7 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
abstract class AbstractClassController
{
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
class ActionPathController
{
/**
* @Route("/path", name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
class DefaultValueController
{
/**
* @Route("/{default}/path", name="action")
*/
public function action($default = 'value')
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
class ExplicitLocalizedActionPathController
{
/**
* @Route(path={"en": "/path", "nl": "/pad"}, name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/here", name="lol")
*/
class InvokableController
{
public function __invoke()
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route(path={"nl": "/hier", "en": "/here"}, name="action")
*/
class InvokableLocalizedController
{
public function __invoke()
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
class LocalizedActionPathController
{
/**
* @Route(path={"en": "/path", "nl": "/pad"}, name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route(path={"en": "/the/path", "nl": "/het/pad"})
*/
class LocalizedMethodActionControllers
{
/**
* @Route(name="post", methods={"POST"})
*/
public function post()
{
}
/**
* @Route(name="put", methods={"PUT"})
*/
public function put()
{
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route(path={"nl": "/nl", "en": "/en"})
*/
class LocalizedPrefixLocalizedActionController
{
/**
* @Route(path={"nl": "/actie", "en": "/action"}, name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route(path={"nl": "/nl"})
*/
class LocalizedPrefixMissingLocaleActionController
{
/**
* @Route(path={"nl": "/actie", "en": "/action"}, name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route(path={"nl": "/nl", "en": "/en"})
*/
class LocalizedPrefixMissingRouteLocaleActionController
{
/**
* @Route(path={"nl": "/actie"}, name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route(path={"en": "/en", "nl": "/nl"})
*/
class LocalizedPrefixWithRouteWithoutLocale
{
/**
* @Route("/suffix", name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/the/path")
*/
class MethodActionControllers
{
/**
* @Route(name="post", methods={"POST"})
*/
public function post()
{
}
/**
* @Route(name="put", methods={"PUT"})
*/
public function put()
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
class MissingRouteNameController
{
/**
* @Route("/path")
*/
public function action()
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
class NothingButNameController
{
/**
* @Route(name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/prefix")
*/
class PrefixedActionLocalizedRouteController
{
/**
* @Route(path={"en": "/path", "nl": "/pad"}, name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/prefix", host="frankdejonge.nl", condition="lol=fun")
*/
class PrefixedActionPathController
{
/**
* @Route("/path", name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/prefix")
*/
class RouteWithPrefixController
{
/**
* @Route("/path", name="action")
*/
public function action()
{
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="localised">
<default key="_controller">MyBundle:Blog:show</default>
<path locale="en">/path</path>
<path locale="fr">/route</path>
</route>
</routes>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing
http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="imported" path="/suffix">
<default key="_controller">MyBundle:Blog:show</default>
</route>
</routes>

View File

@ -0,0 +1,4 @@
---
imported:
controller: ImportedController::someAction
path: /imported

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing
http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="imported">
<default key="_controller">MyBundle:Blog:show</default>
<path locale="en">/suffix</path>
<path locale="fr">/le-suffix</path>
</route>
</routes>

View File

@ -0,0 +1,6 @@
---
imported:
controller: ImportedController::someAction
path:
nl: /voorbeeld
en: /example

View File

@ -0,0 +1,5 @@
---
i_need:
defaults:
_controller: DefaultController::defaultAction
resource: ./localized-route.yml

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing
http://symfony.com/schema/routing/routing-1.0.xsd">
<import resource="./imported-with-locale-but-not-localized.xml">
<prefix locale="fr">/le-prefix</prefix>
<prefix locale="en">/the-prefix</prefix>
</import>
</routes>

View File

@ -0,0 +1,6 @@
---
i_need:
resource: ./imported-with-locale-but-not-localized.yml
prefix:
nl: /nl
en: /en

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing
http://symfony.com/schema/routing/routing-1.0.xsd">
<import resource="./imported-with-locale.xml">
<prefix locale="fr">/le-prefix</prefix>
<prefix locale="en">/the-prefix</prefix>
</import>
</routes>

View File

@ -0,0 +1,6 @@
---
i_need:
resource: ./imported-with-locale.yml
prefix:
nl: /nl
en: /en

View File

@ -0,0 +1,3 @@
---
i_need:
resource: ./localized-route.yml

View File

@ -0,0 +1,9 @@
---
home:
path:
nl: /nl
en: /en
not_localized:
controller: HomeController::otherAction
path: /here

View File

@ -0,0 +1,5 @@
---
importing_with_missing_prefix:
resource: ./localized-route.yml
prefix:
nl: /prefix

View File

@ -0,0 +1,4 @@
---
not_localized:
controller: string
path: /here

View File

@ -0,0 +1,7 @@
---
official:
controller: HomeController::someAction
path:
fr.UTF-8: /omelette-au-fromage
pt-PT: /eu-não-sou-espanhol
pt_BR: /churrasco

View File

@ -0,0 +1,3 @@
---
routename:
controller: Here::here

View File

@ -0,0 +1,17 @@
<?php
namespace Symfony\Component\Routing\Loader\Configurator;
return function (RoutingConfigurator $routes) {
$routes
->collection()
->prefix(array('en' => '/glish'))
->add('foo', '/foo')
->add('bar', array('en' => '/bar'));
$routes
->add('baz', array('en' => '/baz'));
$routes->import('php_dsl_sub_i18n.php')
->prefix(array('fr' => '/ench'));
};

View File

@ -0,0 +1,11 @@
<?php
namespace Symfony\Component\Routing\Loader\Configurator;
return function (RoutingConfigurator $routes) {
$add = $routes->collection('c_')
->prefix('pub');
$add('foo', array('fr' => '/foo'));
$add('bar', array('fr' => '/bar'));
};

View File

@ -84,6 +84,33 @@ class PhpGeneratorDumperTest extends TestCase
$this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter);
}
public function testDumpWithLocalizedRoutes()
{
$this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en'));
$this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_locale', 'nl'));
$code = $this->generatorDumper->dump([
'class' => 'LocalizedProjectUrlGenerator',
]);
file_put_contents($this->testTmpFilepath, $code);
include $this->testTmpFilepath;
$context = new RequestContext('/app.php');
$projectUrlGenerator = new \LocalizedProjectUrlGenerator($context, null, 'en');
$urlWithDefaultLocale = $projectUrlGenerator->generate('test');
$urlWithSpecifiedLocale = $projectUrlGenerator->generate('test', ['_locale' => 'nl']);
$context->setParameter('_locale', 'en');
$urlWithEnglishContext = $projectUrlGenerator->generate('test');
$context->setParameter('_locale', 'nl');
$urlWithDutchContext = $projectUrlGenerator->generate('test');
$this->assertEquals('/app.php/testing/is/fun', $urlWithDefaultLocale);
$this->assertEquals('/app.php/testen/is/leuk', $urlWithSpecifiedLocale);
$this->assertEquals('/app.php/testing/is/fun', $urlWithEnglishContext);
$this->assertEquals('/app.php/testen/is/leuk', $urlWithDutchContext);
}
public function testDumpWithTooManyRoutes()
{
$this->routeCollection->add('Test', new Route('/testing/{foo}'));

View File

@ -11,35 +11,44 @@
namespace Symfony\Component\Routing\Tests\Loader;
use Symfony\Component\Routing\Annotation\Route;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Routing\Loader\AnnotationClassLoader;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\AbstractClassController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\ActionPathController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\DefaultValueController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\ExplicitLocalizedActionPathController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\InvokableController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\InvokableLocalizedController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedActionPathController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedMethodActionControllers;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixLocalizedActionController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixMissingLocaleActionController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixMissingRouteLocaleActionController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\LocalizedPrefixWithRouteWithoutLocale;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\MethodActionControllers;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\MissingRouteNameController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\NothingButNameController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\PrefixedActionLocalizedRouteController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\PrefixedActionPathController;
use Symfony\Component\Routing\Tests\Fixtures\AnnotationFixtures\RouteWithPrefixController;
class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
class AnnotationClassLoaderTest extends TestCase
{
protected $loader;
private $reader;
/**
* @var AnnotationClassLoader
*/
private $loader;
protected function setUp()
{
parent::setUp();
$this->reader = $this->getReader();
$this->loader = $this->getClassLoader($this->reader);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testLoadMissingClass()
{
$this->loader->load('MissingClass');
}
/**
* @expectedException \InvalidArgumentException
*/
public function testLoadAbstractClass()
{
$this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\AbstractClass');
$reader = new AnnotationReader();
$this->loader = new class($reader) extends AnnotationClassLoader {
protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot) {}
};
AnnotationRegistry::registerLoader('class_exists');
}
/**
@ -69,187 +78,144 @@ class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest
$this->assertFalse($this->loader->supports('class', 'foo'), '->supports() checks the resource type if specified');
}
public function getLoadTests()
public function testSimplePathRoute()
{
return array(
array(
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
array('name' => 'route1', 'path' => '/path'),
array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'),
),
array(
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
array('defaults' => array('arg2' => 'foo'), 'requirements' => array('arg3' => '\w+')),
array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'),
),
array(
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
array('options' => array('foo' => 'bar')),
array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'),
),
array(
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
array('schemes' => array('https'), 'methods' => array('GET')),
array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'),
),
array(
'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass',
array('condition' => 'context.getMethod() == "GET"'),
array('arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'),
),
);
$routes = $this->loader->load(ActionPathController::class);
$this->assertCount(1, $routes);
$this->assertEquals('/path', $routes->get('action')->getPath());
}
/**
* @dataProvider getLoadTests
*/
public function testLoad($className, $routeData = array(), $methodArgs = array())
public function testInvokableControllerLoader()
{
$routeData = array_replace(array(
'name' => 'route',
'path' => '/',
'requirements' => array(),
'options' => array(),
'defaults' => array(),
'schemes' => array(),
'methods' => array(),
'condition' => '',
), $routeData);
$this->reader
->expects($this->once())
->method('getMethodAnnotations')
->will($this->returnValue(array($this->getAnnotatedRoute($routeData))))
;
$routeCollection = $this->loader->load($className);
$route = $routeCollection->get($routeData['name']);
$this->assertSame($routeData['path'], $route->getPath(), '->load preserves path annotation');
$this->assertCount(
count($routeData['requirements']),
array_intersect_assoc($routeData['requirements'], $route->getRequirements()),
'->load preserves requirements annotation'
);
$this->assertCount(
count($routeData['options']),
array_intersect_assoc($routeData['options'], $route->getOptions()),
'->load preserves options annotation'
);
$this->assertCount(
count($routeData['defaults']),
$route->getDefaults(),
'->load preserves defaults annotation'
);
$this->assertEquals($routeData['schemes'], $route->getSchemes(), '->load preserves schemes annotation');
$this->assertEquals($routeData['methods'], $route->getMethods(), '->load preserves methods annotation');
$this->assertSame($routeData['condition'], $route->getCondition(), '->load preserves condition annotation');
$routes = $this->loader->load(InvokableController::class);
$this->assertCount(1, $routes);
$this->assertEquals('/here', $routes->get('lol')->getPath());
}
public function testClassRouteLoad()
public function testInvokableLocalizedControllerLoading()
{
$classRouteData = array(
'name' => 'prefix_',
'path' => '/prefix',
'schemes' => array('https'),
'methods' => array('GET'),
);
$methodRouteData = array(
'name' => 'route1',
'path' => '/path',
'schemes' => array('http'),
'methods' => array('POST', 'PUT'),
);
$this->reader
->expects($this->once())
->method('getClassAnnotation')
->will($this->returnValue($this->getAnnotatedRoute($classRouteData)))
;
$this->reader
->expects($this->once())
->method('getMethodAnnotations')
->will($this->returnValue(array($this->getAnnotatedRoute($methodRouteData))))
;
$routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass');
$route = $routeCollection->get($classRouteData['name'].$methodRouteData['name']);
$this->assertSame($classRouteData['path'].$methodRouteData['path'], $route->getPath(), '->load concatenates class and method route path');
$this->assertEquals(array_merge($classRouteData['schemes'], $methodRouteData['schemes']), $route->getSchemes(), '->load merges class and method route schemes');
$this->assertEquals(array_merge($classRouteData['methods'], $methodRouteData['methods']), $route->getMethods(), '->load merges class and method route methods');
$routes = $this->loader->load(InvokableLocalizedController::class);
$this->assertCount(2, $routes);
$this->assertEquals('/here', $routes->get('action.en')->getPath());
$this->assertEquals('/hier', $routes->get('action.nl')->getPath());
}
public function testInvokableClassRouteLoad()
public function testLocalizedPathRoutes()
{
$classRouteData = array(
'name' => 'route1',
'path' => '/',
'schemes' => array('https'),
'methods' => array('GET'),
);
$this->reader
->expects($this->exactly(2))
->method('getClassAnnotation')
->will($this->returnValue($this->getAnnotatedRoute($classRouteData)))
;
$this->reader
->expects($this->once())
->method('getMethodAnnotations')
->will($this->returnValue(array()))
;
$routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass');
$route = $routeCollection->get($classRouteData['name']);
$this->assertSame($classRouteData['path'], $route->getPath(), '->load preserves class route path');
$this->assertEquals(array_merge($classRouteData['schemes'], $classRouteData['schemes']), $route->getSchemes(), '->load preserves class route schemes');
$this->assertEquals(array_merge($classRouteData['methods'], $classRouteData['methods']), $route->getMethods(), '->load preserves class route methods');
$routes = $this->loader->load(LocalizedActionPathController::class);
$this->assertCount(2, $routes);
$this->assertEquals('/path', $routes->get('action.en')->getPath());
$this->assertEquals('/pad', $routes->get('action.nl')->getPath());
}
public function testInvokableClassWithMethodRouteLoad()
public function testLocalizedPathRoutesWithExplicitPathPropety()
{
$classRouteData = array(
'name' => 'route1',
'path' => '/prefix',
'schemes' => array('https'),
'methods' => array('GET'),
);
$methodRouteData = array(
'name' => 'route2',
'path' => '/path',
'schemes' => array('http'),
'methods' => array('POST', 'PUT'),
);
$this->reader
->expects($this->once())
->method('getClassAnnotation')
->will($this->returnValue($this->getAnnotatedRoute($classRouteData)))
;
$this->reader
->expects($this->once())
->method('getMethodAnnotations')
->will($this->returnValue(array($this->getAnnotatedRoute($methodRouteData))))
;
$routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass');
$route = $routeCollection->get($classRouteData['name']);
$this->assertNull($route, '->load ignores class route');
$route = $routeCollection->get($classRouteData['name'].$methodRouteData['name']);
$this->assertSame($classRouteData['path'].$methodRouteData['path'], $route->getPath(), '->load concatenates class and method route path');
$this->assertEquals(array_merge($classRouteData['schemes'], $methodRouteData['schemes']), $route->getSchemes(), '->load merges class and method route schemes');
$this->assertEquals(array_merge($classRouteData['methods'], $methodRouteData['methods']), $route->getMethods(), '->load merges class and method route methods');
$routes = $this->loader->load(ExplicitLocalizedActionPathController::class);
$this->assertCount(2, $routes);
$this->assertEquals('/path', $routes->get('action.en')->getPath());
$this->assertEquals('/pad', $routes->get('action.nl')->getPath());
}
private function getAnnotatedRoute($data)
public function testDefaultValuesForMethods()
{
return new Route($data);
$routes = $this->loader->load(DefaultValueController::class);
$this->assertCount(1, $routes);
$this->assertEquals('/{default}/path', $routes->get('action')->getPath());
$this->assertEquals('value', $routes->get('action')->getDefault('default'));
}
public function testMethodActionControllers()
{
$routes = $this->loader->load(MethodActionControllers::class);
$this->assertCount(2, $routes);
$this->assertEquals('/the/path', $routes->get('put')->getPath());
$this->assertEquals('/the/path', $routes->get('post')->getPath());
}
public function testLocalizedMethodActionControllers()
{
$routes = $this->loader->load(LocalizedMethodActionControllers::class);
$this->assertCount(4, $routes);
$this->assertEquals('/the/path', $routes->get('put.en')->getPath());
$this->assertEquals('/the/path', $routes->get('post.en')->getPath());
}
public function testRouteWithPathWithPrefix()
{
$routes = $this->loader->load(PrefixedActionPathController::class);
$this->assertCount(1, $routes);
$route = $routes->get('action');
$this->assertEquals('/prefix/path', $route->getPath());
$this->assertEquals('lol=fun', $route->getCondition());
$this->assertEquals('frankdejonge.nl', $route->getHost());
}
public function testLocalizedRouteWithPathWithPrefix()
{
$routes = $this->loader->load(PrefixedActionLocalizedRouteController::class);
$this->assertCount(2, $routes);
$this->assertEquals('/prefix/path', $routes->get('action.en')->getPath());
$this->assertEquals('/prefix/pad', $routes->get('action.nl')->getPath());
}
public function testLocalizedPrefixLocalizedRoute()
{
$routes = $this->loader->load(LocalizedPrefixLocalizedActionController::class);
$this->assertCount(2, $routes);
$this->assertEquals('/nl/actie', $routes->get('action.nl')->getPath());
$this->assertEquals('/en/action', $routes->get('action.en')->getPath());
}
public function testMissingPrefixLocale()
{
$this->expectException(\LogicException::class);
$this->loader->load(LocalizedPrefixMissingLocaleActionController::class);
}
public function testMissingRouteLocale()
{
$this->expectException(\LogicException::class);
$this->loader->load(LocalizedPrefixMissingRouteLocaleActionController::class);
}
public function testRouteWithoutName()
{
$routes = $this->loader->load(MissingRouteNameController::class)->all();
$this->assertCount(1, $routes);
$this->assertEquals('/path', reset($routes)->getPath());
}
public function testNothingButName()
{
$routes = $this->loader->load(NothingButNameController::class)->all();
$this->assertCount(1, $routes);
$this->assertEquals('/', reset($routes)->getPath());
}
public function testNonExistingClass()
{
$this->expectException(\LogicException::class);
$this->loader->load('ClassThatDoesNotExist');
}
public function testLoadingAbstractClass()
{
$this->expectException(\LogicException::class);
$this->loader->load(AbstractClassController::class);
}
public function testLocalizedPrefixWithoutRouteLocale()
{
$routes = $this->loader->load(LocalizedPrefixWithRouteWithoutLocale::class);
$this->assertCount(2, $routes);
$this->assertEquals('/en/suffix', $routes->get('action.en')->getPath());
$this->assertEquals('/nl/suffix', $routes->get('action.nl')->getPath());
}
public function testLoadingRouteWithPrefix()
{
$routes = $this->loader->load(RouteWithPrefixController::class);
$this->assertCount(1, $routes);
$this->assertEquals('/prefix/path', $routes->get('action')->getPath());
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Symfony\Component\Routing\Tests\Loader;
use Symfony\Component\Config\FileLocatorInterface;
class FileLocatorStub implements FileLocatorInterface
{
public function locate($name, $currentPath = null, $first = true)
{
if (0 === strpos($name, 'http')) {
return $name;
}
return rtrim($currentPath, '/').'/'.$name;
}
}

View File

@ -117,4 +117,24 @@ class PhpFileLoaderTest extends TestCase
$this->assertEquals($expectedCollection, $routeCollection);
}
public function testRoutingI18nConfigurator()
{
$locator = new FileLocator(array(__DIR__.'/../Fixtures'));
$loader = new PhpFileLoader($locator);
$routeCollection = $loader->load('php_dsl_i18n.php');
$expectedCollection = new RouteCollection();
$expectedCollection->add('foo.en', (new Route('/glish/foo'))->setDefaults(array('_locale' => 'en')));
$expectedCollection->add('bar.en', (new Route('/glish/bar'))->setDefaults(array('_locale' => 'en')));
$expectedCollection->add('baz.en', (new Route('/baz'))->setDefaults(array('_locale' => 'en')));
$expectedCollection->add('c_foo.fr', (new Route('/ench/pub/foo'))->setDefaults(array('_locale' => 'fr')));
$expectedCollection->add('c_bar.fr', (new Route('/ench/pub/bar'))->setDefaults(array('_locale' => 'fr')));
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub_i18n.php')));
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_i18n.php')));
$this->assertEquals($expectedCollection, $routeCollection);
}
}

View File

@ -83,6 +83,45 @@ class XmlFileLoaderTest extends TestCase
}
}
public function testLoadLocalized()
{
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures')));
$routeCollection = $loader->load('localised.xml');
$routes = $routeCollection->all();
$this->assertCount(2, $routes, 'Two routes are loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$this->assertEquals('/route', $routeCollection->get('localised.fr')->getPath());
$this->assertEquals('/path', $routeCollection->get('localised.en')->getPath());
}
public function testLocalisedImports()
{
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routeCollection = $loader->load('importer-with-locale.xml');
$routes = $routeCollection->all();
$this->assertCount(2, $routes, 'Two routes are loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$this->assertEquals('/le-prefix/le-suffix', $routeCollection->get('imported.fr')->getPath());
$this->assertEquals('/the-prefix/suffix', $routeCollection->get('imported.en')->getPath());
}
public function testLocalisedImportsOfNotLocalizedRoutes()
{
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routeCollection = $loader->load('importer-with-locale-imports-non-localized-route.xml');
$routes = $routeCollection->all();
$this->assertCount(2, $routes, 'Two routes are loaded');
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
$this->assertEquals('/le-prefix/suffix', $routeCollection->get('imported.fr')->getPath());
$this->assertEquals('/the-prefix/suffix', $routeCollection->get('imported.en')->getPath());
}
/**
* @expectedException \InvalidArgumentException
* @dataProvider getPathsToInvalidFiles

View File

@ -193,4 +193,86 @@ class YamlFileLoaderTest extends TestCase
$this->assertNotNull($routeCollection->get('api_app_blog'));
$this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath());
}
public function testRemoteSourcesAreNotAccepted()
{
$loader = new YamlFileLoader(new FileLocatorStub());
$this->expectException(\InvalidArgumentException::class);
$loader->load('http://remote.com/here.yml');
}
public function testLoadingLocalizedRoute()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routes = $loader->load('localized-route.yml');
$this->assertCount(3, $routes);
}
public function testImportingRoutesFromDefinition()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routes = $loader->load('importing-localized-route.yml');
$this->assertCount(3, $routes);
$this->assertEquals('/nl', $routes->get('home.nl')->getPath());
$this->assertEquals('/en', $routes->get('home.en')->getPath());
$this->assertEquals('/here', $routes->get('not_localized')->getPath());
}
public function testImportingRoutesWithLocales()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routes = $loader->load('importer-with-locale.yml');
$this->assertCount(2, $routes);
$this->assertEquals('/nl/voorbeeld', $routes->get('imported.nl')->getPath());
$this->assertEquals('/en/example', $routes->get('imported.en')->getPath());
}
public function testImportingNonLocalizedRoutesWithLocales()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routes = $loader->load('importer-with-locale-imports-non-localized-route.yml');
$this->assertCount(2, $routes);
$this->assertEquals('/nl/imported', $routes->get('imported.nl')->getPath());
$this->assertEquals('/en/imported', $routes->get('imported.en')->getPath());
}
public function testImportingRoutesWithOfficialLocales()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routes = $loader->load('officially_formatted_locales.yml');
$this->assertCount(3, $routes);
$this->assertEquals('/omelette-au-fromage', $routes->get('official.fr.UTF-8')->getPath());
$this->assertEquals('/eu-não-sou-espanhol', $routes->get('official.pt-PT')->getPath());
$this->assertEquals('/churrasco', $routes->get('official.pt_BR')->getPath());
}
public function testImportingRoutesFromDefinitionMissingLocalePrefix()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$this->expectException(\InvalidArgumentException::class);
$loader->load('missing-locale-in-importer.yml');
}
public function testImportingRouteWithoutPathOrLocales()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$this->expectException(\InvalidArgumentException::class);
$loader->load('route-without-path-or-locales.yml');
}
public function testImportingWithControllerDefault()
{
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
$routes = $loader->load('importer-with-controller-default.yml');
$this->assertCount(3, $routes);
$this->assertEquals('DefaultController::defaultAction', $routes->get('home.en')->getDefault('_controller'));
$this->assertEquals('DefaultController::defaultAction', $routes->get('home.nl')->getDefault('_controller'));
$this->assertEquals('DefaultController::defaultAction', $routes->get('not_localized')->getDefault('_controller'));
}
}