Skip to content

URL::defaults() are ignored in UrlGenerator::toRoute() #54796

@stancl

Description

@stancl

Laravel Version

12

PHP Version

any

Database Driver & Version

irrelevant

Description

Given:

  • A route like Route::get('/{foo:slug}/example/{bar}', ...)->name('example');
  • URL::defaults(['foo' => 'some_value']);

route('example', $bar) will produce a "Missing required parameter" exception.

On the other hand, route('example', ['bar' => $bar]) will work fine.

The issue comes from UrlGenerator::toRoute():

public function toRoute($route, $parameters, $absolute)
{
    // $parameters = [$bar]
    $parameters = Collection::wrap($parameters)->map(function ($value, $key) use ($route) {
        // $value = $bar, $key = 0
        //     true since it's a model       && true because field '0' (foo:slug) has a binding field (slug)
        return $value instanceof UrlRoutable && $route->bindingFieldFor($key)
                ? $value->{$route->bindingFieldFor($key)}
                : $value;
    })->all();

    // $parameters = [null because we've incorrectly done $bar->slug]

    array_walk_recursive($parameters, function (&$item) {
        if ($item instanceof BackedEnum) {
            $item = $item->value;
        }
    });

    return $this->routeUrl()->to(
        $route, $this->formatParameters($parameters), $absolute
    );
}

The default parameter only gets used in $this->routeUrl() (RouteUrlGenerator), but by that point the UrlGenerator has broken the provided parameters, passing a [null] array.

I think the solution would be something along the lines of: Actually probably a bit more complex than this because defaults can work for latter parameters too, not just the earliest ones. I'll try to send a PR with some reasonable implementation.

  • If $key is numeric, aka we've passed route('example', $bar) not route('example', ['bar' => $bar])
  • And array_key_exists($route->parameterNames()[$key], $this->getDefaultParameters())
  • Skip the [$key] parameter and move on to the next one
  • (Repeat as many times as needed — in my example there's one parameter with a default value but there can be several)

So in this case $bar should be matched against the second parameter, not the first one.

Steps To Reproduce

See above

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions