I’m working on a Laravel 10 project. I’ve got a service that checks the Perk eligibility of a user and constructs some queries, I’m getting the following error:
Call to undefined method StaudenmeirLaravelCteQueryBuilder::()
But after checking the type of checkEligibilityForUser
it’s certainly IlluminateDatabaseEloquentBuilder
so I don’t know what I’m missing, here’s my class and how it’s being called:
Service
<?php
namespace AppServices;
use AppContractsPerkEligibilityContract;
use IlluminateDatabaseEloquentBuilder;
use IlluminateDatabaseEloquentModel;
use IlluminateSupportFacadesSchema;
use IlluminateSupportFacadesLog;
use IlluminateSupportFacadesDB;
use IlluminateSupportStr;
use IlluminateSupportArr;
use AppModelsPerk;
use AppModelsUser;
use CarbonCarbon;
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;
}
}
Called as:
$perk = Perk::find($request->input('id')
$user = auth()->user();
PerkEligibility::perk($perk)->userId($user->id)->isEligible()