|
2 | 2 |
|
3 | 3 | namespace Illuminate\Routing;
|
4 | 4 |
|
| 5 | +use BackedEnum; |
| 6 | +use Illuminate\Contracts\Routing\UrlRoutable; |
5 | 7 | use Illuminate\Routing\Exceptions\UrlGenerationException;
|
6 | 8 | use Illuminate\Support\Arr;
|
| 9 | +use Illuminate\Support\Collection; |
7 | 10 |
|
8 | 11 | class RouteUrlGenerator
|
9 | 12 | {
|
@@ -74,6 +77,8 @@ public function __construct($url, $request)
|
74 | 77 | */
|
75 | 78 | public function to($route, $parameters = [], $absolute = false)
|
76 | 79 | {
|
| 80 | + $parameters = $this->formatParameters($route, $parameters); |
| 81 | + |
77 | 82 | $domain = $this->getRouteDomain($route, $parameters);
|
78 | 83 |
|
79 | 84 | // First we will construct the entire URI including the root and query string. Once it
|
@@ -167,6 +172,95 @@ protected function addPortToDomain($domain)
|
167 | 172 | : $domain.':'.$port;
|
168 | 173 | }
|
169 | 174 |
|
| 175 | + /** |
| 176 | + * Format the array of route parameters. |
| 177 | + * |
| 178 | + * @param \Illuminate\Routing\Route $route |
| 179 | + * @param mixed $parameters |
| 180 | + * @return array |
| 181 | + */ |
| 182 | + protected function formatParameters(Route $route, $parameters) |
| 183 | + { |
| 184 | + $parameters = Arr::wrap($parameters); |
| 185 | + |
| 186 | + $passedParameterCount = count($parameters); |
| 187 | + |
| 188 | + $namedParameters = []; |
| 189 | + $namedQueryParameters = []; |
| 190 | + $routeParametersWithoutDefaultsOrNamedParameters = []; |
| 191 | + |
| 192 | + $routeParameters = $route->parameterNames(); |
| 193 | + |
| 194 | + foreach ($routeParameters as $name) { |
| 195 | + if (isset($parameters[$name])) { |
| 196 | + // Named parameters don't need any special handling... |
| 197 | + $namedParameters[$name] = $parameters[$name]; |
| 198 | + unset($parameters[$name]); |
| 199 | + |
| 200 | + continue; |
| 201 | + } elseif (! isset($this->defaultParameters[$name])) { |
| 202 | + // No named parameter or default value, try to match to positional parameter below... |
| 203 | + array_push($routeParametersWithoutDefaultsOrNamedParameters, $name); |
| 204 | + } |
| 205 | + |
| 206 | + $namedParameters[$name] = ''; |
| 207 | + } |
| 208 | + |
| 209 | + // Named parameters that don't have route parameters will be used for query string... |
| 210 | + foreach ($parameters as $key => $value) { |
| 211 | + if (is_string($key)) { |
| 212 | + $namedQueryParameters[$key] = $value; |
| 213 | + |
| 214 | + unset($parameters[$key]); |
| 215 | + } |
| 216 | + } |
| 217 | + |
| 218 | + // Match positional parameters to the route parameters that didn't have a value in order... |
| 219 | + if (count($parameters) == count($routeParametersWithoutDefaultsOrNamedParameters)) { |
| 220 | + foreach (array_reverse($routeParametersWithoutDefaultsOrNamedParameters) as $name) { |
| 221 | + if (count($parameters) === 0) { |
| 222 | + break; |
| 223 | + } |
| 224 | + |
| 225 | + $namedParameters[$name] = array_pop($parameters); |
| 226 | + } |
| 227 | + } |
| 228 | + |
| 229 | + // If there are extra parameters, just fill left to right... if not, fill right to left and try to use defaults... |
| 230 | + $extraParameters = $passedParameterCount > count($routeParameters); |
| 231 | + |
| 232 | + foreach ($extraParameters ? $namedParameters : array_reverse($namedParameters) as $key => $value) { |
| 233 | + $bindingField = $route->bindingFieldFor($key); |
| 234 | + |
| 235 | + $defaultParameterKey = $bindingField ? "$key:$bindingField" : $key; |
| 236 | + |
| 237 | + if ($value !== '') { |
| 238 | + continue; |
| 239 | + } elseif (! empty($parameters)) { |
| 240 | + $namedParameters[$key] = $extraParameters ? array_shift($parameters) : array_pop($parameters); |
| 241 | + } elseif (isset($this->defaultParameters[$defaultParameterKey])) { |
| 242 | + $namedParameters[$key] = $this->defaultParameters[$defaultParameterKey]; |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + // Any remaining values in $parameters are unnamed query string parameters... |
| 247 | + $parameters = array_merge($namedParameters, $namedQueryParameters, $parameters); |
| 248 | + |
| 249 | + $parameters = Collection::wrap($parameters)->map(function ($value, $key) use ($route) { |
| 250 | + return $value instanceof UrlRoutable && $route->bindingFieldFor($key) |
| 251 | + ? $value->{$route->bindingFieldFor($key)} |
| 252 | + : $value; |
| 253 | + })->all(); |
| 254 | + |
| 255 | + array_walk_recursive($parameters, function (&$item) { |
| 256 | + if ($item instanceof BackedEnum) { |
| 257 | + $item = $item->value; |
| 258 | + } |
| 259 | + }); |
| 260 | + |
| 261 | + return $this->url->formatParameters($parameters); |
| 262 | + } |
| 263 | + |
170 | 264 | /**
|
171 | 265 | * Replace the parameters on the root path.
|
172 | 266 | *
|
|
0 commit comments