validateLogic($config['logic']); $this->validateOperator($config['operator']); $this->logic = $config['logic']; $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. $subQueryLogic = self::LOGIC_AND ? 'whereHas' : 'orWhereHas'; return $query->$subQueryLogic($fragments[0], function (Builder $subQuery) use ($fragments): Builder { return $this->build($subQuery, $fragments[1]); }); } private function resolveLogic(): string { if ($this->operator === self::OPERATOR_IS_BETWEEN) { return $this->logic === self::LOGIC_AND ? 'whereBetween' : 'orWhereBetween'; } return $this->logic === self::LOGIC_AND ? 'where' : 'orWhere'; } private function validateLogic(string $logic): void { if (!in_array($logic, self::VALID_LOGICS, true)) { throw new InvalidArgumentException( sprintf( '%s is not a valid value for logic. Valid values are: %s', $logic, implode(', ', self::VALID_LOGICS) ) ); } } 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) ) ); } } }