Background
In my Laravel application, I have models Organization
, Region
, and Location
, which all have a polymorphic relationship to the User
model via an Assignment
model. (An Assignment
is a three-way relationship between User
, Role
, and one of the other three entities, but that is not strictly relevant to the problem here.)
I wanted to add a showUsers
method to each of the controllers for the other three models that gets all of the users associated with that entity. To avoid copy-pasting the same code in all three controllers, I created a trait
like this:
use AppHttpResourcesUserSimpleWithRoleResource;
use AppModelsAssignment;
use IlluminateDatabaseEloquentModel;
trait GetsRelatedUsers
{
function listUsers(Model $model)
{
// Thanks to explicit model binding,
// `$model` can be any supported model.
$assignments = Assignment::with(['user:id,given_name,surname', 'role:id,name,display_order'])
->has('user') // prevents assignments for soft-deleted users from showing up
->atPlace($model) // a scope that also adds other conditions based on the provided model
->get();
return UserSimpleWithRoleResource::collection($assignments);
}
}
In order to resolve the Model $model
value from the route, I need to have explicit model bindings in my RouteServiceProvider
‘s boot
method, like this:
Route::model('location', Location::class);
Route::model('region', Region::class);
Route::model('organization', Organization::class);
Problem
In my routes file, I have the three controllers for Organization
, Region
, and Location
set up to allow viewing and editing soft-deleted models like this:
Route::apiResource('organizations', AppHttpApiOrganizationsController::class)->withTrashed(['show', 'update']);
Route::apiResource('regions', AppHttpApiRegionsController::class)->withTrashed(['show', 'update']);
Route::apiResource('locations', AppHttpApiLocationsController::class)->withTrashed(['show', 'update']);
Before I added the explicit model binding, the built-in implicit model binding would check the route for the presence of the withTrashed
option, and would change the model resolver to include trashed items. However, explicit bindings don’t do this check.
Short of completely reimplementing the check for $route->allowsTrashedBindings()
in my explicit model bindings, is there a nice way to implement this so that some routes can include trashed items and others cannot?
2
Answers
Here's the best I've been able to come up with so far, though I'm not really happy with it:
Notes:
RouteDefinition
isuse IlluminateRoutingRoute as RouteDefintion;
becauseRoute
is already used by the router facade.$route
parameter in the callback function is undocumented; I found it by looking through the framework source.I think you can do it like this in RouteServiceProvider.
Where one name for model binding is used for routes where you do want softdeletes in the model binding, and one without softdeletes in the model binding.
Though you need to test if this works.
See here: https://github.com/laravel/framework/issues/43008#issuecomment-1170673827