The error here is caused by your use of curly braces ({, }) in your apiResource name parameter, these are not required (check the #nested resource documentation for more info).
So to fix your issue, all you need to do is remove the curly braces.
Route::apiResource('/products', ProductController::class);
Route::apiResource('/products.product-types', ProductTypeController::class);
This will result in standardised routes being produced (use php artisan route:list in your terminal to get an output of your available configured routes by the way).
GET|HEAD products ................................................................... products.index › ProductController@index
POST products ................................................................... products.store › ProductController@store
GET|HEAD products/{product} ........................................................... products.show › ProductController@show
PUT|PATCH products/{product} ....................................................... products.update › ProductController@update
DELETE products/{product} ..................................................... products.destroy › ProductController@destroy
GET|HEAD products/{product}/product-types ......................... products.product-types.index › ProductTypeController@index
POST products/{product}/product-types ......................... products.product-types.store › ProductTypeController@store
GET|HEAD products/{product}/product-types/{product_type} ............ products.product-types.show › ProductTypeController@show
PUT|PATCH products/{product}/product-types/{product_type} ........ products.product-types.update › ProductTypeController@update
DELETE products/{product}/product-types/{product_type} ...... products.product-types.destroy › ProductTypeController@destroy
Update
If your desired route structure is /products/{category}/{sub_category}/{id} then the solution would be to not use the apiResource helper (as this generates standard/conventional REST URI structures) and instead define the route manually.
Route::group(['prefix' => '/products/{category?}/{sub_category?}/{id?}'], function () {
Route::get('/', [DemoController::class, 'index']);
// define other HTTP verb (POST, PATCH, DELETE etc.) routes
});
The above makes each of the parameters optional by using the ? operator.
Then for your index (or whatever you want to name it) method:
public function index(Request $request, $category = null, $sub_category = null, $id = null)
{
// do something
}
You would need to perform sanity checks on the incoming parameters in order to return the correct data.
Alternatively, you could split the routes into groups and use individual controllers.
Route::group(['prefix' => '/categories'], function() {
Route::get('/', [CategoryController::class, 'index']);
Route::group(['prefix' => '/{type}'], function () {
Route::get('/', [ProductTypeController::class, 'show']);
Route::group(['prefix' => '/{sub_type}'], function () {
Route::get('/', [ProductTypeController::class, 'show']);
Route::get('/{id}', [ProductController::class, 'show']);
});
});
});
The above results in the following route definitions:
GET|HEAD categories ......................... CategoryController@index
GET|HEAD categories/{type} ................ ProductTypeController@show
GET|HEAD categories/{type}/{sub_type} ..... ProductTypeController@show
GET|HEAD categories/{type}/{sub_type}/{id} .... ProductController@show
You should then be able to do things like:
/categories
/categories/transport
/categories/transport/cars
/categories/transport/cars/{ID} // ID could be an int or string, up to you