PHPackages                             aliziodev/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. [Utility &amp; Helpers](/categories/utility)
4. /
5. aliziodev/laravel-taxonomy

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

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.

v2.8.0(5mo ago)23318.4k—0%11[1 issues](https://github.com/aliziodev/laravel-taxonomy/issues)MITPHPPHP ^8.2CI passing

Since May 28Pushed 5mo ago4 watchersCompare

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

READMEChangelog (10)Dependencies (10)Versions (24)Used By (0)

[![Laravel Taxonomy](https://raw.githubusercontent.com/aliziodev/laravel-taxonomy/refs/heads/master/art/new-header.svg)](https://raw.githubusercontent.com/aliziodev/laravel-taxonomy/refs/heads/master/art/new-header.svg)

 [![codecov](https://camo.githubusercontent.com/40d195c9349cb588e0318c9146c08289ecd1f3af9a7ae45f8f13f1a291bb0288/68747470733a2f2f636f6465636f762e696f2f67682f616c697a696f6465762f6c61726176656c2d7461786f6e6f6d792f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/aliziodev/laravel-taxonomy) [![Tests](https://github.com/aliziodev/laravel-taxonomy/workflows/Tests/badge.svg)](https://github.com/aliziodev/laravel-taxonomy/actions) [![Code Quality](https://github.com/aliziodev/laravel-taxonomy/workflows/Code%20Quality/badge.svg)](https://github.com/aliziodev/laravel-taxonomy/actions)
 [![Latest Version on Packagist](https://camo.githubusercontent.com/635209a6b668f136f50a1a4df49d732d992bcd67ff71b939c78647648d81da6c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616c697a696f6465762f6c61726176656c2d7461786f6e6f6d792e737667)](https://packagist.org/packages/aliziodev/laravel-taxonomy) [![Total Downloads](https://camo.githubusercontent.com/bf32f558126680576395df6dce358d8415f32a21cf0ee1b6b0e892703a73d409/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616c697a696f6465762f6c61726176656c2d7461786f6e6f6d792e737667)](https://packagist.org/packages/aliziodev/laravel-taxonomy) [![PHP Version](https://camo.githubusercontent.com/28640b3526d414726f4aec34b070535eee1e6ae37ed5e8c575710d48249b552e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f616c697a696f6465762f6c61726176656c2d7461786f6e6f6d792e737667)](https://packagist.org/packages/aliziodev/laravel-taxonomy) [![Laravel Version](https://camo.githubusercontent.com/f87456acad5ed125637df95f99e7fe32dab94b765f5537fcf594766e5e41a15c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31312e302532422d6f72616e67652e737667)](https://laravel.com/) [![Ask DeepWiki](https://camo.githubusercontent.com/0f5ae213ac378635adeb5d7f13cef055ad2f7d9a47b36de7b1c67dbe09f609ca/68747470733a2f2f6465657077696b692e636f6d2f62616467652e737667)](https://deepwiki.com/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.

📖 Documentation
---------------

[](#-documentation)

- [🇺🇸 English Documentation](README.md)
- [🇮🇩 Dokumentasi Bahasa Indonesia](README.id.md)

📋 Table of Contents
-------------------

[](#-table-of-contents)

- [Overview](#overview)
- [Key Features](#key-features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#%EF%B8%8F-configuration)
- [Quick Start](#-quick-start)
- [Basic Usage](#-basic-usage)
- [Hierarchical Data &amp; Nested Sets](#-hierarchical-data--nested-sets)
- [Metadata Support](#-metadata-support)
- [Bulk Operations](#-bulk-operations)
- [Caching &amp; Performance](#-caching--performance)
- [Custom Taxonomy Types](#%EF%B8%8F-custom-taxonomy-types)
- [Real-World Usage Scenarios](#-real-world-usage-scenarios)
- [Advanced Features](#-advanced-features)
- [Best Practices](#-best-practices)
- [Custom Slugs and Error Handling](#custom-slugs-and-error-handling)
- [Troubleshooting](#troubleshooting)
- [Security](#security)
- [Testing](#testing)
- [License](#license)

Overview
--------

[](#overview)

This package is ideal for:

- E-commerce category management
- Blog taxonomies
- Content organization
- Product attributes
- Dynamic navigation
- Any hierarchical data structure

Key Features
------------

[](#key-features)

### Core Functionality

[](#core-functionality)

- **Hierarchical Terms**: Create parent-child relationships between terms
- **Metadata Support**: Store additional data as JSON with each taxonomy
- **Term Ordering**: Control the order of terms with sort\_order
- **Polymorphic Relationships**: Associate taxonomies with any model
- **Multiple Term Types**: Use predefined types (Category, Tag, etc.) or create custom types
- **Composite Unique Slugs**: Slugs are unique within their type, allowing same slug across different types
- **Bulk Operations**: Attach, detach, sync, or toggle multiple taxonomies at once
- **Advanced Querying**: Filter models by taxonomies with query scopes

### Nested Set Features

[](#nested-set-features)

- **Tree Navigation**: Efficient ancestor and descendant queries
- **Tree Manipulation**: Move, insert, and reorganize tree nodes
- **Depth Management**: Track and query by hierarchy depth
- **Tree Validation**: Maintain tree integrity automatically
- **Efficient Queries**: Optimized database queries for hierarchical data

### Performance &amp; Scalability

[](#performance--scalability)

- **Caching System**: Improve performance with built-in caching
- **Database Indexing**: Optimized indexes for fast queries
- **Lazy Loading**: Efficient relationship loading
- **Tree Structure**: Get hierarchical or flat tree representations
- **Pagination Support**: Paginate results for better performance

### Developer Experience

[](#developer-experience)

- **Intuitive API**: Clean and expressive syntax
- **Comprehensive Documentation**: Detailed guides and examples
- **Type Safety**: Full support for Laravel's type system
- **Testing Support**: Built-in testing utilities

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11.0+ or 12.0+
- Composer 2.0+

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

[](#installation)

### Via Composer

[](#via-composer)

```
composer require aliziodev/laravel-taxonomy
```

### Publish Configuration and Migrations

[](#publish-configuration-and-migrations)

You can publish the configuration and migrations using the provided install command:

```
php artisan taxonomy:install
```

Or manually:

```
php artisan vendor:publish --provider="Aliziodev\LaravelTaxonomy\TaxonomyProvider" --tag="taxonomy-config"
php artisan vendor:publish --provider="Aliziodev\LaravelTaxonomy\TaxonomyProvider" --tag="taxonomy-migrations"
```

### Run Migrations

[](#run-migrations)

```
php artisan migrate
```

⚙️ Configuration
----------------

[](#️-configuration)

After publishing the configuration file, you can customize it in `config/taxonomy.php`. Here's a detailed explanation of each option:

```
return [
    // Database table names
    'table_names' => [
        'taxonomies' => 'taxonomies',      // Main taxonomy table
        'taxonomables' => 'taxonomables',  // Polymorphic pivot table
    ],

    // Primary key type for polymorphic relationships
    // Options: 'numeric' (default), 'uuid', 'ulid'
    'morph_type' => 'uuid',

    // Available taxonomy types (can be extended)
    'types' => collect(TaxonomyType::cases())->pluck('value')->toArray(),

    // Custom model binding (for extending the base Taxonomy model)
    'model' => \Aliziodev\LaravelTaxonomy\Models\Taxonomy::class,

    // Slug generation settings
    'slugs' => [
        'generate' => true,                // Auto-generate slugs from names
        'regenerate_on_update' => false,  // Regenerate slug when name changes
    ],
];
```

### Migration Autoload (Multi-tenant)

[](#migration-autoload-multi-tenant)

By default, the package registers its migration paths so they run automatically when you execute `php artisan migrate`. In multi-tenant applications, this can be undesired because migrations may run on the default connection.

You can disable migration autoloading via configuration:

```
// config/taxonomy.php
'migrations' => [
    'autoload' => false,            // Disable autoloading of package migrations
    'paths' => [                    // Optional: custom paths if you still want autoloading
        // database_path('migrations/tenants'),
    ],
],
```

Or with an environment variable:

```
TAXONOMY_AUTOLOAD_MIGRATIONS=false
```

With autoload disabled, run migrations explicitly per tenant or connection, for example:

```
php artisan migrate --path=database/migrations/tenants --database=tenant
```

You can also publish the package migrations and orchestrate them as needed:

```
php artisan vendor:publish --provider="Aliziodev\LaravelTaxonomy\TaxonomyProvider" --tag="taxonomy-migrations"
```

### Configuration Options Explained

[](#configuration-options-explained)

#### Table Names

[](#table-names)

Customize database table names if you need to avoid conflicts or follow specific naming conventions:

```
'table_names' => [
    'taxonomies' => 'custom_taxonomies',
    'taxonomables' => 'custom_taxonomables',
],
```

#### Morph Type

[](#morph-type)

Choose the appropriate morph type based on your model's primary key:

```
// For auto-incrementing integer IDs
'morph_type' => 'numeric',

// For UUID primary keys
'morph_type' => 'uuid',

// For ULID primary keys
'morph_type' => 'ulid',
```

#### Custom Types

[](#custom-types)

Extend or replace the default taxonomy types:

```
'types' => [
    'category',
    'tag',
    'brand',
    'collection',
    'custom_type',
],
```

#### Slug Configuration

[](#slug-configuration)

Control slug generation behavior:

```
'slugs' => [
    'generate' => false,               // Require manual slug input
    'regenerate_on_update' => true,   // Auto-update slugs when names change
],
```

#### Important: Composite Unique Constraint

[](#important-composite-unique-constraint)

Starting from version 2.3.0, slugs are unique within their taxonomy type, not globally. This means:

- ✅ You can have `slug: 'featured'` for both `Category` and `Tag` types
- ✅ Better flexibility for organizing different taxonomy types
- ⚠️ **Breaking Change**: If upgrading from v2.2.x, see [UPGRADE.md](UPGRADE.md) for migration instructions

```
// This is now possible:
Taxonomy::create(['name' => 'Featured', 'slug' => 'featured', 'type' => 'category']);
Taxonomy::create(['name' => 'Featured', 'slug' => 'featured', 'type' => 'tag']);
```

🚀 Quick Start
-------------

[](#-quick-start)

Get up and running with Laravel Taxonomy in minutes:

### 1. Create Your First Taxonomy

[](#1-create-your-first-taxonomy)

```
use Aliziodev\LaravelTaxonomy\Facades\Taxonomy;
use Aliziodev\LaravelTaxonomy\Enums\TaxonomyType;

// Create a category
$electronics = Taxonomy::create([
    'name' => 'Electronics',
    'type' => TaxonomyType::Category->value,
    'description' => 'Electronic products and gadgets',
]);

// Create a subcategory
$smartphones = Taxonomy::create([
    'name' => 'Smartphones',
    'type' => TaxonomyType::Category->value,
    'parent_id' => $electronics->id,
]);

// Create tags
$featured = Taxonomy::create([
    'name' => 'Featured',
    'type' => TaxonomyType::Tag->value,
]);
```

### 2. Associate with Models

[](#2-associate-with-models)

```
// Assuming you have a Product model with HasTaxonomy trait
$product = Product::create([
    'name' => 'iPhone 15 Pro',
    'price' => 999.99,
]);

// Attach taxonomies
$product->attachTaxonomies([$electronics->id, $smartphones->id, $featured->id]);

// Or attach by slug
$product->attachTaxonomies(['electronics', 'smartphones', 'featured']);
```

### 3. Query and Filter

[](#3-query-and-filter)

```
// Find products in electronics category
$products = Product::withTaxonomyType(TaxonomyType::Category)
    ->withTaxonomySlug('electronics')
    ->get();

// Get all taxonomies of a specific type
$categories = Taxonomy::findByType(TaxonomyType::Category);

// Get hierarchical tree
$categoryTree = Taxonomy::tree(TaxonomyType::Category);
```

📖 Basic Usage
-------------

[](#-basic-usage)

### Working with the Taxonomy Facade

[](#working-with-the-taxonomy-facade)

The `Taxonomy` facade provides a clean, expressive API for all taxonomy operations:

```
use Aliziodev\LaravelTaxonomy\Facades\Taxonomy;
use Aliziodev\LaravelTaxonomy\Enums\TaxonomyType;

// Create taxonomies
$category = Taxonomy::create([
    'name' => 'Books',
    'type' => TaxonomyType::Category->value,
    'description' => 'All kinds of books',
    'meta' => [
        'icon' => 'book',
        'color' => '#3498db',
        'featured' => true,
    ],
]);

// Find taxonomies
$taxonomy = Taxonomy::findBySlug('books');
$exists = Taxonomy::exists('books');
$categories = Taxonomy::findByType(TaxonomyType::Category);

// Search taxonomies
$results = Taxonomy::search('science', TaxonomyType::Category);

// Get hierarchical data
$tree = Taxonomy::tree(TaxonomyType::Category);
$flatTree = Taxonomy::flatTree(TaxonomyType::Category);
$nestedTree = Taxonomy::getNestedTree(TaxonomyType::Category);
```

### Working with Model Relationships

[](#working-with-model-relationships)

Once you've added the `HasTaxonomy` trait to your models, you get access to powerful relationship methods:

```
// Basic operations
$product->attachTaxonomies($taxonomyIds);
$product->detachTaxonomies($taxonomyIds);
$product->syncTaxonomies($taxonomyIds);
$product->toggleTaxonomies($taxonomyIds);

// Type-specific operations (NEW)
$product->attachTaxonomiesOfType(TaxonomyType::Category, $categoryIds);
$product->detachTaxonomiesOfType(TaxonomyType::Category, $categoryIds);
$product->syncTaxonomiesOfType(TaxonomyType::Category, $categoryIds);
$product->toggleTaxonomiesOfType(TaxonomyType::Tag, $tagIds);

// Check relationships
$hasCategory = $product->hasTaxonomies($categoryIds);
$hasAllTags = $product->hasAllTaxonomies($tagIds);
$hasType = $product->hasTaxonomyType(TaxonomyType::Category);

// Type-specific relationship checks (NEW)
$hasCategories = $product->hasTaxonomiesOfType(TaxonomyType::Category, $categoryIds);
$hasAllCategories = $product->hasAllTaxonomiesOfType(TaxonomyType::Category, $categoryIds);

// Get related taxonomies
$allTaxonomies = $product->taxonomies;
$categories = $product->taxonomiesOfType(TaxonomyType::Category);
$hierarchical = $product->getHierarchicalTaxonomies(TaxonomyType::Category);

// Utility methods (NEW)
$categoryCount = $product->getTaxonomyCountByType(TaxonomyType::Category);
$firstCategory = $product->getFirstTaxonomyOfType(TaxonomyType::Category);
```

### Query Scopes for Filtering

[](#query-scopes-for-filtering)

Filter your models using powerful query scopes:

```
// Filter by taxonomy type
$products = Product::withTaxonomyType(TaxonomyType::Category)->get();

// Filter by specific taxonomies
$products = Product::withAnyTaxonomies([$category1, $category2])->get();
$products = Product::withAllTaxonomies([$tag1, $tag2])->get();

// Type-specific filtering (NEW)
$products = Product::withAnyTaxonomiesOfType(TaxonomyType::Category, [$category1, $category2])->get();
$products = Product::withAllTaxonomiesOfType(TaxonomyType::Tag, [$tag1, $tag2])->get();
$products = Product::withoutTaxonomiesOfType(TaxonomyType::Category, [$category1])->get();

// Filter by taxonomy slug (any type)
$products = Product::withTaxonomySlug('electronics')->get();

// Filter by taxonomy slug with specific type (recommended)
$products = Product::withTaxonomySlug('electronics', TaxonomyType::Category)->get();

// Filter by hierarchy (includes descendants)
$products = Product::withTaxonomyHierarchy($parentCategoryId)->get();

// Filter by depth level
$products = Product::withTaxonomyAtDepth(2, TaxonomyType::Category)->get();

// Order by taxonomy type (NEW)
$products = Product::orderByTaxonomyType(TaxonomyType::Category)->get();
$products = Product::orderByTaxonomyType(TaxonomyType::Category, 'desc')->get();
```

#### Scope Chaining vs Single Scope with Type

[](#scope-chaining-vs-single-scope-with-type)

With the composite unique constraint, you have two approaches for filtering:

```
// Approach 1: Single scope with type parameter (Recommended)
// Finds products with taxonomy slug='electronics' AND type='category'
$products = Product::withTaxonomySlug('electronics', TaxonomyType::Category)->get();

// Approach 2: Chaining scopes (More flexible for complex queries)
// Finds products that have ANY taxonomy with type='category' AND ANY taxonomy with slug='electronics'
$products = Product::withTaxonomyType(TaxonomyType::Category)
    ->withTaxonomySlug('electronics')
    ->get();

// Note: These may return different results if a product has multiple taxonomies
```

### Pagination Support

[](#pagination-support)

The package supports pagination for search and find methods:

```
// Paginate search results (5 items per page, page 1)
$results = Taxonomy::search('electronic', null, 5, 1);

// Paginate taxonomies by type
$categories = Taxonomy::findByType(TaxonomyType::Category, 10, 1);

// Paginate taxonomies by parent
$children = Taxonomy::findByParent($parent->id, 10, 1);
```

### Complete Controller Example

[](#complete-controller-example)

Here's a comprehensive example of using Laravel Taxonomy in a controller:

```
namespace App\Http\Controllers;

use App\Models\Product;
use Aliziodev\LaravelTaxonomy\Enums\TaxonomyType;
use Aliziodev\LaravelTaxonomy\Facades\Taxonomy;
use Illuminate\Http\Request;

class ProductController extends Controller
{
    public function index(Request $request)
    {
        // Get products filtered by category
        $categorySlug = $request->input('category');
        $query = Product::query();

        if ($categorySlug) {
            $category = Taxonomy::findBySlug($categorySlug, TaxonomyType::Category);
            if ($category) {
                $query->withAnyTaxonomies($category);
            }
        }

        $products = $query->paginate(12);
        $categories = Taxonomy::findByType(TaxonomyType::Category);

        return view('products.index', compact('products', 'categories'));
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'categories' => 'required|array',
            'tags' => 'nullable|array',
        ]);

        $product = Product::create($validated);

        // Attach categories and tags
        $product->attachTaxonomies($validated['categories']);
        if (isset($validated['tags'])) {
            $product->attachTaxonomies($validated['tags']);
        }

        return redirect()->route('products.show', $product)
            ->with('success', 'Product created successfully.');
    }
}
```

🌳 Hierarchical Data &amp; Nested Sets
-------------------------------------

[](#-hierarchical-data--nested-sets)

Laravel Taxonomy uses the Nested Set Model for efficient hierarchical data management:

### Understanding Nested Sets

[](#understanding-nested-sets)

The nested set model stores hierarchical data using `lft` (left) and `rgt` (right) values, along with `depth` for each node. This allows for efficient querying of hierarchical relationships.

```
// Get all descendants of a taxonomy (children, grandchildren, etc.)
$descendants = $taxonomy->getDescendants();

// Get all ancestors of a taxonomy (parent, grandparent, etc.)
$ancestors = $taxonomy->getAncestors();

// Check hierarchical relationships
$isParent = $parent->isAncestorOf($child);
$isChild = $child->isDescendantOf($parent);

// Get the depth level
$level = $taxonomy->getLevel();

// Get only root taxonomies
$roots = Taxonomy::roots()->get();

// Get taxonomies at specific depth
$level2 = Taxonomy::atDepth(2)->get();
```

### Tree Operations

[](#tree-operations)

```
// Move a taxonomy to a new parent
Taxonomy::moveToParent($taxonomyId, $newParentId);

// Rebuild nested set values (useful after bulk operations)
Taxonomy::rebuildNestedSet(TaxonomyType::Category);

// Get different tree representations
$hierarchicalTree = Taxonomy::tree(TaxonomyType::Category);           // Parent-child relationships
$flatTree = Taxonomy::flatTree(TaxonomyType::Category);               // Flat list with depth info
$nestedTree = Taxonomy::getNestedTree(TaxonomyType::Category);        // Nested set ordered
```

📊 Metadata Support
------------------

[](#-metadata-support)

Store additional data with each taxonomy using JSON meta:

```
// Create taxonomy with meta
$category = Taxonomy::create([
    'name' => 'Premium Products',
    'type' => TaxonomyType::Category->value,
    'meta' => [
        'icon' => 'star',
        'color' => '#gold',
        'display_order' => 1,
        'seo' => [
            'title' => 'Premium Products - Best Quality',
            'description' => 'Discover our premium product collection',
            'keywords' => ['premium', 'quality', 'luxury'],
        ],
        'settings' => [
            'show_in_menu' => true,
            'featured' => true,
            'requires_auth' => false,
        ],
    ],
]);

// Access meta
$icon = $category->meta['icon'] ?? 'default';
$seoTitle = $category->meta['seo']['title'] ?? $category->name;

// Update meta
$category->update([
    'meta' => array_merge($category->meta ?? [], [
        'updated_at' => now()->toISOString(),
        'view_count' => ($category->meta['view_count'] ?? 0) + 1,
    ]),
]);
```

🔄 Bulk Operations
-----------------

[](#-bulk-operations)

Efficiently manage multiple taxonomy relationships:

### Basic Bulk Operations

[](#basic-bulk-operations)

```
// Attach multiple taxonomies (won't duplicate existing)
$product->attachTaxonomies([1, 2, 3, 'electronics', 'featured']);

// Detach specific taxonomies
$product->detachTaxonomies([1, 2]);

// Detach all taxonomies
$product->detachTaxonomies();

// Sync taxonomies (removes old, adds new)
$product->syncTaxonomies([1, 2, 3]);

// Toggle taxonomies (attach if not present, detach if present)
$product->toggleTaxonomies([1, 2, 3]);

// Work with different relationship names
$product->attachTaxonomies($categoryIds, 'categories');
$product->attachTaxonomies($tagIds, 'tags');
```

### Advanced Bulk Management

[](#advanced-bulk-management)

```
class BulkTaxonomyService
{
    public function bulkAttach(Collection $models, array $taxonomyIds): void
    {
        $data = [];
        $timestamp = now();

        foreach ($models as $model) {
            foreach ($taxonomyIds as $taxonomyId) {
                $data[] = [
                    'taxonomy_id' => $taxonomyId,
                    'taxonomable_id' => $model->id,
                    'taxonomable_type' => get_class($model),
                    'created_at' => $timestamp,
                    'updated_at' => $timestamp,
                ];
            }
        }

        DB::table('taxonomables')->insert($data);
    }

    public function bulkDetach(Collection $models, array $taxonomyIds): void
    {
        $modelIds = $models->pluck('id');
        $modelType = get_class($models->first());

        DB::table('taxonomables')
            ->whereIn('taxonomy_id', $taxonomyIds)
            ->whereIn('taxonomable_id', $modelIds)
            ->where('taxonomable_type', $modelType)
            ->delete();
    }

    public function bulkSync(Collection $models, array $taxonomyIds): void
    {
        $modelIds = $models->pluck('id');
        $modelType = get_class($models->first());

        // Remove existing associations
        DB::table('taxonomables')
            ->whereIn('taxonomable_id', $modelIds)
            ->where('taxonomable_type', $modelType)
            ->delete();

        // Add new associations
        $this->bulkAttach($models, $taxonomyIds);
    }

    public function migrateType(string $oldType, string $newType): int
    {
        return Taxonomy::where('type', $oldType)
            ->update(['type' => $newType]);
    }

    public function mergeTaxonomies(Taxonomy $source, Taxonomy $target): void
    {
        DB::transaction(function () use ($source, $target) {
            // Move all associations to target
            DB::table('taxonomables')
                ->where('taxonomy_id', $source->id)
                ->update(['taxonomy_id' => $target->id]);

            // Move children to target
            Taxonomy::where('parent_id', $source->id)
                ->update(['parent_id' => $target->id]);

            // Delete source taxonomy
            $source->delete();

            // Rebuild nested set for target's tree
            $target->rebuildNestedSet();
        });
    }
}
```

⚡ Caching &amp; Performance
---------------------------

[](#-caching--performance)

Laravel Taxonomy includes intelligent caching for optimal performance:

### Automatic Caching

[](#automatic-caching)

```
// These operations are automatically cached
$tree = Taxonomy::tree(TaxonomyType::Category);           // Cached for 24 hours
$flatTree = Taxonomy::flatTree(TaxonomyType::Category);   // Cached for 24 hours
$nestedTree = Taxonomy::getNestedTree(TaxonomyType::Category); // Cached for 24 hours
```

### Manual Cache Management

[](#manual-cache-management)

```
// Clear cache for specific type
Taxonomy::clearCacheForType(TaxonomyType::Category);

// Cache is automatically cleared when:
// - Taxonomies are created, updated, or deleted
// - Nested set is rebuilt
// - Taxonomies are moved in hierarchy
```

### Performance Tips

[](#performance-tips)

```
// Use eager loading to avoid N+1 queries
$products = Product::with(['taxonomies' => function ($query) {
    $query->where('type', TaxonomyType::Category->value);
}])->get();

// Use pagination for large datasets
$taxonomies = Taxonomy::findByType(TaxonomyType::Category, 20); // 20 per page

// Use specific queries instead of loading all relationships
$categories = $product->taxonomiesOfType(TaxonomyType::Category);
```

🏷️ Custom Taxonomy Types
------------------------

[](#️-custom-taxonomy-types)

While the package comes with predefined taxonomy types in the `TaxonomyType` enum (Category, Tag, Color, Size, etc.), you can easily define and use your own custom types.

### Defining Custom Types

[](#defining-custom-types)

There are two ways to use custom taxonomy types:

#### 1. Override the types configuration

[](#1-override-the-types-configuration)

You can override the default types by modifying the `types` array in your `config/taxonomy.php` file:

```
'types' => [
    'category',
    'tag',
    // Default types you want to keep

    // Your custom types
    'genre',
    'location',
    'season',
    'difficulty',
],
```

#### 2. Use custom types directly

[](#2-use-custom-types-directly)

You can also use custom types directly in your code without modifying the configuration:

```
use Aliziodev\LaravelTaxonomy\Facades\Taxonomy;

// Create a taxonomy with a custom type
$genre = Taxonomy::create([
    'name' => 'Science Fiction',
    'type' => 'genre', // Custom type not defined in TaxonomyType enum
    'description' => 'Science fiction genre',
]);

// Find taxonomies by custom type
$genres = Taxonomy::findByType('genre');

// Check if a model has taxonomies of a custom type
$product->hasTaxonomyType('genre');

// Get taxonomies of a custom type
$productGenres = $product->taxonomiesOfType('genre');

// Filter models by custom taxonomy type
$products = Product::withTaxonomyType('genre')->get();
```

### Creating a Custom Type Enum

[](#creating-a-custom-type-enum)

For better type safety and organization, you can create your own enum for custom types:

```
namespace App\Enums;

enum GenreType: string
{
    case SciFi = 'sci-fi';
    case Fantasy = 'fantasy';
    case Horror = 'horror';
    case Romance = 'romance';
    case Mystery = 'mystery';

    public static function values(): array
    {
        return array_column(self::cases(), 'value');
    }
}
```

Then use it in your code:

```
use App\Enums\GenreType;
use Aliziodev\LaravelTaxonomy\Facades\Taxonomy;

// Create a taxonomy with a custom type from enum
$genre = Taxonomy::create([
    'name' => 'Science Fiction',
    'type' => GenreType::SciFi->value,
    'description' => 'Science fiction genre',
]);

// Find taxonomies by custom type from enum
$sciFiBooks = Taxonomy::findByType(GenreType::SciFi);
```

🎯 Real-World Usage Scenarios
----------------------------

[](#-real-world-usage-scenarios)

For comprehensive examples of how to use Laravel Taxonomy in real-world applications, please refer to our detailed scenario documentation:

### Available Scenarios

[](#available-scenarios)

1. **[E-commerce Product Catalog](docs/en/ecommerce-product-catalog.md)** - Building a comprehensive e-commerce platform with hierarchical categories, product tags, dynamic navigation, and advanced filtering.
2. **[Content Management System](docs/en/content-management-system.md)** - Creating a flexible CMS with content categorization, tagging, filtering, and SEO optimization.
3. **[Learning Management System](docs/en/learning-management-system.md)** - Developing an educational platform with courses, skills, difficulty levels, and personalized learning paths.
4. **[Multi-tenant Business Application](docs/en/multi-tenant-business-application.md)** - Building a SaaS platform with tenant-specific taxonomies, project management, and customizable workflows.
5. **[Analytics and Reporting](docs/en/analytics-and-reporting.md)** - Implementing comprehensive analytics, reporting dashboards, and automated insights using taxonomy data.

Each scenario includes:

- Complete code examples
- Database setup and migrations
- Controller implementations
- Service layer patterns

🚀 Advanced Features
-------------------

[](#-advanced-features)

### 🔄 Nested Set Model

[](#-nested-set-model)

Laravel Taxonomy uses the Nested Set Model for efficient hierarchical data management:

```
// Get all descendants of a taxonomy
$category = Taxonomy::find(1);
$descendants = $category->getDescendants();

// Get all ancestors of a taxonomy
$ancestors = $category->getAncestors();

// Get siblings
$siblings = $category->getSiblings();

// Check if taxonomy is descendant of another
$isDescendant = $category->isDescendantOf($parentCategory);
```

### Performance Optimization

[](#performance-optimization)

**Caching Strategies**:

```
// Cache taxonomy trees for better performance
class CachedTaxonomyService
{
    public function getCachedTree(string $type, int $ttl = 3600): Collection
    {
        return Cache::remember("taxonomy_tree_{$type}", $ttl, function () use ($type) {
            return Taxonomy::tree($type);
        });
    }

    public function invalidateCache(string $type): void
    {
        Cache::forget("taxonomy_tree_{$type}");
    }

    public function warmCache(): void
    {
        $types = Taxonomy::distinct('type')->pluck('type');

        foreach ($types as $type) {
            $this->getCachedTree($type);
        }
    }
}

// Use in your models
class Product extends Model
{
    use HasTaxonomy;

    protected static function booted()
    {
        static::saved(function ($product) {
            // Invalidate related caches when product taxonomies change
            $types = $product->taxonomies->pluck('type')->unique();
            foreach ($types as $type) {
                Cache::forget("taxonomy_tree_{$type}");
            }
        });
    }
}
```

**Eager Loading for Performance**:

```
// Efficient loading of taxonomies with models
$products = Product::with([
    'taxonomies' => function ($query) {
        $query->select('id', 'name', 'slug', 'type', 'meta')
              ->orderBy('type')
              ->orderBy('name');
    }
])->get();

// Load specific taxonomy types only
$products = Product::with([
    'taxonomies' => function ($query) {
        $query->whereIn('type', ['category', 'brand']);
    }
])->get();

// Preload taxonomy counts
$categories = Taxonomy::where('type', 'category')
    ->withCount(['models as product_count' => function ($query) {
        $query->where('taxonomable_type', Product::class);
    }])
    ->get();
```

### Advanced Querying

[](#advanced-querying)

**Complex Taxonomy Filters**:

```
class ProductFilterService
{
    public function filterByTaxonomies(array $filters): Builder
    {
        $query = Product::query();

        // Filter by multiple categories (OR condition)
        if (!empty($filters['categories'])) {
            $query->withAnyTaxonomies($filters['categories']);
        }

        // Filter by required tags (AND condition)
        if (!empty($filters['required_tags'])) {
            $query->withAllTaxonomies($filters['required_tags']);
        }

        // Filter by brand (exact match)
        if (!empty($filters['brand'])) {
            $query->withTaxonomy($filters['brand']);
        }

        // Filter by price range taxonomy
        if (!empty($filters['price_range'])) {
            $priceRange = Taxonomy::findBySlug($filters['price_range'], 'price_range');
            if ($priceRange) {
                $min = $priceRange->meta['min_price'] ?? 0;
                $max = $priceRange->meta['max_price'] ?? PHP_INT_MAX;
                $query->whereBetween('price', [$min, $max]);
            }
        }

        // Exclude certain taxonomies
        if (!empty($filters['exclude'])) {
            $query->withoutTaxonomies($filters['exclude']);
        }

        return $query;
    }

    public function getFilterOptions(array $currentFilters = []): array
    {
        $baseQuery = $this->filterByTaxonomies($currentFilters);

        return [
            'categories' => $this->getAvailableOptions($baseQuery, 'category'),
            'brands' => $this->getAvailableOptions($baseQuery, 'brand'),
            'tags' => $this->getAvailableOptions($baseQuery, 'tag'),
            'price_ranges' => $this->getAvailableOptions($baseQuery, 'price_range'),
        ];
    }

    private function getAvailableOptions(Builder $query, string $type): Collection
    {
        return Taxonomy::where('type', $type)
            ->whereHas('models', function ($q) use ($query) {
                $q->whereIn('taxonomable_id', $query->pluck('id'));
            })
            ->withCount('models')
            ->orderBy('models_count', 'desc')
            ->get();
    }
}
```

### Data Import/Export

[](#data-importexport)

**Import/Export Functionality**:

```
class TaxonomyImportExportService
{
    public function exportToJson(string $type = null): string
    {
        $query = Taxonomy::with('children');

        if ($type) {
            $query->where('type', $type);
        }

        $taxonomies = $query->whereNull('parent_id')
            ->orderBy('lft')
            ->get();

        return json_encode($this->buildExportTree($taxonomies), JSON_PRETTY_PRINT);
    }

    public function importFromJson(string $json, bool $replaceExisting = false): array
    {
        $data = json_decode($json, true);
        $imported = [];
        $errors = [];

        DB::transaction(function () use ($data, $replaceExisting, &$imported, &$errors) {
            foreach ($data as $item) {
                try {
                    $taxonomy = $this->importTaxonomyItem($item, null, $replaceExisting);
                    $imported[] = $taxonomy->id;
                } catch (Exception $e) {
                    $errors[] = [
                        'item' => $item['name'] ?? 'Unknown',
                        'error' => $e->getMessage(),
                    ];
                }
            }
        });

        return [
            'imported' => count($imported),
            'errors' => $errors,
            'taxonomy_ids' => $imported,
        ];
    }

    private function buildExportTree(Collection $taxonomies): array
    {
        return $taxonomies->map(function ($taxonomy) {
            $item = [
                'name' => $taxonomy->name,
                'slug' => $taxonomy->slug,
                'type' => $taxonomy->type,
                'description' => $taxonomy->description,
                'meta' => $taxonomy->meta,
                'sort_order' => $taxonomy->sort_order,
            ];

            if ($taxonomy->children->isNotEmpty()) {
                $item['children'] = $this->buildExportTree($taxonomy->children);
            }

            return $item;
        })->toArray();
    }

    private function importTaxonomyItem(array $item, ?int $parentId, bool $replaceExisting): Taxonomy
    {
        $existing = null;

        if ($replaceExisting) {
            $existing = Taxonomy::where('slug', $item['slug'])
                ->where('type', $item['type'])
                ->first();
        }

        $taxonomy = $existing ?: new Taxonomy();

        $taxonomy->fill([
            'name' => $item['name'],
            'slug' => $item['slug'],
            'type' => $item['type'],
            'description' => $item['description'] ?? null,
            'parent_id' => $parentId,
            'meta' => $item['meta'] ?? [],
            'sort_order' => $item['sort_order'] ?? 0,
        ]);

        $taxonomy->save();

        // Import children
        if (!empty($item['children'])) {
            foreach ($item['children'] as $child) {
                $this->importTaxonomyItem($child, $taxonomy->id, $replaceExisting);
            }
        }

        return $taxonomy;
    }

    public function exportToCsv(string $type): string
    {
        $taxonomies = Taxonomy::where('type', $type)
            ->with('parent')
            ->orderBy('lft')
            ->get();

        $csv = "Name,Slug,Type,Parent,Description,Meta\n";

        foreach ($taxonomies as $taxonomy) {
            $csv .= sprintf(
                "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n",
                $taxonomy->name,
                $taxonomy->slug,
                $taxonomy->type,
                $taxonomy->parent?->name ?? '',
                $taxonomy->description ?? '',
                json_encode($taxonomy->meta)
            );
        }

        return $csv;
    }
}
```

📋 Best Practices
----------------

[](#-best-practices)

### 1. **Taxonomy Design Principles**

[](#1-taxonomy-design-principles)

```
// ✅ Good: Clear, specific taxonomy types
class TaxonomyTypes
{
    const PRODUCT_CATEGORY = 'product_category';
    const PRODUCT_TAG = 'product_tag';
    const CONTENT_CATEGORY = 'content_category';
    const USER_SKILL = 'user_skill';
}

// ❌ Avoid: Generic, ambiguous types
// 'category', 'tag', 'type' - too generic
```

### 2. **Metadata Best Practices**

[](#2-metadata-best-practices)

```
// ✅ Good: Structured metadata with validation
class CategoryMetadata
{
    public static function validate(array $metadata): array
    {
        return Validator::make($metadata, [
            'icon' => 'nullable|string|max:50',
            'color' => 'nullable|string|regex:/^#[0-9A-Fa-f]{6}$/',
            'featured' => 'boolean',
            'seo_title' => 'nullable|string|max:60',
            'seo_description' => 'nullable|string|max:160',
        ])->validated();
    }
}

// Usage
$category = Taxonomy::create([
    'name' => 'Electronics',
    'type' => TaxonomyTypes::PRODUCT_CATEGORY,
    'meta' => CategoryMetadata::validate([
        'icon' => 'laptop',
        'color' => '#007bff',
        'featured' => true,
    ]),
]);
```

### 3. **Performance Optimization**

[](#3-performance-optimization)

```
// ✅ Good: Efficient querying with proper indexing
class OptimizedTaxonomyQueries
{
    public function getProductsByCategory(string $categorySlug): Collection
    {
        return Product::select(['id', 'name', 'price', 'slug'])
            ->withTaxonomy(
                Taxonomy::where('slug', $categorySlug)
                    ->where('type', TaxonomyTypes::PRODUCT_CATEGORY)
                    ->first()
            )
            ->with(['taxonomies' => function ($query) {
                $query->select(['id', 'name', 'slug', 'type'])
                      ->whereIn('type', [TaxonomyTypes::PRODUCT_TAG, 'brand']);
            }])
            ->limit(20)
            ->get();
    }

    // ✅ Good: Batch operations for better performance
    public function attachCategoriesInBatch(Collection $products, array $categoryIds): void
    {
        $products->chunk(100)->each(function ($chunk) use ($categoryIds) {
            foreach ($chunk as $product) {
                $product->attachTaxonomies($categoryIds);
            }
        });
    }
}
```

### 4. **Error Handling and Validation**

[](#4-error-handling-and-validation)

```
class TaxonomyService
{
    public function createWithValidation(array $data): Taxonomy
    {
        $validator = Validator::make($data, [
            'name' => 'required|string|max:255',
            'type' => 'required|string|max:50',
            'parent_id' => 'nullable|exists:taxonomies,id',
            'meta' => 'nullable|array',
        ]);

        if ($validator->fails()) {
            throw new ValidationException($validator);
        }

        // Check for circular references
        if (isset($data['parent_id'])) {
            $this->validateNoCircularReference($data['parent_id'], $data);
        }

        return Taxonomy::create($validator->validated());
    }

    private function validateNoCircularReference(int $parentId, array $data): void
    {
        $parent = Taxonomy::find($parentId);

        if (!$parent) {
            throw new InvalidArgumentException('Parent taxonomy not found');
        }

        // Check if parent type matches (optional business rule)
        if ($parent->type !== $data['type']) {
            throw new InvalidArgumentException('Parent must be of the same type');
        }

        // Prevent deep nesting (optional business rule)
        if ($parent->depth >= 5) {
            throw new InvalidArgumentException('Maximum nesting depth exceeded');
        }
    }
}
```

### 5. **Testing Strategies**

[](#5-testing-strategies)

```
class TaxonomyTestCase extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        $this->createTestTaxonomies();
    }

    private function createTestTaxonomies(): void
    {
        $this->electronics = Taxonomy::create([
            'name' => 'Electronics',
            'type' => 'category',
        ]);

        $this->smartphones = Taxonomy::create([
            'name' => 'Smartphones',
            'type' => 'category',
            'parent_id' => $this->electronics->id,
        ]);
    }

    /** @test */
    public function it_can_attach_taxonomies_to_models(): void
    {
        $product = Product::factory()->create();

        $product->attachTaxonomy($this->electronics);

        $this->assertTrue($product->hasTaxonomy($this->electronics));
        $this->assertCount(1, $product->taxonomies);
    }

    /** @test */
    public function it_maintains_nested_set_integrity(): void
    {
        $this->electronics->rebuildNestedSet();

        $this->electronics->refresh();
        $this->smartphones->refresh();

        $this->assertEquals(1, $this->electronics->lft);
        $this->assertEquals(4, $this->electronics->rgt);
        $this->assertEquals(2, $this->smartphones->lft);
        $this->assertEquals(3, $this->smartphones->rgt);
    }
}
```

Custom Slugs and Error Handling
-------------------------------

[](#custom-slugs-and-error-handling)

The package provides robust error handling for slug generation and uniqueness:

### Manual Slug Management

[](#manual-slug-management)

When `slugs.generate` is set to `false` in the configuration, you must provide slugs manually:

```
// This will throw MissingSlugException if slugs.generate is false
$taxonomy = Taxonomy::create([
    'name' => 'Test Category',
    'type' => TaxonomyType::Category->value,
    // Missing slug will cause an exception
]);

// Correct way when slugs.generate is false
$taxonomy = Taxonomy::create([
    'name' => 'Test Category',
    'type' => TaxonomyType::Category->value,
    'slug' => 'test-category', // Manually provided slug
]);
```

### Slug Uniqueness (Composite Unique)

[](#slug-uniqueness-composite-unique)

Starting from v3.0, slug uniqueness is enforced within the same taxonomy type (composite unique constraint):

```
// This will work - different types can have same slug
$taxonomy1 = Taxonomy::create([
    'name' => 'Featured Category',
    'slug' => 'featured',
    'type' => TaxonomyType::Category->value,
]);

$taxonomy2 = Taxonomy::create([
    'name' => 'Featured Tag',
    'slug' => 'featured', // Same slug, different type - OK!
    'type' => TaxonomyType::Tag->value,
]);

// This will throw DuplicateSlugException - same type, same slug
$taxonomy3 = Taxonomy::create([
    'name' => 'Another Featured Category',
    'slug' => 'featured', // Duplicate within same type
    'type' => TaxonomyType::Category->value,
]);
```

### Duplicate Slug Detection

[](#duplicate-slug-detection)

```
use Aliziodev\LaravelTaxonomy\Exceptions\DuplicateSlugException;

try {
    Taxonomy::create([
        'name' => 'Another Featured Category',
        'slug' => 'featured', // Duplicate within same type
        'type' => TaxonomyType::Category->value,
    ]);
} catch (DuplicateSlugException $e) {
    // Handle duplicate slug error within the same type
    return response()->json([
        'error' => 'A taxonomy with this slug already exists in this type.',
        'slug' => $e->getSlug(),
        'type' => $e->getType(), // Available in v3.0+
    ], 422);
}
```

### Exception Handling

[](#exception-handling)

The package provides the following exceptions:

- `MissingSlugException`: Thrown when a slug is required but not provided
- `DuplicateSlugException`: Thrown when a slug already exists and a unique slug is required

You can catch these exceptions to provide custom error handling:

```
use Aliziodev\LaravelTaxonomy\Exceptions\MissingSlugException;
use Aliziodev\LaravelTaxonomy\Exceptions\DuplicateSlugException;

try {
    $taxonomy = Taxonomy::create([
        'name' => 'Test Category',
        'type' => TaxonomyType::Category->value,
    ]);
} catch (MissingSlugException $e) {
    // Handle missing slug error
    return back()->withErrors(['slug' => 'A slug is required.']);
} catch (DuplicateSlugException $e) {
    // Handle duplicate slug error
    return back()->withErrors(['slug' => 'This slug already exists. Please choose another.']);
}
```

🎯 Type-Specific Operations
--------------------------

[](#-type-specific-operations)

The package provides specialized methods for working with taxonomies of specific types, offering more precise control over your taxonomy relationships.

### Type-Specific Attachment Methods

[](#type-specific-attachment-methods)

```
use Aliziodev\LaravelTaxonomy\Enums\TaxonomyType;

// Attach only categories to a product
$product->attachTaxonomiesOfType(TaxonomyType::Category, [$category1->id, $category2->id]);

// Attach only tags to a product
$product->attachTaxonomiesOfType(TaxonomyType::Tag, ['featured', 'bestseller']);

// Detach specific categories
$product->detachTaxonomiesOfType(TaxonomyType::Category, [$category1->id]);

// Detach all categories (keep other types)
$product->detachTaxonomiesOfType(TaxonomyType::Category);

// Sync categories (replace all categories with new ones)
$product->syncTaxonomiesOfType(TaxonomyType::Category, [$newCategory1->id, $newCategory2->id]);

// Toggle specific tags
$product->toggleTaxonomiesOfType(TaxonomyType::Tag, [$tag1->id, $tag2->id]);
```

### Type-Specific Relationship Checks

[](#type-specific-relationship-checks)

```
// Check if product has any of the specified categories
$hasAnyCategory = $product->hasTaxonomiesOfType(TaxonomyType::Category, [$category1->id, $category2->id]);

// Check if product has all specified tags
$hasAllTags = $product->hasAllTaxonomiesOfType(TaxonomyType::Tag, [$tag1->id, $tag2->id]);

// Get count of taxonomies by type
$categoryCount = $product->getTaxonomyCountByType(TaxonomyType::Category);
$tagCount = $product->getTaxonomyCountByType(TaxonomyType::Tag);

// Get first taxonomy of specific type
$firstCategory = $product->getFirstTaxonomyOfType(TaxonomyType::Category);
$firstTag = $product->getFirstTaxonomyOfType(TaxonomyType::Tag);
```

### Type-Specific Query Scopes

[](#type-specific-query-scopes)

```
// Find products with any of the specified categories
$products = Product::withAnyTaxonomiesOfType(TaxonomyType::Category, [$category1->id, $category2->id])->get();

// Find products with all specified tags
$products = Product::withAllTaxonomiesOfType(TaxonomyType::Tag, [$tag1->id, $tag2->id])->get();

// Find products without specific categories
$products = Product::withoutTaxonomiesOfType(TaxonomyType::Category, [$category1->id])->get();

// Order products by taxonomy type (products with categories first)
$products = Product::orderByTaxonomyType(TaxonomyType::Category)->get();
$products = Product::orderByTaxonomyType(TaxonomyType::Category, 'desc')->get();
```

### Practical Examples

[](#practical-examples)

#### E-commerce Product Management

[](#e-commerce-product-management)

```
// Set up product categories and tags separately
$product = Product::create(['name' => 'Smartphone']);

// Attach categories
$product->attachTaxonomiesOfType(TaxonomyType::Category, [
    $electronics->id,
    $smartphones->id,
    $mobile->id
]);

// Attach tags
$product->attachTaxonomiesOfType(TaxonomyType::Tag, [
    $featured->id,
    $bestseller->id,
    $newArrival->id
]);

// Update only categories without affecting tags
$product->syncTaxonomiesOfType(TaxonomyType::Category, [
    $electronics->id,
    $smartphones->id,
    $android->id  // Replace mobile with android
]);

// Check product classification
if ($product->hasTaxonomiesOfType(TaxonomyType::Category, [$smartphones->id])) {
    // Apply smartphone-specific logic
}

// Get category count for display
$categoryCount = $product->getTaxonomyCountByType(TaxonomyType::Category);
echo "This product is in {$categoryCount} categories";
```

#### Content Management

[](#content-management)

```
// Find articles in specific categories with certain tags
$articles = Article::withAllTaxonomiesOfType(TaxonomyType::Category, [$technology->id])
    ->withAnyTaxonomiesOfType(TaxonomyType::Tag, [$tutorial->id, $guide->id])
    ->orderByTaxonomyType(TaxonomyType::Category)
    ->get();

// Remove outdated tags while keeping categories
$article->detachTaxonomiesOfType(TaxonomyType::Tag, [$outdated->id, $deprecated->id]);
```

### Benefits of Type-Specific Operations

[](#benefits-of-type-specific-operations)

1. **Precision**: Work with specific taxonomy types without affecting others
2. **Performance**: More efficient queries when dealing with specific types
3. **Maintainability**: Clearer code intent and easier debugging
4. **Flexibility**: Mix and match different taxonomy types as needed
5. **Safety**: Prevent accidental modification of unrelated taxonomy types

Troubleshooting
---------------

[](#troubleshooting)

### Common Issues

[](#common-issues)

#### Taxonomy Not Found

[](#taxonomy-not-found)

If you're having trouble finding a taxonomy by slug, make sure the slug is correct and consider using the `exists` method to check if it exists:

```
if (Taxonomy::exists('electronics')) {
    $taxonomy = Taxonomy::findBySlug('electronics');
}
```

#### Relationship Issues

[](#relationship-issues)

If you're having trouble with relationships, make sure you're using the correct morph type in your configuration. If you're using UUIDs or ULIDs for your models, make sure to set the `morph_type` configuration accordingly.

#### Cache Issues

[](#cache-issues)

If you're not seeing updated data after making changes, you might need to clear the cache:

```
\Illuminate\Support\Facades\Cache::flush();
```

Security
--------

[](#security)

The Laravel Taxonomy package follows good security practices:

- It uses prepared statements for all database queries to prevent SQL injection
- It validates input data before processing
- It uses Laravel's built-in protection mechanisms

If you discover any security issues, please email the author at  instead of using the issue tracker.

Testing
-------

[](#testing)

The package includes comprehensive tests. You can run them with:

```
composer test

// or

vendor/bin/pest
```

📝 Automatic Changelog
---------------------

[](#-automatic-changelog)

This package uses **automated changelog generation** based on [Conventional Commits](https://www.conventionalcommits.org/) and [Semantic Versioning](https://semver.org/).

### How It Works

[](#how-it-works)

- **Commit Analysis**: Every commit message is analyzed to determine the type of change
- **Automatic Versioning**: Version numbers are automatically determined based on commit types
- **Changelog Generation**: `CHANGELOG.md` is automatically updated with release notes
- **GitHub Releases**: Releases are automatically created with detailed release notes

### Commit Message Format

[](#commit-message-format)

```
[optional scope]:

[optional body]

[optional footer(s)]

```

**Examples:**

```
feat: add moveToParent method with performance optimization
fix: resolve nested set corruption on concurrent operations
feat!: change taxonomy structure for multi-tenancy support
```

### Release Types

[](#release-types)

Commit TypeRelease TypeExample`fix:`Patch (1.0.1)Bug fixes`feat:`Minor (1.1.0)New features`feat!:` or `BREAKING CHANGE:`Major (2.0.0)Breaking changes`docs:`, `style:`, `test:`, `chore:`No ReleaseDocumentation, formatting### Automated Workflows

[](#automated-workflows)

- **Auto Changelog**: Triggered on every push to main branch
- **Commitlint**: Validates commit messages on PRs and pushes
- **Release Creation**: Automatically creates GitHub releases with changelogs

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details on our automated changelog system and development workflow.

License
-------

[](#license)

The Laravel Taxonomy package is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

###  Health Score

51

—

FairBetter than 96% of packages

Maintenance70

Regular maintenance activity

Popularity45

Moderate usage in the ecosystem

Community17

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 60% 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

Every ~8 days

Recently: every ~31 days

Total

23

Last Release

175d ago

Major Versions

v1.0.1 → v2.0.02025-05-31

PHP version history (2 changes)v1.0.0PHP ^8.1

v2.0.0PHP ^8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/f7060c3b21de26ce345cac7925c6cf8c469da49a614b9292cf38f8eed1bf099c?d=identicon)[aliziodev](/maintainers/aliziodev)

---

Top Contributors

[![mu-hanz](https://avatars.githubusercontent.com/u/44943686?v=4)](https://github.com/mu-hanz "mu-hanz (84 commits)")[![aliziodev](https://avatars.githubusercontent.com/u/187039973?v=4)](https://github.com/aliziodev "aliziodev (32 commits)")[![semantic-release-bot](https://avatars.githubusercontent.com/u/32174276?v=4)](https://github.com/semantic-release-bot "semantic-release-bot (22 commits)")[![howdu](https://avatars.githubusercontent.com/u/533658?v=4)](https://github.com/howdu "howdu (2 commits)")

---

Tags

laraveltagscategoriesclosure tablenested-settaxonomyhierarchicaltermstree-structurelaravel-termshierarchical-data

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

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

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

###  Alternatives

[barryvdh/laravel-ide-helper

Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.

14.9k123.0M687](/packages/barryvdh-laravel-ide-helper)[spatie/laravel-enum

Laravel Enum support

3655.4M31](/packages/spatie-laravel-enum)[psalm/plugin-laravel

Psalm plugin for Laravel

3274.9M308](/packages/psalm-plugin-laravel)[laracraft-tech/laravel-useful-additions

A collection of useful Laravel additions!

58109.4k](/packages/laracraft-tech-laravel-useful-additions)[api-platform/laravel

API Platform support for Laravel

59126.4k6](/packages/api-platform-laravel)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)

PHPackages © 2026

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