Skip to content

Commit

Permalink
renewed tree behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
bariew committed Jul 22, 2014
1 parent bcc404e commit 9e92742
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 301 deletions.
246 changes: 99 additions & 147 deletions ARTreeBehavior.php
@@ -1,9 +1,15 @@
<?php

namespace bariew\nodeTree;
use yii\base\Behavior;
use yii\db\ActiveRecord;

class ARTreeBehavior extends PTARBehavior
/**
* Class ARTreeBehavior
* @package bariew\nodeTree
* @property ActiveRecord $owner
*/
class ARTreeBehavior extends Behavior
{
/* ATTRIBUTES AND LISTS */

Expand All @@ -12,21 +18,46 @@ class ARTreeBehavior extends PTARBehavior
public $title = 'title';
public $rank = 'rank';
public $url = 'url';
public $name = 'name';
public $slug = 'name';
public $content = 'content';
public $actionPath = '/page/item/update';

public static $uniqueKey = 0;


/* EVENTS*/

public function events()
{
return [
ActiveRecord::EVENT_AFTER_INSERT => 'afterSave',
ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave',
ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert',
ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete',
];
}


public function beforeInsert()
{
$this->set('rank', $this->getLastRank() + 1);
$this->createUrl();
}

public function afterDelete()
{
$this->treeResort($this->get('rank'), -1);
$this->owner->deleteAll($this->getDescendantCondition());
}


/* MENU WIDGET */

public function menuWidget($view='node', $attributes=array(), $return=false)
{
$items = $this->childrenTree($attributes);
$behavior = $this;
$widget = new ARTreeMenuWidget(compact('view', 'items', 'behavior'), $return);
return $widget->run();
}

public function nodeAttributes($model = false, $pid = '')
{
$uniqueKey = self::$uniqueKey++;
Expand All @@ -47,92 +78,19 @@ public function nodeAttributes($model = false, $pid = '')
)
);
}

public function getJsTree()
{
$tree = $this->childrenTree();
foreach ($tree as $key=>$data){
$tree[$key] = $this->setJsData($data);
}
return reset($tree);
}

private function setJsData($data)
{
$result = $this->nodeAttributes($data['model']);
foreach($data['children'] as $child){
$result['children'][] = $this->setJsData($child);
}
return $result;
}

public function getParent()
{
return $this->owner->findOne(array(
$this->id => $this->get('parent_id')
));
}

public function getParentsCriteria($addCriteria = array())
{
$query = new CDbCriteria();
$urls = array();
while($this->url){
$urls[] = $this->url = preg_replace('/(\d+)?\/$/','',$this->url);
}
$query->addInCondition('url', $urls ? $urls : array(0));
$query->mergeWith($addCriteria);
return $query;
}

public function getChildren($addCriteria=array())
{
if($this->owner->isNewRecord){
return array();
}
$query = new CDbCriteria();
$query->order = $this->rank;
$query->addColumnCondition(array($this->parent_id => $this->owner->primaryKey))
->mergeWith($addCriteria);
return $this->find($query, true);
}

public function getDescendants($query)
{
return $query->andWhere($this->getDescendantCondition())->all();
}

public function getDescendantCondition()
{
if(!$url = $this->get('url')){
$url = '/';
}
return ['like', 'url', $url];
}


public function menuWidget($view='node', $attributes=array(), $return=false)
{
$items = $this->childrenTree($attributes);
$behavior = $this;
$widget = new ARTreeMenuWidget(compact('view', 'items', 'behavior'), $return);
return $widget->run();
}


/* TREE SERVICE */
/* TREE BUILD */

public function childrenTree($attributes=array())
public function childrenTree($conditions = array())
{
$query = $this->owner->find();
if($attributes){
$query->andFilterWhere($attributes);
}
$items = $this->getDescendants($query);
$items = $this->owner->find()->where(
array_merge($this->getDescendantCondition(), $conditions)
)->all();
return $this->toTree($items);
}

public function toTree($items)
protected function toTree($items)
{
$id = $this->id;
$result = array();
Expand Down Expand Up @@ -168,90 +126,84 @@ private function rangeTree($items)
return $result;
}

