Laravel RESTful api — динамическое количество параметров запроса

#php #laravel #api

#php #laravel #API

Вопрос:

У меня есть простой веб-сайт, на котором работает Laravel Jetstream с включенными командами. На этом веб-сайте вы можете создавать различные «задачи для выполнения», которые принадлежат команде. У меня есть модель называется Task .

Я пытаюсь создать общедоступный API, чтобы мои пользователи могли запрашивать свои задачи из своих собственных приложений. В моем routes/api.php файле я добавил это:

 Route::middleware('auth:sanctum')->group(function(){
    Route::apiResources([
        'tasks' => AppHttpControllersAPITaskController::class,
    ]);
});
 

И затем в TaskController , я только начал кодировать index метод:

 /**
 * Display a listing of the resource.
 * @queryParam team int The team to pull tasks for.
 * @return IlluminateHttpResponse
*/
public function index()
{

   if(request()->team){
        $tasks = Task::where('team_id', request()->team)->get();
        return TaskResource::collection($tasks);
   }

   return response([
        'status' => 'error',
        'description' => "Missing required parameter `team`."
   ], 422);
      
}
 

Теперь это работает нормально. Я могу сделать запрос GET https://example.org/api/tasks?team=1 и успешно выполнить все связанные с ним задачи team.id = 1 .

Однако, что, если я хочу включить несколько параметров запроса — некоторые обязательные, другие только необязательные. Например, если я хочу разрешить пользователям доступ ко всем задачам с заданным статусом:

https://example.org/api/tasks?team=1amp;status=0

Каковы наилучшие методы для этого? Поскольку я вижу, к чему все идет сейчас, я закончу с большим количеством if/else инструкций, просто чтобы проверить допустимые параметры и дать правильный код ответа на ошибку, если чего-то не хватает.

Редактировать

Я изменил свой URL на: https://example.org/api/teams/{team}/tasks — так что теперь team он должен быть добавлен к URL. Однако я не уверен, как добавлять фильтры с помощью конструктора запросов Spatie:

 public function index(Team $team)
{

   $tasks = QueryBuilder::for($team)
            ->with('tasks')
            ->allowedFilters('tasks.status')
            ->toSql();
   dd($tasks);

}
 

Итак, приведенное выше просто выводит:

 "select * from `teams`"
 

Как я могу выбрать связь tasks с team помощью фильтров?

Ответ №1:

Правильный путь

В качестве продвинутого решения я создал несколько пользовательских способов обработки параметров поискового запроса. Что вы в основном и хотите сделать, лучшим решением на сегодняшний день является конструктор запросов пакетов spatie.

 QueryBuilder::for(Task::class)
    ->allowedFilters(['status', 'team_id'])
    ->get();
 

Эта операция будет выполнять то же самое, что вы хотите сделать, и вы можете фильтровать ее следующим образом.

 ?fields[status]=1
 

По моему опыту, делать team_id доступным для поиска по команде и подобным случаям не стоит, просто используйте его один к одному между столбцами и вводом. Пакет обладает широкими возможностями для особых случаев и настройки.

Простой способ

Что-то столь же прямое, как ваша проблема, не требует, чтобы пакет отклонялся от курса. Это просто удобно и позволяет избежать написания некоторого кода котельной.

Это довольно простая задача, когда у вас есть параметр запроса и столбец, в котором вам нужно выполнить поиск. Это может быть представлено в виде массива, где параметр $key запроса и $value столбец.

 $searchable = [
    'team' => 'team_id',
    'status' => 'status',
];
 

Вместо того, чтобы выполнять кучу операторов if, вы можете упростить это. Проверка, соответствует ли запрос вашему $searchables , и если действовать по нему.

 $request = resolve(Request::class);
$query = Task::query();

foreach ($this->seachables as $key => $value) {
    if ($query->query->has($key)) {
        $query->where($value, $query->query->get($key))
    }
}

$tasks = $query->get();
 

Это довольно простой пример, и здесь возникает проблема, не связанная с пакетом. Вы должны подумать о том, как обрабатывать запросы типа handle, запросы отношений и т. Д.

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

Подведение итогов

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

Комментарии:

1. Какой подробный и очень хорошо сформулированный ответ. Отличные объяснения, недостатки различных решений и хорошо написанные примеры. Это именно то, что я искал! Спасибо!

2. Я внес небольшую правку в свой OP, так как я немного изменил URL API. Не возражаете, если я быстро взгляну?

3. Как вы отправили параметр запроса для своего примера? он не будет искать, если

4. Дох! Это действительно было причиной. Я сделал это так: ?status=1 . Изменен на ?filter[status]=1 и все работает. Спасибо!