mirror of
https://github.com/koel/koel
synced 2024-11-28 15:00:42 +00:00
101 lines
3.3 KiB
PHP
101 lines
3.3 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use App\Factories\SmartPlaylistRuleParameterFactory;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use InvalidArgumentException;
|
|
|
|
class Rule
|
|
{
|
|
public const OPERATOR_IS = 'is';
|
|
public const OPERATOR_IS_NOT = 'isNot';
|
|
public const OPERATOR_CONTAINS = 'contains';
|
|
public const OPERATOR_NOT_CONTAIN = 'notContain';
|
|
public const OPERATOR_IS_BETWEEN = 'isBetween';
|
|
public const OPERATOR_IS_GREATER_THAN = 'isGreaterThan';
|
|
public const OPERATOR_IS_LESS_THAN = 'isLessThan';
|
|
public const OPERATOR_BEGINS_WITH = 'beginsWith';
|
|
public const OPERATOR_ENDS_WITH = 'endsWith';
|
|
public const OPERATOR_IN_LAST = 'inLast';
|
|
public const OPERATOR_NOT_IN_LAST = 'notInLast';
|
|
|
|
public const VALID_OPERATORS = [
|
|
self::OPERATOR_BEGINS_WITH,
|
|
self::OPERATOR_CONTAINS,
|
|
self::OPERATOR_ENDS_WITH,
|
|
self::OPERATOR_IN_LAST,
|
|
self::OPERATOR_IS,
|
|
self::OPERATOR_IS_BETWEEN,
|
|
self::OPERATOR_IS_GREATER_THAN,
|
|
self::OPERATOR_IS_LESS_THAN,
|
|
self::OPERATOR_IS_NOT,
|
|
self::OPERATOR_NOT_CONTAIN,
|
|
self::OPERATOR_NOT_IN_LAST,
|
|
];
|
|
|
|
private $operator;
|
|
private $value;
|
|
private $model;
|
|
private $parameterFactory;
|
|
|
|
private function __construct(array $config)
|
|
{
|
|
$this->validateOperator($config['operator']);
|
|
|
|
$this->value = $config['value'];
|
|
$this->model = $config['model'];
|
|
$this->operator = $config['operator'];
|
|
|
|
$this->parameterFactory = new SmartPlaylistRuleParameterFactory();
|
|
}
|
|
|
|
public static function create(array $config): self
|
|
{
|
|
return new static($config);
|
|
}
|
|
|
|
public function build(Builder $query, ?string $model = null): Builder
|
|
{
|
|
if (!$model) {
|
|
$model = $this->model;
|
|
}
|
|
|
|
$fragments = explode('.', $model, 2);
|
|
|
|
if (count($fragments) === 1) {
|
|
return $query->{$this->resolveLogic()}(
|
|
...$this->parameterFactory->createParameters($model, $this->operator, $this->value)
|
|
);
|
|
}
|
|
|
|
// If the model is something like 'artist.name' or 'interactions.play_count', we have a subquery to deal with.
|
|
// We handle such a case with a recursive call which, in theory, should work with an unlimited level of nesting,
|
|
// though in practice we only have one level max.
|
|
return $query->whereHas($fragments[0], function (Builder $subQuery) use ($fragments): Builder {
|
|
return $this->build($subQuery, $fragments[1]);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Resolve the logic of a (sub)query base on the configured operator.
|
|
* Basically, if the operator is "between," we use "whereBetween." Otherwise, it's "where." Simple.
|
|
*/
|
|
private function resolveLogic(): string
|
|
{
|
|
return $this->operator === self::OPERATOR_IS_BETWEEN ? 'whereBetween' : 'where';
|
|
}
|
|
|
|
private function validateOperator(string $operator): void
|
|
{
|
|
if (!in_array($operator, self::VALID_OPERATORS, true)) {
|
|
throw new InvalidArgumentException(
|
|
sprintf(
|
|
'%s is not a valid value for operators. Valid values are: %s',
|
|
$operator,
|
|
implode(', ', self::VALID_OPERATORS)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|