Route binding with two different models

While still leveraging the framework

Joel Clermont
Joel Clermont
2024-01-15

I saw a recent question about the best way to set up a route that would allow passing a ULID for one of two different models (job and referrer).

Because it's a ULID and not a numeric integer, there would be no overlap in IDs between the two models, so you could query the Job model first, and if it's not found, query the Referrer model.

Putting aside whether this is a good idea or not, I thought it would be fun to see how I would set this up.

While you could handle this logic yourself within the controller, I really like leveraging framework features as much as possible. Could we get Laravel's route model binding to handle this use case?

Yes! By using the RouteServiceProvider, we can register an explicit model binding with our desired logic:

// app/Providers/RouteServiceProvider.php

public function boot()
{
    Route::bind('jobOrReferrer', function ($value) {
        $job = Job::find($value);

        return $job ?? Referrer::findOrFail($value);
    });
}

Then we could register a route like this:

// routes/web.php

Route::get('share/{jobOrReferrer}', [Controllers\ShareController::class, 'show'])->name('share.show');

And then our controller action can use a union type as a type-hint, and our model will be automatically injected by Laravel:

// app/Http/Controllers/ShareController.php

public function show(Job|Referrer $jobOrReferrer)
{
    // do something with your model...
}

Here to help,

Joel

P.S. Have a question of your own? Hit reply and ask your question or set up a time to pair.

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼

Level up your Laravel skills!

Each 2-minute email has real-world advice you can use.