webhappens/traverser

Easily traverse nested object structures without worrying about recursion.

v0.3.2 2024-06-26 14:29 UTC

This package is auto-updated.

Last update: 2024-10-26 15:08:36 UTC


README

tests

Traverser

Easily traverse nested object structures without worrying about recursion. Great for building menus, navigation, sitemaps and more!

Installation

Install via composer:

composer require webhappens/traverser

Import the class into your namespace:

use WebHappens\Traverser\Traverser;

Getting started

In order for the traverser to understand your hierarchy, you must ensure it can resolve "id", "parent" and "children" for each class in that hierarchy.

By default, it will look for "methods" or "properties" (in that order) by each of these names. If your classes extend from Illuminate\Database\Eloquent\Model it will resolve the "id" automatically from the getKey method.

Your classes should typically look something like this:

public $id;

/**
 * @return object|null
 */
public function parent()
{
    //
}

/**
 * @return array
 */
public function children()
{
    //
}

Next, create a new instance of the traverser class and pass in an instance of the class you'd like to use as your starting point for traversal.

$traverser = Traverser::make($current);

You can now call the various traversal methods on this instance.

$descendants = $traverser->descendants();

As a convenience you might want to put this on a base class or trait:

// Within a base class or trait

public function traverser()
{
    return Traverser::make($this);
}

And then call it like this:

// Within a class that extends the base class / uses the trait

$this->traverser()->descendants();

For a deeper insight on using the traverser you should take a look at the tests.

Custom mapping names

You may override the default "id", "parent" and "children" mapping names for each class if you want to.

To do this pass a second argument into the constructor containing a custom mapping array.

$traverser = Traverser:make($current, [
    Page::class => ['id' => 'uri'],
    Post::class => ['parent' => 'category', 'children' => 'comments'],
    Comment::class => ['parent' => 'post'],
]);

If you're using Laravel you may want to bind this to the Service Container.

// Within AppServiceProvider.php

$this->app->bind('traverser', function () {
    return \WebHappens\Traverser\Traverser::make()->maps([...]);
});

And then resolve it like this:

// Within your application code

$traverser = resolve('traverser')->current($current);

Note that you can construct a new traverser instance without passing any arguments and use the maps and current methods instead.

Inferring parent and children

In most situations your objects will only have data about who their "parent" is or who their "children" are, not both. For this reason the traverser allows you to easily infer one from the other.

To infer parent you should pass an array of all possible parents into the inferParent method. Each of these objects must be able to resolve its own children.

// Within Category.php

public function parent()
{
    return $this->traverser()->inferParent(static::all());
}

public function children()
{
    return array_merge($this->categories(), $this->posts());
}

// Within Post.php

public function category()
{
    return $this->traverser()->inferParent(Category::all());
}

To infer children you should pass an array of all possible children into the inferChildren method. Each of these objects must be able to resolve its own parent.

// Within Page.php

public function parent()
{
    // ...
    return new static($parentId);
}

public function children()
{
    return $this->traverser()->inferChildren(static::all());
}

Traversal methods

$parent = $this->traverser()->parent();
$children = $this->traverser()->children();
$ancestors = $this->traverser()->ancestors();
$ancestorsAndSelf = $this->traverser()->ancestorsAndSelf();
$descendants = $this->traverser()->descendants();
$descendantsAndSelf = $this->traverser()->descendantsAndSelf();
$siblings = $this->traverser()->siblings();
$siblingsAndSelf = $this->traverser()->siblingsAndSelf();
$siblingsNext = $this->traverser()->siblingsNext();
$siblingsAfter = $this->traverser()->siblingsAfter();
$siblingsPrevious = $this->traverser()->siblingsPrevious();
$siblingsBefore = $this->traverser()->siblingsBefore();
$siblingsPosition = $this->traverser()->siblingsPosition();

Credits

License

The MIT License (MIT). Please see License File for more information.