В моем приложении Laravel есть модели Organization, Region и Location, которые имеют полиморфное отношение к модели User через модель Assignment. (Assignment — это трехсторонняя связь между User, Role и одной из трех других сущностей, но это не имеет строгого отношения к рассматриваемой задаче.)
Я хотел добавить метод showUsers к каждому контроллеру трех других моделей, который получает всех пользователей, связанных с этим объектом. Чтобы избежать копирования одного и того же кода во все три контроллера, я создал такой trait:
use App\Http\Resources\UserSimpleWithRoleResource;
use App\Models\Assignment;
use Illuminate\Database\Eloquent\Model;
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);
}
}
Чтобы определить значение Model $model из маршрута, мне нужно иметь явные привязки модели в методе RouteServiceProvider моего boot, например:
Route::model('location', Location::class);
Route::model('region', Region::class);
Route::model('organization', Organization::class);
В моем файле маршрутов у меня есть три контроллера для Organization, Region и Location, настроенные так, чтобы разрешить просмотр и редактирование обратимо удаляемых моделей следующим образом:
Route::apiResource('organizations', \App\Http\Api\Organizations\Controller::class)->withTrashed(['show', 'update']);
Route::apiResource('regions', \App\Http\Api\Regions\Controller::class)->withTrashed(['show', 'update']);
Route::apiResource('locations', \App\Http\Api\Locations\Controller::class)->withTrashed(['show', 'update']);
До того, как я добавил явную привязку модели, встроенная неявная привязка модели проверяла маршрут на наличие опции withTrashed и меняла преобразователь модели, включив в него удаленные элементы. Однако явные привязки не выполняют эту проверку.
Если не считать полной повторной реализации проверки $route->allowsTrashedBindings() в моих явных привязках модели, есть ли хороший способ реализовать это, чтобы некоторые маршруты могли включать в себя удаленные элементы, а другие — нет?






Я думаю, вы можете сделать это вот так в RouteServiceProvider. Где одно имя для привязки модели используется для маршрутов, для которых требуется мягкое удаление в привязке модели, и одно имя без мягкого удаления в привязке модели.
Route::bind('location', function ($value) {
return Location::where('id', $value)->firstOrFail();
});
Route::bind('location-with-trashed', function ($value) {
return Location::withTrashed()->where('id', $value)->firstOrFail();
});
Хотя вам нужно проверить, работает ли это.
Смотрите здесь: https://github.com/laravel/framework/issues/43008#issuecomment-1170673827
Это не делает то, что я хочу. Сюда всегда будут относиться устаревшие модели, но есть маршруты, для которых они мне не нужны. Я хочу использовать параметры маршрута, включать ли удаленные модели или нет.
Да, но именно так работает явная привязка модели. Вы определяете, как эта модель привязывается ко всем маршрутам, используя привязку модели под этим именем. Если вы не хотите, чтобы вам приходилось использовать неявную привязку, создайте собственные преобразователи привязок или не используйте привязку модели на маршрутах, где вы хотите получить программные удаления. Или вы определяете привязку модели с именем locationWithtrashed или чем-то, что вы используете для получения программных удалений, и другим именем для привязок без них. Это должно сработать, хотя я это не проверял.
Вот лучшее, что мне удалось придумать на данный момент, хотя я этим не очень доволен:
Route::bind('location', function ($id, RouteDefintion $route) {
return Location::query()
->when($route->allowsTrashedBindings(), function ($query) {
$query->withTrashed();
})
->findOrFail($id);
});
Route::bind('region', function ($id, RouteDefintion $route) {
return Region::query()
->when($route->allowsTrashedBindings(), function ($query) {
$query->withTrashed();
})
->findOrFail($id);
});
Route::bind('organization', function ($id, RouteDefintion $route) {
return Organization::query()
->when($route->allowsTrashedBindings(), function ($query) {
$query->withTrashed();
})
->findOrFail($id);
});
Примечания:
RouteDefinition — это use Illuminate\Routing\Route as RouteDefintion;, потому что Route уже используется фасадом маршрутизатора.$route в функции обратного вызова недокументирован; Я нашел это, просматривая исходный код фреймворка.В версии 11.10 Laravel представлена «Поддержка моделей с мягким удалением при использовании явной привязки модели маршрута», см. PR здесь: https://github.com/laravel/framework/pull/51651
Выполнение:
Route::get('/users/{user}', ...)->withTrashed();
Route::model('user', User::class);
Можете ли вы попытаться добавить объем модели, а затем расширить указанную модель до трех нужных моделей вместо признака?