Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама
Новини 17 липня 2026

Створення пошукового ендпоінту Laravel Scout із HTTP-методом QUERY

HTTP-метод QUERY - це нове доповнення до специфікації HTTP (RFC 10008), розроблене саме для одного завдання: запитів. Це безпечний метод із підтримкою кешування, який передає параметри в тілі запиту, надаючи виразність POST-payload без відмови від read-only семантики GET - і без необхідності втискати складні фільтри в URL, який може досягти обмежень довжини.

Laravel 13.19 додав Http::query() до HTTP-клієнта разом із тестовими хелперами query() та queryJson(). Повноцінний хелпер Route::query() вже злито для Laravel 14, але чекати не обов'язково: роутер Laravel вже сьогодні приймає користувацькі HTTP-дієслова через Route::match(). У цьому tutorial ми використаємо його для побудови невеликого пошукового ендпоінту на основі database-драйвера Laravel Scout.

Коли потрібен метод QUERY

Один пошуковий рядок, як у нашому прикладі, не обов'язково потребує QUERY - метод починає виправдовувати себе, коли запит розростається до чогось, що GET не може зручно обробити:

  • Структуровані фільтри з діапазонами, масивами та групами and/or (пошук продуктів чи оголошень)
  • Геопросторові запити, що надсилають координати полігонів ("пошук у цій області карти")
  • Пакетні запити, що отримують сотні записів за ID
  • Пошук за чутливими значеннями, як-от email або телефони, які не повинні потрапляти в URL, логи доступу або історію браузера
  • Ендпоінти звітності з діапазонами дат і group-by, які є читанням, але сьогодні моделюються як POST /reports/run

Налаштування проєкту

Починаючи зі свіжого Laravel 13-додатку, встановіть файл API-маршрутів і Scout:

php artisan install:api
composer require laravel/scout

Database-драйвер 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 до моделі та визначте, які колонки Scout має шукати:

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 не підтримує Route::query() у Laravel 13 (він буде доступний починаючи з Laravel 14). Однак роутер Laravel реєструє маршрути для будь-якого дієслова, яке ви передаєте в Route::match(), включно з QUERY.

Додайте наступне до 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:

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() Laravel 13.19 дзеркалить 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-клієнт Laravel може викликати QUERY-ендпоінти методом Http::query(), доданим у версії 13.19:

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 - вбудований сервер жорстко кодує методи запитів, які він приймає. Середовища на основі Nginx, як-от Laravel Herd та Valet, передають метод до вашого додатку. Тестові хелпери Laravel HTTP повністю уникають цієї проблеми, оскільки вони відправляють запити безпосередньо через фреймворк.

Використання QUERY на веб-маршрутах

Наш ендпоінт знаходиться в routes/api.php, тому захист CSRF взагалі не виникає. Однак якщо ви зареєструєте QUERY-маршрут у routes/web.php, ви отримаєте помилку 419: middleware PreventRequestForgery Laravel 13 трактує лише GET, HEAD та OPTIONS як запити читання, тому QUERY отримує ту саму перевірку токена, що й POST. RFC 10008 визначає QUERY як безпечний метод, і наступний мажорний реліз Laravel звільняє його відповідно - але до того часу у вас є два варіанти.

Перший варіант переносить майбутню поведінку, розширюючи middleware та додаючи QUERY до списку read-дієслів:

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\PreventRequestForgery as Middleware;

class PreventRequestForgery extends Middleware
{
    /**
     * Визначити, чи HTTP-запит використовує read-дієслово.
     * QUERY є безпечним методом (RFC 10008), тому трактуємо його як GET.
     * Laravel 14 поставляється з цією поведінкою; видаліть це перевизначення після оновлення.
     */
    protected function isReading($request)
    {
        return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS', 'QUERY']);
    }
}

Потім замініть його в групі middleware web у 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, тоді як POST, PUT, PATCH та DELETE зберігають повний захист, відповідаючи тому, що Laravel 14 робитиме з коробки. Коли ви оновитеся, видаліть клас і рядок replace, і нічого більше не зміниться.

Другий варіант виключає один маршрут із middleware:

use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;

Route::match(['QUERY'], '/articles/search', $handler)
    ->withoutMiddleware(PreventRequestForgery::class);

