Oh Dear is the all-in-one monitoring tool for your entire website. We monitor uptime, SSL certificates, broken links, scheduled tasks and more. You'll get a notifications for us when something's wrong. All that paired with a developer friendly API and kick-ass documentation. O, and you'll also be able to create a public status page under a minute. Start monitoring using our free trial now.

An opinionated tagging package for Laravel apps

Original – by Freek Van der Herten – 4 minute read

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.

Stay up to date with all things Laravel, PHP, and JavaScript.

You can follow me on these platforms:

On all these platforms, regularly share programming tips, and what I myself have learned in ongoing projects.

Every month I send out a newsletter containing lots of interesting stuff for the modern PHP developer.

Expect quick tips & tricks, interesting tutorials, opinions and packages. Because I work with Laravel every day there is an emphasis on that framework.

Rest assured that I will only use your email address to send you the newsletter and will not use it for any other purposes.

Comments

What are your thoughts on "An opinionated tagging package for Laravel apps"?

Comments powered by Laravel Comments
Want to join the conversation? Log in or create an account to post a comment.