framework: Route generation broken

  • Laravel Version: 9.17.0
  • PHP Version: 8.1.0
  • Database Driver & Version: MySQL 8.0

Description:

When updating to Laravel > 9.13 route generation breaks. I tested with 9.14, 9.15, 9.16 and 9.17. I think the bug got introduced by #42425

Steps To Reproduce:

Route in web.php:

Route::group([
    'prefix' => 'extranet',
    'as' => 'extranet.',
    'middleware' => 'auth',
], function () {
    Route::get('gallery/{folder:slug}/albums', [GalleryFolderAlbumsController::class, 'index'])->name('gallery-folders.albums.index');
});

Laravel v9.13 (correct behaviour):

dd(route('extranet.gallery-folders.albums.index', $folder)); // -> http://localhost/de/extranet/gallery/aut-neque-dicta-qui-possimus-porro-sit-vel-iusto-voluptatem-quia-ut-excepturi-quaerat-labore-nobis-nesciunt-id-alias-voluptas-voluptatem/albums

Laravel v9.17 (wrong behaviour):

dd(route('extranet.gallery-folders.albums.index', $folder)); // -> http://localhost/de/extranet/gallery/1/albums

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 22 (19 by maintainers)

Most upvoted comments

The only thing that needs to be reset in that case is the bindingFieldFor method of the Route class. I did already revert all the other changes in one of the follow-up PRs. Just as a heads up, the test case for the bug that I initially tried to solve in #42425 will start failing. The test case that reproduces this ticket would look like this

// tests/Routing/RoutingUrlGeneratorTest.php

public function testRoutableInterfaceRoutingWithUrlDefaults()
{
    $url = new UrlGenerator(
        $routes = new RouteCollection,
        Request::create('http://www.foo.com/')
    );

    $url->defaults(['locale' => 'foo']);
    $route = new Route(['GET'], '{locale}/bar/{baz:slug}', ['as' => 'routable']);
    $routes->add($route);

    $model = new RoutableInterfaceStub;
    $model->key = 'routable';

    $this->assertSame('/foo/bar/test-slug', $url->route('routable', $model, false));
    $this->assertSame('/foo/bar/test-slug', $url->route('routable', [$model], false));
    $this->assertSame('/foo/bar/test-slug', $url->route('routable', ['baz' => $model], false));
}

Hey @driesvints I was able to reproduce the issue in this repo:

https://github.com/mateusjunges/laravel-bug-report-42707

Steps to run the project:

  • Clone the repo
  • Create a database and configure your .env
  • Run php artisan migrate --seed
  • Start the server with php artisan serve
  • Open your browser and go to http://localhost:8000

I can reproduce it using laravel v9.18.0, but when using the code from 9.x it is already solved and the URL is generated as expected.

Hope it helps

I found that it works in a fresh installation, but I found out that the problem only occurs when URL::defaults(...) is used (Test Examples both from fresh installation):

    public function test_routing_without_defaults() // passes
    {
        $url = new UrlGenerator(
            $routes = new RouteCollection,
            Request::create('http://www.foo.com/')
        );

        $route = new Route(['GET'], 'posts/{post:slug}', ['as' => 'routable']);
        $routes->add($route);

        $post = Post::create(['title' => 'Test title', 'slug' => 'test-slug']);

        $this->assertSame('/posts/test-slug', $url->route('routable', $post, false));
        $this->assertSame('/posts/test-slug', $url->route('routable', ['post' => $post], false));
        $this->assertSame('/posts/test-slug', $url->route('routable', [$post], false));
    }

    public function test_routing_with_defaults() // does not pass
    {
        $url = new UrlGenerator(
            $routes = new RouteCollection,
            Request::create('http://www.foo.com/')
        );

        $url->defaults(['locale' => 'de']); // URL::defaults(['locale' => 'de']);

        $route = new Route(['GET'], '{locale}/posts/{post:slug}', ['as' => 'routable']);
        $routes->add($route);

        $post = Post::create(['title' => 'Test title', 'slug' => 'test-slug']);

        $this->assertSame('/de/posts/test-slug', $url->route('routable', $post, false));
        $this->assertSame('/de/posts/test-slug', $url->route('routable', ['post' => $post], false));
        $this->assertSame('/de/posts/test-slug', $url->route('routable', [$post], false));
    }

Output of the failing test:

Failed asserting that two strings are identical.
--- Expected
+++ Actual
@@ @@
-'/de/posts/test-slug'
+'/de/posts/1'