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) ) ); } } }