Урок 07

Фильтрация и сортировка

Фильтры через query-параметры

$query = Post::query();

if ($request->filled('search')) {
    $query->where('title', 'like', "%{$request->search}%");
}

if ($request->filled('status')) {
    $query->where('status', $request->status);
}

if ($request->filled('user_id')) {
    $query->where('user_id', $request->user_id);
}

return PostResource::collection($query->paginate(15));

Метод when

Чище, чем if:

$query = Post::query()
    ->when($request->filled('search'), fn ($q) => $q->where('title', 'like', "%{$request->search}%"))
    ->when($request->filled('status'), fn ($q) => $q->where('status', $request->status))
    ->when($request->filled('user_id'), fn ($q) => $q->where('user_id', $request->user_id));

Сортировка с белым списком

Никогда не передавай имя колонки напрямую из запроса — это SQL-инъекция:

$sort = $request->get('sort', 'created_at');
$direction = $request->get('direction', 'desc');

$allowed = ['title', 'created_at', 'views', 'published_at'];
if (!in_array($sort, $allowed)) {
    $sort = 'created_at';
}

$direction = in_array($direction, ['asc', 'desc']) ? $direction : 'desc';

$posts = Post::orderBy($sort, $direction)->paginate(15);

Множественная сортировка

$posts = Post::orderBy('published_at', 'desc')
    ->orderBy('title', 'asc')
    ->paginate(15);

Фильтр по диапазону дат

$query->when($request->filled('from'), fn ($q) => $q->where('created_at', '>=', $request->from))
      ->when($request->filled('to'), fn ($q) => $q->where('created_at', '<=', $request->to));

Фильтр по массиву

$query->when($request->filled('ids'), fn ($q) => $q->whereIn('id', $request->ids));

Связи с фильтром

$query->whereHas('comments', function ($q) use ($request) {
    $q->where('approved', true);
});

Query Object

Если фильтров много — их выносят в отдельный класс:

namespace App\Queries;

use Illuminate\Http\Request;
use Illuminate\Database\Eloquent\Builder;

class PostFilter
{
    public function __construct(private Request $request) {}

    public function apply(Builder $query): Builder
    {
        return $query
            ->when($this->request->filled('search'), fn ($q) => $q->where('title', 'like', "%{$this->request->search}%"))
            ->when($this->request->filled('status'), fn ($q) => $q->where('status', $this->request->status));
    }
}

Использование:

$query = (new PostFilter($request))->apply(Post::query());