namespace App\Library\Model;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
trait WithRequestFilter
{
public $aliasEnabled = false;
protected $expression = [
'$like',
'$is',
'$between',
'$in',
'$gt',
'$gte',
'$lt',
'$lte',
'$eq',
'$not',
];
protected $operator = [
'$or',
'$and',
];
protected $fieldAlias = [
'permission' => 'permission_id',
'role' => 'role_id',
'user' => 'user_id'
];
protected $availableFields = ['*'];
public function setFieldNameAlias($key, $value)
{
$this->fieldAlias[$key] = $value;
}
protected function buildFilter(array $filter): Builder
{
if ($this instanceof Model) {
return $this->setFilter(static::query(), $filter);
} else {
throw new Exception('Only available with Eloquent Model');
}
}
protected function setFilter(Builder $query, array $filter): Builder
{
foreach ($filter as $key => $value) {
if (in_array($key, $this->operator)) {
$operator = $key;
$this->buildOperator($query, $value, $operator);
} else {
$field = $key;
$this->buildExpression($query, $field, $value);
}
}
return $query;
}
private function buildOperator(Builder $query, $filter, string $operator = '$and')
{
if ($operator === '$or') {
$query->orWhere(function ($query) use ($filter) {
$this->buildOperator($query, $filter);
});
} else {
foreach ($filter as $key => $value) {
if (in_array($key, $this->operator)) {
$operator = $key;
$this->buildOperator($query, $value, $operator);
} else {
$field = $key;
$this->buildExpression($query, $field, $value);
}
}
}
}
private function buildExpression(Builder $query, string $fieldName, $fieldValue, $not = false)
{
if (is_array($fieldValue)) {
foreach ($fieldValue as $expression => $value) {
if ($expression === '$not') {
$this->buildExpression($query, $fieldName, $value, true);
} else {
$this->buildCondition($query, $fieldName, $value, $expression, $not);
}
}
} else {
$this->buildCondition($query, $fieldName, $fieldValue, '$eq', $not);
}
}
private function buildCondition(Builder $query, string $field, $value, string $expression, bool $negative = false)
{
$fieldName = $this->getFieldName($field);
if (!$this->fieldAvailable($fieldName)) {
throw new InvalidParameterException("Field name $field not available.");
}
switch ($expression) {
case '$like':
if ($negative) {
$query->where($fieldName, 'not like', "%$value%");
} else {
$query->where($fieldName, 'like', "%$value%");
}
break;
case '$is':
if (is_null($value)) {
$negative ? $query->whereNotNull($fieldName) : $query->whereNull($fieldName);
} else {
throw new InvalidParameterException('The value of key "$is" must be null');
}
break;
case '$between':
if (is_array($value) && count($value) === 2) {
$negative ? $query->whereNotBetween($fieldName, $value) : $query->whereBetween($fieldName, $value);
} else {
throw new InvalidParameterException('The value of key "$between" must be an array of length 2');
}
break;
case '$in':
$values = is_array($value) ? $value : [$value];
$negative ? $query->whereNotIn($fieldName, $value) : $query->whereIn($fieldName, $values);
break;
case '$gt':
$negative ? $query->where($fieldName, '<=', $value) : $query->where($fieldName, '>', $value);
break;
case '$gte':
$negative ? $query->where($fieldName, '<', $value) : $query->where($fieldName, '>=', $value);
break;
case '$lt':
$negative ? $query->where($fieldName, '>=', $value) : $query->where($fieldName, '<', $value);
break;
case '$lte':
$negative ? $query->where($fieldName, '>', $value) : $query->where($fieldName, '<=', $value);
break;
case '$eq':
$negative ? $query->where($fieldName, '<>', $value) : $query->where($fieldName, $value);
break;
default:
if (method_exists($this, 'buildHandler')) {
call_user_func([$this, 'buildHandler'], $query, $fieldName, $value, $expression, $negative);
} elseif (is_array($value)) {
throw new InvalidParameterException("the value of field $fieldName can't be an array");
} elseif (is_null($value)) {
$negative ? $query->whereNull($fieldName) : $query->whereNotNull($fieldName);
} else {
$negative ? $query->where($fieldName, '<>', $value) : $query->where($fieldName, $value);
}
}
}
protected function getFieldName(string $field): string
{
if ($this->aliasEnabled) {
return $this->fieldAlias[$field] ?? $field;
} else {
return $field;
}
}
protected function fieldAvailable($field): bool
{
if (empty($this->availableFields)) {
return false;
}
if (in_array($field, $this->availableFields)) {
return true;
}
if (in_array('*', $this->availableFields)) {
return true;
}
return false;
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結