Rinvex Categories

repository·master·Indexed 19 days ago

https://github.com/rinvex/laravel-categories

A polymorphic Laravel package for managing hierarchical categories. It integrates Nested Sets for tree structures, Sluggable for URL-friendly slugs, and Translatable for multi-language support. The package provides tools for creating root and child categories, managing ancestors and descendants, and querying models using category scopes. It includes CLI commands for publishing resources and managing migrations.

Tokens
5.2K
Snippets
24
Records
26
Agent score
67%

What's inside rinvex-laravel-categories

  1. Insert and move categories in the tree

    master

    Structural manipulations in Rinvex Categories are deferred until you call save() on the model. Most methods return a boolean indicating if the operation was successful.

    Important: Because moving categories involves multiple database queries, a transaction is automatically started when a category is saved. It is safe to use global transactions if working with multiple models.

    To verify if a category actually changed its position in the tree after a save, use the hasMoved() method.

    if ($category->save()) {
        $moved = $category->hasMoved();
    }
  2. Install Rinvex Categories

    master

    Follow these steps to install the package, publish its resources, and run the migrations:

    1. Install via Composer.
    2. Publish migrations and configuration files.
    3. Run the package-specific migration command.
    composer require rinvex/laravel-categories
    
    php artisan rinvex:publish:categories
    
    php artisan rinvex:migrate:categories
  3. Build or rebuild a tree from an array

    master

    Building a tree recursively

    When using the create method, if the attributes array contains a children key, the service will recursively create the nested structure.

    Rebuilding a tree

    You can mass-update the tree structure using rebuildTree($data, $delete).

    • $data: An array of categories. If an id is provided, the existing category is updated. If no id is provided, a new category is created.
    • $delete: A boolean indicating whether to delete categories that exist in the database but are missing from the $data array. Defaults to false.
    // Recursive creation
    $category = app('rinvex.categories.category')->create([
        'name' => 'New Category',
        'children' => [
            ['name' => 'Child 1', 'children' => [['name' => 'Grandchild']]],
        ],
    ]);
    
    // Rebuilding
    app('rinvex.categories.category')->rebuildTree($data, $delete);
  4. Delete categories safely

    master

    To delete a category, use the model's delete() method:

    $category->delete();

    WARNING:

    1. Deleting a category will also automatically delete all of its descendants.
    2. DO NOT use a direct query builder delete like app('rinvex.categories.category')->where('id', $id)->delete(). This bypasses the nested set logic and will break the tree structure.
  5. Create categories as roots or children

    master

    You can create categories using the rinvex.categories.category service.

    Creating a Root Category

    By default, creating a category without specifying a parent appends it to the end of the tree as a root.

    Making an existing category a root

    You can move an existing category to the root level using saveAsRoot() (implicit save) or makeRoot()->save() (explicit save).

    Appending/Prepending to a parent

    To make a category a child of an existing $parent category:

    • Append (last child): Use appendToNode($parent)->save(), $parent->appendNode($category), or $category->parent()->associate($parent)->save().
    • Prepend (first child): Use prependToNode($parent)->save() or $parent->prependNode($category).

    Inserting relative to a neighbor

    You can position a category before or after a specific $neighbor category:

    • Explicit save: $category->afterNode($neighbor)->save() or $category->beforeNode($neighbor)->save().
    • Implicit save: $category->insertAfterNode($neighbor) or $category->insertBeforeNode($neighbor).
    // Create as root
    app('rinvex.categories.category')->create($attributes);
    
    // Append to parent
    $category->appendToNode($parent)->save();
    
    // Prepend to parent
    $category->prependToNode($parent)->save();
    
    // Insert after neighbor
    $category->afterNode($neighbor)->save();
  6. Register Rinvex Categories via Service Provider

    master

    The package uses the Rinvex\Categories\Providers\CategoriesServiceProvider to register its components. It handles configuration merging, Eloquent model binding, and morph mapping for relations.

    Key configuration and registration details:

    • Config Key: The package configuration is accessed via rinvex.categories.
    • Model Binding: The category model is bound to the IoC container under the key rinvex.categories.category.
    • Morph Mapping: The package automatically maps the category morph type to the model class defined in config('rinvex.categories.models.category') to ensure polymorphic relations work correctly.
  7. Check and fix tree consistency

    master

    If you suspect the nested set structure is corrupted, you can check for errors and fix them.

    Checking for errors

    • isBroken(): Returns true if the tree has structural errors.
    • countErrors(): Returns an array of error statistics:
      • oddness: Wrong _lft and _rgt values.
      • duplicates: Duplicate _lft or _rgt values.
      • wrong_parent: parent_id does not match _lft/_rgt values.
      • missing_parent: parent_id points to a non-existent category.

    Fixing the tree

    Use fixTree() to repair the structure. It uses the parent_id column to recalculate the correct _lft and _rgt values.

    // Check if broken
    if (app('rinvex.categories.category')->isBroken()) {
        $errors = app('rinvex.categories.category')->countErrors();
        app('rinvex.categories.category')->fixTree();
    }
  8. Query categories with depth and order constraints

    master

    Category Depth

    To find the level of a category (where root is 0), use withDepth():

    $category = app('rinvex.categories.category')->withDepth()->find($id);
    echo $category->depth;

    Ordering

    By default, categories are ordered by their _lft value. Use these methods on the query builder:

    • defaultOrder(): Order by _lft value.
    • reversed(): Reverse the default order.

    Shifting position

    To move a category up or down within its parent to change its order:

    • $category->up() or $category->down().
    • $category->down(3): Shift down by 3 positions.

    Query Constraints

    • whereIsRoot(): Only root categories.
    • whereIsAfter($id) / whereIsBefore($id): Categories positioned after/before a specific ID.
    • whereDescendantOf($category): Categories within a specific subtree.
    • whereDescendantOrSelf($category): Includes the target category in the descendant result set.
    • whereAncestorOf($category): Categories that are ancestors of the target.
    // Get categories at depth 1
    $result = app('rinvex.categories.category')->withDepth()->having('depth', '=', 1)->get();
    
    // Order by lft
    $result = app('rinvex.categories.category')->defaultOrder()->get();
    
    // Shift position
    $category->down(3);
  9. Detach categories from a model

    master

    Use the detachCategories() method to remove categories from a model. It accepts the same input types as attachCategories(). To remove all currently attached categories, call the method without arguments or with null.

    // Detach specific categories
    $post->detachCategories([1, 2]);
    
    // Detach all categories
    $post->detachCategories();
  10. Check if a model has specific categories

    master

    Use hasAnyCategories() to check if any of the provided categories are attached to the model. Use hasAllCategories() to perform a strict comparison to check if all of the provided categories are attached. Both methods return a boolean.

    // Returns true if the model has any of these IDs
    $post->hasAnyCategories([1, 2, 5]);
    
    // Returns true only if the model has all of these slugs
    $post->hasAllCategories(['first-category', 'second-category']);
  11. Convert category collections to trees

    master

    Building a Tree

    Convert a flat collection of categories into a nested structure using toTree(). This populates the parent and children relationships on each model.

    Building a Flat Tree

    To get a list where children immediately follow their parents (useful for non-recursive rendering), use toFlatTree().

    Getting a Subtree

    To load only a specific subtree starting from a root:

    $root = app('rinvex.categories.category')->find($rootId);
    $tree = $root->descendants->toTree($root);
    // Convert to nested tree
    $tree = app('rinvex.categories.category')->get()->toTree();
    
    // Convert to flat tree (parent followed by children)
    $flatTree = app('rinvex.categories.category')->get()->toFlatTree();