[Routing] Allow force-generation of trailing parameters using eg \"/exports/news.{!_format}\"
This commit is contained in:
parent
f2590d196f
commit
9fab3d62ec
@ -156,21 +156,26 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
|||||||
$message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.';
|
$message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.';
|
||||||
foreach ($tokens as $token) {
|
foreach ($tokens as $token) {
|
||||||
if ('variable' === $token[0]) {
|
if ('variable' === $token[0]) {
|
||||||
if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
|
$varName = $token[3];
|
||||||
|
if ($important = ('!' === $varName[0])) {
|
||||||
|
$varName = substr($varName, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$optional || $important || !array_key_exists($varName, $defaults) || (null !== $mergedParams[$varName] && (string) $mergedParams[$varName] !== (string) $defaults[$varName])) {
|
||||||
// check requirement
|
// check requirement
|
||||||
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
|
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$varName])) {
|
||||||
if ($this->strictRequirements) {
|
if ($this->strictRequirements) {
|
||||||
throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]])));
|
throw new InvalidParameterException(strtr($message, array('{parameter}' => $varName, '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$varName])));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->logger) {
|
if ($this->logger) {
|
||||||
$this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]));
|
$this->logger->error($message, array('parameter' => $varName, 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$varName]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = $token[1].$mergedParams[$token[3]].$url;
|
$url = $token[1].$mergedParams[$varName].$url;
|
||||||
$optional = false;
|
$optional = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,7 +111,7 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
|
|
||||||
// Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
|
// Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
|
||||||
// in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
|
// in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
|
||||||
preg_match_all('#\{\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
preg_match_all('#\{!?\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||||
foreach ($matches as $match) {
|
foreach ($matches as $match) {
|
||||||
$varName = substr($match[0][0], 1, -1);
|
$varName = substr($match[0][0], 1, -1);
|
||||||
// get all static text preceding the current variable
|
// get all static text preceding the current variable
|
||||||
@ -184,6 +184,9 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName);
|
$tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName);
|
||||||
|
if ('!' === $varName[0]) {
|
||||||
|
$varName = substr($varName, 1);
|
||||||
|
}
|
||||||
$variables[] = $varName;
|
$variables[] = $varName;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,6 +286,10 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
// Text tokens
|
// Text tokens
|
||||||
return preg_quote($token[1], self::REGEX_DELIMITER);
|
return preg_quote($token[1], self::REGEX_DELIMITER);
|
||||||
} else {
|
} else {
|
||||||
|
if ('variable' === $token[0] && '!' === $token[3][0]) {
|
||||||
|
$token[3] = substr($token[3], 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Variable tokens
|
// Variable tokens
|
||||||
if (0 === $index && 0 === $firstOptional) {
|
if (0 === $index && 0 === $firstOptional) {
|
||||||
// When the only token is an optional variable token, the separator is required
|
// When the only token is an optional variable token, the separator is required
|
||||||
|
@ -397,6 +397,27 @@ class UrlGeneratorTest extends TestCase
|
|||||||
$this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html')));
|
$this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testImportantVariable()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', (new Route('/{page}.{!_format}'))->addDefaults(array('_format' => 'mobile.html')));
|
||||||
|
$generator = $this->getGenerator($routes);
|
||||||
|
|
||||||
|
$this->assertSame('/app.php/index.xml', $generator->generate('test', array('page' => 'index', '_format' => 'xml')));
|
||||||
|
$this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html')));
|
||||||
|
$this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index')));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Routing\Exception\MissingMandatoryParametersException
|
||||||
|
*/
|
||||||
|
public function testImportantVariableWithNoDefault()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/{page}.{!_format}'));
|
||||||
|
$generator = $this->getGenerator($routes);
|
||||||
|
|
||||||
|
$generator->generate('test', array('page' => 'index'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
|
* @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
|
||||||
*/
|
*/
|
||||||
|
@ -177,6 +177,15 @@ class UrlMatcherTest extends TestCase
|
|||||||
$this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar'));
|
$this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testMatchImportantVariable()
|
||||||
|
{
|
||||||
|
$collection = new RouteCollection();
|
||||||
|
$collection->add('index', new Route('/index.{!_format}', array('_format' => 'xml')));
|
||||||
|
|
||||||
|
$matcher = $this->getUrlMatcher($collection);
|
||||||
|
$this->assertEquals(array('_route' => 'index', '_format' => 'xml'), $matcher->match('/index.xml'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
|
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user