mickrus
mickrus

Reputation: 13

Laravel nested routes

I use laravel 7 and store following tree structures in database:

Catalogs table:

category1
--category11
--category12
----category121
----category122
--category13

Articles table:

news
--news1
--news2

Вasic Laravel routes looks like:

Route::get('category/{id}', 'categoryController@show');
Route::get('news/{id}', 'newsController@show');

But in this case "category" url's segment is necessary for each catalog's URL and "news" url's segment is necessary for each new's URL

How can I route the following urls with Laravel Routes:

http://sitename.com/category1
http://sitename.com/category1/category11
http://sitename.com/category1/category12/category121
http://sitename.com/news
http://sitename.com/news/news1

?

Upvotes: 0

Views: 1819

Answers (1)

Martin Bean
Martin Bean

Reputation: 39429

You would need a “catch-all” route (registered after all of your other routes if you want all of the path to be configurable).

You could then explode the path on slashes, check each element is a valid category slug and is also a child of the previous category.

// All other routes...

Route::get('/{category_path}', 'CategoryController@show')->where('category_path', '.*');

You could also do this with a custom route binding:

Route::bind('category_path', function ($path) {
    $slugs = explode('/', $path);

    // Look up all categories and key by slug for easy look-up
    $categories = Category::whereIn('slug', $slugs)->get()->keyBy('slug');

    $parent = null;   

    foreach ($slugs as $slug) {
        $category = $categories->get($slug);

        // Category with slug does not exist
        if (! $category) {
            throw (new ModelNotFoundException)->setModel(Category::class);
        }

        // Check this category is child of previous category
        if ($parent && $category->parent_id != $parent->getKey()) {
            // Throw 404 if this category is not child of previous one
            abort(404);
        }

        // Set $parent to this category for next iteration in loop
        $parent = $category;
    }

    // All categories exist and are in correct hierarchy
    // Return last category as route binding
    return $category;
});

Your category controller would then receive the last category in the path:

class CategoryController extends Controller
{
    public function show(Category $category)
    {
        // Given a URI like /clothing/shoes,
        // $category would be the one with slug = shoes
    }
}

Upvotes: 3

Related Questions