The direction I am thinking towards is something like this.
/* Got rid of {slug} parameter where ids are used */
Route::permanentRedirect('/businesses/{id}', '/{id}');
/* Still not happy with this because it is either slug or id. Not always slug as the name refers. Even though slug and id in the database can be equal (int 1 as id and sting "1" as slug). */
Route::get('/{slug}', [BusinessesController::class, 'show'])
->name('businesses.show');
Using parameters with nullable types and default values allows using the same same show() method for multiple routes but the method itself does not get cleaner :)
public function show(?String $slug = null, ?int $id = null)
{
/* GET /{id} redirect to /{slug} */
if ($id !== null) {
$business = Business::find($id);
if (!$business) {
abort(404);
}
$slug = $business->slug ?? $id;
return redirect()->action(
[BusinessesController::class, 'show'],
['slug' => $slug],
301);
}
/* GET /{slug} */
$business = Business::where('slug', $slug)
->orWhere('id', $slug)
->first();
if (!$business) {
abort(404);
}
/* Another redirect if provided slug equals id but slug for the Business exists */
if ($business->slug !== null && $slug === strval($business->id)) {
return redirect()->action(
[BusinessesController::class, 'show'],
['slug' => $business->slug],
301);
}
$data = [
'business' => $business,
'metaTitle' => $business->name
];
return view('businesses.show', $data);
}