Search code examples
laravellaravel-routing

Laravel 8 - friendly url that call multiple controllers depending on match (products, categories, pages) - How to design it?


i would like to build a route that catch clean seo friendly url and call correct controller to display page. Examples:

https://mypage.com/some-friendly-url-separated-with-dashes [PageController]
https://mypage.com/some-cool-eletronic-ipod [ProductController]
https://mypage.com/some-furniture-drawers [CategoryController]

So I have in app route:

Route::get('/{friendlyUrl}', 'RouteController@index');

Each friendly url is a unique url(string) so there is no duplicate between pages/products/categories. There is also no pattern between urls - they could be any string used in seo(only text plus dashes/ sometimes params).

Is it wise to build one db table that keeps all urls in on place with info what to call ( url | controller_name | action_name) - as an example.

Another question is - how to call different controllers depending on url used? (for above example -> RouteController catch friendly urls -finds match in db table -> then calls correct controller)

Many thanks for any help. Have a nice day

Mark


Solution

  • There's two approaches you can take to this.

    Proactive:

    In web.php

    $slugs = Product::pluck('slug');
    foreach ($slugs as $slug) {
        Route::get($slug, 'ProductController@index');
    }
    
    $slugs = Category::pluck('slug');
    foreach ($slugs as $slug) {
        Route::get($slug, 'CategoryController@index');
    }
    
    $slugs = Page::pluck('slug');
    foreach ($slugs as $slug) {
        Route::get($slug, 'PagesController@index');
    }
    

    Then you can determine the product in the appropriate controler via e.g.

    $actualItem = Product::where('slug', request()->path())->first();
    

    The downside to this approach is that all routes are registered on every request even if they are not used meaning you hit the database on every request to populate them. Also, routes can't be cached when using this approach.

    Reactive:

    In this approach you use the fallback route:

    In web.php:

    Route::fallback(function (Request $request) {
       if (Page::where('slug', $request->path())->exists()) {
            return app()->call([ PageController::class, 'index' ]);
       }
       if (Category::where('slug', $request->path())->exists()) {
            return app()->call([ CategoryController::class, 'index' ]);
       }
       if (Product::where('slug', $request->path())->exists()) {
            return app()->call([ ProductController::class, 'index' ]);
       }
       abort(404);
    });