Installation:
composer require nevadskiy/laravel-tree
Publish the migration (if needed) and run:
php artisan vendor:publish --provider="Nevadskiy\Tree\TreeServiceProvider"
Model Integration:
Use the Tree trait in your Eloquent model:
use Nevadskiy\Tree\Traits\Tree;
class Category extends Model
{
use Tree;
}
First Use Case:
$root = Category::create(['name' => 'Root']);
$child = $root->children()->create(['name' => 'Child']);
$tree = Category::root()->withDescendants()->get();
Tree, TreeScope, and TreeQueryBuilder in src/Traits.database/migrations/ for column requirements (e.g., path for materialized paths).Tree Construction:
Model::root() to create or fetch root nodes.
$root = Category::root()->create(['name' => 'Electronics']);
children() for hierarchical creation:
$subcategory = $root->children()->create(['name' 'Laptops']);
Querying:
withDescendants() or withAncestors():
$subtree = Category::find($id)->withDescendants()->get();
wherePathContains() for filtering:
$results = Category::wherePathContains('/1/')->get();
Reordering:
move():
$node->move($newParent, 'last'); // or 'first', 'before', 'after'
Serialization:
$treeArray = $root->toTree();
parent_id column exists (though the package uses materialized paths, it respects parent_id for compatibility).Category::root()->withDescendants()).path updates in observers or model events to prevent corruption.TreeTestCase (if provided) or mock tree behaviors in unit tests.Path Corruption:
path updates can break the tree structure.move() or save() methods instead of direct DB updates.path values with:
$node->path; // Should match expected hierarchy (e.g., "/1/4/")
Performance:
withDescendants() queries.maxDepth() or use lazy loading:
$tree = Category::root()->withDescendants()->limitDepth(3)->get();
Circular References:
A is parent of B, B is parent of A).parent_id in model setParentIdAttribute:
public function setParentIdAttribute($value)
{
if ($this->id === $value) {
throw new \Exception("Cannot be its own parent.");
}
$this->attributes['parent_id'] = $value;
}
Migration Conflicts:
path columns may conflict with the package’s migration.php artisan vendor:publish --provider="Nevadskiy\Tree\TreeServiceProvider" --tag="migrations" --force
Category::addGlobalScope('logPath', function (Builder $builder) {
$builder->tap(function ($query) {
\Log::debug('Querying paths:', [$query->getQuery()->wheres]);
});
});
depth() to verify hierarchy levels:
$node->depth; // Should increment with each level (root = 0)
Custom Path Separator:
/ separator in the model:
protected $pathSeparator = '.';
Custom Scopes:
TreeScope to add custom query logic:
use Nevadskiy\Tree\Scopes\TreeScope;
class CustomScope extends TreeScope
{
public function apply(Builder $builder, Model $model)
{
parent::apply($builder, $model);
$builder->where('active', 1); // Example: Only active nodes
}
}
Event Hooks:
tree.moved or tree.created events to trigger side effects:
\Event::listen('tree.moved', function ($model) {
// Update related data (e.g., cache, notifications)
});
Alternative Storage:
parent_id only:
use Nevadskiy\Tree\Traits\Tree as BaseTree;
trait CustomTree extends BaseTree
{
protected $useMaterializedPath = false;
}
How can I help you explore Laravel packages today?