public function cloneTo($pid)
{
$className = get_class($this->owner);
$new = new $className;
$new->attributes = $this->owner->attributes;
$this->ownerBehavior($new)->move($pid);
foreach($this->getChildren() as $child){
$this->ownerBehavior($child)->cloneTo($new->id);
}
return true;
}


/* SYSTEM SERVICE */
/* TREE UPDATE */

public function createUrl()
protected function createUrl()
{
$model = $this->owner;
$oldUrl = $this->get('url');
$newUrl = (($parent = $this->getParent()) ? $this->ownerBehavior($parent)->get('url') : '/')
. $this->get('name') . "/";
$newUrl = (($parent = $this->getParent()) ? $parent->{$this->url} : '/') . $this->get('slug') . "/";
if($newUrl == $oldUrl){
return true;
return false;
}
if($oldUrl && $this->get('parent_id')){
\Yii::$app->db->createCommand("
UPDATE {$model->tableName()}
if (!$this->get('parent_id')) {
return $this->set('slug', '');
}
return ($oldUrl)
? \Yii::$app->db->createCommand("
UPDATE {$this->owner->tableName()}
SET {$this->url} = REPLACE({$this->url}, '{$oldUrl}', '{$newUrl}')
WHERE {$this->url} LIKE '{$oldUrl}%'
")->execute();
}
$attributes = $this->get('parent_id')
? [$this->url => $newUrl]
: [$this->name => ''];
return $model->updateAll($attributes, ['id'=>$model->primaryKey]);
")->execute()
: $this->set('url', $newUrl);
}

public function move($pid, $position=false)
public function treeMove($pid, $rank = false)
{
if($position === false){
$position = ($lastChild = $this->owner->findOne(array(
$this->parent_id => $pid
), array(
'order' => 'rank DESC',
'condition' => 'id != '.$this->get('id') * 1
))) ? $lastChild->rank+1 : 0;
if ($rank === false) {
$rank = $this->getLastRank() + 1;
}
return $this->treeResort(-1) // resort old parent children
->set('rank', $position)
->set('parent_id', $pid)
->treeResort() // resort new parent children
->owner->save(false);
$selfShift = ($pid == $this->get('parent_id')) && ($rank > $this->get('rank'));
$this->treeResort($this->get('rank'), -1);
$this->set('parent_id', $pid)->createUrl();
$this->treeResort($rank - ($selfShift ? 0 : 1), 1);
return $this->owner->updateAttributes([
$this->parent_id => $pid,
$this->rank => $rank
]);
}

private function treeResort($increment = 1)
private function treeResort($rank = 0, $increment = 1)
{
if(!$pid = $this->get('parent_id')){
return $this;
};
$condition = "{$this->parent_id} = {$pid}
AND {$this->rank} >= {$this->get('rank')}"
. (($id = $this->get('id')) ? " AND id != {$id}" : "");
\Yii::$app->db->createCommand("
UPDATE {$this->owner->tableName()} SET {$this->rank} = ({$this->rank}+{$increment})
WHERE {$condition}
")->execute();
return $this;
return $this->owner->updateAllCounters(
['rank' => $increment],
"{$this->parent_id} = :parent_id AND rank > :rank",
[':rank' => $rank, ':parent_id' => $this->get('parent_id')]
);
}


/* GETTERS */

protected function getParent()
{
return $this->owner->findOne(array(
$this->id => $this->get('parent_id')
));
}


/* SYSTEM */

public static function afterSave($event)
protected function getDescendantCondition()
{
$behavior = self::fromEvent($event);
if($behavior->attributesChanged(array('parent_id'))){
$behavior->createUrl();
if(!$url = $this->get('url')){
$url = '/';
}
return ['like', 'url', $url];
}

protected function get($attributeName)
{
return $this->owner->{$this->$attributeName};
}

protected function set($attributeName, $value)
{
$this->owner->{$this->$attributeName} = $value;
return $this;
}

public static function afterDelete($event)
protected function getLastRank()
{
$behavior = self::fromEvent($event);
$behavior->treeResort(-1);
$behavior->owner->deleteAll($behavior->getDescendantCondition());
$max = $this->owner->find()->where([$this->parent_id => $this->get('parent_id')])->max($this->rank);
return is_numeric($max) ? $max : -1;
}
}

0 comments on commit 9e92742

Please sign in to comment.