|
|
<?php
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
class StatisticsConfig extends SoftDeletesModel
|
|
|
{
|
|
|
|
|
|
protected $casts = [
|
|
|
'config_json' => 'json',
|
|
|
];
|
|
|
|
|
|
/**
|
|
|
* 根据配置执行统计计算
|
|
|
*
|
|
|
* @param array $params 参数数组,包含:page, page_size, show_type
|
|
|
* @return array 返回统计结果数组
|
|
|
* @throws \Exception
|
|
|
*/
|
|
|
public function calculateStatistics($params = [])
|
|
|
{
|
|
|
$page = isset($params['page']) ? (int) $params['page'] : 1;
|
|
|
$pageSize = isset($params['page_size']) ? (int) $params['page_size'] : 10;
|
|
|
$showType = isset($params['show_type']) ? $params['show_type'] : 'statistics';
|
|
|
if (!in_array($showType, ['statistics', 'list'])) {
|
|
|
$showType = 'statistics';
|
|
|
}
|
|
|
|
|
|
$configJson = $this->config_json;
|
|
|
if (empty($configJson)) {
|
|
|
throw new \Exception('配置数据为空');
|
|
|
}
|
|
|
|
|
|
// 如果 config_json 是字符串,尝试解析
|
|
|
if (is_string($configJson)) {
|
|
|
$configJson = json_decode($configJson, true);
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
|
throw new \Exception('配置 JSON 格式错误:' . json_last_error_msg());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 检查数据结构
|
|
|
if (!isset($configJson['data_source'])) {
|
|
|
throw new \Exception('配置数据缺少 data_source 字段,当前配置键:' . implode(', ', array_keys($configJson)));
|
|
|
}
|
|
|
|
|
|
// 获取主模型
|
|
|
$mainModelName = $configJson['data_source']['main_model'] ?? '';
|
|
|
if (empty($mainModelName)) {
|
|
|
$dataSourceKeys = isset($configJson['data_source']) ? implode(', ', array_keys($configJson['data_source'])) : '无';
|
|
|
throw new \Exception('主模型名称未配置,data_source 中的键:' . $dataSourceKeys);
|
|
|
}
|
|
|
$mainModel = $this->getModel($mainModelName);
|
|
|
if (empty($mainModel)) {
|
|
|
throw new \Exception('主模型不存在:' . $mainModelName . ',可用的模型:user, company, course_sign, course, course_type');
|
|
|
}
|
|
|
|
|
|
$query = $mainModel::query();
|
|
|
|
|
|
// 获取主模型表名
|
|
|
$tableName = $mainModel::make()->getTable();
|
|
|
|
|
|
// 加载关联模型
|
|
|
$relations = $configJson['data_source']['relations'] ?? [];
|
|
|
if (!empty($relations)) {
|
|
|
foreach ($relations as $relation) {
|
|
|
$query->with($relation);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 应用条件
|
|
|
$conditions = $configJson['conditions'] ?? [];
|
|
|
if (!empty($conditions['items'])) {
|
|
|
$logic = $conditions['logic'] ?? 'and';
|
|
|
if ($logic === 'or') {
|
|
|
$query->where(function ($q) use ($conditions, $tableName) {
|
|
|
foreach ($conditions['items'] as $index => $item) {
|
|
|
if ($index === 0) {
|
|
|
$this->applyCondition($q, $item, 'and', 0, $tableName);
|
|
|
} else {
|
|
|
$q->orWhere(function ($subQ) use ($item, $tableName) {
|
|
|
$this->applyCondition($subQ, $item, 'and', 0, $tableName);
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
} else {
|
|
|
foreach ($conditions['items'] as $item) {
|
|
|
$this->applyCondition($query, $item, 'and', 0, $tableName);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 执行统计
|
|
|
$statistics = $configJson['statistics'] ?? [];
|
|
|
$statisticsType = $statistics['type'] ?? 'count';
|
|
|
$groupBy = $statistics['group_by'] ?? null;
|
|
|
// 确保空字符串也被视为不分组
|
|
|
$groupBy = !empty($groupBy) ? $groupBy : null;
|
|
|
|
|
|
// 保存原始查询用于获取列表
|
|
|
$listQuery = clone $query;
|
|
|
|
|
|
if ($groupBy) {
|
|
|
// 分组统计
|
|
|
$groupParts = explode('.', $groupBy);
|
|
|
|
|
|
if (count($groupParts) > 1) {
|
|
|
// 关联模型字段分组,需要 join
|
|
|
$relationName = $groupParts[0];
|
|
|
$fieldName = $groupParts[1];
|
|
|
$relationModel = $this->getRelationModel($mainModel, $relationName);
|
|
|
if ($relationModel) {
|
|
|
$relationTable = $relationModel::make()->getTable();
|
|
|
$relationKey = $this->getRelationKey($mainModel, $relationName);
|
|
|
$query->join($relationTable, $tableName . '.' . $relationKey, '=', $relationTable . '.id');
|
|
|
$selectFields = [$relationTable . '.' . $fieldName . ' as group_value'];
|
|
|
} else {
|
|
|
$selectFields = [$groupBy . ' as group_value'];
|
|
|
}
|
|
|
} else {
|
|
|
// 主模型字段分组
|
|
|
$selectFields = [$tableName . '.' . $groupBy . ' as group_value'];
|
|
|
}
|
|
|
|
|
|
// 根据统计类型构建 SQL
|
|
|
$statisticsField = $statistics['field'] ?? null;
|
|
|
if ($statisticsType === 'sum' && $statisticsField) {
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $statisticsField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
// 如果还没有 join,需要 join
|
|
|
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$selectFields[] = DB::raw('SUM(' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('SUM(' . $tableName . '.' . $statisticsField . ') as total');
|
|
|
}
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('SUM(' . $tableName . '.' . $statisticsField . ') as total');
|
|
|
}
|
|
|
} elseif ($statisticsType === 'max' && $statisticsField) {
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $statisticsField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
// 如果还没有 join,需要 join
|
|
|
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$selectFields[] = DB::raw('MAX(' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('MAX(' . $tableName . '.' . $statisticsField . ') as total');
|
|
|
}
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('MAX(' . $tableName . '.' . $statisticsField . ') as total');
|
|
|
}
|
|
|
} elseif ($statisticsType === 'min' && $statisticsField) {
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $statisticsField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
// 如果还没有 join,需要 join
|
|
|
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$selectFields[] = DB::raw('MIN(' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('MIN(' . $tableName . '.' . $statisticsField . ') as total');
|
|
|
}
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('MIN(' . $tableName . '.' . $statisticsField . ') as total');
|
|
|
}
|
|
|
} elseif ($statisticsType === 'count_distinct' && isset($statistics['distinct_field'])) {
|
|
|
// 去重数量统计
|
|
|
$distinctField = $statistics['distinct_field'];
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $distinctField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
// 如果还没有 join,需要 join
|
|
|
$query->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$selectFields[] = DB::raw('COUNT(DISTINCT ' . $fieldRelationTable . '.' . $fieldFieldName . ') as total');
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('COUNT(DISTINCT ' . $tableName . '.' . $distinctField . ') as total');
|
|
|
}
|
|
|
} else {
|
|
|
$selectFields[] = DB::raw('COUNT(DISTINCT ' . $tableName . '.' . $distinctField . ') as total');
|
|
|
}
|
|
|
} else {
|
|
|
// count 统计总数量
|
|
|
$selectFields[] = DB::raw('COUNT(*) as total');
|
|
|
}
|
|
|
|
|
|
$query->select($selectFields);
|
|
|
|
|
|
if (count($groupParts) > 1) {
|
|
|
$query->groupBy($relationTable . '.' . $fieldName);
|
|
|
} else {
|
|
|
$query->groupBy($tableName . '.' . $groupBy);
|
|
|
}
|
|
|
} else {
|
|
|
// 不分组统计,获取所有数据列表
|
|
|
// 列表查询保持原样,获取所有符合条件的记录
|
|
|
}
|
|
|
|
|
|
// 排序(分组统计时对分组结果排序,不分组时对列表排序)
|
|
|
if (!empty($statistics['order_by'])) {
|
|
|
$orderField = $statistics['order_by']['field'] ?? ($groupBy ? 'total' : 'id');
|
|
|
$orderDirection = $statistics['order_by']['direction'] ?? 'desc';
|
|
|
$query->orderBy($orderField, $orderDirection);
|
|
|
if (!$groupBy) {
|
|
|
// 不分组时,列表查询也需要排序
|
|
|
$listQuery->orderBy($orderField, $orderDirection);
|
|
|
}
|
|
|
} else {
|
|
|
// 没有指定排序时,不分组情况使用默认排序
|
|
|
if (!$groupBy) {
|
|
|
$listQuery->orderBy('id', 'desc');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 获取统计查询的 SQL 语句(在执行查询前)
|
|
|
$statisticsSql = $query->toSql();
|
|
|
$statisticsBindings = $query->getBindings();
|
|
|
$statisticsSqlFull = $this->getFullSql($statisticsSql, $statisticsBindings);
|
|
|
|
|
|
// 获取分组统计结果列表(分页)
|
|
|
// 先获取所有结果用于计算统计结果
|
|
|
$allResults = $query->get();
|
|
|
|
|
|
// 对结果进行分页处理
|
|
|
$totalCount = $allResults->count();
|
|
|
$pagedResults = $allResults->slice(($page - 1) * $pageSize, $pageSize);
|
|
|
|
|
|
// 格式化分组统计结果
|
|
|
$data = [];
|
|
|
foreach ($pagedResults as $item) {
|
|
|
$row = [
|
|
|
'group_value' => $item->group_value ?? null,
|
|
|
'total' => round($item->total, $this->decimal_places)
|
|
|
];
|
|
|
$data[] = $row;
|
|
|
}
|
|
|
|
|
|
// 计算统计结果
|
|
|
$statisticsResult = null;
|
|
|
$pagination = null;
|
|
|
|
|
|
if ($groupBy) {
|
|
|
// 分组统计:根据统计类型计算最终结果
|
|
|
$statisticsField = $statistics['field'] ?? null;
|
|
|
if ($statisticsType === 'sum' && $statisticsField) {
|
|
|
// 分组求和时,统计结果是所有分组的总和
|
|
|
$allTotalValue = 0;
|
|
|
foreach ($allResults as $item) {
|
|
|
$allTotalValue += $item->total;
|
|
|
}
|
|
|
$statisticsResult = round($allTotalValue, $this->decimal_places);
|
|
|
} elseif ($statisticsType === 'max' && $statisticsField) {
|
|
|
// 分组最大值时,统计结果是所有分组中的最大值
|
|
|
$maxValue = null;
|
|
|
foreach ($allResults as $item) {
|
|
|
if ($maxValue === null || $item->total > $maxValue) {
|
|
|
$maxValue = $item->total;
|
|
|
}
|
|
|
}
|
|
|
$statisticsResult = $maxValue !== null ? round($maxValue, $this->decimal_places) : 0;
|
|
|
} elseif ($statisticsType === 'min' && $statisticsField) {
|
|
|
// 分组最小值时,统计结果是所有分组中的最小值
|
|
|
$minValue = null;
|
|
|
foreach ($allResults as $item) {
|
|
|
if ($minValue === null || $item->total < $minValue) {
|
|
|
$minValue = $item->total;
|
|
|
}
|
|
|
}
|
|
|
$statisticsResult = $minValue !== null ? round($minValue, $this->decimal_places) : 0;
|
|
|
} else {
|
|
|
// 分组计数时,统计结果是所有分组的计数总和
|
|
|
$allTotalValue = 0;
|
|
|
foreach ($allResults as $item) {
|
|
|
$allTotalValue += $item->total;
|
|
|
}
|
|
|
$statisticsResult = $allTotalValue;
|
|
|
}
|
|
|
|
|
|
// 分页信息
|
|
|
$pagination = [
|
|
|
'current_page' => $page,
|
|
|
'page_size' => $pageSize,
|
|
|
'total' => $totalCount,
|
|
|
'total_pages' => ceil($totalCount / $pageSize)
|
|
|
];
|
|
|
} else {
|
|
|
// 不分组统计:先获取 SQL,再执行查询
|
|
|
// 克隆查询用于获取 SQL(避免执行后无法获取)
|
|
|
$calcQuery = clone $listQuery;
|
|
|
$listQueryForData = clone $listQuery;
|
|
|
|
|
|
// 根据统计类型计算统计值
|
|
|
$statisticsField = $statistics['field'] ?? null;
|
|
|
if ($statisticsType === 'sum' && $statisticsField) {
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $statisticsField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$statisticsResult = round($calcQuery->sum($fieldRelationTable . '.' . $fieldFieldName), $this->decimal_places);
|
|
|
} else {
|
|
|
$statisticsResult = round($listQuery->sum($statisticsField), $this->decimal_places);
|
|
|
}
|
|
|
} else {
|
|
|
$statisticsResult = round($listQuery->sum($statisticsField), $this->decimal_places);
|
|
|
}
|
|
|
} elseif ($statisticsType === 'max' && $statisticsField) {
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $statisticsField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$statisticsResult = round($calcQuery->max($fieldRelationTable . '.' . $fieldFieldName), $this->decimal_places);
|
|
|
} else {
|
|
|
$statisticsResult = round($listQuery->max($statisticsField), $this->decimal_places);
|
|
|
}
|
|
|
} else {
|
|
|
$statisticsResult = round($listQuery->max($statisticsField), $this->decimal_places);
|
|
|
}
|
|
|
} elseif ($statisticsType === 'min' && $statisticsField) {
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $statisticsField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$statisticsResult = round($calcQuery->min($fieldRelationTable . '.' . $fieldFieldName), $this->decimal_places);
|
|
|
} else {
|
|
|
$statisticsResult = round($listQuery->min($statisticsField), $this->decimal_places);
|
|
|
}
|
|
|
} else {
|
|
|
$statisticsResult = round($listQuery->min($statisticsField), $this->decimal_places);
|
|
|
}
|
|
|
} elseif ($statisticsType === 'count_distinct' && isset($statistics['distinct_field'])) {
|
|
|
// 去重数量统计
|
|
|
$distinctField = $statistics['distinct_field'];
|
|
|
// 处理关联模型字段
|
|
|
$fieldParts = explode('.', $distinctField);
|
|
|
if (count($fieldParts) > 1) {
|
|
|
$fieldRelationName = $fieldParts[0];
|
|
|
$fieldFieldName = $fieldParts[1];
|
|
|
$fieldRelationModel = $this->getRelationModel($mainModel, $fieldRelationName);
|
|
|
if ($fieldRelationModel) {
|
|
|
$fieldRelationTable = $fieldRelationModel::make()->getTable();
|
|
|
$fieldRelationKey = $this->getRelationKey($mainModel, $fieldRelationName);
|
|
|
$calcQuery->leftJoin($fieldRelationTable, $tableName . '.' . $fieldRelationKey, '=', $fieldRelationTable . '.id');
|
|
|
$statisticsResult = $calcQuery->selectRaw('COUNT(DISTINCT ' . $fieldRelationTable . '.' . $fieldFieldName . ') as total')->value('total') ?? 0;
|
|
|
} else {
|
|
|
$statisticsResult = $listQuery->selectRaw('COUNT(DISTINCT ' . $distinctField . ') as total')->value('total') ?? 0;
|
|
|
}
|
|
|
} else {
|
|
|
$statisticsResult = $listQuery->selectRaw('COUNT(DISTINCT ' . $tableName . '.' . $distinctField . ') as total')->value('total') ?? 0;
|
|
|
}
|
|
|
} else {
|
|
|
// count 统计总数量
|
|
|
$statisticsResult = $listQuery->count();
|
|
|
}
|
|
|
|
|
|
// 获取统计查询的 SQL(在计算完统计值之后,确保包含所有修改)
|
|
|
$statisticsSqlForCalc = $calcQuery->toSql();
|
|
|
$statisticsBindingsForCalc = $calcQuery->getBindings();
|
|
|
$statisticsSqlFull = $this->getFullSql($statisticsSqlForCalc, $statisticsBindingsForCalc);
|
|
|
|
|
|
// 获取列表总数(用于分页)
|
|
|
$listTotalCount = $listQueryForData->count();
|
|
|
|
|
|
// 不分组时,获取分页数据作为列表
|
|
|
$listResults = $listQueryForData->skip(($page - 1) * $pageSize)->take($pageSize)->get();
|
|
|
$data = [];
|
|
|
foreach ($listResults as $item) {
|
|
|
$data[] = $item->toArray();
|
|
|
}
|
|
|
|
|
|
// 分页信息
|
|
|
$pagination = [
|
|
|
'current_page' => $page,
|
|
|
'page_size' => $pageSize,
|
|
|
'total' => $listTotalCount,
|
|
|
'total_pages' => ceil($listTotalCount / $pageSize)
|
|
|
];
|
|
|
}
|
|
|
|
|
|
// 根据显示类型决定返回的数据
|
|
|
if ($showType === 'statistics') {
|
|
|
// 只返回统计数据
|
|
|
return [
|
|
|
'total' => $statisticsResult,
|
|
|
'sql' => $statisticsSqlFull
|
|
|
];
|
|
|
} else {
|
|
|
// 只返回列表数据
|
|
|
return [
|
|
|
'list' => $data,
|
|
|
'pagination' => $pagination,
|
|
|
'sql' => $statisticsSqlFull
|
|
|
];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取模型实例
|
|
|
*/
|
|
|
private function getModel($modelName)
|
|
|
{
|
|
|
$models = [
|
|
|
'user' => User::class,
|
|
|
'company' => Company::class,
|
|
|
'course_sign' => CourseSign::class,
|
|
|
'course' => Course::class,
|
|
|
'course_type' => CourseType::class,
|
|
|
];
|
|
|
return $models[$modelName] ?? null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 应用查询条件
|
|
|
*/
|
|
|
private function applyCondition($query, $condition, $logic, $index, $tableName = null)
|
|
|
{
|
|
|
$key = $condition['key'] ?? '';
|
|
|
$operator = $condition['operator'] ?? 'eq';
|
|
|
$value = $condition['value'] ?? '';
|
|
|
|
|
|
if (empty($key)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 处理关联模型的字段
|
|
|
$keyParts = explode('.', $key);
|
|
|
if (count($keyParts) > 1) {
|
|
|
// 关联模型字段,使用 whereHas
|
|
|
$relationName = $keyParts[0];
|
|
|
$fieldName = $keyParts[1];
|
|
|
$query->whereHas($relationName, function ($q) use ($fieldName, $operator, $value) {
|
|
|
$this->applyOperator($q, $fieldName, $operator, $value);
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 主模型字段,添加表名前缀避免字段歧义
|
|
|
$qualifiedKey = $tableName ? $tableName . '.' . $key : $key;
|
|
|
$this->applyOperator($query, $qualifiedKey, $operator, $value);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取关联模型
|
|
|
*/
|
|
|
private function getRelationModel($mainModel, $relationName)
|
|
|
{
|
|
|
$model = new $mainModel();
|
|
|
if (method_exists($model, $relationName)) {
|
|
|
$relation = $model->$relationName();
|
|
|
return get_class($relation->getRelated());
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取关联键名
|
|
|
*/
|
|
|
private function getRelationKey($mainModel, $relationName)
|
|
|
{
|
|
|
$model = new $mainModel();
|
|
|
if (method_exists($model, $relationName)) {
|
|
|
$relation = $model->$relationName();
|
|
|
return $relation->getForeignKeyName();
|
|
|
}
|
|
|
return 'id';
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 获取完整的 SQL 语句(包含绑定参数)
|
|
|
*/
|
|
|
private function getFullSql($sql, $bindings)
|
|
|
{
|
|
|
if (empty($bindings)) {
|
|
|
return $sql;
|
|
|
}
|
|
|
|
|
|
$fullSql = $sql;
|
|
|
foreach ($bindings as $binding) {
|
|
|
$value = is_numeric($binding) ? $binding : "'" . addslashes($binding) . "'";
|
|
|
$fullSql = preg_replace('/\?/', $value, $fullSql, 1);
|
|
|
}
|
|
|
|
|
|
return $fullSql;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 应用操作符
|
|
|
*/
|
|
|
private function applyOperator($query, $key, $operator, $value)
|
|
|
{
|
|
|
switch ($operator) {
|
|
|
case 'eq':
|
|
|
$query->where($key, $value);
|
|
|
break;
|
|
|
case 'neq':
|
|
|
$query->where($key, '!=', $value);
|
|
|
break;
|
|
|
case 'gt':
|
|
|
$query->where($key, '>', $value);
|
|
|
break;
|
|
|
case 'egt':
|
|
|
$query->where($key, '>=', $value);
|
|
|
break;
|
|
|
case 'lt':
|
|
|
$query->where($key, '<', $value);
|
|
|
break;
|
|
|
case 'elt':
|
|
|
$query->where($key, '<=', $value);
|
|
|
break;
|
|
|
case 'like':
|
|
|
$query->where($key, 'like', '%' . $value . '%');
|
|
|
break;
|
|
|
case 'notlike':
|
|
|
$query->where($key, 'not like', '%' . $value . '%');
|
|
|
break;
|
|
|
case 'in':
|
|
|
$array = explode(',', $value);
|
|
|
$query->whereIn($key, $array);
|
|
|
break;
|
|
|
case 'notin':
|
|
|
$array = explode(',', $value);
|
|
|
$query->whereNotIn($key, $array);
|
|
|
break;
|
|
|
case 'between':
|
|
|
list($from, $to) = explode(',', $value);
|
|
|
if (!empty($from) && !empty($to)) {
|
|
|
$query->whereBetween($key, [$from, $to]);
|
|
|
}
|
|
|
break;
|
|
|
case 'notbetween':
|
|
|
list($from, $to) = explode(',', $value);
|
|
|
if (!empty($from) && !empty($to)) {
|
|
|
$query->whereNotBetween($key, [$from, $to]);
|
|
|
}
|
|
|
break;
|
|
|
case 'isnull':
|
|
|
$query->whereNull($key);
|
|
|
break;
|
|
|
case 'isnotnull':
|
|
|
$query->whereNotNull($key);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|