-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Route to URL generation / reverse routing #66
Comments
Instead of trying to directly use the regex (which, as you say, doesn't work with options in any case -- this is not described by a single regex) I'd suggest working on the parsed result instead. Something similar to this should do: public function getPath(array $params = array()) {
$routeParser = new RouteParser\Std;
// (Maybe store the parsed form directly)
$routes = $routeParser->parse($this->pattern);
// One route pattern can correspond to multiple routes if it has optional parts
foreach ($routes as $route) {
$url = '';
$paramIdx = 0;
foreach ($route as $part) {
// Fixed segment in the route
if (is_string($part)) {
$url .= $part;
continue;
}
// Placeholder in the route
if ($paramIdx === count($params)) {
throw new LogicException('Not enough parameters given');
}
$url .= $params[$paramIdx++];
}
// If number of params in route matches with number of params given, use that route.
// Otherwise try to find a route that has more params
if ($paramIdx === count($params)) {
return $url;
}
}
throw new LogicException('Too many parameters given');
} Note that with optionals there can be ambiguity as to which URL should be generated. E.g. with If you don't care about optionals, then your code should work fine, or alternative the inner loop of the above snippet should be enough. |
That seems quite easy, I'll give it a shot. What do you think of the idea to make such a function part of this library's API? It can't be a very uncommon use-case. |
I'd appreciate this too :) |
Agree with @anlutro and @fredemmott . The router is really nice, just a thought here - will the fast route will stay as request router or the functionality described above could be implemented in FastRoute someday (I guess, making it a 'response router' also)? |
@sitilge I wouldn't know how to integrate URL generation into the project as it currently is. We'd at least need named routes for that to reasonably work. |
@nikic at chesskid.com we're using fast route with named routes, which makes route generation quite easy. if you're interested in the idea i can send code. |
@lackovic10 Please share |
I created an URL reconstructor, and then I saw this issue with the example code. My version is also checking that the regular expression is matching the named parameter, and it supports providing URL parameters as named or indexed (which works if you mix those). Throwing my 3 cents here, as I did not made the effort to see how I could open a PR for this: private static function replaceRouteParameters(string $route, array $parameters): string
{
$routeDatas = (new Std())->parse($route);
$placeholders = $parameters;
$url = "";
foreach ($routeDatas as $routeData) {
foreach ($routeData as $data) {
if (is_string($data)) {
// This is a string, so nothing to replace inside of it
$url .= $data;
} elseif (is_array($data)) {
// This is an array, so it contains in first the name of the parameter, and in second the regular expression.
// Example, [0 => "name", 1 => "[^/]"]
[$parameterName, $regularExpression] = $data;
$parameterValue = null;
if (isset($placeholders[$parameterName])) {
// If the parameter name is found by its key in the $parameters parameter, we use it
$parameterValue = $placeholders[$parameterName];
// We remove it from the remaining placeholders values
unset($placeholders[$parameterName]);
} elseif (isset($placeholders[0])) {
// Else, we take the first parameter in the $parameters parameter
$parameterValue = $placeholders[0];
// We remove it from the remaining available placeholders values
array_shift($placeholders);
} else {
throw new InvalidArgumentException("parameter $parameterName missing for route $route");
}
// Checking if the value found matches the regular expression of the associated route parameter
$matches = [];
$success = preg_match("/" . str_replace("/", "\/", $regularExpression) . "/", (string) $parameterValue, $matches);
if ($success !== 1 || (isset($matches[0]) && $parameterValue != $matches[0])) {
throw new InvalidArgumentException("parameter $parameterName does not matches regular expression $regularExpression for route $route");
}
$url .= $parameterValue;
}
}
}
return $url;
} It comes from a package I made to provide a standalone router, based on nikic's: folded/routing. If you guys like the idea, I may see in the next weeks how I can integrate it in a PR for this package. |
Previously, I used
FastRoute\RouteParser\Std::VARIABLE_REGEX
withpreg_replace_callback
in order to make a FastRoute-compatible route URL generator, but in 0.6 (more specifically 31fa869) this was changed.Obviously my tests picked it up and I fixed it by wrapping the regex string in
~
and~x
, but I'm now uncertain if that's the correct approach - I guess it won't work properly with the new trailing optional segments.Could a
preg_replace_callback
compatible regex, including necessary flags, be part of the public API for FastRoute? Or, taking it a step further, could a "FastRoute-pattern to URL generator" class/function be part of the package?The text was updated successfully, but these errors were encountered: