'albums.name', Rule::MODEL_ARTIST_NAME => 'artists.name', Rule::MODEL_DATE_ADDED => 'songs.created_at', Rule::MODEL_DATE_MODIFIED => 'songs.updated_at', ]; private const CLAUSE_WHERE = 'where'; private const CLAUSE_WHERE_BETWEEN = 'whereBetween'; private const CLAUSE_WHERE_NOT_BETWEEN = 'whereNotBetween'; public string $clause; public array $parameters; private function __construct(string $clause, ...$parameters) { $this->clause = $clause; $this->parameters = $parameters; } public static function fromRule(Rule $rule): self { $operator = $rule->operator; $value = $rule->value; // If the rule is a date rule and the operator is "is" or "is not", we need to // convert the date to a range of dates and use the "between" or "not between" operator instead, // as we store dates as timestamps in the database. if (in_array($rule->model, self::DATE_MODELS, true)) { $nextDay = Carbon::createFromFormat('Y-m-d', $value[0])->addDay()->format('Y-m-d'); if ($operator === Rule::OPERATOR_IS) { $operator = Rule::OPERATOR_IS_BETWEEN; $value = [$value[0], $nextDay]; } elseif ($operator === Rule::OPERATOR_IS_NOT) { $operator = Rule::OPERATOR_IS_NOT_BETWEEN; $value = [$value[0], $nextDay]; } } $column = array_key_exists($rule->model, self::MODEL_COLUMN_REMAP) ? self::MODEL_COLUMN_REMAP[$rule->model] : $rule->model; $resolvers = [ Rule::OPERATOR_BEGINS_WITH => [$column, 'LIKE', "$value[0]%"], Rule::OPERATOR_ENDS_WITH => [$column, 'LIKE', "%$value[0]"], Rule::OPERATOR_IS => [$column, '=', $value[0]], Rule::OPERATOR_IS_NOT => [$column, '<>', $value[0]], Rule::OPERATOR_CONTAINS => [$column, 'LIKE', "%$value[0]%"], Rule::OPERATOR_NOT_CONTAIN => [$column, 'NOT LIKE', "%$value[0]%"], Rule::OPERATOR_IS_LESS_THAN => [$column, '<', $value[0]], Rule::OPERATOR_IS_GREATER_THAN => [$column, '>', $value[0]], Rule::OPERATOR_IS_BETWEEN => [$column, $value], Rule::OPERATOR_IS_NOT_BETWEEN => [$column, $value], Rule::OPERATOR_NOT_IN_LAST => static fn () => [$column, '<', now()->subDays($value[0])], Rule::OPERATOR_IN_LAST => static fn () => [$column, '>=', now()->subDays($value[0])], ]; Assert::keyExists($resolvers, $operator); $clause = match ($operator) { Rule::OPERATOR_IS_BETWEEN => self::CLAUSE_WHERE_BETWEEN, Rule::OPERATOR_IS_NOT_BETWEEN => self::CLAUSE_WHERE_NOT_BETWEEN, default => self::CLAUSE_WHERE, }; $parameters = $resolvers[$operator] instanceof Closure ? $resolvers[$operator]() : $resolvers[$operator]; return new self($clause, ...$parameters); } }