Laravel 10报错:调用未定义方法Staudenmeir\LaravelCte\Query\Builder::()
Laravel 10:调用未定义方法
Staudenmeir\LaravelCte\Query\Builder::() 问题排查 我在开发Laravel 10项目时,编写了一个检查用户Perk资格的服务类,构建查询时遇到错误:
Call to undefined method Staudenmeir\LaravelCte\Query\Builder::()
已确认checkEligibilityForUser返回的是Illuminate\Database\Eloquent\Builder实例,但不清楚问题原因。以下是服务类代码及调用方式:
服务类代码
<?php namespace App\Services; use App\Contracts\PerkEligibilityContract; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; use Illuminate\Support\Arr; use App\Models\Perk; use App\Models\User; use Carbon\Carbon; use Throwable; class PerkEligibilityService implements PerkEligibilityContract { /** * The perk */ private Perk $perk; /** * The user id for lookup */ private int $userId; /** * The user ids for lookup */ private array $userIds; /** * Validate and return operator */ private function validateOperator(string $operator): string { return match ($operator) { '!=' => '!=', '>' => '>', '>=' => '>=', '<' => '<', '<=' => '<=', default => '=' }; } /** * Return the current date or date specified */ private function currentDate(string $date = 'now'): Carbon { try { return Carbon::parse($date); } catch (Throwable $err) { return Carbon::now(); } } /** * Calculate relative date * * ">=" - refers to "In the last N days", so any date from them to now * "<=" - refers to "More than N days", so any date before a given date not up to now */ private function relativeDate(string $value, string $operator = '>='): Carbon { [$amount, $unit] = explode(':', $value); $now = $this->currentDate(); return match ($unit) { 'second', 'seconds' => $operator === '>=' ? $now->subSeconds((int) $amount) : $now->addSeconds((int) $amount), 'minute', 'minutes' => $operator === '>=' ? $now->subMinutes((int) $amount) : $now->addMinutes((int) $amount), 'hour', 'hours' => $operator === '>=' ? $now->subHours((int) $amount) : $now->addHours((int) $amount), 'day', 'days' => $operator === '>=' ? $now->subDays((int) $amount) : $now->addDays((int) $amount), 'week', 'weeks' => $operator === '>=' ? $now->subWeeks((int) $amount) : $now->addWeeks((int) $amount), 'month', 'months' => $operator === '>=' ? $now->subMonths((int) $amount) : $now->addMonths((int) $amount), 'year', 'years' => $operator === '>=' ? $now->subYears((int) $amount) : $now->subYears((int) $amount), default => $now }; } /** * Validate and return value */ private function validateValue(string $dataType, string $operatorType, string $operator, mixed $value): mixed { return match ($dataType) { 'number' => (int) $value, 'date' => match ($operatorType) { 'relative' => $this->relativeDate($value, $operator), default => $this->currentDate('now') }, default => $value }; } /** * Apply query condition */ private function applyQueryCondition(object $rule, Builder $query, string $column = ''): void { if (! Schema::hasColumn($query->getModel()->getTable(), $column ?? '')) return; // modify the value based on data type and dynamic field $value = $this->validateValue( $rule->data_type, $rule->operator_type, $this->validateOperator($rule->operator), $rule->value ); // no need to return, query is directly applied back on $query instance match ($rule->operator) { 'like' => $query->where($column, 'like', $value), '%like%' => $query->where($column, 'like', "%$value%"), 'like%' => $query->where($column, 'like', "$value%"), '%like' => $query->where($column, 'like', "%$value"), 'is_empty' => $query->where($column, '=', ''), 'not_empty' => $query->where($column, '!=', ''), 'is_null' => $query->whereNull($column), 'not_null' => $query->whereNotNull($column), default => $query->where($column, $this->validateOperator($rule->operator), $value) }; } /** * Check the eligibility */ private function checkEligibilityConditions(Builder $models): void { $this->perk->rules->each(function (object $rule) use ($models) { if (Str::contains($rule->attribute, '.')) { $collection = Str::of($rule->attribute)->explode('.'); $relationship = $collection->slice(0, -1)->implode('.'); $column = $collection->last(); $models->whereHas($relationship, function (Builder $query) use ($rule, $column) { $this->applyQueryCondition($rule, $query, $column); }); } else { $this->applyQueryCondition($rule, $models, $rule->attribute); } }); } /** * Check the eligibility for the user */ private function checkEligibilityForUser(): bool { $user = User::query()->where('id', $this->userId); $this->checkEligibilityConditions($user); return $user->exists(); } /** * Check the eligibility for the users */ private function checkEligibilityForUsers(): int { $users = User::query()->whereIn('id', $this->userIds); $this->checkEligibilityConditions($users); return $users->count(); } /** * Set's the perk */ public function perk(Perk $perk): self { $this->perk = $perk->load(['rules' => fn ($query) => $query->enabled()]); return $this; } /** * Set's the user id */ public function userId(int $userId): self { $this->userId = $userId; return $this; } /** * Set's the user ids */ public function userIds(array $userIds): self { $this->userIds = $userIds; return $this; } /** * Get a count of users that are eligible */ public function areEligible(): int { if ($this->perk->rules->isNotEmpty()) { return $this->checkEligibilityForUsers(); } return count($this->userIds); } /** * Is the user eligible */ public function isEligible(): bool { if ($this->perk->rules->isNotEmpty()) { return $this->checkEligibilityForUser(); } return true; } }
调用代码
$perk = Perk::find($request->input('id')); $user = auth()->user(); PerkEligibility::perk($perk)->userId($user->id)->isEligible();
问题原因及修复方案
核心原因
错误提示中的::()说明代码尝试调用一个空方法名,大概率是applyQueryCondition方法里的match表达式存在解析问题:PHP的match语法在某些场景下(比如分支值异常、扩展类兼容问题)会生成无效的查询构造器调用,再加上项目使用了Staudenmeir\LaravelCte扩展替换了原生查询构造器,进一步触发了这个奇怪的错误。
具体修复步骤
替换
match为if-else结构
把applyQueryCondition中的match语句改为传统的条件判断,避免解析异常:// 替换原match代码 if ($rule->operator === 'like') { $query->where($column, 'like', $value); } elseif ($rule->operator === '%like%') { $query->where($column, 'like', "%$value%"); } elseif ($rule->operator === 'like%') { $query->where($column, 'like', "$value%"); } elseif ($rule->operator === '%like') { $query->where($column, 'like', "%$value"); } elseif ($rule->operator === 'is_empty') { $query->where($column, '=', ''); } elseif ($rule->operator === 'not_empty') { $query->where($column, '!=', ''); } elseif ($rule->operator === 'is_null') { $query->whereNull($column); } elseif ($rule->operator === 'not_null') { $query->whereNotNull($column); } else { $operator = $this->validateOperator($rule->operator); $query->where($column, $operator, $value); }校验
$rule->operator的合法性
在applyQueryCondition开头添加日志,排查是否存在异常的operator值:Log::debug('Perk rule details', ['rule' => $rule]);确保所有Perk规则中的operator都是
validateOperator方法中定义的合法值,避免空值或未定义值进入逻辑。更新Laravel CTE扩展版本
执行命令更新扩展,确保和Laravel 10兼容:composer update staudenmeir/laravel-cte
内容的提问来源于stack exchange,提问作者Ryan H
相关产品推荐
相关产品推荐

