1, 'AND', "age" => ['>=', 18], 'OR', "profile" => ['<=', 12], 'AND', ['AND', "Name" => "Alex", * ['OR', "Age" => ['>', 18], "FirstName" => "Titanic"]]] * * задаёт следующий массив условий: * * `id` = 1 AND `age` >= 18 OR `profile` <= 12 AND (`Name` = 'Alex' AND (`Age` > 18 OR `FirstName` = * 'Titanic' * * @param array $conditions Массив условий. * * @return ConditionBuilder Объект построителя условий запроса выборки. */ public static function Parse (array $conditions): ConditionBuilder { // Создаём объект для цепочки $builder = new ConditionBuilder(); // Перебираем условия foreach ($conditions as $key => $condition) { // - если это индексированная часть массива if (is_numeric($key)) { // -- это может быть логический оператор? Проверим, является ли $condition строкой if (is_string($condition)) { // --- если да, то добавляем его в цепочку $builder = $builder->AddLogicalOperator($condition); // --- если ошибка if ($builder === false) // ---- то выбрасываем исключение throw new InvalidArgumentException("Неверный логический оператор: $condition / The logical operator is invalid: $condition."); // --- идём к следующему элементу continue; } // -- это группа условий, парсим её и добавляем результат $builder = $builder->AddGroupA(self::ParseConditionGroup($condition)); // -- идём к следующему элементу continue; } // - это условие, парсим его и добавляем результат $builder = $builder->AddConditionA(self::ParseCondition($key, $condition)); } // Возвращаем объект return $builder; } /** * Проверяет, является ли оператор логическим, и возвращает правильно отформатированный оператор. * * @param string $operator Оператор. * * @return false|string Возвращает правильно отформатированный оператор или false, если оператор * не является логическим. */ private static function PrepareLogicalOperator (string $operator): false|string { // Задаем массив логических операторов $logicalOperators = ['AND', 'OR', 'NOT', 'XOR', 'NAND', 'NOR']; // Переводим оператор в верхний регистр $operator = strtoupper($operator); // Если оператор не входит в массив логических операторов if (!in_array($operator, $logicalOperators, true)) // - возвращаем false return false; // Возвращаем правильный оператор return $operator; } /** * Обрабатывает условие, заданное в виде массива. * * @param string $column Имя столбца. * @param mixed $conditions Условия. * * @return Condition Возвращает условие. */ private static function ParseCondition (string $column, mixed $conditions): Condition { // Проверяем условия // - если это не массив if (!is_array($conditions)) // -- то добавляем оператор равенства и обрабатываем как массив $conditions = ['=', $conditions]; // - если это массив в массиве if (is_array($conditions[0])) // -- то извлекаем первый элемент, как весь массив $conditions = $conditions[0]; // - если это массив с длинной меньше 2 if (count($conditions) < 2) // -- то исправляем это $conditions = array_merge(['='], $conditions); // Добавляем условие return new Condition($column, $conditions[0], $conditions[1]); } /** * Обрабатывает группу условий. * * @param array $conditions Условия. * * @return ConditionGroup Возвращает группу условий. */ private static function ParseConditionGroup (array $conditions): ConditionGroup { // Получаем оператор $operator = $conditions[0]; // Удаляем его из массива условий $conditions = array_slice($conditions, 1); // Задаем массив условий группы $conditionItems = []; // Перебираем условия foreach ($conditions as $key => $condition) { // - если это индексированная часть массива if (is_numeric($key)) { // -- то это группа условий, парсим её и добавляем результат $conditionItems[] = self::ParseConditionGroup($condition); // -- идём к следующему элементу continue; } // - если это не индексированная часть массива, то это условие, парсим его и добавляем результат $conditionItems[] = self::ParseCondition($key, [$condition]); } // Возвращаем группу условий return new ConditionGroup($operator, $conditionItems); } /** * Добавляет группу условий. * * @param string $logicalOperator Оператор связок внутри группы. * @param array $conditions Условия. * * @return ConditionBuilder Возвращает объект для цепочек. */ public function AddGroup (string $logicalOperator, array $conditions): ConditionBuilder { return $this->AddGroupA(new ConditionGroup($logicalOperator, $conditions)); } /** * Собирает условие в виде пригодном для SQL. Также возвращает массив параметров защиты от SQL-инъекций. * * @return Tuple (string, array) Возвращает условие в виде, пригодном для SQL и массив параметров для защиты от * SQL-инъекций. */ public function Build (): Tuple { // Очищаем цепочку от пустых элементов ArrayExtension::RemoveEmpties($this->Conditions); // Парсим цепочку и возвращаем результат return $this->ProcessConditions($this->Conditions); } /** * @inheritDoc */ public function ToArray (): array { // Задаем массив результата $result = [ 'type_class' => ConditionBuilder::class ]; // Перебираем условия foreach ($this->Conditions as $condition) { // - если это группа условий if ($condition instanceof ConditionGroup) { // -- парсим её и добавляем результат $result['conditions'][] = $condition->ToArray(); // -- идём к следующему элементу continue; } // - если это условие if ($condition instanceof Condition) { // -- парсим его и добавляем результат $result['conditions'][] = $condition->ToArray(); // -- идём к следующему элементу continue; } // - иначе считаем, что это логический оператор $result['conditions'][] = $condition; } // Возвращаем результат return $result; } /** * @inheritDoc */ public function FromArray (array $array): void { // Удаляем тип класса $preparedConditions = array_diff(['type_class' => ConditionBuilder::class], $array); // Очищаем массив $this->Conditions = []; // Перебираем условия foreach ($preparedConditions as $condition) { // - получаем тип $type = (isset($condition['type_class'])) ? $condition['type_class'] : ""; // - если это группа условий if ($type == ConditionGroup::class) { // -- создаём объект $conditionGroup = new ConditionGroup(); // -- восстанавливаем его из массива $conditionGroup->FromArray($condition); // -- добавляем его в цепочку $this->Conditions[] = $conditionGroup; // -- идём к следующему элементу continue; } // - если это условие if ($type == Condition::class) { // -- создаём объект $conditionGroup = new Condition(); // -- восстанавливаем его из массива $conditionGroup->FromArray($condition); // -- добавляем его в цепочку $this->Conditions[] = $conditionGroup; // -- идём к следующему элементу continue; } // - иначе считаем, что это логический оператор $this->Conditions[] = $condition; } } /** * Получает количество условий в цепочке. * * @return int Возвращает количество условий. */ public function Count (): int { // Задаем счетчик $count = 0; // Перебираем условия foreach ($this->Conditions as $condition) { // - если это условие if ($condition instanceof Condition) // -- то сразу увеличиваем счетчик $count++; // - если это группа условий if ($condition instanceof ConditionGroup) // -- то считаем их количество и добавляем к счетчику $count += $condition->Count(); } // Возвращаем результат return $count; } /** * Добавляет группу условий. * * @param ConditionGroup $group Группа условий. * * @return ConditionBuilder Возвращает объект для цепочек. */ private function AddGroupA (ConditionGroup $group): ConditionBuilder { // Добавляем условие $this->Conditions[] = $group; // Возвращаем объект return $this; } /** * Собирает условие в виде, пригодном для SQL. * * @param array $conditions Условия. * * @return Tuple (string, array) Возвращает условие в виде, пригодном для SQL и массив параметров для защиты * от SQL-инъекций. */ private function ProcessConditions (array $conditions): Tuple { // Задаём массив частей $parts = []; // Задаём массив параметров для защиты от SQL-инъекций $params = []; // Задаём счётчик $count = 0; // Перебираем условия foreach ($conditions as $condition) { // - если это группа условий if ($condition instanceof ConditionGroup) { // -- парсим её и добавляем результат $result = $condition->GetConditions($count); // -- добавляем условие в массив частей $parts[] = $result->Get(0); // -- добавляем параметры для защиты от SQL-инъекций в массив $params = array_merge($params, $result->Get(1)); // -- увеличиваем счётчик $count = $count + count($result->Get(1)); // -- идём к следующему элементу continue; } // - если это условие if ($condition instanceof Condition) { // -- парсим его $result = $condition->Get($count); // -- добавляем условие в массив частей $parts[] = $result->Get(0); // -- добавляем параметры для защиты от SQL-инъекций в массив $params = array_merge($params, $result->Get(1)); // -- увеличиваем счётчик на 1 $count++; // -- идём к следующему элементу continue; } // - иначе считаем, что это логический оператор, проверим это $condition = self::PrepareLogicalOperator($condition); // - если это не логический оператор if ($condition === false) // -- то пропускаем его continue; // - добавляем его в массив частей $parts[] = $condition; } // Возвращаем результат return new Tuple(implode(' ', $parts), $params); } }