Home > Back-end >  Translate javascript makeTree in Laravel
Translate javascript makeTree in Laravel

Time:07-08

I'm using Laravel 8. Currently I have this function in my projet to build a json tree but it's client side and I'd like to make this tree on the json response laravel endpoint.

 makeTree = (nodes, parentId) => {
    return nodes
      .filter((node) => node.parent_id === parentId)
      .reduce(
        (tree, node) => [
          ...tree,
          {
            ...node,
            children: this.makeTree(nodes, node.id),
          },
        ],
        []
      );
  };

Actually the endpoint returns the flat data like this :

[{"id":1,"parent_id":null,"value":"Val1"} {"id":2,"parent_id":1,"value":"Val2"} ...]

Then I send the received array in my makeTree function to build the tree:

[
  {
    "id":1,
    "parent_id":null,
    "value":"Val1",
    "children":[
      {
        "id":2,
        "parent_id":1,
        "value":"Val2",
        "children":[]
      },
      {
        "id":3,
        "parent_id":1,
        "value":"Val3",
        "children":[]
      },
    ]
  }
    ...
]

Here is my Model :

class MyTree extends Model
{
    protected $table = 'my_tree';
    public $timestamps = true;
    protected $fillable = [
        'parent_id',
        'value',
    ];

    /**
     * A child belongs to a parent.
     *
     * @return MyTree
     */
    public function parent()
    {
        return $this->belongsTo(MyTree::class, 'parent_id');
    }

    /**
     * An Parent has many Children.
     *  *
     * @return MyTree[]
     */
    public function children()
    {
        return $this->hasMany(MyTree::class, 'parent_id');
    }
}

Can you help me to build the makeTree function server side with php and laravel relationships ? There is a way to do it with recursive function like makeTree ?

CodePudding user response:

Honestly i didn't dope dive into the javascript snippet, but i can share here some code stuff which i've used in the past and it worked so well. Actually i've found some snippet before and improved that for my own. Don't have that exact link currenty (will update in case of having that), but this is what i've got:

Here you can use infinite deep of tree-branches.

Let's say we've following MyTree model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class MyTree extends Model
{
    protected $table = 'my_tree';

    protected $fillable = [
        'id',
        'parent_id',
        'value',
    ];
}

So we can get all the MyTree items from DB with a query, and convert it to the tree array like this, using getNestedItems method:

NOTE: here's something additional, but it wont prevent you to get your solution. Here's an additional argument to exclude some root item:

public function getNestedItems($exclude_parent_id = null): array
{
    $arr = MyTree::select([
        'id',
        'parent_id',
        'value',
    ])
        ->when(isset($exclude_parent_id), function ($query_id) use ($exclude_parent_id) {
            return $query_id->where('id', '!=', $exclude_parent_id);
        })
        ->get()
        ->toArray();

    if (count($arr) > 0) {
        $new = [];
        foreach ($arr as $a) {
            $parent_id = empty($a['parent_id']) ? 0 : $a['parent_id'];
            $new[$parent_id][] = $a;
        }

        $tree = $this->mekeTree($new, $new[0], $exclude_parent_id);

        return [
            'count' => count($arr),
            'tree' => $tree,
        ];
    } else {
        return [
            'count' => 0,
            'tree' => [],
        ];
    }
}

As you can see we've called makeTree method from getNestedItems, which will recursively create the tree "branches". So yes, it's a recursive function with usage of PHP references:

protected function mekeTree(&$list, $parent, $exclude_parent_id): array
{
    $tree = [];
    foreach ($parent as $k => $l) {
        if(isset($list[$l['id']])) {
            if ($l['id'] == $exclude_parent_id) {
                continue;
            }
            $l['children'] = $this->mekeTree($list, $list[$l['id']], $exclude_parent_id);
        }
        $tree[] = $l;
    }

    return $tree;
}

Also here is a Gist for that i've just created.

Enjoy!

CodePudding user response:

You can try this to get your structure using Collection and the method groupBy() to simplify the code.

$myTrees = Mytree::get();

$groupedTree = $myTrees->groupBy('parent_id');
foreach ($myTrees as $myTree) {
    if ( ! $myTree->parent_id) {
        $myTree->setRelation('parent', null);
    }

    $children = $groupedTree->get($myTree->id, [ ]);

    foreach ($children as $child) {
        $child->setRelation('parent', $myTree);
    }

    $myTree->setRelation('children',collect($children));
}
$finalTree = [];
foreach ($myTrees as $myTree) {
    if ($myTrees->getParentId() == null) {
        $finalTree[] = $myTree;
    }
}

return collect($finalTree);

One limit is it will only work if the root of the tree has parent_id = null

I would also suggest you use laravel-nestedset and simply run

$finalTree = Mytree::get()->toTree();
  • Related