roundly-consulting / tags-for-laravel
Typed taxonomies for any Eloquent model: flat or nested, single- or multi-select, curated or free-form, tenant-scoped and translatable, with autocomplete, usage counts and query scopes.
Package info
github.com/roundly-consulting/tags-for-laravel
pkg:composer/roundly-consulting/tags-for-laravel
Fund package maintenance!
Requires
- php: ^8.4
- ext-json: *
- ext-mbstring: *
- illuminate/auth: ^12.0|^13.0
- illuminate/cache: ^12.0|^13.0
- illuminate/console: ^12.0|^13.0
- illuminate/container: ^12.0|^13.0
- illuminate/contracts: ^12.0|^13.0
- illuminate/database: ^12.0|^13.0
- illuminate/events: ^12.0|^13.0
- illuminate/http: ^12.0|^13.0
- illuminate/routing: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- illuminate/validation: ^12.0|^13.0
- roundly-consulting/enums-for-laravel: ^1.1.0
- roundly-consulting/package-toolkit-for-laravel: ^1.4.0
- roundly-consulting/query-builder-for-laravel: ^1.2.1
- roundly-consulting/sluggable-for-laravel: ^1.1.3
- roundly-consulting/translatable-for-laravel: ^1.1.2
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- nunomaduro/collision: ^8.5
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- roundly-consulting/testing-for-laravel: ^1.6.1
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Tags for Laravel
Typed taxonomies for any Eloquent model: flat labels or nested category trees, single- or multi-select, curated or free-form, global or tenant-scoped, and translatable in every locale — with autocomplete, usage counts and query scopes that never N+1.
Installation
Requires PHP 8.4, Laravel 12 or 13, and PostgreSQL 12+ or MySQL 8.0.23+.
composer require roundly-consulting/tags-for-laravel
php artisan vendor:publish --tag="tags-migrations"
php artisan migrate
Tag names are stored per locale: make sure your app locale is in translatable.locales (default en, sk).
Usage
Make a model taggable:
use Illuminate\Database\Eloquent\Model; use RoundlyConsulting\Tags\Concerns\HasTags; use RoundlyConsulting\Tags\Contracts\Taggable; final class Concert extends Model implements Taggable { use HasTags; }
Declare your types, build a tree, tag, query and suggest:
use RoundlyConsulting\Tags\Facades\Tags; // Declare your tag types once, in a service provider Tags::define('genre')->hierarchical()->single(); // a curated category tree, one per model Tags::define('label')->max(5); // free-form labels, up to five // Build the tree (names are translatable; a plain string is the current locale) $music = Tags::type('genre')->make(['en' => 'Music', 'sk' => 'Hudba'])->color('#f24e29')->create(); $jazz = Tags::type('genre')->make('Jazz')->under($music)->create(); // Tag any model that uses HasTags $concert->syncTags($jazz, 'genre'); $concert->attachTags(['Live', 'Outdoor'], 'label'); // unknown labels are created on the fly // Query: "music" also matches everything under it Concert::withAnyTags(['music'], 'genre', descendants: true)->get(); Concert::withAllTags(['live', 'outdoor'], 'label')->get(); // Read without N+1 foreach (Concert::with('tags')->get() as $concert) { echo $concert->tagOf('genre')?->name; // Jazz echo $concert->tagsOf('label')->pluck('name')->join(', '); // Live, Outdoor } // Autocomplete and popularity Tags::type('label')->suggest('liv'); // prefix matches first, in every locale Tags::type('label')->popular(10);
Documentation
The full documentation — configuration, every feature and its API, and testing — lives on our website: roundly-consulting.com/open-source/docs/tags-for-laravel
Release notes are in CHANGELOG.md. To contribute, see the contributing guide.
Support our work
This package is free and open source, built and maintained by Roundly Consulting. If it saves you time, please consider supporting our open-source work — a one-time donation, a monthly pledge on Patreon or a crypto donation helps fund maintenance, new features and new packages.
License
The MIT License (MIT). See LICENSE.md.