spatie/laravel-tags
Add flexible tagging to Laravel Eloquent models with the HasTags trait. Create, attach, detach, and query tags with ease, with built-in support for tag types, translations, and sorting—ideal for organizing content across your app.
Start by installing the package via Composer and publishing the migrations and config:
composer require spatie/laravel-tags
php artisan vendor:publish --provider="Spatie\Tags\TagsServiceProvider" --tag="tags-migrations"
php artisan migrate
Add the HasTags trait to any Eloquent model you want to tag:
use Spatie\Tags\HasTags;
class Article extends Model
{
use HasTags;
}
Now you can immediately tag models using strings—tags are auto-created if they don’t exist:
$article = Article::create([
'title' => 'Laravel Tips',
'tags' => ['php', 'laravel'],
]);
Use the tags property or relationships to access tags, and scopes like withAnyTags() to query by tag.
tags array in create() or fill(), or use syncTags() to replace all tags on an existing model.attachTag() / attachTags() for incremental tagging; detachTag() to remove individual tags.withAnyTags(['tag1', 'tag2']): models matching any tagwithAllTags([...]): models matching all tagswithoutTags([...]): exclude models with those tagssyncTagsWithType([...], 'type') or attachTag($tag, 'type') to keep context-specific tag collections (e.g., categories vs topics).name column. Use setTranslation('name', 'fr', 'mon tag') on Tag instances.$model->tagsTranslated() or $model->tagsTranslated('fr').Spatie\Tags\Tag, override getLocale() to set a default locale for tags (e.g., admin-facing French but app locale English).config/tags.php via tag_model.slug is auto-generated using Str::slug() by default. Customize by setting 'slugger' => 'App\Str::slugCustom' in config or a custom closure.withAnyTags() and withAllTags() ignore tag types unless you explicitly pass a Tag instance created with a type. Use withAnyTagsOfType() for type-based filtering of models, not just tags.Tag::where('name->fr', 'mon tag'). Ensure your DB supports JSON querying (e.g., MySQL ≥5.7.8, PostgreSQL, SQLite ≥3.9).order_column). Remember to call save() after moveToStart(), swapOrder(), or setNewOrder(). Also note that order applies globally across tag types.HasTags adds a morphMany relation on taggables, which is efficient but can be a bottleneck with huge tag sets. Use loadMissing('tags') in loops and consider pagination for large tag lists.taggables pivot table uses a polymorphic relation, so ensure foreign keys are allowed if your DB enforces them.Tag::findOrCreate() in tests for idempotency, and wrap multi-language scenarios in App::setLocale() assertions.How can I help you explore Laravel packages today?