feature #12979 [Router] added appending of new optional document fragment (rodnaph)

This PR was merged into the 3.2-dev branch.

Discussion
----------

[Router] added appending of new optional document fragment

added a new optional parameter to the generate method for the document fragment. when specified this is appended to generated urls.

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | none
| License       | MIT
| Doc PR        | none

Commits
-------

6d79a56 [Routing] adds _fragment special option to url generation for document fragment
This commit is contained in:
Fabien Potencier 2016-06-16 18:09:07 +02:00
commit 61e5ddc106
5 changed files with 53 additions and 3 deletions

View File

@ -257,6 +257,13 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
$url = $schemeAuthority.$this->context->getBaseUrl().$url; $url = $schemeAuthority.$this->context->getBaseUrl().$url;
} }
// extract unused parameters
$extra = array_diff_key($parameters, $variables, $defaults);
// extract fragment
$fragment = isset($extra['_fragment']) ? $extra['_fragment'] : '';
unset($extra['_fragment']);
// add a query string if needed // add a query string if needed
$extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) { $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) {
return $a == $b ? 0 : 1; return $a == $b ? 0 : 1;
@ -268,6 +275,10 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
$url .= '?'.strtr($query, array('%2F' => '/')); $url .= '?'.strtr($query, array('%2F' => '/'));
} }
if ('' !== $fragment) {
$url .= '#'.strtr(rawurlencode($fragment), array('%2F' => '/', '%3F' => '?'));
}
return $url; return $url;
} }

View File

@ -69,6 +69,8 @@ interface UrlGeneratorInterface extends RequestContextAwareInterface
* *
* If there is no route with the given name, the generator must throw the RouteNotFoundException. * If there is no route with the given name, the generator must throw the RouteNotFoundException.
* *
* The special parameter _fragment will be used as the document fragment suffixed to the final URL.
*
* @param string $name The name of the route * @param string $name The name of the route
* @param mixed $parameters An array of parameters * @param mixed $parameters An array of parameters
* @param int $referenceType The type of reference to be generated (one of the constants) * @param int $referenceType The type of reference to be generated (one of the constants)

View File

@ -31,9 +31,10 @@ class RouteCompiler implements RouteCompilerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
* @throws \LogicException If a variable is referenced more than once * @throws \InvalidArgumentException If a path variable is named _fragment
* @throws \DomainException If a variable name is numeric because PHP raises an error for such * @throws \LogicException If a variable is referenced more than once
* subpatterns in PCRE and thus would break matching, e.g. "(?P<123>.+)". * @throws \DomainException If a variable name is numeric because PHP raises an error for such
* subpatterns in PCRE and thus would break matching, e.g. "(?P<123>.+)".
*/ */
public static function compile(Route $route) public static function compile(Route $route)
{ {
@ -59,6 +60,13 @@ class RouteCompiler implements RouteCompilerInterface
$staticPrefix = $result['staticPrefix']; $staticPrefix = $result['staticPrefix'];
$pathVariables = $result['variables']; $pathVariables = $result['variables'];
foreach ($pathVariables as $pathParam) {
if ('_fragment' === $pathParam) {
throw new \InvalidArgumentException(sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath()));
}
}
$variables = array_merge($variables, $pathVariables); $variables = array_merge($variables, $pathVariables);
$tokens = $result['tokens']; $tokens = $result['tokens'];

View File

@ -634,6 +634,25 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
); );
} }
public function testFragmentsCanBeAppendedToUrls()
{
$routes = $this->getRoutes('test', new Route('/testing'));
$url = $this->getGenerator($routes)->generate('test', array('_fragment' => 'frag ment'), true);
$this->assertEquals('/app.php/testing#frag%20ment', $url);
$url = $this->getGenerator($routes)->generate('test', array('_fragment' => '0'), true);
$this->assertEquals('/app.php/testing#0', $url);
}
public function testFragmentsDoNotEscapeValidCharacters()
{
$routes = $this->getRoutes('test', new Route('/testing'));
$url = $this->getGenerator($routes)->generate('test', array('_fragment' => '?/'), true);
$this->assertEquals('/app.php/testing#?/', $url);
}
protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null) protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null)
{ {
$context = new RequestContext('/app.php'); $context = new RequestContext('/app.php');

View File

@ -175,6 +175,16 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
$compiled = $route->compile(); $compiled = $route->compile();
} }
/**
* @expectedException \InvalidArgumentException
*/
public function testRouteWithFragmentAsPathParameter()
{
$route = new Route('/{_fragment}');
$compiled = $route->compile();
}
/** /**
* @dataProvider getNumericVariableNames * @dataProvider getNumericVariableNames
* @expectedException \DomainException * @expectedException \DomainException