1. Go to this page and download the library: Download aliziodev/laravel-taxonomy library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
aliziodev / laravel-taxonomy example snippets
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
],
];
// 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']);
// 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);
// 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();
// 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 (
// 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
// 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);
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' => '
// 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();
// 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
// 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');
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();
});
}
}
// 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
// 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
// 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);
'types' => [
'category',
'tag',
// Default types you want to keep
// Your custom types
'genre',
'location',
'season',
'difficulty',
],
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();
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');
}
}
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);
// 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);
// 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}");
}
});
}
}
// 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();
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 y($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();
}
}
// ✅ 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);
}
});
}
}
class TaxonomyService
{
public function createWithValidation(array $data): Taxonomy
{
$validator = Validator::make($data, [
'name' => ' 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');
}
}
}
// 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
]);
// 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,
]);
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);
}
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
if (Taxonomy::exists('electronics')) {
$taxonomy = Taxonomy::findBySlug('electronics');
}