Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Taxonomy Laravel Package

aliziodev/laravel-taxonomy

Manage categories, tags, and any hierarchical structure in Laravel. Terms live in one table, attach to any model via a polymorphic pivot, and nested-set hierarchies keep ancestor and descendant lookups to a single query.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package occupies a well-defined seam: one taxonomies table plus a polymorphic taxonomables pivot, with hierarchy stored redundantly as both parent_id and nested-set lft/rgt/depth. That is the correct shape for a catalog/classification domain, and the deliberate redundancy is a genuine design decision rather than an accident — nested set gives single-query ancestor/descendant reads, parent_id gives a correctness fallback (ancestors()/descendants()) when raw writes have drifted the bounds. It is a self-contained domain library, not a framework extension, so it does not couple us to application internals beyond Eloquent.

Integration feasibility. Low engineering cost to adopt: composer require, one install command, one migration, one trait on each classifying model. No service provider gymnastics, no queue workers, no external services. The HasTaxonomy trait delivers relation, attach/sync/detach/toggle, type-scoped variants, and a broad scope vocabulary (withTaxonomySlug, withAllTaxonomies, withTaxonomyHierarchy, filterByTaxonomies) which covers ~80% of typical filtering requirements without us writing query code.

Technical risk. The risks are concentrated, not diffuse:

  • Single-maintainer / near-zero adoption. 245 stars, 0 dependents, one author. That is the dominant risk and it does not correlate with code quality — the repo shows 100% coverage, PHPStan/larastan, Pint, Codecov, CI matrix, semantic-release, bilingual docs, changelog, and an unusually mature AGENTS.md that pre-documents its own footguns. This is an engineering-credible package with a supply-chain-fragile footprint. Treat any dependency here as "we may inherit maintenance."
  • Irreversible configuration. morph_type (numeric/uuid/ulid) must be correct before first migrate; changing it later means rewriting the pivot. This is a one-way door and must be a gating review item, especially in a UUID/ULID-keyed codebase (the shipped default is uuid, which will silently mismatch integer-keyed models).
  • API surface friction. The facade proxies TaxonomyManager and is not an Eloquent builder — no where(), no delete(). Query building requires importing the model under an alias. Two classes named Taxonomy with different capabilities is a predictable source of developer error and a code-review checklist item.
  • Silent-failure semantics. Attach methods accept ids/models and ignore slugs without error. getDescendants()/getAncestors() read lft/rgt off the instance and return an empty collection for a stale parent. bulkCreate() skips model events entirely. These are all "wrong answer, no exception" behaviors — the most expensive class of bug to detect in production.
  • Deprecation debt. The $name relationship parameter is deprecated for removal in 3.0, selecting pivot morph columns the shipped migration does not create. We must forbid its use from day one or we inherit a 3.0 migration.
  • Data-quality flag. The stated last release is dated 2026-11-03 (future) with 0 dependents. Before any commitment, verify actual release cadence and commit activity from Packagist/GitHub directly rather than trusting the aggregated metadata.

Key questions to resolve before approval.

  1. Are our key-classifying models integer-, UUID-, or ULID-keyed, and are they consistent? (Determines morph_type; blocks migration.)
  2. Do we need per-term record counts? The taxonomy model has no models relation — counting requires aggregating from the pivot or the inverse side, which is a real product-feature constraint for admin UIs.
  3. Is multi-tenancy in scope? If yes, resolveCacheScopeUsing() is mandatory and the unique(['slug','type','deleted_at']) index must be replaced (no tenant_id column ships).
  4. Who owns the fork? If the answer is "nobody," cap the blast radius — do not put this under revenue-critical catalog navigation.

Integration Approach

Stack fit. Requires PHP 8.2+ and Laravel 11/12/13. Confirm our fleet is on Laravel 11+ before scoping; this package will not serve an L10 LTS tail without a backport we would own.

Migration path. The package ships its own migration and does not retrofit an existing taxonomy schema. Two viable paths:

Path When Cost
Greenfield No existing category/tag tables Low — install, migrate, add trait
Backfill Existing categories/tags/pivot tables Medium — ETL into taxonomies + taxonomables, slug collision handling, rebuildNestedSet() per type, dual-write window

For backfill, Taxonomy::bulkCreate() is the correct tool and materially changes the cost estimate: documented measurements show 10,000 rows at 0.95s / 32 queries versus 14.8s / 40,000 queries for a create() loop (nested: 1.0s / 37 vs 22.0s / 59,980). Accepts any iterable, so a generator keeps memory flat on large datasets — but it does not fire model events, so any observer-based logic in the backfill must be done explicitly.

