Skip to content

Instantly share code, notes, and snippets.

@Meroje
Created May 30, 2013 06:24
Show Gist options
  • Save Meroje/5676023 to your computer and use it in GitHub Desktop.
Save Meroje/5676023 to your computer and use it in GitHub Desktop.
<?php
with(new Page())->makeRoot();
with(new Page())->makePreviousSiblingOf(Page::find(1))
with(new Page())->makeNextSiblingOf(Page::find(1))
with(new Page())->makeLastChildOf(Page::find(5))
with(new Page())->makeFirstChildOf(Page::find(2))
Page::find(2)->children()
Page::find(2)->parent()
Page::find(2)->sibling()
Page::find(2)->isDescendant(Page::find(3))
Page::find(2)->isAncestor(Page::find(3))
Page::find(2)->isLeaf()
Page::allRoot()
Page::allLeaf()
Page::find(2)->getTreeDepth()
<?php
Schema::create('yourtable', function(Blueprint $table)
{
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->string('title');
$table->string('tree_path');
$table->integer('tree_order')->unsigned()->default(0);
$table->integer('tree_pid')->unsigned()->default(0);
$table->integer('tree_depth')->unsigned()->default(0);
$table->index('tree_path');
});
<?php
namespace Model\Tree;
abstract class MaterializedPathTable extends \Eloquent {
protected $columnTreePid = 'tree_pid';
protected $columnTreeOrder = 'tree_order';
protected $columnTreePath = 'tree_path';
protected $columnTreeDepth = 'tree_depth';
public function makeRoot()
{
$this->fill(array(
$this->columnTreePath => '.0.',
$this->columnTreePid => 0,
$this->columnTreeDepth => 0,
$this->columnTreeOrder => (static::allRoot()->max($this->columnTreeOrder) + 1)
));
$this->save();
return $this;
}
public function makeFirstChildOf($parent)
{
if ($this->exists and $this->isAncestor($parent)) throw new \Exception('Cant move Ancestor to Descendant');
if (!$parent->exists) throw new \Exception('Parent doesnt exist');
$this_ = $this;
\DB::transaction(function() use (&$this_, &$parent)
{
$parent->children(1)->increment($this_->getColumnTreeOrder());
if ($this_->exists)
{
$children = $this_->children()->get();
foreach($children as $child)
{
$child->update(array(
$child->getColumnTreePath() => str_replace($this_->getTreePath(), $parent->getTreePath().$parent->getKey().'.', $child->getTreePath()),
$child->getColumnTreeDepth() => ( $parent->getTreeDepth() + 1 + ($child->getTreeDepth() - $this_->getTreeDepth()) ),
));
}
}
$this_->fill(array(
$this_->getColumnTreePath() => $parent->getTreePath().$parent->getKey().'.',
$this_->getColumnTreePid() => $parent->getKey(),
$this_->getColumnTreeOrder() => 0,
$this_->getColumnTreeDepth() => ($parent->getTreeDepth() + 1)
));
$this_->save();
});
return $this;
}
public function makeLastChildOf($parent)
{
if ($this->exists and $this->isAncestor($parent)) throw new \Exception('Cant move Ancestor to Descendant');
if (!$parent->exists) throw new \Exception('Parent doesnt exist');
$this_ = $this;
\DB::transaction(function() use (&$this_, &$parent)
{
if ($this_->exists)
{
$children = $this_->children()->get();
foreach($children as $child)
{
$child->update(array(
$child->getColumnTreePath() => str_replace($this_->getTreePath(), $parent->getTreePath().$parent->getKey().'.', $child->getTreePath()),
$child->getColumnTreeDepth() => ( $parent->getTreeDepth() + 1 + ($child->getTreeDepth() - $this_->getTreeDepth()) ),
));
}
}
$this_->fill(array(
$this_->getColumnTreePath() => $parent->getTreePath().$parent->getKey().'.',
$this_->getColumnTreePid() => $parent->getKey(),
$this_->getColumnTreeOrder() => ($parent->children(1)->max($parent->getColumnTreeOrder())+1),
$this_->getColumnTreeDepth() => ($parent->getTreeDepth() + 1)
));
$this_->save();
});
return $this;
}
public function makePreviousSiblingOf($sibling)
{
return $this->processSiblingOf($sibling, '>=');
}
public function makeNextSiblingOf($sibling)
{
return $this->processSiblingOf($sibling, '>');
}
public function parent()
{
return $this->newQuery()->where($this->getKeyName(), '=', $this->getTreePid());
}
public function sibling()
{
return $this->newQuery()->where($this->columnTreePid, '=', $this->getTreePid());
}
public function children($depth=0)
{
$query = $this->newQuery();
if ($depth==1)
{
$query->where($this->columnTreePid, '=', $this->getKey());
}
else
{
$query->where($this->columnTreePath, 'like', $this->getTreePath().$this->getKey().'.%');
}
if ($depth)
{
$query->where($this->columnTreeDepth, '<=', $this->getTreeDepth() + $depth);
}
return $query;
}
public function isDescendant($ancestor)
{
if (!$this->exists) throw new \Exception('Model doesnt exist');
return strpos($this->getTreePath(), $ancestor->getTreePath().$ancestor->getKey().'.')!==false and $ancestor->getTreePath()!==$this->getTreePath();
}
public function isAncestor($descendant)
{
if (!$this->exists) throw new \Exception('Model doesnt exist');
return strpos($descendant->getTreePath(), $this->getTreePath().$this->getKey().'.')!==false and $descendant->getTreePath()!==$this->getTreePath();
}
public function isLeaf()
{
if (!$this->exists) throw new \Exception('Model doesnt exist');
return !count($this->children(1)->get()->toArray());
}
public function relativeDepth($object)
{
return abs($this->getTreeDepth() - $object->getTreeDepth());
}
public static function allRoot()
{
$instance = new static;
$query = $instance->newQuery()->where($instance->getColumnTreePid(), '=', 0);
return $query;
}
public static function allLeaf()
{
$instance = with(new static);
$query = $instance->newQuery();
$query->select($instance->getTable().'.*');
$query->leftJoin($instance->getTable().' as t_2', function($join) use ($instance)
{
$join->on($instance->getTable().'.'.$instance->getKeyName(), '=', 't_2.'.$instance->getColumnTreePid());
})
->whereNull('t_2.id');
return $query;
}
public function getTreePid()
{
return $this->getAttribute($this->columnTreePid);
}
public function getTreeOrder()
{
return $this->getAttribute($this->columnTreeOrder);
}
public function getTreePath()
{
return $this->getAttribute($this->columnTreePath);
}
public function getTreeDepth()
{
return $this->getAttribute($this->columnTreeDepth);
}
public function getColumnTreePid()
{
return $this->columnTreePid;
}
public function getColumnTreeOrder()
{
return $this->columnTreeOrder;
}
public function getColumnTreePath()
{
return $this->columnTreePath;
}
public function getColumnTreeDepth()
{
return $this->columnTreeDepth;
}
public function setColumnTreePid($name)
{
$this->columnTreePid = $name;
}
public function setColumnTreeOrder($name)
{
$this->columnTreeOrder = $name;
}
public function setColumnTreePath($name)
{
$this->columnTreePath = $name;
}
public function setColumnTreeDepth($name)
{
$this->columnTreeDepth = $name;
}
protected function processSiblingOf($sibling, $op)
{
if ($this->exists and $this->isAncestor($sibling)) throw new \Exception('Cant move Ancestor to Descendant');
if (!$sibling->exists) throw new \Exception('Sibling doesnt exist');
$this_ = &$this;
\DB::transaction(function() use (&$this_, &$sibling, $op)
{
$sibling->sibling()->where($this_->getColumnTreeOrder(), $op, $sibling->getTreeOrder())->increment($this_->getColumnTreeOrder());
if ($this_->exists)
{
$children = $this_->children()->get();
foreach($children as $child)
{
$child->update(array(
$child->getColumnTreePath() => str_replace($this_->getTreePath(), $sibling->getTreePath(), $child->getTreePath()),
$child->getColumnTreeDepth() => ( $sibling->getTreeDepth() + ($child->getTreeDepth() - $this_->getTreeDepth()) ),
));
}
}
$this_->fill(array(
$this_->getColumnTreePath() => $sibling->getTreePath(),
$this_->getColumnTreePid() => $sibling->getTreePid(),
$this_->getColumnTreeOrder() => $sibling->getTreeOrder()+($op=='>'?1:0),
$this_->getColumnTreeDepth() => $sibling->getTreeDepth(),
));
$this_->save();
});
return $this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment