Skip to content

Commit d5c3899

Browse files
Add Boost skill for Scout development (#979)
* Add Boost skill for Scout development * Update SKILL.blade.php --------- Co-authored-by: Taylor Otwell <taylor@laravel.com>
1 parent 954ec86 commit d5c3899

1 file changed

Lines changed: 186 additions & 0 deletions

File tree

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
---
2+
name: scout-development
3+
description: "Develops full-text search with Laravel Scout. Activates when installing or configuring Scout; choosing a search engine (Algolia, Meilisearch, Typesense, Database, Collection); adding the Searchable trait to models; customizing toSearchableArray or searchableAs; importing or flushing search indexes; writing search queries with where clauses, pagination, or soft deletes; configuring index settings; troubleshooting search results; or when the user mentions Scout, full-text search, search indexing, or search engines in a Laravel project. Make sure to use this skill whenever the user works with search functionality in Laravel, even if they don't explicitly mention Scout."
4+
license: MIT
5+
metadata:
6+
author: laravel
7+
---
8+
@php
9+
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
10+
@endphp
11+
# Scout Full-Text Search
12+
13+
## Documentation First
14+
15+
**Always use `search-docs` before writing Scout code.** The documentation covers every engine, configuration option, and edge case in detail. This skill teaches you how to navigate Scout — the docs have the implementation specifics.
16+
17+
```
18+
search-docs(queries: ["Scout installation"], packages: ["laravel/framework@12.x"])
19+
```
20+
21+
The Scout docs live under the `laravel/framework` package — not `laravel/scout`.
22+
23+
Effective search patterns:
24+
25+
- Installation & setup: `"Scout installation"`, `"Scout queueing"`
26+
- Engine setup: `"Scout Algolia"`, `"Scout Meilisearch"`, `"Scout Typesense"`
27+
- Model configuration: `"Scout configuring searchable data"`, `"Scout configuring model indexes"`
28+
- Searching: `"Scout searching"`, `"Scout where clauses"`, `"Scout pagination"`
29+
- Indexing: `"Scout batch import"`, `"Scout adding records"`, `"Scout removing records"`
30+
- Advanced: `"Scout soft deleting"`, `"Scout customizing engine searches"`, `"Scout custom engines"`
31+
32+
The docs are organized into these main sections: Installation, Driver Prerequisites, Configuration, Database/Collection Engines, Indexing, Searching, Custom Engines. Use these section names as search anchors.
33+
34+
## When to Apply
35+
36+
Activate this skill when:
37+
38+
- Installing or configuring Scout
39+
- Choosing a search engine for a Laravel application
40+
- Making Eloquent models searchable
41+
- Customizing indexed data or index names
42+
- Writing search queries, filters, or pagination
43+
- Importing or flushing search indexes
44+
- Troubleshooting search results or indexing issues
45+
- Choosing between search engines
46+
47+
## Installation
48+
49+
Before installing, check if Scout is already in the project — look for `laravel/scout` in `composer.json` and `config/scout.php`. If already installed, skip to the relevant section (engine configuration, model setup, or searching).
50+
51+
### 1. Install Scout
52+
53+
```bash
54+
composer require laravel/scout
55+
{{ $assist->artisanCommand('vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"') }}
56+
```
57+
58+
### 2. Add the Searchable trait
59+
60+
```php
61+
use Illuminate\Database\Eloquent\Model;
62+
use Laravel\Scout\Searchable;
63+
64+
class Post extends Model
65+
{
66+
use Searchable;
67+
}
68+
```
69+
70+
This registers a model observer that automatically keeps your search index in sync with Eloquent records.
71+
72+
## Choosing an Engine
73+
74+
Before presenting engine options, check if Scout is already configured in the application:
75+
76+
1. Check `.env` for `SCOUT_DRIVER` — if set, the application already has a configured engine.
77+
2. Check `config/scout.php` for the `driver` key and any engine-specific settings.
78+
3. Check `composer.json` for engine SDKs (`algolia/algoliasearch-client-php`, `meilisearch/meilisearch-php`, `typesense/typesense-php`).
79+
80+
If an engine is already configured, skip the engine selection step and work with the existing setup. Only present the engine comparison if Scout is not yet installed or the user explicitly wants to switch engines.
81+
82+
When no engine is configured, present these options and let the user decide — never choose for them.
83+
84+
| Engine | Type | Best For | Tradeoffs |
85+
|--------|------|----------|-----------|
86+
| **Database** | Built-in | Typical applications, simple search | No external deps. MySQL/PostgreSQL only. LIKE + full-text indexes. No typo tolerance. |
87+
| **Collection** | Built-in | Local dev, tiny datasets (<500 records) | Loads all records into memory. Most portable but least efficient. |
88+
| **Algolia** | Hosted SaaS | Advanced search without managing infra | Typo tolerance, analytics, faceting. Paid service. No self-hosting. |
89+
| **Meilisearch** | Self-hosted / Cloud | Teams wanting infrastructure control | Fast, open-source. Self-hostable or cloud. Requires filterable attribute config. |
90+
| **Typesense** | Self-hosted / Cloud | Keyword, semantic, geo, vector search | Open-source. Self-hostable or cloud. Strict schema requirements. |
91+
92+
After the user chooses, use `search-docs` for that engine's prerequisites and configuration.
93+
94+
Set the engine in `.env`:
95+
96+
```ini
97+
SCOUT_DRIVER=database
98+
```
99+
100+
For third-party engines (Algolia, Meilisearch, Typesense): install their PHP SDK and strongly consider enabling queue support in `config/scout.php` for production.
101+
102+
## Model Configuration
103+
104+
Use `search-docs` for full configuration details. Key customization points:
105+
106+
```php
107+
// Control what data gets indexed
108+
public function toSearchableArray(): array
109+
{
110+
return [
111+
'id' => $this->id,
112+
'title' => $this->title,
113+
'body' => $this->body,
114+
];
115+
}
116+
117+
// Custom index name (no effect with database engine)
118+
public function searchableAs(): string
119+
{
120+
return 'posts_index';
121+
}
122+
```
123+
124+
For Algolia/Meilisearch/Typesense: configure index settings (filterable, sortable, searchable attributes) in `config/scout.php`, then sync:
125+
126+
```bash
127+
{{ $assist->artisanCommand('scout:sync-index-settings') }}
128+
```
129+
130+
## Searching
131+
132+
Basic patterns:
133+
134+
```php
135+
// Simple search
136+
$results = Model::search('query')->get();
137+
138+
// With filtering
139+
$results = Model::search('query')->where('status', 'active')->get();
140+
141+
// Paginated
142+
$results = Model::search('query')->paginate(15);
143+
144+
// Eager load relationships on results
145+
$results = Model::search('query')
146+
->query(fn ($q) => $q->with('category'))
147+
->get();
148+
149+
// Raw engine results
150+
$results = Model::search('query')->raw();
151+
```
152+
153+
Use `search-docs` for advanced querying — `whereIn`, `whereNotIn`, soft deletes, custom indexes, and engine-specific options.
154+
155+
## Key Artisan Commands
156+
157+
| Command | Purpose |
158+
|---------|---------|
159+
| `{{ $assist->artisanCommand('scout:import "App\\Models\\Post"') }}` | Import model records into search index |
160+
| `{{ $assist->artisanCommand('scout:queue-import "App\\Models\\Post"') }}` | Import via queued jobs (large datasets) |
161+
| `{{ $assist->artisanCommand('scout:flush "App\\Models\\Post"') }}` | Remove all model records from search index |
162+
| `{{ $assist->artisanCommand('scout:sync-index-settings') }}` | Sync index settings to the search engine |
163+
| `{{ $assist->artisanCommand('scout:index posts') }}` | Create a search index |
164+
| `{{ $assist->artisanCommand('scout:delete-index posts') }}` | Delete a search index |
165+
166+
## Testing
167+
168+
Scout does **not** have a `Scout::fake()` method. Available approaches:
169+
170+
- **NullEngine** — set `SCOUT_DRIVER=null` to disable all indexing in tests
171+
- **CollectionEngine** — set `SCOUT_DRIVER=collection` for in-memory search without external services
172+
- **`Model::withoutSyncingToSearch(fn() => ...)`** — temporarily pause indexing in a callback
173+
- **`Model::disableSearchSyncing()`** — globally disable syncing in test setUp
174+
175+
Use `search-docs` for detailed testing patterns.
176+
177+
## Common Pitfalls
178+
179+
- **Meilisearch filterable attributes** — you must configure filterable attributes in `config/scout.php` under `meilisearch.index-settings` and run `scout:sync-index-settings` **before** using `where`, `whereIn`, or `whereNotIn`. Without this, filtering silently returns wrong results.
180+
- **Meilisearch data type casting** — `toSearchableArray()` must return properly cast values: integers as `(int)`, floats as `(float)`. Wrong types cause silent filter failures.
181+
- **Database engine limitations** — `searchableAs()`, `getScoutKey()`, `getScoutKeyName()`, and `shouldBeSearchable()` have no effect with the database engine. It queries your actual tables directly.
182+
- **Global scopes break pagination** — search engines are not aware of Eloquent global scopes. Recreate scope constraints using Scout `where` clauses instead.
183+
- **`query()` is not for filtering** — with third-party engines, the `query()` callback runs after results are already retrieved. Use Scout `where` clauses for filtering; use `query()` only for eager-loading or customizing the Eloquent hydration query.
184+
- **Missing queue configuration** — third-party engines should always have `scout.queue` enabled in production. Without it, indexing runs synchronously and slows down requests.
185+
- **Typesense schema requirements** — `id` must be cast as `(string)` and timestamps as Unix integers (`$this->created_at->timestamp`).
186+
- **`shouldBeSearchable()` bypass** — this method only applies via `save()`, `create()`, queries, or relationships. Calling `searchable()` directly on a model or collection bypasses it entirely.

0 commit comments

Comments
 (0)