Compatibility. Coexistence with an existing taxonomy implementation is possible only if slugs/types are namespaced, because slug uniqueness is scoped to (slug, type) within a single table. Plan an explicit cutover rather than a long parallel run.

Sequencing.

  1. Spike (1 sprint, timeboxed): prototype on a throwaway branch — validate morph_type against real models, measure nested-set read latency under our data volume, verify bulkCreate on a production-shaped dataset.
  2. Config freeze: lock morph_type, cache.ttl, cache.scope, migrations.autoload (must be false for per-tenant migration runs). Treat as an ADR.
  3. Schema + backfill: migrate, ETL via bulkCreate, rebuildNestedSet() per type, reconcile counts against source.
  4. Read paths: adopt query scopes and filterByTaxonomies() behind our existing repository/service boundary — do not let controllers bind directly to the facade.
  5. Write paths: attach/sync via the trait; forbid raw DB::table('taxonomies')->insert() and TaxonomyModel::where(...)->delete() in review (both bypass model hooks and corrupt lft/rgt/depth).
  6. Admin/CRUD: expose tree operations, mindful that children are re-parented, not deleted, with a soft-deleted term and that restore() does not move them back.

Operational Impact

Maintenance. Ongoing cost is low in steady state and spiky at Laravel majors. The package tracks L11/12/13, which implies an active maintainer today — but with one author and zero dependents, there is no ecosystem redundancy. Budget a named internal owner and a quarterly review of upstream activity; if the repo goes quiet for two consecutive LTS cycles, we are the maintainers. Mitigate by consuming only documented public API (which their ReadmeExamplesTest/DocsExamplesTest pin, so it should not rot silently) and keeping our integration behind a thin internal interface so a fork-and-vendor swap is a contained change.

Support. Upstream support is best-effort, single-maintainer, GitHub-issue-driven — no SLA, no commercial backing, no security disclosure policy documented. Our team absorbs first-line support. The saving grace: the codebase is small and fully covered, so internal engineers can read it end-to-end; the AGENTS.md/skills docs are an unusually good onboarding artifact and materially reduce the "bus factor" cost of debugging.

Scaling. Read scaling is a strength: nested-set ancestor/descendant lookups are single-query, and tree()/flatTree()/getNestedTree() are cached with version-key-based invalidation (works on stores without tag support — relevant if we are on Redis without clustering or on a store lacking Cache::tags). Write scaling is the constraint: create() costs 4–7 queries per row because each insert widens the nested set; wide trees and frequent re-parenting mean range updates. Mitigations: bulkCreate() for imports, moveToParent() over manual parent_id writes, and avoid high-frequency reordering of large subtrees in request paths.

Failure modes and detection. These are the operational runbook items:

Failure Symptom Detection / Remedy
Stale instance nested-set read Empty descendants, no error refresh() before read, or use descendants() (id-keyed, drift-immune)
Raw insert / query-delete lft/rgt/depth null or wrong, tree unusable php artisan taxonomy:rebuild-nested-set --force; forbid in code review
morph_type mismatch Attach silently no-ops or type errors at query time Gating config review pre-migrate
Slug passed to attach Nothing attached, no exception Contract test; resolve via findBySlug() first
Multi-tenant cache bleed Tenant A served Tenant B's tree resolveCacheScopeUsing(); assert in tenant isolation tests
Double-layer caching Permanently stale tree Never wrap tree()/flatTree()/getNestedTree() in Cache::remember() — second layer misses invalidation
bulkCreate skips events Observers/audit hooks don't fire Route imports deliberately; replicate side effects explicitly

Ramp-up. For a Laravel-fluent team: roughly 1–2 days to producer-level, dominated by three conceptual hurdles — facade-vs-model duality, nested-set staleness, and type-scoped attach semantics. All three are documented with worked examples (and in Bahasa Indonesia as well), which shortens onboarding. Budget an additional half-day for whoever owns the morph_type and multi-tenancy configuration decisions, since those are the irreversible ones.

Recommendation. Approve for internal, non-revenue-critical classification (content tagging, internal catalogs, taxonomies behind an admin surface) with a named owner and the code-review guardrails above. Defer for revenue-critical catalog navigation or anything requiring per-term counts, multi-tenant isolation, or a multi-year support commitment until we either accept fork ownership explicitly or see adoption and release cadence materially improve. The engineering quality justifies the trial; the bus factor justifies the cap.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style