Open source
Tags for Laravel
composer require roundly-consulting/tags-for-laravelOverview
Typed taxonomies for any Eloquent model. Declare each kind of tag once — a curated category tree with one genre per concert, free-form labels up to five, a skills tree shared by candidates and job ads — and the same rules hold in your code, your forms, your queued jobs and the opt-in JSON API. Names are translatable with one slug per locale, tags are global or owned by a team or tenant, and reads, query scopes, autocomplete and usage counts never N+1. MIT-licensed and built only on official Laravel components and Roundly’s own translatable, sluggable, enums and query-builder packages — no third-party tagging vendor.
What you get
Typed taxonomies
Declare each kind of tag once — a flat label list or a category tree, one per model or up to a max, curated or free-form.
One tag per meaning, every locale
Rock, rock and ROCK! are one tag; C, C++ and C# stay three. Translatable names, one slug per locale and stable slugs for URLs that never move.
Global or tenant-scoped
Tags shared by everyone or owned by a team, client or project — a scope never sees, uses or edits another scope’s tags.
Category trees
Nest, move, reorder, delete with refuse, reparent or the whole branch, and match “everything under Music” in one query.
Queries and discovery
Any, all and none scopes with descendants, a page of models with their tags in two queries, autocomplete across locales and popularity.
Forms, filters and an opt-in API
A validation rule, a query-builder filter, a summary resource and a JSON API that never names a PHP class or leaks another tenant’s tag.
Facade, DI or actions
One Tags facade, an injectable manager or single-purpose actions — and a recording fake that still performs every call.
Documentation
Use cases
Where the package fits — events, publishing, help desks, shops, job boards and multi-tenant SaaS — and the shipped capability behind each one.
Installation
Install via Composer, set key types and table names, publish and run the migrations, check your locales and make a model taggable.
Configuration
Every config key with its env variable and default — key types, tables, types, names and slugs, limits, API aliases, abilities and routes.
Tag types
Declare typed taxonomies in code or config — trees or flat lists, single or limited, curated or free-form — and adjust any package’s types.
Taggable models
The Taggable contract and HasTags trait — tagScope() and tagTypes(), the read-only tags() relation, factories and what deleting a model does.
The Tags facade
Tour the Tags facade — define and types, the type() catalog handle, the for($model) handle, flat tagging verbs, builders and data objects.
DI and actions
Skip the facade: inject TagsManager or call a single-purpose action — every action with its execute() signature and facade path.
Tagging models
Attach, detach, sync and clear by model, key, slug or name — limits, curated lists, on-the-fly creation, TagChanges and reads without N+1.
Names, slugs and translations
One tag per meaning — normalised names, per-locale slugs along the locale chain, duplicates and reserved names, stable and manual slugs, colours and icons.
Managing tags
Create, seed idempotently, find, rename, recolour, delete and restore tags through the catalog — plus the Tag model’s columns, relations and scopes.
Category trees
Nest tags under parents, move and reorder them, delete with refuse, reparent or the whole subtree, and read trees and breadcrumbs in one query.
Tenant-scoped tags
Tags owned by a team, client or project next to shared global ones — scoped catalogs, strict write isolation, global-only types and scoped queries.
Querying models
withAnyTags, withAllTags, withoutTags and withTagsOf — with descendants and scopes — plus the inverse taggedModels() relation.
Autocomplete and popular tags
suggest() matches slug prefixes in every locale, then names — popular() ranks by usage count or live per class, limited to records you may show.
Validation, filters and resources
TagsRule for form requests, TagsFilter for query-builder index endpoints and TagSummaryResource for nesting tags in your own API resources.
JSON API
An opt-in JSON API — types, lists and trees, suggest and popular, manager CRUD with moves and reorders, gated by your abilities, with no class names.
Concurrency and integrity
How tagging, tree and catalog writes stay correct under parallel writers — a fixed lock order, one key-ordered sweep, savepoints and deadlock retries.
Events
Eight after-commit events — Tagged, Untagged and six catalog events — for search indexes, caches, audit logs and webhooks.
Exceptions
Every TagsException with its stable error code and HTTP status — plus how to present them through errorCode(), clientMessage() and status().
Artisan commands
tags:types lists every type, tags:doctor checks data and setup, tags:repair fixes paths, scope keys, orphans and usage counts.
Testing
Tags::fake() records every operation and still performs it — every assertion and its negative twin, recorded operations, Pest matchers and the factory.
Requirements
PHP 8.4+, Laravel 12 or 13, PostgreSQL 12+ or MySQL 8.0.23+ (SQLite for tests), ext-json, ext-mbstring and five Roundly packages.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.