[CORE][ROUTES] Update urlmapper to search dynamic routes before static ones when generating URLs.

This solves the problem of routes that differ only in having
or not $_GET params. The ones not having params (static) were
being matched first during URL generation.

The way this problem was solved was by separating the $reverse
array in both $reverse_statics and $reverse_dynamics and explicitly
traversing this last one first in the generation function. Note that
maintaining the $reverse array and unshifting dynamic routes to its
head ( and therefore to the front of the static ones ) doesn't work
since even among dynamic routes the order of arrival should be kept.
This commit is contained in:
tenma 2019-08-19 22:43:50 +01:00 committed by Diogo Peralta Cordeiro
parent 06fb856d24
commit 23512bea14

View File

@ -57,7 +57,8 @@ class URLMapper
protected $statics = []; protected $statics = [];
protected $variables = []; protected $variables = [];
protected $reverse = []; protected $reverse_dynamics = [];
protected $reverse_statics = [];
protected $allpaths = []; protected $allpaths = [];
/** /**
@ -97,10 +98,10 @@ class URLMapper
if (empty($paramNames)) { if (empty($paramNames)) {
$this->statics[$path] = $args; $this->statics[$path] = $args;
if (array_key_exists($action, $this->reverse)) { if (array_key_exists($action, $this->reverse_statics)) {
$this->reverse[$action][] = [$args, $path]; $this->reverse_statics[$action][] = [$args, $path];
} else { } else {
$this->reverse[$action] = [[$args, $path]]; $this->reverse_statics[$action] = [[$args, $path]];
} }
} else { } else {
// fix for the code that still make improper use of this function's params // fix for the code that still make improper use of this function's params
@ -115,7 +116,6 @@ class URLMapper
// $variables is used for path matching, so we can't store invalid routes // $variables is used for path matching, so we can't store invalid routes
if ($should) { if ($should) {
$regex = self::makeRegex($path, $paramPatterns); $regex = self::makeRegex($path, $paramPatterns);
if (isset($this->variables[$regex]) || !$acceptHeaders) { if (isset($this->variables[$regex]) || !$acceptHeaders) {
$this->variables[$regex] = [$args, $paramNames]; $this->variables[$regex] = [$args, $paramNames];
} else { } else {
@ -128,10 +128,10 @@ class URLMapper
$format = $this->makeFormat($path); $format = $this->makeFormat($path);
if (array_key_exists($action, $this->reverse)) { if (array_key_exists($action, $this->reverse_dynamics)) {
$this->reverse[$action][] = [$args, $format, $paramNames]; $this->reverse_dynamics[$action][] = [$args, $format, $paramNames];
} else { } else {
$this->reverse[$action] = [[$args, $format, $paramNames]]; $this->reverse_dynamics[$action] = [[$args, $format, $paramNames]];
} }
} }
} }
@ -164,48 +164,58 @@ class URLMapper
$action = $args[self::ACTION]; $action = $args[self::ACTION];
if (!array_key_exists($action, $this->reverse)) { if (!array_key_exists($action, $this->reverse_dynamics) && !array_key_exists($action, $this->reverse_statics)) {
throw new Exception(sprintf('No candidate paths for action "%s"', $action)); throw new Exception(sprintf('No candidate paths for action "%s"', $action));
} }
$candidates = $this->reverse[$action]; $candidates = $this->reverse_dynamics[$action];
foreach ($candidates as $candidate) { foreach ($candidates as $candidate) {
if (count($candidate) == 2) { // static list($tryArgs, $format, $paramNames) = $candidate;
list($tryArgs, $tryPath) = $candidate;
foreach ($tryArgs as $key => $value) { foreach ($tryArgs as $key => $value) {
if (!array_key_exists($key, $args) || $args[$key] != $value) { if (!array_key_exists($key, $args) || $args[$key] != $value) {
// next candidate // next candidate
continue 2; continue 2;
}
} }
// success
$path = $tryPath;
} else {
list($tryArgs, $format, $paramNames) = $candidate;
foreach ($tryArgs as $key => $value) {
if (!array_key_exists($key, $args) || $args[$key] != $value) {
// next candidate
continue 2;
}
}
// success
$toFormat = [];
foreach ($paramNames as $name) {
if (!array_key_exists($name, $args)) {
// next candidate
continue 2;
}
$toFormat[] = $args[$name];
}
$path = vsprintf($format, $toFormat);
} }
// success
$toFormat = [];
foreach ($paramNames as $name) {
if (!array_key_exists($name, $args)) {
// next candidate
continue 2;
}
$toFormat[] = $args[$name];
}
$path = vsprintf($format, $toFormat);
if (!empty($qstring)) {
$formatted = http_build_query($qstring);
$path .= '?' . $formatted;
}
return $path;
}
$candidates = $this->reverse_statics[$action];
foreach ($candidates as $candidate) {
list($tryArgs, $tryPath) = $candidate;
foreach ($tryArgs as $key => $value) {
if (!array_key_exists($key, $args) || $args[$key] != $value) {
// next candidate
continue 2;
}
}
// success
$path = $tryPath;
if (!empty($qstring)) { if (!empty($qstring)) {
$formatted = http_build_query($qstring); $formatted = http_build_query($qstring);
$path .= '?' . $formatted; $path .= '?' . $formatted;