<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2022/11/28
 * Time: 9:18
 */

namespace app\common\model;


use think\Db;
use think\Model;

/**
 * 提供树形数据结构的方法
 * 需要 id,pid,tree_path,status,title 等属性
 * Class Tree
 * @package app\common\model
 */
class Tree extends Model
{
    const STATUS_OPEN = 1;
    const STATUS_CLOSE = 0;

    /**
     * 获得树形数据结构,递归方式
     * 节点多的时候,效率比较慢
     * @param $nodelist [节点集合]
     * @param int $id [起始id,顶级id]
     * @param int $level
     * @return array
     */
    public function treelist($nodelist, $id = 0, $level = 0)
    {
        static $cates = array();
        foreach ($nodelist as $value) {
            if ($value['pid'] == $id) {
                $value['level'] = $level + 1;
                $value['str'] = $level == 0 ? "" : str_repeat('&emsp;', $level) . '└ ';
                $cates[] = $value;
                $this->treelist($nodelist, $value['id'], $value['level']);
            }
        }
        return $cates;
    }

    /**
     * 获得树形数据结构,sql排序方式
     * 通过tree_path排序获得树形结构,速度比递归快,要保证tree_path的正确性
     * @param $nodelist [节点集合]
     * @return false|\PDOStatement|string|\think\Collection
     */
    public function treelist2($nodelist = null)
    {
        if (!$nodelist) {
            $nodelist = $this->where("status", self::STATUS_OPEN)->order("tree_path")->select();
        }
        /** @var Tree $cate */
        foreach ($nodelist as $i => $cate) {
            $deep = $cate->getDepth();
            $nodelist[$i]['str'] = $deep == 1 ? "" : str_repeat('&emsp;', $deep) . '└ ';
        }
        return $nodelist;
    }

    /**
     * 更新所有节点tree_path属性
     * @param int $nodeId [起始id,顶级id]
     */
    public function updateTreePath($nodeId = 0)
    {
        $allCates = $this->select();
        $cates = $this->treelist($allCates, $nodeId);
        //update tree_path
        foreach ($cates as $cate) {
            $pdoc = null;
            //find pdoc
            foreach ($cates as $pcate) {
                if ($pcate->id == $cate->pid) {
                    $pdoc = $pcate;
                    break;
                }
            }
            //set tree_path
            $ppath = isset($pdoc) ? $pdoc->tree_path . '-' : "";
            $cate->tree_path = $ppath . $cate->id;
            $cate->allowField(true)->save();
        }
    }

    /**
     * 获取当前是第几层节点
     * @return int
     */
    public function getDepth()
    {
        $var = explode('-', $this->tree_path);
        return count($var);
    }

    /**
     * 获取顶层的节点id
     * @return mixed
     */
    public function getTopId()
    {
        $var = explode('-', $this->tree_path);
        return $var[0];
    }

    /**
     * 判断当前节点是否属于隐藏状态(包括上级节点隐藏)
     * @return bool
     */
    public function isHide()
    {
        if ($this->status == self::STATUS_CLOSE) {
            return true;
        }
        //上级是否隐藏
        $ids = explode('-', $this->tree_path);
        $exits = $this->where('id', 'in', $ids)->where('status', self::STATUS_CLOSE)->value('id');
        if ($exits) {
            return true;
        }
        return false;
    }

    //是否只是父级隐藏
    public function isParentHide()
    {
        $path = str_replace($this->id, '', $this->tree_path);
        $ids = explode('-', $path);
        $exits = $this->where('id', 'in', $ids)->where('status', self::STATUS_CLOSE)->value('id');
        if ($exits) {
            return true;
        }
        return false;
    }

    /**
     * 获取所有隐藏的节点id集合(包括上级节点隐藏)
     * @return array
     */
    public function getHideIdsAll()
    {
        $hidePids = $this->where(['pid' => ['<>', 0], 'status' => self::STATUS_CLOSE])->column('id');
        $hideIds = [];
        foreach ($hidePids as $pid) {
            $hideIds = array_merge($hideIds, $this->getChildsIdByPid($pid));
        }
        return $hideIds;
    }

    //获取父节点
    public function getDirectParentNode()
    {
        return self::get($this->pid);
    }

    /**
     * 获取上级节点id集合
     * @param $contain [是否包含自己]
     * @return array
     */
    public function getParentsIds($contain = true)
    {
        $ids = explode('-', $this->tree_path);
        if ($contain === false) {
            unset($ids[array_search($this->id, $ids, true)]);
        }
        return $ids;
    }

    /**
     * 获取上级节点名字集合
     * @param $contain [是否包含自己]
     * @param $field string [指定获取字段]
     * @return array
     */
    public function getParentsNames($contain = true, $field = "title")
    {
        $ids = $this->getParentsIds($contain);
        $names = $this->where('id', 'in', $ids)->order('tree_path')->column($field);
        return $names;
    }

    //获取所有子节点
    public function getDirectChildNodes()
    {
        return self::where('pid', $this->id)->select();
    }

    //获取一个子节点
    public function getDirectChildNode()
    {
        return self::where('pid', $this->id)->find();
    }

    /**
     * 获取所有子节点id
     * @param $id [指定节点id]
     * @return array
     */
    public function getDirectChildIdsByPid($id)
    {
        return $this->where('pid', $id)->column('id');
    }

    /**
     * 获取所有下级(子孙)节点id
     * @param $id [指定节点id]
     * @param $contain [是否包含指定节点]
     * @return array
     */
    public function getChildsIdByPid($id, $contain = true)
    {
        $tablename = $this->getTable();
        $sql = "select a.id from {$tablename} a,(select id,tree_path p from {$tablename} where id=?) b where a.id = b.id or a.tree_path like concat(b.p,'-%')";
        $cates = Db::query($sql, [$id]);
        $ids = [];
        foreach ($cates as $cate) {
            if ($contain === false && $id == $cate['id']) {
                continue;
            }
            $ids[] = $cate['id'];
        }
        return $ids;
    }

}