laraarabdev/laravel-turbo-tags
Laravel Turbo Tags is a lightweight package for adding fast, flexible tagging to your Eloquent models. Attach, sync, and query tags with minimal setup, clean syntax, and performance-friendly queries for small apps and large datasets alike.
TurboTags is a polymorphic tagging package for Laravel — attach tags to any Eloquent model, then query popular/recent tags. Laravel 13 support is in. Start here:
Install and migrate
composer require laraarabdev/laravel-turbo-tags
php artisan vendor:publish --tag="turbo-tags-migrations"
php artisan migrate
Publish config too (--tag="turbo-tags-config") if you want to tweak table names or cache TTLs.
Tag your first model — add the trait to the models that should be taggable:
use LaraArabDev\TurboTags\Concerns\HasTags;
class Post extends Model
{
use HasTags;
}
First use case — tagging a post and reading the "popular" list:
$post->attachTags(['laravel', 'php']);
$post->detachTags(['php']);
$post->syncTags(['laravel', 'eloquent']);
Tag::mostPopular(10); // ordered by usage count
Tag::recent(10); // newest tags
Tag::recentMostPopular(10); // recency-weighted popularity
Read the README's API reference first — it has EN + AR merged into one file with an FAQ, which is the fastest path to the exact method signatures. The HasReporting trait is where the reporting methods live if you need to extend them.
Tag input normalization. Do the normalization at the edge (form request / action), not in the controller — trim, lowercase, dedupe, and cap length before hitting syncTags(). The package resolves tag IDs internally, but garbage in is still garbage out.
Bulk attach vs. sync. Prefer syncTags() for editorial forms (checkboxes, tag inputs) so removals are respected. Use attachTags() for append-only flows (imports, webhooks). Never mix them in the same request path — you'll get confusing diffs.
Reporting in views. mostPopular() and recent() are read-heavy — cache them at the query layer rather than the view layer:
$popular = Cache::remember('tags.popular', now()->addHour(), fn () => Tag::mostPopular(20));
Wrap the invalidation in your tag mutation actions (attach/detach/sync) — the package's cache-flush safety was hardened in this release, but your app-level cache is your responsibility.
Polymorphic scope. Because tags are polymorphic, "popular" is global by default. If you need per-type popularity (e.g. popular tags for posts only), check whether the reporting methods accept a model-type constraint; if not, drop to a Tag::withCount([...]) query scoped by taggable_type.
Integration with filtering. Use tags as a filter dimension on index pages — query the tag once, then whereHas('tags', ...) on your model. Keep tag lookups by slug/name (whatever the package exposes) rather than by ID in URLs so links stay stable across re-seeds.
Seeding and fixtures. Tags are data, not config. Seed a canonical set in a dedicated seeder, and use firstOrCreate semantics via syncTags() in tests to keep fixtures idempotent.
New package, pre-1.0. v0.1.0 is the first release after a scaffold → implement → revert → reapply cycle. Pin the version ("~0.1.0") and read the diff before bumping. Expect naming churn before 1.0.
Tag ID resolution. The release notes flag "improve cache flush safety and tag ID resolution" — this is the area where bugs surfaced. If you see duplicate tags or stale counts after concurrent writes, that's the first place to look. Wrap multi-tag mutations in DB::transaction() and flush your app-level caches in the same transaction boundary.
"Recent" is ambiguous. Confirm from the README whether recent() sorts by tag creation time or by last attached time — they produce very different UX for a tag cloud. Test it explicitly with a fixture.
mostPopular performance. Popularity is a count over a polymorphic pivot. On large datasets add indexes on (taggable_type, taggable_id) and on the tag foreign key. Check the migration before prod deploy; don't assume indexes are there.
Cache invalidation. The package's cache flush is internal — your Cache::remember() layer around reporting calls is not. Centralize tag mutation in a single action class so you only have one place to forget stale caches.
Mutation-testing CI. This repo uses Pest mutate (replacing Infection mid-release). That's a sign the maintainers care about test quality, but also that CI config was in flux — if you're forking or contributing, run the workflow locally first.
Extension points. HasReporting is an extracted trait — if you need custom ranking (e.g. weighted by recency and a manual "featured" flag), you can compose your own trait alongside it instead of forking the package. Keep custom ranking in app code, not vendor patches.
Arabic docs are first-class. The merged README includes Arabic; if your team is bilingual, the AR section is maintained, not an afterthought. Useful when onboarding.
How can I help you explore Laravel packages today?