Installation:
composer require lecturize/laravel-taxonomies
php artisan vendor:publish --provider="Lecturize\Taxonomies\TaxonomiesServiceProvider"
php artisan migrate
Ensure database/migrations is in composer.json classmap.
First Use Case:
Attach the HasCategories trait to a model (e.g., Post):
use Lecturize\Taxonomies\Traits\HasCategories;
class Post extends Model
{
use HasCategories;
}
Run migrations and test with:
php artisan migrate:fresh
Quick Taxonomy Creation: Define a taxonomy in a migration or via Tinker:
use Lecturize\Taxonomies\Models\Taxonomy;
Taxonomy::create(['name' => 'post_categories']);
Model Integration:
HasCategories for basic taxonomy support.CanHaveCategories contract:
class Post extends Model implements CanHaveCategories
{
public function getTaxonomies(): array
{
return ['post_categories'];
}
}
Term Management:
$post->categories()->sync([1, 2, 3]); // Sync with existing terms
$post->categories()->create(['name' => 'New Category']); // Create new
$parent = Term::find(1);
$child = $parent->children()->create(['name' => 'Subcategory']);
Querying:
$posts = Post::withCategories(['post_categories' => [1, 2]])->get();
$terms = $post->getTerms('post_categories');
Slugs & SEO:
EloquentSluggable (published via package) for automatic slugs:
class Term extends Model
{
use \Cviebrock\EloquentSluggable\Sluggable;
public function getSlugOptions()
{
return ['source' => 'name'];
}
}
API/JSON Responses:
toArray() or toJson():
$post->load('categories');
return $post->toJson();
Migration Classmap:
database/migrations to composer.json classmap will break migrations. Fix:
"autoload": {
"classmap": ["database/migrations"]
}
Term Sync Behavior:
sync() detaches all terms not in the given array. Use attach() to add without detaching:
$post->categories()->attach([4, 5]); // Adds without detaching others
Nested Term Queries:
N+1 queries. Use with():
$terms = Term::with('children')->find(1);
Taxonomy Validation:
getTaxonomies() returns an array of string taxonomy names (not IDs or models).Slug Conflicts:
public function getSlugOptions()
{
return ['source' => 'name', 'onUpdate' => true];
}
Term Not Found?:
Verify the taxonomy name matches exactly (case-sensitive) in getTaxonomies().
Check if the term exists in the terms table.
Relationship Errors:
Ensure the pivot table exists (e.g., taxonomy_term_model for Post and post_categories).
Custom Pivot Tables:
Override pivot table names in the HasCategories trait:
protected $taxonomyPivotTable = 'custom_pivot_name';
Term Scopes:
Add global scopes to Term model for reusable queries:
class TermScope extends Scope
{
public function apply(Builder $builder, $taxonomy)
{
$builder->where('taxonomy', $taxonomy);
}
}
Event Listeners: Listen for term creation/deletion:
Term::created(function ($term) {
// Log or notify
});
API Resources:
Extend TaxonomyResource or TermResource for custom JSON structures:
class CustomTermResource extends Resource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'path' => $this->getPath(), // Custom method
];
}
}
Eager Load Terms:
Always use with() to avoid N+1 queries:
$posts = Post::with(['categories' => function ($query) {
$query->where('taxonomy', 'post_categories');
}])->get();
Cache Taxonomies: Cache taxonomy lists if rarely changed:
$taxonomies = Cache::remember('taxonomies', 60, function () {
return Taxonomy::all()->pluck('name');
});
How can I help you explore Laravel packages today?