PHPackages                             easydot/nested-categories - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. easydot/nested-categories

ActiveLibrary

easydot/nested-categories
=========================

Nested Categories

v1.0.0(3y ago)015MITPHP

Since Jan 6Pushed 3y agoCompare

[ Source](https://github.com/easydot/nested-categories)[ Packagist](https://packagist.org/packages/easydot/nested-categories)[ RSS](/packages/easydot-nested-categories/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

nested-categories
=================

[](#nested-categories)

Forked from denis-kisel/nested-categories

This library provide optimize common usage categories functional, such as: `category tree`, `breadcrumbs`, `child category items` by one sql query

### Support for

[](#support-for)

`mysql5.7.22+``laravel``php8.0+`

### Install package

[](#install-package)

Install via composer

```
composer require easydot/nested-categories

```

### Install or upgrade Category table

[](#install-or-upgrade-category-table)

If you have not yet category table, install it:

```
# Created table 'categories'
php artisan nested-category:install

#Or specify table name
php artisan nested-category:install --table-name=categories

```

If you have category table already, just upgrade it:
**Suppose what your table has fields: `id`, `parent_id`**

```
#Specify model to upgrade
php artisan nested-category:upgrade App\\Models\\Category

```

### Configure

[](#configure)

Add trait `NestableCategory` to category model

```
use Easydot\NestedCategory\NestableCategory;

class Category extends Model
{
    use NestableCategory;
    ....
}
```

Add `path` to fillable or set guarded

```
class Category extends Model
{
    ....
    protected $fillable = ['path'];
    ....
}

#OR
class Category extends Model
{
    ....
    protected $guarded = [];
    ....
}
```

**\[Optional\]**
Add trait `AutoRebuildNested` to category model for auto rebuild category structure after events: `created`, `updated`, `deleted`. BUT ITS DONT AUTO REBUILD AFTER BATCH OPERATION(see [Rebuild Structure](#rebuild-structure)).

> Background use one query for rebuild all table

```
use Easydot\NestedCategory\NestableCategory;
use Easydot\NestedCategory\AutoRebuildNested;

class Category extends Model
{
    use NestableCategory, AutoRebuildNested;
    ....
}

#The same
$category->save();
$category->rebuild();

$category->update();
$category->rebuild();

$category->delete();
$category->rebuild();
```

Usage
-----

[](#usage)

### Tree As Array

[](#tree-as-array)

idparent\_idnameorder1NULLParent021Child1031Child21```
$result = Category::asArrayTree();
dump($result)
//Output:
[
    'id' => 1,
    'parent_id' => NULL,
    'name' => 'Parent',
    'children' => [
        [
            'id' => 2,
            'parent_id' => 1,
            'name' => 'Child1',
            'children' => []
        ],
        [
            'id' => 3,
            'parent_id' => 1,
            'name' => 'Child2',
            'children' => []
        ]
    ]
]

# Specify needed fields
$result = Category::asArrayTree(fields: ['name', 'order']);
dump($result)
//Output:
[
    'name' => 'Parent',
    'order' => 0,
    'children' => [
        [
            'name' => 'Child1',
            'order' => 0,
            'children' => []
        ],
        [
            'name' => 'Child2',
            'order' => 1,
            'children' => []
        ]
    ]
]

# Specify cache time(minutes or DateTimeInterface)
$result = Category::asArrayTree(cacheTTL: 10);
//Cached data

$result = Category::asArrayTree(cacheTTL: 10);
//Data from previous cache

$result = Category::asArrayTree();
//Data without cache

# Get array of objects
$result = Category::asArrayTree(associative: false);
dump($result)
//Output:
[
    {
        name: 'Parent',
        order: 0,
        children: [{...}]
    },
....
]
```

### Breadcrumbs

[](#breadcrumbs)

Backend use one sql query for N nested categories

```
$category = Category::find(2)
dump($category->breadcrumbs());

//Output
Collection {
    array:2 [
        Category {id: 1, ...},
        Category {id: 2, ...},
    ]
}
```

### Leafs/child category items(Nested Products, Posts, Podcasts, etc..)

[](#leafschild-category-itemsnested-products-posts-podcasts-etc)

Input tables: `categories`(id, parent\_id, name), `products`(id, category\_id, name).
Backend use one sql query for nested leafs

```
ParentCategory(id: 1)
│   Product_1
│   Product_2
│
└───ChildCategory_1(id: 2)
│   │   Product_3
│   │   Product_4
│   │
│   └───ChildCategory_1_1(id: 3)
│       │   Product_5
│       │   Product_6
│
└───ChildCategory_2(id: 4)
    │   Product_7
    │   Product_8

```

```
#GetAllProducts
$products = Category::find(1)->leafs(App\Models\Product::class)->get();
dump($products->count());
//Output: 8

```

```
#In Models\Category
....
public nestedProducts() :Builder
{
    return $this->leafs(Product::class)
}

public nestedPosts() :Builder
{
    return $this->leafs(Post::class)
}
....

#Client Code
$products = Category::find(1)->nestedProducts()->where('name', 'like', '%some%')->get();
$products = Category::first()->nestedPosts()->count();
```

### Rebuild Structure

[](#rebuild-structure)

After BATCH CRUD operations for rebuild categories structure, need to use `rebuild` method. Or you can use trait `AutoRebuildNested` after single operation(see more in [Configure](#configure))

> Background use one query for rebuild all table

```
# Inserts
Category::insert([
    ['id' => 1, 'parent_id' => null],
    ['id' => 2, 'parent_id' => 1],
    ['id' => 3, 'parent_id' => 2],
    .....
])

Category::rebuild();

# Delete
Category::where('is_active', false)->delete();
Category::rebuild();
```

### Additional Commands

[](#additional-commands)

```
# Rebuild specify category
php artisan nested-category:rebuild App\\Models\\Category

```

### Testing

[](#testing)

```
cd vendor/easydot/nested-categories
vendor/bin/phpunit test

```

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 85.7% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

1227d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c5e822417acdf0b3c242d4b458213f3bfa31551d42f66a2315ecbebb81f46488?d=identicon)[easydot](/maintainers/easydot)

---

Top Contributors

[![den-is-ua](https://avatars.githubusercontent.com/u/235117855?v=4)](https://github.com/den-is-ua "den-is-ua (6 commits)")[![ibouzikas](https://avatars.githubusercontent.com/u/102241465?v=4)](https://github.com/ibouzikas "ibouzikas (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/easydot-nested-categories/health.svg)

```
[![Health](https://phpackages.com/badges/easydot-nested-categories/health.svg)](https://phpackages.com/packages/easydot-nested-categories)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
