Installation:
composer require cviebrock/eloquent-taggable
Run migrations:
php artisan vendor:publish --provider="Cviebrock\EloquentTaggable\ServiceProvider" --tag="migrations"
php artisan migrate
First Use Case:
Add the Taggable trait to your Eloquent model (e.g., Post.php):
use Cviebrock\EloquentTaggable\Taggable;
class Post extends Model
{
use Taggable;
}
Tagging a Model:
$post = new Post();
$post->tags()->attach(['laravel', 'php', 'eloquent']);
$post->save();
database/migrations/[timestamp]_create_tags_table.php and database/migrations/[timestamp]_create_model_tag_ppivot_table.php for schema details.hasManyThrough) are well-documented in the source.Tagging Models:
$model->tags()->attach(['tag1', 'tag2']); // Array or comma-separated string
$model->tags()->attach('tag3'); // Single tag
$model->tags()->detach(['tag1']); // Remove specific tags
$model->tags()->detach(); // Remove all tags
Querying by Tags:
$posts = Post::tags(['laravel'])->get();
$posts = Post::tags(['laravel', 'php'])->get();
$posts = Post::notTags(['spam'])->get();
Tag Management:
$model->tags()->sync(['new', 'tags']);
$model->tags()->toggle(['tag1', 'tag2']);
Custom Tag Models:
Extend the default Tag model by publishing the provider and modifying the Tag class:
php artisan vendor:publish --provider="Cviebrock\EloquentTaggable\ServiceProvider" --tag="config"
Override Cviebrock\EloquentTaggable\Tag in app/Models/Tag.php.
Scopes for Complex Queries: Use the package’s built-in scopes or create custom ones:
class Post extends Model
{
use Taggable;
public function scopePopular($query)
{
return $query->tags(['popular'])->orderBy('views', 'desc');
}
}
API Responses: Eager-load tags to avoid N+1 queries:
$posts = Post::with('tags')->get();
Or use the tagList accessor for a comma-separated string:
$post->tagList; // "laravel,php,eloquent"
Validation: Validate tags in Form Requests:
public function rules()
{
return [
'tags' => 'sometimes|array',
'tags.*' => 'string|max:255',
];
}
Migration Conflicts:
tags table, drop it before running the package migrations to avoid conflicts.tags (for tag definitions) and [model]_tag (pivot table).Case Sensitivity:
Tags are case-sensitive by default. Normalize tags (e.g., strtolower()) if case-insensitive behavior is needed:
$model->tags()->attach(array_map('strtolower', ['Laravel', 'PHP']));
Tag Duplication: The package prevents duplicate tags automatically, but ensure your input is sanitized to avoid edge cases (e.g., trailing whitespace):
$tags = array_map('trim', explode(',', $request->input('tags')));
Performance with Large Datasets:
whereHas('tags') on tables with millions of rows. Use tags() scopes instead for better performance:
// Faster:
Post::tags(['laravel'])->get();
// Slower (may trigger N+1):
Post::whereHas('tags', fn($q) => $q->where('name', 'laravel'))->get();
Caching Tag Queries: Cache frequent tag-based queries (e.g., "trending tags") to reduce database load:
$trendingTags = Cache::remember('trending-tags', now()->addHours(1), function () {
return Tag::whereHas('models')->orderByDesc('models_count')->limit(10)->get();
});
Check Pivot Table:
If tags aren’t attaching, verify the pivot table exists and has the correct columns (model_id, tag_id, and optionally created_at, updated_at).
Tag Existence:
Ensure tags exist before attaching. Use syncWithoutDetaching to avoid errors:
$model->tags()->syncWithoutDetaching(['tag1', 'tag2']); // Won't detach missing tags
Log Queries: Enable Laravel query logging to debug tag-related queries:
DB::enableQueryLog();
$model->tags()->attach(['test']);
dd(DB::getQueryLog());
Custom Tag Sync Logic:
Override the syncTags method in your model:
public function syncTags(array $tags)
{
// Custom logic (e.g., log tag changes)
$this->tags()->sync($tags);
}
Tag Events:
Listen for tag-related events (e.g., tagging, tagged) via the taggable facade:
use Cviebrock\EloquentTaggable\Taggable as EloquentTaggable;
EloquentTaggable::listen('tagging', function ($model, $tags) {
// Log or process tags before attachment
});
Custom Tag Model Methods:
Add methods to the Tag model for business logic:
class Tag extends \Cviebrock\EloquentTaggable\Tag
{
public function isTrending()
{
return $this->models_count > 100;
}
}
API Resources:
Create a custom TagResource to format tag responses:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => Str::slug($this->name),
'models_count' => $this->models()->count(),
];
}
How can I help you explore Laravel packages today?