Skip to content

Instantly share code, notes, and snippets.

@potfur
Last active August 29, 2015 13:56
Show Gist options
  • Save potfur/8954044 to your computer and use it in GitHub Desktop.
Save potfur/8954044 to your computer and use it in GitHub Desktop.
Self nesting category [nested set]
<?php
namespace gist;
/**
* Self nesting node
* Can use parent_id/order or left/right nested set or convert one to another
*
* @package gist
* @author Michal Wachowski <[email protected]>
*/
class Node
{
protected $id;
protected $left = 0;
protected $right = 1;
protected $parent_id = 0;
protected $order = 0;
protected $title;
/** @var array|self[] */
public $children = array();
/**
* Constructor
*
* @param array|self[] $children
*/
public function __construct($children = array())
{
foreach (get_object_vars($this) as $field => $value) {
if (array_key_exists($field, $children)) {
$this->$field = $children[$field];
}
}
}
/**
* Builds tree using parent_id & order properties
*
* @return $this
*/
public function parentify()
{
$tArr = $this->gather(array($this->id => &$this), 'id');
uasort(
$tArr, function (self $a, self $b) {
return $a->order - $b->order;
}
);
$uArr = array();
foreach ($tArr as $i => &$category) {
if (!$category->id) {
unset($category);
continue;
}
if (isset($tArr[$category->parent_id])) {
$tArr[$category->parent_id]->children[] = &$category;
$uArr[] = $i;
}
unset($category);
}
foreach ($uArr as $i) {
unset($tArr[$i]);
}
return $this;
}
/**
* Builds tree using nested set left-right properties
*
* @return $this
*/
public function nestify()
{
$tArr = $this->gather(array($this->left => $this), 'left');
$lft = &$this->left;
$rgt = &$this->right;
uasort(
$tArr, function (self $a, self $b) use (&$lft, &$rgt) {
$lft = (int) min($lft, $a->left, $b->left);
$rgt = (int) max($rgt, $a->right, $b->right);
return $a->left - $b->left;
}
);
$this->right++;
$this->buildSet($this, $tArr);
return $this;
}
/**
* Builds set
* Internal, used by nestify
*
* @param $parent
* @param $tArr
*/
protected function buildSet(&$parent, &$tArr)
{
$rArr = array();
while ($category = array_shift($tArr)) {
if ($category->left > $parent->left && $category->right < $parent->right) {
$parent->children[] = $category;
} else {
$rArr[] = $category;
}
}
$tArr = $rArr;
foreach ($parent->children as $category) {
$this->buildSet($category, $parent->children);
unset($category);
}
}
/**
* Flattens tree to simple array as children in root node
* Preserves all property values
*
* @return $this
*/
public function flatten()
{
$this->children = $this->gather();
return $this;
}
/**
* Gathers nodes
*
* @param array $tArr
* @param null|string $key
*
* @return array
*/
protected function gather($tArr = array(), $key = null)
{
foreach ($this->children as &$category) {
if ($key) {
$tArr[$category->$key] = &$category;
} else {
$tArr[] = &$category;
}
$tArr = $category->gather($tArr, $key);
unset($category);
}
$this->children = array();
return $tArr;
}
/**
* Rebuilds properties in tree
*
* @param int $i
* @param int $parent_id
*
* @return $this
*/
public function rebuild(&$i = 0, $parent_id = 0)
{
$this->left = $i;
foreach ($this->children as &$category) {
$i++;
$category->parent_id = $parent_id;
$category->rebuild($i, $category->id);
unset($category);
}
$this->right = ++$i;
return $this;
}
}
<?php
use gist\Node;
$root = new Node(['id' => 0, ]);
$root->children[] = new Node(['id' => 1, 'parent_id' => 0]);
$root->children[] = new Node(['id' => 2, 'parent_id' => 1]);
$root->children[] = new Node(['id' => 3, 'parent_id' => 0]);
$root->children[] = new Node(['id' => 4, 'parent_id' => 3]);
var_dump($root->parentify()->rebuild());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment