eloquent-taggable: tags on Laravel Eloquent models in one trait
Easily add the ability to tag your Eloquent models in Laravel.
At a glance
- What is it?
- Eloquent-Taggable adds tagging to Laravel Eloquent models with a single trait, a publishable migration set and a tag service, plus query scopes, model events and configurable delimiters, normalisation and glue. The package version tracks the Laravel version, so an upgrade means matching both.
- Who is it for?
- Adopt eloquent-taggable if you run Laravel and need many-to-many tags on your models with a small, well understood API, especially if you want tagging to work with existing tag records rather than free text. Skip it if your application already has a tagging layer or if you need tag hierarchies, since this is a flat many-to-many relation.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 95 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The package version has to match your Laravel version
Eloquent-Taggable exists to easily add the ability to tag your Eloquent models in Laravel, and the first thing the installation section tells you is that the version you install depends on your framework version. The rule from version 6.0 onwards is that the package's version should match the Laravel version, so Laravel 13 needs the 13 line of the package, Laravel 12 needs 12, and so on down through Laravel 6. Below that the numbering diverges, with Laravel 5.8 mapping to package 3.5, 5.7 to 3.4, 5.6 to 3.3, 5.5 to 3.2 and 5.4 to 3.1, and there is a footnote that version 3.1 of the package requires PHP 7.0 or later even though Laravel 5.4 does not. Older Laravel versions can use older versions of the package, although they are no longer supported or maintained, and the README points at CHANGELOG.md and UPGRADING.md for specifics, with a warning to make sure you are reading the correct README for your version because GitHub displays the master branch by default. The package is on Packagist as cviebrock/eloquent-taggable and is MIT licensed, with badges for the latest stable version, the latest unstable version, total downloads and a SensioLabs Insight scan.
Installing is a composer require and two artisan publish calls
The install sequence is four steps and the first is the composer requirement.
$ composer require cviebrock/eloquent-taggableThe package registers its service provider automatically, so there is nothing to add to the providers array. The second step publishes the configuration file.
php artisan vendor:publish --provider="Cviebrock\EloquentTaggable\ServiceProvider" --tag "config"The third publishes the migrations.
php artisan vendor:publish --provider="Cviebrock\EloquentTaggable\ServiceProvider" --tag "migrations"The README notes that if you modify the migrations you can add more fields but should not remove existing ones, and that if you want to change the table names used for the package you do it in the configuration file under the tables key rather than in the migration. The fourth step runs the migration to create the required tables.
composer dump-autoload
php artisan migrateThere is a detail in that last step worth reading twice, that the migration file is not published to your application but will run anyway. The package ships its migrations inside the package and registers them to run from there, which is why publishing them is a separate and optional step, and why a dumped autoloader is needed before the migrate command picks them up.
One trait on the model, and one attribute you must not use
Making a model taggable means adding a trait, and the trait is the whole integration.
use Cviebrock\EloquentTaggable\Taggable;
class MyModel extends Eloquent
{
use Taggable;
}That is the entire change to the model, and the README's summary is that the model is now taggable. There is one constraint, and it is the kind that causes a confusing error if you hit it blind. Make sure your model does not have an attribute and/or column in its database table called tags, because the trait will add that attribute for you. Since the trait exposes the model's tags as a relationship, a model that already has a column of that name collides with the accessor the trait defines. It is a one-line check before you add the trait, and it is the only thing in the setup that can fail in a confusing way.
tag, untag, detag and retag, by name or by id
The tag method accepts either a delimited string or an array, and it is additive rather than replacing.
// Pass in a delimited string:
$model->tag('Apple,Banana,Cherry');
// Or an array:
$model->tag(['Apple', 'Banana', 'Cherry']);Because it is additive, calling it again adds to what is already there, so a second call with a single tag leaves the model with four. Removing works in three flavours, untag for individual tags, detag for all of them, and retag as a short form for detagging and then tagging.
$model->tag('Apple,Banana,Cherry');
$model->untag('Banana');
// $model is now just tagged with "Apple" and "Cherry"
$model->detag();
// $model has no tags$model->tag('Apple,Banana,Cherry');
$model->retag('Etrog,Fig,Grape');
// $model is now just tagged with "Etrog", "Fig", and "Grape"There is a second family of methods that works with tag model primary keys rather than names, and the README gives the reason. If you already have an array of Tag model IDs, you can tag a model using those IDs instead of the tag names, with tagById and untagById. The practical case is a form with a multi-select dropdown or a list of checkboxes of all tags, where the submitted data is an array of tag IDs that can then be applied with retagById. The comment in the example notes the assumption that no other tags exist yet, which is the caveat: the by-id methods operate on existing tag records, so they are the right choice when your tags are managed entities and the wrong choice when they are free text typed by a user.
Reading tags back, and the shape of the data you get
Reading the tags of a model gives you an Eloquent Collection rather than a plain array, and the README is explicit about that, saying you get the array of all tags, technically an Eloquent Collection.
foreach($model->tags as $tag)
{
echo $tag->name;
}The reason to note the type is that it changes how you write the consuming code. An Eloquent Collection is iterable and has collection methods, but it is not a plain array, so code that type hints an array, or that indexes with square brackets expecting integer keys, will behave differently. The relationship also means the tags are loaded lazily by default, so a loop over tags inside a list of models is the classic place to introduce an N plus 1 query, and the fix is the usual eager loading one. The repository's own code style and tooling is worth noting for a package like this, with a .php-cs-fixer.php configuration for formatting, a phpstan.dist.neon for static analysis, phpunit.xml for the test suite, a resources/ directory for the language files, a src/ directory for the implementation, and a TODO.md alongside the CHANGELOG.md and CONTRIBUTING.md.
Configuration covers delimiters, glue, normalisation and the model
The configuration section is the part of the package that reveals how much thought went into the details, and the README lists the keys under a set of headings. The delimiters setting controls how a delimited tag string is split, which is why the examples use comma separated values. The glue setting controls how the tags are stored, and it exists because the natural way to store a set of tags is as one string in one column, which makes the set queryable only by exact match, whereas storing them as separate rows in a pivot table makes them queryable by the database's own relational machinery. The normalizer setting decides what happens to the case and spacing of a tag before it is stored, which is what stops Apple, apple and Apple with a trailing space becoming three distinct tags. The connection setting chooses the database connection for the package's own tables, so they can live in a different database from your models. The throwEmptyExceptions setting controls whether tagging with an empty string raises. The taggedModels and model settings name the models and the tag model class, and the tables setting names the tables, which the install section already told you to change here rather than in the migration. There is also a TagService class and a Tag model, so the package gives you a place to hang behaviour that is not about the relation itself.
Where eloquent-taggable is the wrong choice
The first limit is that the tagging is flat. The package gives you a many-to-many relation between models and tags, and the tag itself is a name with a normalizer and a delimiter, but there is no notion of a tag belonging to another tag, so a hierarchy like location, then city, then district has to be encoded in the string with your own delimiter convention. If your taxonomy is a tree, this is the wrong shape and you would be better off with a different data model. The second is the version coupling, and it is the practical cost. From 6.0 onwards the package version tracks the Laravel version, which means every Laravel upgrade is also a package upgrade, and the older lines for Laravel 5.x are explicitly no longer supported or maintained. A package that follows your framework is easy to reason about, but it also means you cannot stay on a stable package version while moving the framework, and the release history in this repository is thin, with the package carrying no GitHub releases and the last push to the repository on 2026-07-20, so the tag is not a reliable signal of where the code actually is. The third is scope. There is nothing here for tag synonyms, for tag merging, for tag analytics, or for a moderation workflow over tags, and while the TagService and the model events give you somewhere to build those, you are building them yourself. The fourth is the collision rule, since the trait claims the tags attribute on the model, and a codebase that already stores a tags column will need that renamed before the trait can be used.
Editorial conclusion
Adopt eloquent-taggable if you run Laravel and need many-to-many tags on your models with a small, well understood API, especially if you want tagging to work with existing tag records rather than free text. Skip it if your application already has a tagging layer or if you need tag hierarchies, since this is a flat many-to-many relation. Verify first that your Laravel version has a matching package version, since from 6.0 onwards the two version lines move together, and that your tagged models do not already have a column or attribute called tags, which the trait claims for itself.
Frequently asked questions
How do I add tagging to a Laravel Eloquent model?
Install the package with composer require cviebrock/eloquent-taggable, publish the config and migrations with the artisan vendor:publish commands for the Cviebrock\EloquentTaggable\ServiceProvider, then run composer dump-autoload and php artisan migrate. On the model, add the trait with use Cviebrock\EloquentTaggable\Taggable; and make sure the model has no column or attribute called tags, which the trait claims for itself.
Which version of eloquent-taggable should I install?
The package version should match your Laravel version as of version 6.0, so Laravel 13 needs the 13 line and Laravel 12 needs the 12 line. Below that, Laravel 5.8 maps to package 3.5, 5.7 to 3.4, 5.6 to 3.3, 5.5 to 3.2 and 5.4 to 3.1, and the older lines for those framework versions are no longer supported or maintained.
What is the difference between tag, untag, detag and retag in eloquent-taggable?
tag is additive, accepting either a delimited string or an array and adding to the existing tags. untag removes individual tags by name or by id, detag removes all of them, and retag is a short form for detagging and then tagging. There are also tagById, untagById and retagById variants for working with existing tag records, which is what you want when a multi-select form submits tag IDs.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cviebrock-eloquent-taggable)Was this useful?