Using routes
You can define a route like this in your config/routes.php:
return [ '<country:{slug}>/<city:{slug}>/<productType:{slug}>' => ['template' => '_category'], ];
This will match any URL with three slug-like segments in the path and load the specified template. Your template will have access to the named parameters (country, city and productType), but Craft will not check if those are actual categories. That's on you to check in the template, for example:
{% set countryCategory = craft.categories().slug(country).one() %} {% set cityCategory = craft.categories().slug(city).ancestorOf(countryCategory).one() %} {% set productTypeCategory = craft.categories().slug(productType).one() %} {% if not countryCategory and not cityCategory and not productTypeCategory %} {% exit 404 %} {% endif %}
To support URLs with only one or two segments in the path, you have two options:
- Define two additional URL rules similar to this one with only one and two segments, respectively.
- Use Default Parameter Values in the route definition (I never used that, but it should work).
Using URI formats (Original answer)
This only works if the categories are related and each category has a unique URL, see comments.
You don't need a custom route, you can do this with the Entry URI format (or Category URI format for categories) setting of the category group. The entry URI template can be a full Twig template and has access to all fields on the entry. For example, if the first category group has a field secondCategory you can pull the slug their slug in like this:
{{ object.parent.uri ?? '' }}/{{ object.slug }}/{{ object.secondCategory.one().slug ?? '' }}
{{ object.parent.uri ?? '' }} adds the parent's URI to the current categories URI, this is how you get hierarchical URLs for nested structures.
{{ object.secondCategory.one().slug ?? '' }} adds the slug of the second category related to the first category as the last URL segment.