There are a lot of quality tagging packages out there. Most of them offer the same thing: creating tags, associating them with models and some functions to easily retrieve models with certain tags. But in our projects at Spatie we need more functionality. Last week we released our own - very opinionated - tagging package aptly called laravel-tags. It comes with batteries included. Out of the box it has support for translating tags, multiple tag types and sorting capabilities.

After the package is installed (be sure to check the requirements first) the only thing you have to do is to add the HasTags trait to an Eloquent model to make it taggable.

use Illuminate\Database\Eloquent\Model;
use Spatie\Tags\HasTags;

class NewsItem extends Model
{
    use HasTags;
    ...
}

Here are some code examples of what that trait allows you to do:

//create a model with some tags
$newsItem = NewsItem::create([
   'name' => 'testModel',
   'tags' => ['tag', 'tag2'], //tags will be created if they don't exist
]);

//attaching tags
$newsItem->attachTag('tag3');
$newsItem->attachTags(['tag4', 'tag5']);

//detaching tags
$newsItem->detachTag('tag3');
$newsItem->detachTags(['tag4', 'tag5']);

//syncing tags
$newsItem->syncTags(['tag1', 'tag2']); // all other tags on this model will be detached

//retrieving models that have any of the given tags
NewsItem::withAnyTags(['tag1', 'tag2']);

//retrieve models that have all of the given tags
NewsItem::withAllTags(['tag1', 'tag2']);

Under the hood those tags will be converted to Spatie\Tags\Tag models.

Adding translations

If you're creating a multilingual app it's really easy to translate the tags. Here's a quick example.

$tag = Tag::findOrCreate('my tag'); //store in the current locale of your app

//let's add some translation for other languages
$tag->setTranslation('name', 'fr', 'mon tag');
$tag->setTranslation('name', 'nl', 'mijn tag');

//don't forget to save the model
$tag->save();

$tag->getTranslation('name', 'fr'); // returns 'mon tag'

$tag->name // returns the name of the tag in current locale of your app.

The translations of the tags are stored in the name column of the tags table. It's a json column. To find a tag with a specific translation you can just use Laravel's query builder which has support for json columns.

 \Spatie\Tags\Tag
   ->where('name->fr', 'mon tag')
   ->first();

Behind the scenes spatie/laravel-translatable is used. You can use any method provided by that package.

Using tag types

In your application you might want to have multiple collections of tags. For example: you might want one group of tags for your News model and another group of tags for your BlogPost model.

To create separate collections of tags you can use tag types.

//creating a tag with a certain type
$tagWithType = Tag::create('headline', 'newsTag'):

In addition to strings, all methods mentioned previously in this post can take instances of Tag as well.

$newsItem->attachTag($tagWithType);
$newsItem->detachTag($tagWithType);
...

The provided method scopes, withAnyTags and withAllTags, can take instances of Spatie\Tags\Tag too:

$tag = Tag::create('gossip', 'newsTag');
$tag2 = Tag::create('headline', 'newsTag');

NewsItem::withAnyTags([$tag, $tag2])->get();

To get all tags with a specific type use the getWithType method.

$tagA = Tag::findOrCreate('tagA', 'firstType');
$tagB = Tag::findOrCreate('tagB', 'firstType');
$tagC = Tag::findOrCreate('tagC', 'secondType');
$tagD = Tag::findOrCreate('tagD', 'secondType');

Tag::getWithType('firstType'); // returns a collection with $tagA and $tagB

//there's also a scoped version
Tag::withType('firstType')->get(); // returns the same result

Sorting tags

Whenever a tag is created it's order_column will be set the highest value in that column + 1.

$tag = Tag::findOrCreate('tagA');
$tag->order_column; // returns 1
$tag = Tag::findOrCreate('tagB');
$tag->order_column; // returns 2

Under the hood spatie/eloquent-sortable is used, so you can use any model provided by that package. Here are some examples:

//get all tags sorted on `order_column`
$orderedTags = Tags::ordered()->get(); 

//set a new order entirely
Tags::setNewOrder($arrayWithTagIds);

$myModel->moveOrderUp();
$myModel->moveOrderDown();

//move the tag to the first or last position
$myModel->moveToStart();
$myModel->moveToEnd();

$tag->swapOrder($anotherTag);

Of course you can also manually change the value of the order_column.

$tag->order_column = 10;
$tag->save();

If you want to know more about the package check out the documentation. Want to read at the source code (or ⭐ the project ?)? Head over the repo to GitHub. This isn't the first Laravel package we've made, take a look at the list of Laravel packages on our company website to see if there's anything there that can be used on your projects.