Метод HTTP QUERY — це нове доповнення до специфікації HTTP (RFC 10008), розроблене спеціально для запитів. Це безпечний метод, що підтримує кешування та передає параметри в тілі запиту. Він поєднує гнучкість структури POST із семантикою GET, позбавляючи потреби втискати складні фільтри в обмежений за довжиною URL.
У Laravel 13.19 до HTTP-клієнта додали Http::query(), а також хелпери для тестування query() та queryJson(). Повноцінна підтримка Route::query() вже запланована для Laravel 14, проте використовувати цей метод можна й сьогодні через Route::match(). У цьому туторіалі ми створимо ендпоінт для пошуку за допомогою бази даних та Laravel Scout.
Простий рядок пошуку не обов'язково потребує QUERY, але цей метод стає незамінним, коли запит стає занадто складним для GET:
- Структуровані фільтри з діапазонами, масивами та групами logic (наприклад, пошук товарів).
- Геопросторові запити з координатами полігонів («пошук у цій області карти»).
- Масовий пошук сотень записів за ID.
- Пошук за конфіденційними даними (email або номери телефонів), яким не місце в URL, логах сервера чи історії браузера.
- Звіти з датами та групуванням, які технічно є читанням даних, але зараз часто реалізуються через
POST /reports/run.
# Налаштування
Створіть новий застосунок Laravel 13, додайте файл API-маршрутів та встановіть Scout:
php artisan install:api
composer require laravel/scout
Драйвер бази даних у Scout не потребує зовнішніх сервісів — він використовує WHERE LIKE запити, що ідеально підходить для невеликих та середніх проєктів. Увімкніть його у файлі .env:
SCOUT_DRIVER=database
Далі створіть модель Article з міграцією та фабрикою:
php artisan make:model Article -mf
У міграції визначимо поля title та body:
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
$table->timestamps();
});
Додайте трейт Searchable до моделі та вкажіть поля для пошуку:
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Article extends Model
{
use HasFactory, Searchable;
protected $fillable = ['title', 'body'];
public function toSearchableArray(): array
{
return [
'title' => $this->title,
'body' => $this->body,
];
}
}
# Визначення маршруту QUERY
Маршрутизатор Laravel 13 ще не підтримує Route::query() (це з'явиться в Laravel 14). Проте ми можемо зареєструвати будь-який кастомний метод через Route::match().
Додайте наступний код у routes/api.php:
use App\Models\Article;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::match(['QUERY'], '/articles/search', function (Request $request) {
$validated = $request->validate([
'search' => ['required', 'string'],
'per_page' => ['sometimes', 'integer', 'between:1,50'],
]);
return Article::search($validated['search'])
->paginate($validated['per_page'] ?? 15);
});
Важливо: валідація та $request->input() працюють із JSON-тілом запиту так само як у POST. Команда php artisan route:list підтвердить реєстрацію методу:
QUERY api/articles/search
Запит до цього ендпоінта виглядає як POST, але має семантику GET — критерії пошуку передаються в тілі:
QUERY /api/articles/search HTTP/1.1
Content-Type: application/json
Accept: application/json
{"search": "scout", "per_page": 10}
# Тестування ендпоінта
Хелпер queryJson() працює аналогічно до postJson(), тому написання тестів буде звичним:
namespace Tests\Feature;
use App\Models\Article;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ArticleSearchTest extends TestCase
{
use RefreshDatabase;
public function test_it_searches_articles_with_the_query_method(): void
{
Article::factory()->create(['title' => 'Getting Started With Laravel Scout']);
Article::factory()->create(['title' => 'Queues in Depth']);
$this->queryJson('/api/articles/search', ['search' => 'scout'])
->assertOk()
->assertJsonCount(1, 'data')
->assertJsonPath('data.0.title', 'Getting Started With Laravel Scout');
}
public function test_it_validates_the_search_term(): void
{
$this->queryJson('/api/articles/search', [])
->assertUnprocessable()
->assertJsonValidationErrors('search');
}
}
Для звернення до таких ендпоінтів з коду використовуйте метод Http::query():
use Illuminate\Support\Facades\Http;
$response = Http::acceptJson()->query('https://example.com/api/articles/search', [
'search' => 'scout',
]);
$articles = $response->json('data');
# Важливий нюанс: Вбудований сервер PHP
Якщо ви використовуєте php artisan serve, запити з методом QUERY отримають відповідь 501 Not Implemented ще до того, як потраплять у Laravel. Вбудований сервер PHP має жорстко прописаний список дозволених методів. Середовища на базі Nginx, такі як Laravel Herd або Valet, коректно пропускають цей метод. У тестах Laravel цієї проблеми немає, оскільки запити проходять безпосередньо через фреймворк.
# Використання QUERY у веб-маршрутах
У routes/api.php захист CSRF вимкнено. Проте в routes/web.php ви отримаєте помилку 419: Laravel 13 вважає безпечними лише GET, HEAD та OPTIONS. Хоча RFC 10008 визначає QUERY як безпечний метод, і у наступній версії це буде виправлено, зараз є два шляхи вирішення.
Перший варіант — розширити Middleware та додати QUERY до списку «безпечних» методів:
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery as Middleware;
class PreventRequestForgery extends Middleware
{
protected function isReading($request)
{
return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS', 'QUERY']);
}
}
Замініть стандартний Middleware у bootstrap/app.php:
use App\Http\Middleware\PreventRequestForgery;
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery as BasePreventRequestForgery;
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(replace: [
BasePreventRequestForgery::class => PreventRequestForgery::class,
]);
})
Тепер усі QUERY-маршрути будуть звільнені від CSRF-перевірки, як і в майбутній Laravel 14.
Другий варіант — вимкнути перевірку для конкретного маршруту:
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
Route::match(['QUERY'], '/articles/search', $handler)
->withoutMiddleware(PreventRequestForgery::class);
Пам'ятайте: фреймворк довіряє вашим QUERY-обробникам так само як і GET. Не змінюйте стан системи (не робіть записів у БД) всередині цих маршрутів.
# Перед запуском у Production
Хоча Laravel добре підтримує QUERY, фреймворк — це лише частина ланцюжка. Перевірте наступне:
- Проміжні вузли: CDN, фаєрволи (WAF) та балансувальники навантаження можуть блокувати невідомі методи. Перевірте весь шлях запиту.
- Браузерні обмеження:
fetch()підтримує QUERY, але HTML-форми — ні. Також QUERY не входить до «білого списку» CORS, тому міждоменні запити завжди викликатимуть preflight-запит. - Кешування: Хоча за специфікацією метод можна кешувати, браузери та CDN ще не реалізували повноцінну підтримку кешування для QUERY.
- Інструменти специфікації: Підтримка QUERY з'явилася в OpenAPI лише у версії 3.2 (вересень 2025 року). Старіші генератори SDK можуть не розпізнати цей метод.
# Що далі?
З виходом нової мажорної версії Laravel ви зможете змінити Route::match(['QUERY'], ...) на лаконічний Route::query(...) та видалити кастомний Middleware. В іншому ваш код залишиться незмінним. Докладніше про метод читайте у RFC 10008.