Разрешить маршрутам Laravel указывать `withTrashed`, несмотря на явные привязки модели

Фон

В моем приложении 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() в моих явных привязках модели, есть ли хороший способ реализовать это, чтобы некоторые маршруты могли включать в себя удаленные элементы, а другие — нет?

Можете ли вы попытаться добавить объем модели, а затем расширить указанную модель до трех нужных моделей вместо признака?

Ademir Šehić 22.03.2024 10:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
1
138
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я думаю, вы можете сделать это вот так в 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

Это не делает то, что я хочу. Сюда всегда будут относиться устаревшие модели, но есть маршруты, для которых они мне не нужны. Я хочу использовать параметры маршрута, включать ли удаленные модели или нет.

Moshe Katz 22.03.2024 14:05

Да, но именно так работает явная привязка модели. Вы определяете, как эта модель привязывается ко всем маршрутам, используя привязку модели под этим именем. Если вы не хотите, чтобы вам приходилось использовать неявную привязку, создайте собственные преобразователи привязок или не используйте привязку модели на маршрутах, где вы хотите получить программные удаления. Или вы определяете привязку модели с именем locationWithtrashed или чем-то, что вы используете для получения программных удалений, и другим именем для привязок без них. Это должно сработать, хотя я это не проверял.

Sitethief 25.03.2024 11:17
Ответ принят как подходящий

Вот лучшее, что мне удалось придумать на данный момент, хотя я этим не очень доволен:

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);

Другие вопросы по теме