atannex / tagging
Contextual taxonomies and polymorphic tagging for Laravel: tenants, aliases, hierarchy, translations, weighted and expiring assignments.
Requires
- php: ^8.2
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/events: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.24
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-28 02:01:15 UTC
README
A contextual vocabulary engine for Laravel. Attach tags, categories, skills, classifications, or editorial labels to any Eloquent model, with explicit tenant, taxonomy, and assignment-context boundaries.
Tag identity is tenant + taxonomy + normalized slug. Assignment identity is tag + morph type + subject key + context; source labels never create a second assignment identity. TagSpace is immutable and keeps tenant context explicit in requests, queues, and long-running workers.
It has no dependencies on other Atannex packages.
Installation
composer require atannex/tagging php artisan migrate php artisan vendor:publish --tag=tagging-config
Provider/facade discovery is automatic. Migrations load automatically; publishing migrations is optional. Do not load both published and package migrations: set tagging.load_migrations=false when managing published copies.
Quick start
use Atannex\Tagging\Concerns\HasTags; use Illuminate\Database\Eloquent\Model; class Article extends Model { use HasTags; } $article = Article::findOrFail($id); $article->attachTags(['Laravel', 'Architecture']); $article->syncTags(['PHP', 'Laravel']); Article::withAllTags(['PHP', 'Laravel'])->paginate();
For an explicit space:
use Atannex\Tagging\Facades\Tagging; $topics = Tagging::for('tenant-42', 'topics', 'editorial'); $topics->attach($article, ['Architecture'], ['weight' => 2.5, 'source' => 'editor']); $topics->tags($article); $loaded = $topics->eagerLoad(Article::limit(50)->get()); $loaded->tags($article); // Use a model included in the loaded collection.
Defaults remain tenant '', taxonomy tags, context default. Strict tenant selection, ownership integration, taxonomy constraints, review protection, and persisted provenance are opt-in. Partitioning is not authorization: authorize the business action in your application and choose the space from trusted state.
Compatibility and verification
- Laravel 13 requires PHP 8.3+. Laravel 12 retains PHP 8.2+ support.
- Laravel 11 remains a legacy compatibility line, not a security-supported recommendation.
- Real MySQL 8.4 and PostgreSQL 16 suites use independent processes and observed database lock waits. SQLite regression tests are not presented as race verification.
- Tested on PHP 8.2–8.5 with Laravel 11–13, on SQLite, MySQL 8.4 and PostgreSQL 16.
Security
Report vulnerabilities privately; see SECURITY.md.
Package layout
| Path | Contents |
|---|---|
src/Tagging.php, src/TagSpace.php |
Entry point and the immutable tag space every API hangs off |
src/Concerns, src/Models, src/Relations |
HasTags trait, Tag / Assignment models, the string-key morph relation |
src/Query |
Matching, facets, eager loading, assignment filters |
src/Vocabulary |
Lifecycle, merge/delete, import/export, health checks, duplicate suggestions |
src/Bulk, src/Automation, src/Jobs |
Bulk tagging over queries, rule-based auto-tagging, the queued TagSubject job |
src/Governance, src/Taxonomy, src/Tenancy, src/Hierarchy |
Assignment policies, taxonomy definitions, tenant guards, hierarchy strategy |
src/History, src/Events, src/Operations |
Review decisions and provenance, domain events, pruning and diagnostics |
src/Contracts, src/Exceptions, src/Rules, src/Console |
Extension points, exceptions, the TagSelection validation rule, Artisan commands |
config, database/migrations, lang |
Published configuration, schema, validation messages |
The raw tagAssignments relationship intentionally includes every space, inactive tag, and expired assignment. Use the contextual APIs for tenant-aware reads. Direct model updates and raw SQL bypass domain rules and event/provenance coverage.