A dot nests URIs; shallow() keeps the parent only for listing and creating, since a review's ID is unique on its own. Route::singleton() registers a resource with no ID, such as a product's cover.
Route::resource('products.reviews', ReviewController::class)
->shallow()->only(['index', 'store', 'show', 'destroy']);
Route::singleton('products.cover', CoverController::class)
->creatable()->except(['create', 'edit', 'update']);Output
$ COLUMNS=95 php artisan route:list --path=review | grep review
GET|HEAD products/{product}/reviews ..... products.reviews.index › ReviewController@index
POST products/{product}/reviews ..... products.reviews.store › ReviewController@store
GET|HEAD reviews/{review} .......................... reviews.show › ReviewController@show
DELETE reviews/{review} .................... reviews.destroy › ReviewController@destroy
$ curl -s -H 'Accept: application/json' $B/books/1/reviews/1 \
| jq -r .message | sed 's/, called.*//' | fold -w 90
App\Http\Controllers\ReviewController::show(): Argument #1 ($review) must be of type App\M
odels\Review, string givenA singleton has show, edit and update; creatable() adds create, store and destroy. The error came from a test route /books/{product}/reviews/{review} aimed at the shallow show(Review $review): parameters arrive in URI order, so nested actions must accept the parent. With both, the route served product 2's review under product 1 until ->scopeBindings() made it a 404.