DEV Community

Benjamin Delespierre
Benjamin Delespierre

Posted on

4 2

Simple tag trait for Laravel models

Tags are very common in any application. This snippet leverages Eloquent's polymorphism to give any model taggable capabilities.

Usage

$model->addTag('trending')->addTag('wonderful');
$model->hasTag('trending'); // true
$model->hasAnyTag('important', 'valuable', 'woderful'); // true
$model->removeTag('trending');
Enter fullscreen mode Exit fullscreen mode

Implementation

app/HasTags.php

<?php

namespace App;

use App\Tag;
use Illuminate\Support\Arr;

trait HasTags
{
    public function tags()
    {
        return $this->morphToMany(Tag::class, 'taggable');
    }

    public function hasTag($tag): bool
    {
        return $this->tags()->get()->contains(Tag::wrap($tag));
    }

    public function hasAnyTag(...$tags): bool
    {
        foreach (Arr::flatten($tags) as $tag) {
            if ($this->hasTag($tag)) {
                return true;
            }
        }

        return false;
    }

    public function addTag(...$tag): void
    {
        foreach (Arr::flatten($tag) as $tag) {
            $this->tags()->attach(Tag::wrap($tag));
        }
    }

    public function removeTag(...$tag): void
    {
        foreach (Arr::flatten($tag) as $tag) {
            $this->tags()->detach(Tag::wrap($tag));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

app/Tag.php

<?php

namespace App;

use App\HasName;
use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    use HasName;

    protected $fillable = [
        'name',
    ];

    public function __toString()
    {
        return (string) $this->name;
    }

    public function posts()
    {
        return $this->morphedByMany(Post::class, 'taggable');
    }

    public function setNameAttribute($value)
    {
        if (! preg_match($regex = '/^[a-z0-9_]+$/', $value)) {
            throw new \InvalidArgumentException("invalid tag format: {$value} (it should match $regex)");
        }

        $this->attributes['name'] = $value;
    }
}
Enter fullscreen mode Exit fullscreen mode

Note: for the HasName trait, see this post


app/Post.php

<?php

namespace App;

class Post
{
    use Taggable;

    // ...
}
Enter fullscreen mode Exit fullscreen mode

Bonus

Here's a JavaScript function to cleanup a tag input in the front-end:

function cleanupTag(tag) {
    // see https://stackoverflow.com/a/37511463/381220
    return tag.toLowerCase()
        .normalize("NFD").replace(/[\u0300-\u036f]/g, "")
        .replace(/[^a-z0-9_]*/g, '');
}
Enter fullscreen mode Exit fullscreen mode

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay