php artisan boost:add-skill banulakwin/laravel-seo-engine
Save this content to: AGENTS.md
# Agent guide: `banulakwin/laravel-seo-engine`
Portable polymorphic SEO meta for Eloquent models.
## Static pages (`seo_meta` + config)
### Keys and storage
- **`config('seo.static_page_model_type')`** (default `static_page`) is stored in **`seo_meta.model_type`**.
- **Page slug** (e.g. `home`, `privacy-policy`) is stored in **`seo_meta.model_id`** as a **string** (migration uses string columns).
### Adding SEO for a new static route
1. Add an entry under **`config('seo.pages')`** (or a PHP file required from `config/seo.php`):
```php
'promotions' => [
'title' => 'Promotions',
'description' => 'Limited-time offers.',
// optional: image, keywords, is_index, is_follow, og_*, twitter_*
],
```
2. Run **`php artisan seo:sync`** once (or in deploy) to **`firstOrCreate`** missing rows — **never** overwrites existing DB content.
3. In controllers, pass SEO to Inertia using **`inertia_seo_page('promotions')`**, **`inertia_seo($model)`**, or **`app(SeoService::class)->forStaticPage('promotions')`** mapped to your head contract.
### Reading in PHP
- **`seoPage('home')`** → `?SeoMeta` for the static type + id.
- **`SeoService::forStaticPage('home')`** → stable array shape (DB row if present, else **virtual** array built only from `config('seo.pages')` + `defaults`).
## Dynamic models (`Seoable`)
Attach **`Seoable`** to Eloquent models; SEO row is created on **`created`**.
- **`seoKeywordsAttribute`** (`string|list<string>`) -> resolves meta keywords.
- **`seoCanonicalUrlAttribute`** (`string|list<string>`) -> resolves canonical URL.
- Automatic request URL discovery for canonicals is **removed** to prevent admin/Livewire URL leaks. Use the model attribute to define public URLs.
## Do not
- Use **`updateOrCreate`** in **`seo:sync`** (would overwrite user edits).
- Commit generated files (`vendor/`, `composer.lock`, `coverage/`).
## Testing & Quality
```bash
composer test # PHPUnit
composer pint # Laravel Pint code style fix
composer pint:check # Pint check only (no fix)
composer phpstan # PHPStan level max on src/
composer quality # All: pint + phpstan + test
```
## CI
GitHub Actions runs tests, Pint, and PHPStan on push/PR (`.github/workflows/tests.yml`).
banulakwin/laravel-seo-enginePortable polymorphic SEO meta for Eloquent models.
seo_meta + config)config('seo.static_page_model_type') (default static_page) is stored in seo_meta.model_type.home, privacy-policy) is stored in seo_meta.model_id as a string (migration uses string columns).config('seo.pages') (or a PHP file required from config/seo.php):'promotions' => [
'title' => 'Promotions',
'description' => 'Limited-time offers.',
// optional: image, keywords, is_index, is_follow, og_*, twitter_*
],
php artisan seo:sync once (or in deploy) to firstOrCreate missing rows — never overwrites existing DB content.inertia_seo_page('promotions'), inertia_seo($model), or app(SeoService::class)->forStaticPage('promotions') mapped to your head contract.seoPage('home') → ?SeoMeta for the static type + id.SeoService::forStaticPage('home') → stable array shape (DB row if present, else virtual array built only from config('seo.pages') + defaults).Seoable)Attach Seoable to Eloquent models; SEO row is created on created.
seoKeywordsAttribute (string|list<string>) -> resolves meta keywords.seoCanonicalUrlAttribute (string|list<string>) -> resolves canonical URL.updateOrCreate in seo:sync (would overwrite user edits).vendor/, composer.lock, coverage/).composer test # PHPUnit
composer pint # Laravel Pint code style fix
composer pint:check # Pint check only (no fix)
composer phpstan # PHPStan level max on src/
composer quality # All: pint + phpstan + test
GitHub Actions runs tests, Pint, and PHPStan on push/PR (.github/workflows/tests.yml).
How can I help you explore Laravel packages today?