building-sign/app/service/Repository.php

307 lines
9.2 KiB
PHP
Raw Normal View History

2023-01-09 08:41:41 +00:00
<?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);
}
}