mujahidabbas/laravel-user-tags
Laravel User Tags lets you attach custom tags to user models, making it easy to categorize, filter, and segment users. It offers a simple API for adding, removing, and querying tags, so you can organize users with lightweight labels throughout your Laravel application.
v1.0.0 is the initial release, so there is no upgrade/migration path to consider yet. Treat it as a focused per-user tagging layer for Eloquent models: useful for favorites, bookmarks, inbox labels, saved filters, and other “this user marked this record” workflows.
Start here:
HasUserTags trait to the models that should be taggable.auth() or an explicit user argument.class Post extends Model
{
use HasUserTags; // import the trait namespace from the package README
}
Typical authenticated-user flow:
$post->attachUserTag('favorite');
$favorites = Post::withAnyUserTags(['favorite'])->get();
The first valuable use case is per-user favorites or labels on shared records. Do not use this as a replacement for a global taxonomy unless you intentionally want each user’s tags isolated from everyone else’s.
Use the three write methods as the core mutation API:
$post->attachUserTag('important');
$post->detachUserTag('important');
$post->syncUserTags(['important', 'reviewed']);
Use the query scopes for user-specific filtering:
Post::withAnyUserTags(['important', 'reviewed'])->get();
Post::withAllUserTags(['important', 'reviewed'])->get();
Post::withoutUserTags(['archived'])->get();
A common controller pattern:
public function index(Request $request)
{
$validated = $request->validate([
'tags' => ['sometimes', 'array'],
'tags.*' => ['string', 'max:50'],
]);
$tags = $validated['tags'] ?? [];
return Post::query()
->when($tags, fn ($query) => $query->withAnyUserTags($tags))
->paginate();
}
For “replace this user’s tags on this record” flows, wrap syncUserTags() in a transaction and authorize the action before mutating:
DB::transaction(function () use ($post, $request) {
$post->syncUserTags($request->array('tags'));
});
For tests, assert isolation explicitly. The key behavior is not just “the tag exists,” but “user A’s tags do not affect user B’s queries”:
$this->actingAs($userA);
$post->attachUserTag('favorite');
$this->actingAs($userB);
$this->assertSame(0, Post::withAnyUserTags(['favorite'])->count());
If the package exposes a tag relationship or query builder relation, use it for display screens to avoid repeatedly issuing per-model tag queries. If it does not, keep read paths query-scope based and avoid loading tags one model at a time.
auth()->user() implicitly or require an explicit user argument. This matters most in jobs, commands, tests, and service classes where no authenticated user exists.withAnyUserTags([]), withAllUserTags([]), withoutUserTags([]), and syncUserTags([]) do in your installed version. The empty-list case is a common source of accidental “return everything” or “remove everything” bugs.composer.json until you have reviewed subsequent releases and confirmed backward compatibility.How can I help you explore Laravel packages today?