/**
 * 设置需要追加的输出属性
 * @access public
 * @param array $append 属性列表
 * @return $this
 */
public function append($append = [])
{
    $this->append = $append;// 属性追加
    return $this;
}

/**
 * 设置需要隐藏的输出属性
 * @access public
 * @param array $hidden 属性列表
 * @return $this
 */
public function hidden($hidden = [])
{
    $this->hidden = $hidden;// 属性隐藏
    return $this;
}

/**
 * 设置需要输出的属性
 * @param array $visible
 * @return $this
 */
public function visible($visible = [])
{
    $this->visible = $visible;// 输出的属性
    return $this;
}

/**
 * 转换当前模型对象为数组
 * @access public
 * @return array
 */
public function toArray()
{
    $item = [];

    //过滤属性
    if (!empty($this->visible)) {
        $data = array_intersect_key($this->data, array_flip($this->visible));
    } elseif (!empty($this->hidden)) {
        $data = array_diff_key($this->data, array_flip($this->hidden));
    } else {
        $data = $this->data;
    }

    foreach ($data as $key => $val) {
        if ($val instanceof Model || $val instanceof Collection) {
            // 关联模型对象
            $item[$key] = $val->toArray();
        } elseif (is_array($val) && reset($val) instanceof Model) {
            // 关联模型数据集
            $arr = [];
            foreach ($val as $k => $value) {
                $arr[$k] = $value->toArray();
            }
            $item[$key] = $arr;
        } else {
            // 模型属性
            $item[$key] = $this->getAttr($key);
        }
    }
    // 追加属性(必须定义获取器)
    if (!empty($this->append)) {
        foreach ($this->append as $name) {
            $item[$name] = $this->getAttr($name);
        }
    }
    return !empty($item) ? $item : [];
}// 转换为数组

/**
 * 转换当前模型对象为JSON字符串
 * @access public
 * @param integer   $options json参数
 * @return string
 */
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
    return json_encode($this->toArray(), $options);// 转换wield json
}

/**
 * 获取模型对象的主键
 * @access public
 * @param string $name 模型名
 * @return mixed
 */
public function getPk($name = '')
{// 获取主键
    if (!empty($name)) {
        $table = $this->db()->getTable($name);
        return $this->db()->getPk($table);
    } elseif (empty($this->pk)) {
        $this->pk = $this->db()->getPk();
    }
    return $this->pk;
}

/**
 * 判断一个字段名是否为主键字段
 * @access public
 * @param string $key 名称
 * @return bool
 */
protected function isPk($key)
{// 检测是否为主键
    $pk = $this->getPk();
    if (is_string($pk) && $pk == $key) {
        return true;
    } elseif (is_array($pk) && in_array($key, $pk)) {
        return true;
    }
    return false;
}

/**
 * 保存当前数据对象
 * @access public
 * @param array     $data 数据
 * @param array     $where 更新条件
 * @param string    $sequence     自增序列名
 * @return integer|false
 */
public function save($data = [], $where = [], $sequence = null)
{// 数据保存
    if (!empty($data)) {// 数据不为空
        // 数据自动验证
        if (!$this->validateData($data)) {
            return false;
        }
        // 数据对象赋值
        foreach ($data as $key => $value) {// 数据对象赋值
            $this->setAttr($key, $value, $data);
        }
        if (!empty($where)) {// 条件不为空 代表为 更新状态
            $this->isUpdate = true;
        }
    }

    // 检测字段
    if (!empty($this->field)) {// 字段不为空
        $this->db();
        foreach ($this->data as $key => $val) {// 删除数据里面的 多有的字段信息
            if (!in_array($key, $this->field)) {
                unset($this->data[$key]);
            }
        }
    }

    // 数据自动完成
    $this->autoCompleteData($this->auto);// 数据自动完成

    // 自动写入更新时间
    if ($this->autoWriteTimestamp && $this->updateTime) {// 时间属性设置
        $this->setAttr($this->updateTime, null);
    }

    // 事件回调
    if (false === $this->trigger('before_write', $this)) {// 回调
        return false;
    }

    if ($this->isUpdate) {// 执行更新
        // 自动更新
        $this->autoCompleteData($this->update);

        // 事件回调
        if (false === $this->trigger('before_update', $this)) {
            return false;
        }

        // 去除没有更新的字段
        $data = [];
        foreach ($this->data as $key => $val) {
            if (in_array($key, $this->change) || $this->isPk($key)) {
                $data[$key] = $val;
            }
        }

        if (!empty($this->readonly)) {
            // 只读字段不允许更新
            foreach ($this->readonly as $key => $field) {
                if (isset($data[$field])) {
                    unset($data[$field]);
                }
            }
        }

        if (empty($where) && !empty($this->updateWhere)) {
            $where = $this->updateWhere;
        }

        if (!empty($where)) {
            $pk = $this->getPk();
            if (is_string($pk) && isset($data[$pk])) {
                unset($data[$pk]);
            }
        }

        $result = $this->db()->where($where)->update($data);// 还是使用数据库 模型的更新
        // 清空change
        $this->change = [];
        // 更新回调
        $this->trigger('after_update', $this);
    } else {
        // 自动写入
        $this->autoCompleteData($this->insert);

        // 自动写入创建时间
        if ($this->autoWriteTimestamp && $this->createTime) {
            $this->setAttr($this->createTime, null);
        }

        if (false === $this->trigger('before_insert', $this)) {
            return false;
        }

        $result = $this->db()->insert($this->data);// 数据模型的插入

        // 获取自动增长主键
        if ($result) {
            $insertId = $this->db()->getLastInsID($sequence);// 获取自动增长的主键
            $pk       = $this->getPk();
            if (is_string($pk) && $insertId) {
                $this->data[$pk] = $insertId;
            }
        }
        // 标记为更新
        $this->isUpdate = true;
        // 清空change
        $this->change = [];
        // 新增回调
        $this->trigger('after_insert', $this);
    }
    // 写入回调
    $this->trigger('after_write', $this);

    return $result;
}

/**
 * 保存多个数据到当前数据对象
 * @access public
 * @param array     $dataSet 数据
 * @param boolean   $replace 是否自动识别更新和写入
 * @return array|false
 */
public function saveAll($dataSet, $replace = true)// 基于事务的 批量插入及更新
{
    if ($this->validate) {//批量数据验证
        // 数据批量验证
        $validate = $this->validate;
        foreach ($dataSet as $data) {
            if (!$this->validate($validate)->validateData($data)) {
                return false;
            }
        }
    }

    $result = [];
    $db     = $this->db();
    $db->startTrans();// 启动事务
    try {
        $pk = $this->getPk();
        if (is_string($pk) && $replace) {
            $auto = true;
        }
        foreach ($dataSet as $key => $data) {
            if (!empty($auto) && isset($data[$pk])) {
                $result[$key] = self::update($data);
            } else {
                $result[$key] = self::create($data);
            }
        }
        $db->commit();// 提交事务
        return $result;
    } catch (\Exception $e) {
        $db->rollback();// 否则回滚
        throw $e;
    }
}