Tricks

How to use Spatie Tags with types

Jan 20, 2023
Al Elliott
Form builder

Install the Spatie tags package according to the instructions.

https://filamentphp.com/docs/2.x/spatie-laravel-tags-plugin/installation

As standard, you can create or apply tags without types. But I wanted to separate tags into types, so this is what I did.

Using tags with ‘type’

This requires a new model to be created.

This new model will extend the Spatie\Tag model, and be used whenever you want to get tags of this type.

Step 1: Create new model for the type

E.g, If you want to have a tag type of ‘skill’, create a new model called Skill.php

You need to extend Spatie\Tag model and set the table name like this:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Spatie\Tags\Tag as SpatieTag;
 
class Skill extends SpatieTag
{
use HasFactory; //optional
 
protected $table = 'tags';
}

Step 2: Constrain by the type

Now you need to add a booted method to both scope the retrieval of tags, and intercept the creating of tags to add the tag type.

This goes in your new ‘tag’ model (the one you just created above).

In the example below, I want the type to be skill. (Replace this with whatever you want your type to be.)

// Be sure to import this class:
use Illuminate\Database\Eloquent\Builder;
 
class Skill extends SpatieTag
{
 
protected static function booted()
{
// Only apply when there's an authenticated user
if (auth()->check()) {
static::addGlobalScope('skills', function (Builder $builder) {
$builder->where('type', 'skill');
});
 
static::creating(function ($model) {
$model->type = 'skill';
});
}
}
}

Step 03: Use your new model in Spatie Tags package

Now go to the model that you want to associate with this new tag type.

Firstly, if you haven’t already, ensure you are using the HasTags trait from the Spatie Tags package.

use Spatie\Tags\HasTags;
 
class Guest extends Model
{
use HasFactory, SoftDeletes, **HasTags**;

Then add a method to this class to tell Spatie Tag package that you are using a new model.

use Spatie\Tags\HasTags;
 
class Guest extends Model
{
use HasFactory, SoftDeletes, HasTags;
 
**public static function getTagClassName(): string
{
return Skill::class;
}**

Step 4: Define the new relationship

In my example, I wanted a guest to have skills.

So in my Guest.php model, I add the following method:

public function skills(): MorphToMany
{
return $this
->morphToMany(self::getTagClassName(), 'taggable', 'taggables', null, 'tag_id')
->orderBy('order_column');
}

NOTE: The original tags() method still exists in the trait, but won’t work unless you redefine the relationship.

So if you get an error message like the following, then you’ve forgotten to add this last step!

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'taggables.skill_id' in 'field list'

That’s it. You can now use this new skills() relationship in the select form field.

Select::make('skills')
->relationship('skills', 'name') //The relationship you just created
->createOptionForm([
TextInput::make('name')
->required()
])
->multiple()
->preload()
->placeholder('Type to search or add new using \'+\' button on the right'),

No comments yet…