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

Питання на співбесіді: Search

Найпопулярніші питання з реальних Laravel/PHP співбесід для всіх рівнів

3 питання

Найпростіший пошук - фільтр за кількома колонками, і для більшості сайтів цього достатньо надовго.

Запит:

public function index(Request $request)
{
    $term = $request->string('q')->trim()->toString();

    $vacancies = Vacancy::query()
        ->when($term !== '', function ($query) use ($term) {
            $query->where(function ($query) use ($term) {
                $query->where('title', 'like', "%{$term}%")
                    ->orWhere('description', 'like', "%{$term}%");
            });
        })
        ->paginate(15)
        ->withQueryString();

    return view('vacancies.index', compact('vacancies'));
}

Дві деталі, на яких зазвичай помиляються:

1. Дужки навколо orWhere. Без вкладеного замикання умова «розклеїться»: where(A)->orWhere(B) разом з іншими фільтрами дасть фільтр AND A OR B, і пошук почне повертати чуже. Вкладене замикання групує умови правильно.

2. Порожній запит. Без when() пошук за порожнім рядком перетвориться на like '%%' - формально працює, але виконує зайву роботу.

Про безпеку: підставляти значення в where() безпечно - Eloquent параметризує запит. Небезпечним є лише whereRaw("title like '%{$term}%'") зі вставкою рядка напряму.

Коли цього перестане вистачати: like '%...%' не використовує індекс і не знає словоформ, тож на десятках тисяч записів пошук стає повільним, а «розробники» не знаходять «розробник». Тоді переходять на повнотекстовий індекс бази або Laravel Scout.

Докладніше в документації: Запити до бази даних

Scout - це загальний інтерфейс до пошукових рушіїв: модель позначається трейтом, а рушій (Meilisearch, Algolia, Typesense, база) підмінюється конфігурацією.

class Vacancy extends Model
{
    use Searchable;

    /**
     * @return array<string, mixed>
     */
    public function toSearchableArray(): array
    {
        return [
            'title' => $this->title,
            'description' => $this->description,
            'company' => $this->company?->name,
        ];
    }
}

Vacancy::search('laravel middle')->paginate(15);

Індексація відбувається автоматично на збереженні моделі, а масово - через php artisan scout:import.

Коли вистачить LIKE:

  • Пошук по одній-двох колонках, записів - тисячі.
  • Достатньо точного входження, без урахування словоформ.
  • Не хочеться ще одного сервісу в інфраструктурі.
Vacancy::where('title', 'like', "%{$term}%")->get();

Де LIKE перестає працювати:

  • Не використовує індекс із % на початку - на великій таблиці це повний перебір.
  • Не знає словоформ. «розробник» не знайдеться за запитом «розробники», і жодних синонімів.
  • Не ранжує. Збіг у заголовку та згадка в кінці опису однакові за вагою.
  • Не прощає помилок - один зайвий символ, і результат порожній.
  • Не шукає по кількох сутностях одразу.

Проміжний варіант - повнотекстовий індекс самої бази: whereFullText() у MySQL, tsvector у PostgreSQL. Він знімає перші три пункти без окремого сервісу, хоча за якістю ранжування поступається спеціалізованим рушіям.

Практичне правило: починайте з LIKE, переходьте на повнотекстовий індекс, коли обсяг виріс, і на Scout - коли потрібні релевантність, помилки в запиті й фасети.

Докладніше в документації: Scout

Laravel Scout - драйверна абстракція повнотекстового пошуку. Додаєте трейт до моделі - і вона автоматично синхронізується з пошуковим індексом (Meilisearch, Algolia, Elasticsearch, навіть database).

class Post extends Model
{
    use Searchable;

    public function toSearchableArray(): array
    {
        return ['title' => $this->title, 'body' => $this->body];
    }
}

Post::search('laravel queues')->paginate(15);
  • Індекс оновлюється на події моделі (краще - через чергу).
  • Початкове наповнення: php artisan scout:import "App\Models\Post".
  • Meilisearch дає швидкий typo-tolerant пошук «з коробки»; Elasticsearch - складніші аналітичні запити.

Докладніше в документації: Laravel Scout