PHPackages                             wuwx/laravel-taxonomy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. wuwx/laravel-taxonomy

ActiveLibrary

wuwx/laravel-taxonomy
=====================

A Drupal-inspired taxonomy package for Laravel models.

v1.0.0(1mo ago)01↓100%MITPHPPHP ^8.2CI passing

Since Apr 7Pushed 1mo agoCompare

[ Source](https://github.com/wuwx/laravel-taxonomy)[ Packagist](https://packagist.org/packages/wuwx/laravel-taxonomy)[ RSS](/packages/wuwx-laravel-taxonomy/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (9)Versions (2)Used By (0)

Laravel Taxonomy
================

[](#laravel-taxonomy)

[![tests](https://github.com/wuwx/laravel-taxonomy/actions/workflows/tests.yml/badge.svg)](https://github.com/wuwx/laravel-taxonomy/actions/workflows/tests.yml)

A Drupal-inspired taxonomy package for Laravel applications. It provides:

- **Vocabularies** via `Taxonomy` — group terms into categories, tags, locations, etc.
- **Hierarchical terms** via `Term` — powered by `kalnoy/nestedset` for efficient tree queries
- **Polymorphic assignment** — attach terms to any Eloquent model
- **Rich query scopes** — `withAnyTerms`, `withAllTerms`, `withoutTerms`, `byTaxonomies`
- **Translations** — `name` and `description` are translatable via `spatie/laravel-translatable`
- **Slug auto-generation** — powered by `spatie/laravel-sluggable` with scoped uniqueness
- **Events** — `TermAttached`, `TermDetached`, `TermsSynced` dispatched automatically
- **Pivot data** — `order` and `metadata` on the pivot table
- **Artisan commands** — `taxonomy:list`, `taxonomy:tree`, `taxonomy:create-term`

Installation
------------

[](#installation)

```
composer require wuwx/laravel-taxonomy
php artisan laravel-taxonomy:install
```

If you prefer manual setup:

```
php artisan vendor:publish --tag=laravel-taxonomy-config
php artisan vendor:publish --tag=laravel-taxonomy-migrations
php artisan migrate
```

Quick Start
-----------

[](#quick-start)

Attach the `HasTaxonomyTerms` trait to a model:

```
use Wuwx\LaravelTaxonomy\Traits\HasTaxonomyTerms;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasTaxonomyTerms;
}
```

Create a vocabulary and some terms:

```
use Wuwx\LaravelTaxonomy\Models\Taxonomy;

$topics = Taxonomy::query()->create(['name' => 'Topics']);

$php = $topics->createTerm(['name' => 'PHP']);
$laravel = $topics->createTerm(['name' => 'Laravel'], parent: $php);
```

Assign and query:

```
$post->attachTerm($php);
$post->attachTerms(['php', 'laravel'], taxonomy: 'topics');

Post::withAnyTerms(['php', 'laravel'], taxonomy: 'topics')->get();
```

Taxonomies And Terms
--------------------

[](#taxonomies-and-terms)

Create a taxonomy (slug is auto-generated if omitted):

```
$topics = Taxonomy::query()->create([
    'name' => 'Topics',
    'description' => 'Development topics',
    'is_hierarchical' => true,
]);
```

Create root and child terms:

```
$backend = $topics->createTerm(['name' => 'Backend']);
$php = $topics->createTerm(['name' => 'PHP'], parent: $backend);
$laravel = $topics->createTerm(['name' => 'Laravel'], parent: $php);
```

Look up terms:

```
$topics->findTermBySlug('php');
$topics->rootTerms();
```

Slugs are auto-generated and unique — within the same taxonomy, duplicate names produce `php`, `php-1`, `php-2`, etc. Taxonomy slugs are globally unique.

If a taxonomy is not hierarchical, creating child terms will throw an `InvalidArgumentException`.

Assigning Terms To Models
-------------------------

[](#assigning-terms-to-models)

```
$post->attachTerm($php);
$post->attachTerm('laravel', taxonomy: 'topics');
$post->attachTerms([$php, $laravel]);

$post->syncTerms(['php', 'laravel'], taxonomy: 'topics');
$post->syncTerms(['php'], taxonomy: 'topics', detaching: false);

$post->detachTerm('laravel', taxonomy: 'topics');
$post->detachTerms(['php', 'laravel'], taxonomy: 'topics');
$post->detachAllTerms();
```

String-based term resolution requires a taxonomy:

```
$post->attachTerm('laravel', taxonomy: 'topics');
$post->attachTerms(['php', 'laravel'], taxonomy: $topics);
```

### Pivot Data

[](#pivot-data)

Attach terms with extra pivot data (`order` and `metadata` columns):

```
$post->attachTerm($php, pivot: ['order' => 1, 'metadata' => json_encode(['primary' => true])]);
$post->attachTerms([$php, $laravel], pivot: ['order' => 5]);

$post->terms->first()->pivot->order;    // 1
$post->terms->first()->pivot->metadata; // '{"primary":true}'
```

Checking Attached Terms
-----------------------

[](#checking-attached-terms)

```
$post->hasTerm($php);
$post->hasTerm('laravel', taxonomy: 'topics');

$post->hasAnyTerms(['php', 'go'], taxonomy: 'topics');
$post->hasAllTerms(['php', 'laravel'], taxonomy: 'topics');
```

Unknown terms resolve to `false`.

Querying Models By Terms
------------------------

[](#querying-models-by-terms)

```
Post::whereHasTerm('laravel', taxonomy: 'topics')->get();

Post::withAnyTerms(['php', 'laravel'], taxonomy: 'topics')->get();
Post::withAllTerms(['php', 'laravel'], taxonomy: 'topics')->get();

Post::withoutTerms(['deprecated'], taxonomy: 'statuses')->get();
Post::withoutAnyTerms()->get();
```

### Multi-Taxonomy Filtering

[](#multi-taxonomy-filtering)

Filter by multiple vocabularies at once — AND between vocabularies, OR within:

```
Post::byTaxonomies([
    'topics' => ['php', 'laravel'],   // has php OR laravel
    'cities' => ['shanghai'],          // AND has shanghai
])->get();
```

Translations
------------

[](#translations)

`name` and `description` are translatable via `spatie/laravel-translatable`:

```
$topics = Taxonomy::query()->create([
    'name' => ['en' => 'Topics', 'zh' => '主题'],
    'description' => ['en' => 'Blog topics', 'zh' => '博客主题'],
]);

$php = $topics->createTerm([
    'name' => ['en' => 'PHP', 'zh' => 'PHP 编程'],
]);

app()->setLocale('zh');
$topics->name; // '主题'
$php->name;    // 'PHP 编程'
```

Single-language usage works as before — just pass a plain string:

```
$topics = Taxonomy::query()->create(['name' => 'Topics']);
```

Working With Trees
------------------

[](#working-with-trees)

`Term` uses `kalnoy/nestedset` internally, so all tree operations are single-query, not recursive.

```
$laravel->parent;
$php->children()->get();
$backend->descendants()->get();

$laravel->ancestors()->get();
$laravel->siblings()->get();

$backend->isRoot();
$laravel->isLeaf();

$backend->isAncestorOf($laravel);
$laravel->isDescendantOf($backend);

$backend->ancestors()->count();  // 0
$php->ancestors()->count();      // 1
$laravel->ancestors()->count();  // 2
```

Build tree structures for menus, navigation, or selects:

```
$tree = $topics->toTree();         // nested with children relations
$flatTree = $topics->toFlatTree(); // flat list with computed depth attribute
```

Events
------

[](#events)

All attach/detach/sync operations dispatch events:

OperationEvent`attachTerm` / `attachTerms``TermAttached``detachTerm` / `detachTerms` / `detachAllTerms``TermDetached``syncTerms``TermsSynced````
use Wuwx\LaravelTaxonomy\Events\TermAttached;

Event::listen(TermAttached::class, function (TermAttached $event) {
    // $event->model   — the Eloquent model
    // $event->termIds — array of attached term IDs
});
```

`TermsSynced` also includes `$event->changes` with `attached`, `detached`, and `updated` arrays.

Artisan Commands
----------------

[](#artisan-commands)

```
php artisan taxonomy:list                              # list all taxonomies with term counts
php artisan taxonomy:tree topics                       # tree view of a taxonomy's terms
php artisan taxonomy:create-term topics "PHP"           # create a term
php artisan taxonomy:create-term topics "Laravel" --parent=php  # create a child term
```

Configuration
-------------

[](#configuration)

The default config file is `config/laravel-taxonomy.php`:

```
return [
    'table_names' => [
        'taxonomies' => 'taxonomies',
        'terms' => 'taxonomy_terms',
        'morph_pivot' => 'termables',
    ],

    'models' => [
        'taxonomy' => Taxonomy::class,
        'term' => Term::class,
    ],
];
```

Both `Taxonomy` and `Term` support Route Model Binding via `slug` by default.

Testing
-------

[](#testing)

```
vendor/bin/pint --test
vendor/bin/phpunit
```

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance93

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 90.5% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

35d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9c1977bba4586c70a4bdb6faaee4823184904ff3bd3246e79069871465500611?d=identicon)[wuwx](/maintainers/wuwx)

---

Top Contributors

[![wuwx](https://avatars.githubusercontent.com/u/4401?v=4)](https://github.com/wuwx "wuwx (19 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (2 commits)")

---

Tags

laraveldrupaltagscategoriestaxonomy

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/wuwx-laravel-taxonomy/health.svg)

```
[![Health](https://phpackages.com/badges/wuwx-laravel-taxonomy/health.svg)](https://phpackages.com/packages/wuwx-laravel-taxonomy)
```

###  Alternatives

[spatie/laravel-activitylog

A very simple activity logger to monitor the users of your website or application

5.8k45.4M309](/packages/spatie-laravel-activitylog)[spatie/laravel-health

Monitor the health of a Laravel application

85810.0M83](/packages/spatie-laravel-health)[clickbar/laravel-magellan

This package provides functionality for working with the postgis extension in Laravel.

423715.4k1](/packages/clickbar-laravel-magellan)[aliziodev/laravel-taxonomy

Laravel Taxonomy is a flexible and powerful package for managing taxonomies, categories, tags, and hierarchical structures in Laravel applications. Features nested-set support for optimal query performance on hierarchical data structures.

23318.4k](/packages/aliziodev-laravel-taxonomy)[outerweb/settings

Application wide settings stored in your database

4899.2k5](/packages/outerweb-settings)[io238/laravel-iso-countries

Ready-to-use Laravel models and relations for country (ISO 3166), language (ISO 639-1), and currency (ISO 4217) information with multi-language support.

5462.3k](/packages/io238-laravel-iso-countries)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
