Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

elliotk's avatar

Categories, Products and Routing

Ok guys, here's the thing....

I've created a model for Categories which works nicely with Sub Categories (from an Eloquent perspective)

Cat 1
Cat 1 -> Sub 1
Cat 1 -> Sub 1 -> Sub 2
Cat 1 -> Sub 1 -> Sub 2 -> Sub 3

I really don't want to limit the depth unless I have to.

I then have a Product Model, and any given Product can be associated to any level of category, I had in mind that a Product would be placed in the lowest category, and roll up, but I may have it so that it needs to be placed at every level - we'll see.

URL's will look like this (all driven on slugs)

/cat1/sub2/sub3/product-name
/cat3/product-name
/cat1/sub2/product-name

So a few questions

  1. How do you handle N depth of category in the route?
  2. How then do you determine whether a given route is a Product or a Category?

I'm considering some kind of centralised slug management, which allows unique slugs and determines the correct controller to hand off to. Seems complex.

Part of me things I should just do

/cat1
/sub2 (which is really /cat1/sub2)
/sub3/product-name

{category}
{category}/{product}

I just feel, for nested categories, you lose some readability if the categories don't stack in the address bar and tell a story?

I welcome all and any feedback.

0 likes
4 replies
elliotk's avatar

Can anyone provide any guidance on this?

Thanks

martinbean's avatar

How then do you determine whether a given route is a Product or a Category?

@elliotk You can’t. Eloquent isn’t clairevoyant. It doesn’t know if you’re wanting a category or a product. It’s just going to route based on the conditions you give it.

If you want a route that supports multiple levels of slugs, then you have a couple of options. Either have a route that looks up the categories and products matching each “segment” in your URL path, checks the relationship between them, and determines if it’s a category or product being requested.

Alternatively, save the entire path against your categories and products. Have a route that looks the slug up, and displays the matching category/product.

lostdreamer_nl's avatar

Not the nicest looking way, but you could do it with this (add this as your last route):

Route::get('/{categories}', function ($categories) {
    $categories = explode('/', $categories);
    $product = array_pop($categories);

    dd($categories, $product);

})->where('categories', '(.*)');

The regular expression will catch anything, so watch out a bit.

going to example.com/cat1/cat2/cat3/prod1 will output:

// $categories
array:3 [▼
  0 => "cat1"
  1 => "cat2"
  2 => "cat3"
]

// $product
"prod1"

I'm guessing you can continue from there.

1 like
nhalstead's avatar

I know this is an old post but, Honestly, if you are storing the relation between the categories within the same table being a one to many relation for the cats -> subcats, I would store just the immediate parent category slug or id within the URL and skip storing the entire list of cats so you would have subcat/productId. You have to think if it provides any value to have that in the url. In the code you can always reverse that lookup process for its parents.

Of course this depends on your data model but that is my 2 cents.

Please or to participate in this conversation.