Це добре працює для разового ендпоінту, але вам потрібно буде пам'ятати про це на кожному QUERY-маршруті, який ви додаєте, і це повністю видаляє middleware з маршруту, а не класифікує дієслово як читання. Оскільки перевизначення middleware - це зміна двох файлів, яка чисто зникає після оновлення, ми б обрали його першим.

Одна примітка щодо перевірки будь-якого підходу: CSRF middleware пропускає перевірку, коли середовище додатка - testing, тому feature-тест не отримає 419, якщо ви спочатку не переназначите середовище (наприклад, $this->app['env'] = 'production'; у методі setUp() вашого тесту).

Нарешті, пам'ятайте, що означає звільнення: фреймворк довіряє, що ваші QUERY-обробники є read-only, так само як він довіряє вашим GET-обробникам. Не змінюйте стан у QUERY-маршруті.

Перед відправкою QUERY у production

Laravel добре обробляє QUERY - маршрути навіть працюють із php artisan route:cache - але фреймворк - це лише один крок у ланцюгу. Кілька речей, які потрібно перевірити перед використанням методу в production API:

  • Кожен проміжний сервіс має пропускати метод. Ми вже бачили, як вбудований сервер PHP повертає 501; CDN, WAF та load balancer можуть бути так само категоричними щодо дієслів, які вони не розпізнають. Тестуйте повний шлях від клієнта до додатка, а не лише додаток.
  • Браузерні виклики мають застереження. fetch() надсилає QUERY без проблем, але HTML-форми говорять лише GET та POST, і QUERY не є CORS-safelisted методом - кожен cross-origin запит викличе preflight.
  • Переваги кешування поки що на папері. RFC визначає QUERY як cacheable, але браузери та CDN ще не реалізують кешування QUERY, тому не приймайте метод лише через цю перевагу сьогодні.
  • Інструменти специфікацій наздоганяють. OpenAPI отримав повноцінну операцію query лише у версії 3.2, випущеній у вересні 2025 року - генератори та SDK-ланцюжки інструментів, які все ще орієнтуються на 3.0 або 3.1, не можуть описати ендпоінт, а деякі HTTP-клієнтські бібліотеки жорстко кодують дієслова, які вони приймають.

Жодне з цього не є dealbreaker для внутрішнього API, де ви контролюєте обидва кінці - що також є найпростішим місцем для першої спроби QUERY.

Що далі

Коли вийде наступна мажорна версія Laravel, виклик Route::match(['QUERY'], ...) може стати Route::query('/articles/search', ...), а перевизначення CSRF вище можна видалити - решта коду в цьому tutorial залишається незмінною.

Між маршрутизацією, HTTP-клієнтом і тестовими хелперами Laravel покриває обидва кінці методу QUERY сьогодні. Ви можете дізнатися більше про метод QUERY у RFC 10008 та слідкувати за підтримкою маршрутизації в Pull Request #60655.

5

Читати в документації

Коментарі

Увійдіть, щоб залишити коментар

Будьте першим, хто залишить коментар!

Читайте також

PayZephyr
Новини 12 вересня 2026

PayZephyr: Єдиний API для роботи з Stripe, Paystack та PayPal

PayZephyr - Laravel-пакет від Nwaneri Chukwunyere Kenneth, який об'єднує вісім платіжних провайдерів під одним зручним API. Підтримує автоматичне перемикання, захист від подвійної оплати, підписки та повернення коштів.

2
Laravel Telescope
Новини 11 вересня 2026

Artisan-команди для дебагу в Laravel Telescope 5.24.0

Laravel Telescope 5.24.0 додає дві нові Artisan-команди для роботи із записами із терміналу. Тепер можна переглядати запити, винятки, джоби та запити до бази даних без відкриття веб-інтерфейсу, а також отримувати дані у форматі JSON для скриптів та AI-агентів.

3

Пакети за темою

Laravel Scout

laravel/scout

Повнотекстовий пошук для Eloquent-моделей через єдиний драйверний інтерфейс: Meilisearch, Typesense, Algolia, база даних або колекція. Індекс синхронізується автоматично при змінах моделі.

1,673 v11.5.0 13 2

Laravel Folio

laravel/folio

Маршрутизація на основі файлів: Blade-шаблон у resources/views/pages автоматично стає сторінкою, а ім'я файлу задає URL і його параметри. Прибирає маршрути-однорядковики з routes/web.php.

604 v1.2.0 13 1