aliziodev/laravel-taxonomy
Flexible Laravel package for managing taxonomies, categories, tags, and hierarchical trees. Includes nested-set support for fast hierarchy queries, metadata, bulk operations, caching, and custom taxonomy types. Compatible with Laravel 11+.
Installation:
composer require aliziodev/laravel-taxonomy
php artisan taxonomy:install
php artisan migrate
Add Trait to Model:
use Aliziodev\LaravelTaxonomy\Traits\HasTaxonomy;
class Product extends Model
{
use HasTaxonomy;
}
First Use Case:
// Create a taxonomy
$electronics = Taxonomy::create([
'name' => 'Electronics',
'type' => \Aliziodev\LaravelTaxonomy\Enums\TaxonomyType::Category->value,
]);
// Attach to a model
$product = Product::create(['name' => 'Laptop']);
$product->attachTaxonomies($electronics->id);
Taxonomy facade for direct taxonomy operations.HasTaxonomy trait methods for model relationships.// Create parent-child relationships
$parent = Taxonomy::create(['name' => 'Parent', 'type' => TaxonomyType::Category->value]);
$child = Taxonomy::create([
'name' => 'Child',
'type' => TaxonomyType::Category->value,
'parent_id' => $parent->id,
]);
// Get hierarchical tree
$tree = Taxonomy::tree(TaxonomyType::Category);
// Move a taxonomy (reparent)
$child->moveTo($parent->id);
// Attach taxonomies to multiple model types
$product->attachTaxonomies([$electronics->id, $featured->id]);
$article->attachTaxonomies([$technology->id]);
// Query models by taxonomy
$products = Product::withTaxonomySlug('electronics')->get();
$articles = Article::withTaxonomySlug('technology')->get();
// Sync taxonomies (replace existing)
$product->syncTaxonomies([$electronics->id, $laptops->id]);
// Toggle taxonomies (add if missing, remove if present)
$product->toggleTaxonomies([$discount->id]);
// Store metadata with taxonomies
$category = Taxonomy::create([
'name' => 'Books',
'type' => TaxonomyType::Category->value,
'meta' => [
'icon' => 'book',
'color' => '#3498db',
'featured' => true,
],
]);
// Access metadata
$icon = $category->meta->icon;
// Get all descendants of a taxonomy
$descendants = Taxonomy::getDescendants($electronics->id);
// Filter models by taxonomy hierarchy
$products = Product::withTaxonomyHierarchy($electronics->id)->get();
Extend the default types in config/taxonomy.php:
'types' => [
'category',
'tag',
'brand',
'collection',
'custom_type', // Add your custom type
],
Override slug generation in a service provider:
use Aliziodev\LaravelTaxonomy\Events\GeneratingSlug;
GeneratingSlug::macro('custom', function ($name, $type) {
return Str::slug($name . '-' . $type);
});
Leverage Laravel's cache for performance:
// Cache taxonomy trees
Cache::remember('taxonomy-tree-' . $type, now()->addHours(1), function () use ($type) {
return Taxonomy::tree($type);
});
Format taxonomy data for APIs:
$taxonomy->toArray([
'id', 'name', 'slug', 'type',
'meta' => function () {
return $this->meta;
},
'children' => function () {
return $this->children()->with('children')->get();
},
]);
Use the tree() method to render nested menus:
@foreach(Taxonomy::tree(TaxonomyType::Category) as $category)
<li>
{{ $category->name }}
@if($category->children->count())
<ul>
@include('taxonomy.menu', ['categories' => $category->children])
</ul>
@endif
</li>
@endforeach
// This is allowed:
Taxonomy::create(['name' => 'Featured', 'slug' => 'featured', 'type' => 'category']);
Taxonomy::create(['name' => 'Featured', 'slug' => 'featured', 'type' => 'tag']);
// Correct:
Product::withTaxonomySlug('featured', TaxonomyType::Category)->get();
flatTree() for large hierarchies or limit depth:
Taxonomy::flatTree(TaxonomyType::Category)->whereDepth('<=', 3);
config/taxonomy.php:
'migrations' => [
'autoload' => false,
],
morph_type: 'uuid' with integer IDs causes errors.config/taxonomy.php:
'morph_type' => 'numeric', // For integer IDs
// or
'morph_type' => 'uuid', // For UUID IDs
Cache::forget('taxonomy-tree-' . $type);
Enable query logging to debug nested set queries:
DB::enableQueryLog();
$tree = Taxonomy::tree(TaxonomyType::Category);
dd(DB::getQueryLog());
Validate hierarchy integrity:
// Check if a taxonomy is a descendant
$isDescendant = Taxonomy::isDescendant($childId, $parentId);
// Get path to root
$path = Taxonomy::getPath($taxonomyId);
Log bulk operations to avoid silent failures:
$product->syncTaxonomies([$electronics->id, $laptops->id], true); // Force sync
Debug slug conflicts:
// Check if slug exists for a type
$exists = Taxonomy::where('slug', 'featured')
->where('type', TaxonomyType::Category->value)
->exists();
Extend the base Taxonomy model:
class CustomTaxonomy extends \Aliziodev\LaravelTaxonomy\Models\Taxonomy
{
protected $casts = [
'meta' => 'array',
'is_active' => 'boolean',
];
public function scopeActive($query)
{
return $query->where('is_active', true);
}
}
Update config/taxonomy.php:
'model' => App\Models\CustomTaxonomy::class,
Add scopes to filter taxonomies:
use Illuminate\Database\Eloquent\Builder;
class Taxonomy extends \Aliziodev\LaravelTaxonomy\Models\Taxonomy
{
public function scopeFeatured($query)
{
return $query->where('meta->featured', true);
}
}
Listen to taxonomy events:
Taxonomy::created(function ($taxonomy) {
// Log creation
\Log::info("Taxonomy created: {$taxonomy->name}");
});
Taxonomy::deleted(function ($taxonomy) {
// Clean up related data
$taxonomy->taxonomables()->delete();
});
Override
How can I help you explore Laravel packages today?