307 lines
9.2 KiB
PHP
307 lines
9.2 KiB
PHP
|
<?php
|
|||
|
|
|||
|
namespace app\service;
|
|||
|
|
|||
|
use Exception;
|
|||
|
use think\db\exception\DataNotFoundException;
|
|||
|
use think\db\exception\DbException;
|
|||
|
use think\db\exception\ModelNotFoundException;
|
|||
|
use think\Model;
|
|||
|
use think\facade\Db;
|
|||
|
use think\Collection;
|
|||
|
use think\facade\Env;
|
|||
|
use think\facade\Log;
|
|||
|
use think\Paginator;
|
|||
|
use app\exception\RepositoryException;
|
|||
|
|
|||
|
/**
|
|||
|
* 领域模型-仓储
|
|||
|
*
|
|||
|
* Class Repository
|
|||
|
* @package app\service
|
|||
|
*/
|
|||
|
class Repository
|
|||
|
{
|
|||
|
/**
|
|||
|
* @var array 已加载对象列表
|
|||
|
*/
|
|||
|
private static $objects = [];
|
|||
|
|
|||
|
/**
|
|||
|
* @var Model 模型对象
|
|||
|
*/
|
|||
|
protected $model;
|
|||
|
|
|||
|
// 布尔值数字关系
|
|||
|
public const BOOL_FALSE = 0;
|
|||
|
public const BOOL_TRUE = 1;
|
|||
|
|
|||
|
/**
|
|||
|
* 获取当前子对象实列(单例形式返回)
|
|||
|
*
|
|||
|
* @param Model|null $model 模型对象。未指定则自动获取
|
|||
|
* @return mixed
|
|||
|
*/
|
|||
|
public static function getInstance(Model $model = null)
|
|||
|
{
|
|||
|
$class = get_called_class();
|
|||
|
|
|||
|
if (isset(self::$objects[$class]) && self::$objects[$class] !== null) {
|
|||
|
return self::$objects[$class];
|
|||
|
}
|
|||
|
|
|||
|
$obj = new $class();
|
|||
|
if ($model) {
|
|||
|
$obj->model = $model;
|
|||
|
} elseif (strpos($class, '\\repository') > 0) {
|
|||
|
$model = str_replace('\\repository', '\\model', $class);
|
|||
|
//去掉末尾Repository app\model\AccountRepository =》 app\model\Account
|
|||
|
$model = substr($model, 0, strlen($model) - strlen('Repository'));
|
|||
|
if (class_exists($model)) {
|
|||
|
$obj->model = new $model;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
self::$objects[$class] = $obj;
|
|||
|
return $obj;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @param callable $callback
|
|||
|
* @param mixed $failReturn
|
|||
|
* @param bool $transaction
|
|||
|
* @return mixed
|
|||
|
* @throws RepositoryException
|
|||
|
*/
|
|||
|
protected function access(callable $callback, $failReturn, bool $transaction = false)
|
|||
|
{
|
|||
|
$exception = null;
|
|||
|
try {
|
|||
|
if ($transaction) {
|
|||
|
Db::startTrans();
|
|||
|
}
|
|||
|
|
|||
|
$r = $callback();
|
|||
|
|
|||
|
if ($transaction) {
|
|||
|
Db::commit();
|
|||
|
}
|
|||
|
|
|||
|
if ($r) {
|
|||
|
return $r;
|
|||
|
}
|
|||
|
|
|||
|
if ($failReturn instanceof Exception) {
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
return $failReturn;
|
|||
|
} catch (Exception $e) {
|
|||
|
if ($transaction) {
|
|||
|
Db::rollback();
|
|||
|
}
|
|||
|
|
|||
|
if ($e instanceof RepositoryException) {
|
|||
|
throw $e;
|
|||
|
}
|
|||
|
|
|||
|
$name = 'Domain - Repository - 未知错误';
|
|||
|
|
|||
|
$traces = $e->getTrace();
|
|||
|
|
|||
|
foreach ($traces as $i => $trace) {
|
|||
|
if (!empty($trace['class']) && $trace['class'] === Repository::class && $trace['function'] === 'access') {
|
|||
|
$trace = $traces[$i - 2] ?? null;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!empty($trace) && !empty($trace['file'][1]) && !empty($trace['line'])) {
|
|||
|
$parts = explode('application\\', $trace['file']);
|
|||
|
if (!empty($parts[1])) {
|
|||
|
$name = $parts[1].':'.$trace['line'];
|
|||
|
} else {
|
|||
|
$name = $trace['file'].':'.$trace['line'];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
$exception = $e;
|
|||
|
|
|||
|
$line = '['.$name.'] '.$e->getMessage();
|
|||
|
Log::error($line);
|
|||
|
|
|||
|
throw new RepositoryException('Repository异常'.(Env::get('app_debug') ? ': '.$line : ''), 5009);
|
|||
|
} finally {
|
|||
|
if ($exception && $failReturn instanceof RepositoryException) {
|
|||
|
if (Env::get('app_debug')) {
|
|||
|
$failReturn = new RepositoryException($failReturn->getMessage().': '.$exception->getMessage(),
|
|||
|
$failReturn->getCode());
|
|||
|
}
|
|||
|
throw $failReturn;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 获取当前model对象
|
|||
|
*
|
|||
|
* @return Model
|
|||
|
*/
|
|||
|
public function getModel(): Model
|
|||
|
{
|
|||
|
return $this->model;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据条件查询列表
|
|||
|
*
|
|||
|
* @param array $where 查询条件
|
|||
|
* @param array $fields 查询字段 []表示全部
|
|||
|
* @param int $page 默认第一页 0不限制
|
|||
|
* @param int $limit 限制条数 0不限制
|
|||
|
* @param callable|null $callback 更为复杂的条件 使用闭包查询
|
|||
|
* @return array
|
|||
|
* @throws RepositoryException
|
|||
|
*/
|
|||
|
public function findList(array $where = [], array $fields = [], int $page = 1, int $limit = 0, callable $callback = null, array $order = []): ?array
|
|||
|
{
|
|||
|
$failData = [
|
|||
|
'total' => 0,
|
|||
|
'current' => $page,
|
|||
|
'size' => $limit,
|
|||
|
'list' => new Collection(),
|
|||
|
];
|
|||
|
return $this->access(function () use ($where, $fields, $page, $limit, $callback, $order) {
|
|||
|
return $this->model->findList($where, $fields, $page, $limit, $callback, $order);
|
|||
|
}, $failData);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据条件查询列表[带分页 适用于后台]
|
|||
|
*
|
|||
|
* @param array $data 查询数据
|
|||
|
* @param array $pageParams 分页参数
|
|||
|
* @param callable|null $callback 复杂查询条件 使用闭包查询
|
|||
|
* @return Paginator
|
|||
|
*/
|
|||
|
public function findListWithPaginate(array $data = [], array $pageParams = [], callable $callback = null): Paginator
|
|||
|
{
|
|||
|
return $this->model->findListWithPaginate($data, $pageParams, $callback);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 根据主键 ID 查询
|
|||
|
*
|
|||
|
* @param int $id ID
|
|||
|
* @param array $fields 要返回的字段,默认全部
|
|||
|
* @return Mixed
|
|||
|
* @throws RepositoryException
|
|||
|
*/
|
|||
|
public function findById(int $id, array $fields = [], callable $callback = null)
|
|||
|
{
|
|||
|
return $this->access(function () use ($id, $fields, $callback) {
|
|||
|
return $this->model->findById($id, $fields, $callback);
|
|||
|
}, null);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* @param array $where
|
|||
|
* @param array $fields
|
|||
|
* @return array|Model|null
|
|||
|
* @throws DataNotFoundException
|
|||
|
* @throws DbException
|
|||
|
* @throws ModelNotFoundException
|
|||
|
*/
|
|||
|
public function findOneByWhere(array $where, array $fields = [])
|
|||
|
{
|
|||
|
return $this->model->field($fields)->where($where)->find();
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 创建
|
|||
|
*
|
|||
|
* @param array $data 数据
|
|||
|
* @return Model
|
|||
|
* @throws RepositoryException
|
|||
|
*/
|
|||
|
public function create(array $data): Model
|
|||
|
{
|
|||
|
return $this->access(function () use ($data) {
|
|||
|
return $this->model->create($data);
|
|||
|
}, new RepositoryException('创建失败'));
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 更新
|
|||
|
*
|
|||
|
* @param array $data 数据
|
|||
|
* @param array $where 条件
|
|||
|
* @return bool|Exception
|
|||
|
* @throws RepositoryException
|
|||
|
*/
|
|||
|
public function update(array $data, array $where): bool
|
|||
|
{
|
|||
|
return $this->access(function () use ($data, $where) {
|
|||
|
return $this->model->where($where)->find()->save($data);
|
|||
|
}, new RepositoryException('更新失败'));
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 删除
|
|||
|
*
|
|||
|
* @param array $where 删除条件
|
|||
|
* @param bool $softDelete 是否软删除 默认false
|
|||
|
* @param string $softDeleteTime 删除时间 softDelete=true时有效
|
|||
|
* @param string $softDeleteField 软删除字段 softDelete=true时有效
|
|||
|
* @return bool
|
|||
|
* @throws RepositoryException
|
|||
|
*/
|
|||
|
public function delete(array $where, bool $softDelete = false, string $softDeleteTime = '', string $softDeleteField = 'deleted_at'): bool
|
|||
|
{
|
|||
|
return $this->access(function () use ($where, $softDelete, $softDeleteField, $softDeleteTime) {
|
|||
|
// 注意:如果model中引入了软删除trait,$softDelete又设置false 将无法正确删除
|
|||
|
return $this->model->where($where)
|
|||
|
->when($softDelete, function ($q) use ($softDeleteField, $softDeleteTime) {
|
|||
|
$softDeleteTime = $softDeleteTime ?: date('Y-m-d H:i:s');
|
|||
|
$q->useSoftDelete($softDeleteField, $softDeleteTime);
|
|||
|
})->delete();
|
|||
|
}, false);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 排序
|
|||
|
*
|
|||
|
* @param int $id 排序ID
|
|||
|
* @param string $type 排序类型 向上、向下
|
|||
|
* @param int $num 移动位数
|
|||
|
* @param string $listType 列表的排序类型 降序|升序
|
|||
|
* @param array $where 额外条件 格式如:
|
|||
|
* $map[] = ['name','like','think'];
|
|||
|
* $map[] = ['status','=',1];
|
|||
|
* @return array
|
|||
|
*/
|
|||
|
public function sort(int $id,string $type,int $num,string $listType, array $where = []): array
|
|||
|
{
|
|||
|
return $this->model->sort($id, $type, $num, $listType, $where);
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 日志记录
|
|||
|
*
|
|||
|
* @param string $msg
|
|||
|
* @param Exception|null $e
|
|||
|
* @param string $level
|
|||
|
* @param string $channel
|
|||
|
*/
|
|||
|
public static function log(string $msg, Exception $e = null, string $level = 'error', string $channel = 'file')
|
|||
|
{
|
|||
|
if ($e != null) {
|
|||
|
$msg = sprintf("[%s]%s:%s %s", $msg, $e->getFile(), $e->getLine(), $e->getMessage());
|
|||
|
} else {
|
|||
|
$msg = sprintf("%s", $msg);
|
|||
|
|
|||
|
}
|
|||
|
Log::channel($channel)->$level($msg);
|
|||
|
}
|
|||
|
}
|