<?php/***+------------------* madong*+------------------* Copyright (c) https://gitee.com/motion-code All rights reserved.*+------------------* Author: Mr. April (405784684@qq.com)*+------------------* Official Website: http://www.madong.tech*/namespace madong\query;use Illuminate\Database\Eloquent\Builder;use Illuminate\Database\Eloquent\Model;use madong\query\exception\QueryParseException;/*** 查询构建器辅助类* 将解析后的查询参数应用到 Laravel Query Builder** 设计原则:* - 返回 Builder,让用户灵活处理* - 自动检测并调用 Model 的 scope 方法* - 简洁明了,不过度封装*/class QueryBuilderHelper{/*** @var QueryParamParser|null*/private ?QueryParamParser $parser = null;/*** 构造函数** @param QueryParamParser|null $parser*/public function __construct(?QueryParamParser $parser = null){$this->parser = $parser;}/*** 应用所有查询参数到查询构建器** @param Builder $query* @param array $params* @param array $options 配置选项* - keyword_fields: 关键词搜索字段* - scopes: 启用的搜索器列表 ['user_name', 'real_name']* - scope_map: 自定义搜索器映射 ['user_name' => 'customSearch']* - strict: 严格模式(精确匹配)* @return Builder*/public function apply(Builder $query, array $params, array $options = []): Builder{$keywordFields = $options['keyword_fields'] ?? [];$scopes = $options['scopes'] ?? []; // 配置的搜索器$scopeMap = $options['scope_map'] ?? []; // 自定义映射$applySort = $options['apply_sort'] ?? false; // 是否应用排序,默认为 关闭// 解析参数$parsed = $this->getParser()->parse($params);// 获取模型实例$model = $query->getModel();// 应用筛选条件(优先使用搜索器)$this->applyConditions($query, $parsed['conditions'], $model, $scopes, $scopeMap);// 应用排序(如果启用)if ($applySort) {foreach ($parsed['sort'] as $sort) {$query->orderBy($sort['field'], $sort['direction']);}}// 应用关键词搜索if ($parsed['keyword'] && !empty($keywordFields)) {$this->applyKeywordSearch($query, $parsed['keyword'], $keywordFields);}return $query;}/*** 应用筛选条件(优先使用搜索器)** @param Builder $query* @param array $conditions* @param Model $model* @param array $scopes* @param array $scopeMap*/private function applyConditions(Builder $query,array $conditions,Model $model,array $scopes = [],array $scopeMap = []): void {foreach ($conditions as $condition) {if (!$condition instanceof QueryCondition) {continue;}$field = $condition->field;$value = $condition->value;// 1. 检查自定义映射if (isset($scopeMap[$field])) {$scopeMethod = 'scope' . ucfirst($scopeMap[$field]);if (method_exists($model, $scopeMethod)) {$query->{$scopeMap[$field]}($value);continue;}}// 2. 检查是否在启用列表中if (!empty($scopes) && !in_array($field, $scopes)) {// 未在启用列表,使用普通查询$condition->apply($query);continue;}// 3. 自动检测并应用搜索器$scopeName = $this->fieldToScopeName($field);if ($scopeName && method_exists($model, $scopeName)) {// 使用搜索器$query->{lcfirst(substr($scopeName, 5))}($value);} else {// 使用普通查询条件$condition->apply($query);}}}/*** 将字段名转换为搜索器方法名* user_name -> scopeUserName** @param string $field* @return string|null*/private function fieldToScopeName(string $field): ?string{// 转换为驼峰命名$camelCase = str_replace(' ', '', ucwords(str_replace('_', ' ', $field)));return 'scope' . ucfirst($camelCase);}/*** 应用关键词搜索** @param Builder $query* @param string $keyword* @param array $fields*/private function applyKeywordSearch(Builder $query, string $keyword, array $fields): void{if (empty($fields)) {return;}$query->where(function ($q) use ($keyword, $fields) {foreach ($fields as $field) {$q->orWhere($field, 'like', "%{$keyword}%");}});}/*** 构建分页查询** @param Builder $query* @param int $page* @param int $pageSize* @return array ['items' => [], 'total' => 0]*/public function paginate(Builder $query, int $page, int $pageSize): array{$total = $query->count();$items = $query->offset(($page - 1) * $pageSize)->limit($pageSize)->get()->toArray();return ['items' => $items,'total' => $total,'page' => $page,'pageSize' => $pageSize,'totalPages' => (int)ceil($total / $pageSize),];}/*** 获取解析器实例** @return QueryParamParser*/private function getParser(): QueryParamParser{return $this->parser ?? new QueryParamParser();}/*** 应用 filters 条件到查询构建器* 支持多种 filters 格式:keyvalue、数组、标准对象** @param Builder $query* @param array $filters* @return Builder*/public static function applyFiltersToQuery(Builder $query, array $filters): Builder{foreach ($filters as $key => $filter) {if (is_string($key) && is_string($filter)) {// keyvalue 格式:'field' => 'op:value'$parts = explode(':', $filter, 2);if (count($parts) === 2) {[$operator, $value] = $parts;} else {$operator = 'eq';$value = $parts[0];}self::applyFilter($query, $key, $operator, $value);} elseif (is_array($filter) && count($filter) === 3) {// 数组格式:[field, operator, value]self::applyFilter($query, $filter[0], $filter[1], $filter[2]);} elseif (isset($filter['field']) && isset($filter['operator']) && isset($filter['value'])) {// 标准对象格式:{field, operator, value}self::applyFilter($query, $filter['field'], $filter['operator'], $filter['value']);}}return $query;}/*** 应用单个过滤条件到查询构建器** @param Builder $query* @param string $field* @param string $operator* @param mixed $value* @return Builder*/public static function applyFilter(Builder $query, string $field, string $operator, $value): Builder{$dbOperatorMap = ['eq' => '=','ne' => '!=','gt' => '>','gte' => '>=','lt' => '<','lte' => '<=','like' => 'like','in' => 'in','not_in' => 'not in',];$dbOperator = $dbOperatorMap[$operator] ?? '=';switch ($dbOperator) {case 'like':$query->where($field, 'like', "%{$value}%");break;case 'in':$values = is_array($value) ? $value : explode(',', $value);$query->whereIn($field, $values);break;case 'not in':$values = is_array($value) ? $value : explode(',', $value);$query->whereNotIn($field, $values);break;default:$query->where($field, $dbOperator, $value);break;}return $query;}}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。