123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- <?php
- namespace Dcat\Admin\Traits;
- use Dcat\Admin\Exception\AdminException;
- use Dcat\Admin\Support\Helper;
- use Dcat\Admin\Tree;
- use Illuminate\Database\Eloquent\Builder;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Support\Arr;
- use Illuminate\Support\Facades\Request;
- use Spatie\EloquentSortable\SortableTrait;
- /**
- * @property string $parentColumn
- * @property string $titleColumn
- * @property string $orderColumn
- * @property string $depthColumn
- * @property string $defaultParentId
- * @property array $sortable
- */
- trait ModelTree
- {
- use SortableTrait;
- /**
- * @var array
- */
- protected static $branchOrder = [];
- /**
- * @var \Closure[]
- */
- protected $queryCallbacks = [];
- /**
- * @return string
- */
- public function getParentColumn()
- {
- return property_exists($this,'parentColumn') ? $this->parentColumn : 'parent_id';
- }
- /**
- * Get title column.
- *
- * @return string
- */
- public function getTitleColumn()
- {
- return property_exists($this,'titleColumn') ? $this->titleColumn : 'title';
- }
- /**
- * Get order column name.
- *
- * @return string
- */
- public function getOrderColumn()
- {
- return property_exists($this,'orderColumn') ? $this->orderColumn : 'order';
- }
- /**
- * Get depth column name.
- *
- * @return string
- */
- public function getDepthColumn()
- {
- return property_exists($this,'depthColumn') ? $this->depthColumn : '';
- }
- /**
- * @return string
- */
- public function getDefaultParentId()
- {
- return property_exists($this,'defaultParentId') ? $this->defaultParentId : '0';
- }
- /**
- * Set query callback to model.
- *
- * @param \Closure|null $query
- * @return $this
- */
- public function withQuery(\Closure $query = null)
- {
- $this->queryCallbacks[] = $query;
- return $this;
- }
- /**
- * Format data to tree like array.
- *
- * @return array
- */
- public function toTree(array $nodes = null)
- {
- if ($nodes === null) {
- $nodes = $this->allNodes();
- }
- return Helper::buildNestedArray(
- $nodes,
- $this->getDefaultParentId(),
- $this->getKeyName(),
- $this->getParentColumn()
- );
- }
- /**
- * Get all elements.
- *
- * @return static[]|\Illuminate\Support\Collection
- */
- public function allNodes()
- {
- return $this->callQueryCallbacks(new static())
- ->orderBy($this->getOrderColumn(), 'asc')
- ->get();
- }
- /**
- * @param $this $model
- * @return $this|Builder
- */
- protected function callQueryCallbacks($model)
- {
- foreach ($this->queryCallbacks as $callback) {
- if ($callback) {
- $model = $callback($model);
- }
- }
- return $model;
- }
- /**
- * Set the order of branches in the tree.
- *
- * @param array $order
- * @return void
- */
- protected static function setBranchOrder(array $order)
- {
- static::$branchOrder = array_flip(Arr::flatten($order));
- static::$branchOrder = array_map(function ($item) {
- return ++$item;
- }, static::$branchOrder);
- }
- /**
- * Save tree order from a tree like array.
- *
- * @param array $tree
- * @param int $parentId
- */
- public static function saveOrder($tree = [], $parentId = 0, $depth = 1)
- {
- if (empty(static::$branchOrder)) {
- static::setBranchOrder($tree);
- }
- foreach ($tree as $branch) {
- $node = static::find($branch['id']);
- $node->{$node->getParentColumn()} = $parentId;
- $node->{$node->getOrderColumn()} = static::$branchOrder[$branch['id']];
- $node->getDepthColumn() && $node->{$node->getDepthColumn()} = $depth;
- $node->save();
- if (isset($branch['children'])) {
- static::saveOrder($branch['children'], $branch['id'], $depth + 1);
- }
- }
- }
- protected function determineOrderColumnName()
- {
- return $this->getOrderColumn();
- }
- public function moveOrderDown()
- {
- $orderColumnName = $this->determineOrderColumnName();
- $parentColumnName = $this->getParentColumn();
- $sameOrderModel = $this->getSameOrderModel('>');
- if ($sameOrderModel) {
- $this->$orderColumnName = $this->$orderColumnName + 1;
- $this->save();
- return $this;
- }
- $swapWithModel = $this->buildSortQuery()
- ->limit(1)
- ->ordered()
- ->where($orderColumnName, '>', $this->$orderColumnName)
- ->where($parentColumnName, $this->$parentColumnName)
- ->first();
- if (! $swapWithModel) {
- return false;
- }
- return $this->swapOrderWithModel($swapWithModel);
- }
- public function moveOrderUp()
- {
- $orderColumnName = $this->determineOrderColumnName();
- $parentColumnName = $this->getParentColumn();
- $swapWithModel = $this->buildSortQuery()
- ->limit(1)
- ->ordered('desc')
- ->where($orderColumnName, '<', $this->$orderColumnName)
- ->where($parentColumnName, $this->$parentColumnName)
- ->first();
- if ($swapWithModel) {
- return $this->swapOrderWithModel($swapWithModel);
- }
- $sameOrderModel = $this->getSameOrderModel('<');
- if (! $sameOrderModel) {
- return false;
- }
- $sameOrderModel->$orderColumnName = $sameOrderModel->$orderColumnName + 1;
- $sameOrderModel->save();
- return $this;
- }
- protected function getSameOrderModel(string $operator = '<')
- {
- $orderColumnName = $this->determineOrderColumnName();
- $parentColumnName = $this->getParentColumn();
- return $this->buildSortQuery()
- ->limit(1)
- ->orderBy($orderColumnName)
- ->orderBy($this->getKeyName())
- ->where($this->getKeyName(), $operator, $this->getKey())
- ->where($orderColumnName, $this->$orderColumnName)
- ->where($parentColumnName, $this->$parentColumnName)
- ->first();
- }
- public function moveToStart()
- {
- $parentColumnName = $this->getParentColumn();
- $firstModel = $this->buildSortQuery()
- ->limit(1)
- ->ordered()
- ->where($parentColumnName, $this->$parentColumnName)
- ->first();
- if ($firstModel->id === $this->id) {
- return $this;
- }
- $orderColumnName = $this->determineOrderColumnName();
- $this->$orderColumnName = $firstModel->$orderColumnName;
- $this->save();
- $this->buildSortQuery()->where($this->getKeyName(), '!=', $this->id)->increment($orderColumnName);
- return $this;
- }
- /**
- * Get options for Select field in form.
- *
- * @param \Closure|null $closure
- * @param string $rootText
- * @return array
- */
- public static function selectOptions(\Closure $closure = null, $rootText = null)
- {
- $rootText = $rootText ?: admin_trans_label('root');
- $options = (new static())->withQuery($closure)->buildSelectOptions();
- return collect($options)->prepend($rootText, 0)->all();
- }
- /**
- * Build options of select field in form.
- *
- * @param array $nodes
- * @param int $parentId
- * @param string $prefix
- * @param string $space
- * @return array
- */
- protected function buildSelectOptions(array $nodes = [], $parentId = 0, $prefix = '', $space = ' ')
- {
- $d = '├─';
- $prefix = $prefix ?: $d.$space;
- $options = [];
- if (empty($nodes)) {
- $nodes = $this->allNodes()->toArray();
- }
- foreach ($nodes as $index => $node) {
- if ($node[$this->getParentColumn()] == $parentId) {
- $currentPrefix = $this->hasNextSibling($nodes, $node[$this->getParentColumn()], $index) ? $prefix : str_replace($d, '└─', $prefix);
- $node[$this->getTitleColumn()] = $currentPrefix.$space.$node[$this->getTitleColumn()];
- $childrenPrefix = str_replace($d, str_repeat($space, 6), $prefix).$d.str_replace([$d, $space], '', $prefix);
- $children = $this->buildSelectOptions($nodes, $node[$this->getKeyName()], $childrenPrefix);
- $options[$node[$this->getKeyName()]] = $node[$this->getTitleColumn()];
- if ($children) {
- $options += $children;
- }
- }
- }
- return $options;
- }
- protected function hasNextSibling($nodes, $parentId, $index)
- {
- foreach ($nodes as $i => $node) {
- if ($node[$this->getParentColumn()] == $parentId && $i > $index) {
- return true;
- }
- }
- }
- /**
- * {@inheritdoc}
- */
- protected static function boot()
- {
- parent::boot();
- static::saving(function (Model $branch) {
- $parentColumn = $branch->getParentColumn();
- if (
- $branch->getKey()
- && Request::has($parentColumn)
- && Request::input($parentColumn) == $branch->getKey()
- ) {
- throw new AdminException(trans('admin.parent_select_error'));
- }
- if (Request::has(Tree::SAVE_ORDER_NAME)) {
- $order = Request::input(Tree::SAVE_ORDER_NAME);
- Request::offsetUnset(Tree::SAVE_ORDER_NAME);
- Tree::make(new static())->saveOrder($order);
- $branch->{$branch->getKeyName()} = true;
- return false;
- }
- return $branch;
- });
- static::deleting(function ($model) {
- static::query()
- ->where($model->getParentColumn(), $model->getKey())
- ->get()
- ->each
- ->delete();
- });
- }
- }
|