20250819
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
use goodboyalex\php_components_pack\classes\Tuple;
|
||||
use goodboyalex\php_components_pack\extensions\StringExtension;
|
||||
use goodboyalex\php_components_pack\interfaces\IArrayable;
|
||||
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
||||
use PHPUnit\Event\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
@@ -101,15 +102,19 @@
|
||||
/**
|
||||
* Формирует условие.
|
||||
*
|
||||
* @param DBDriver $driver Тип драйвера СУБД.
|
||||
* @param int $index Индекс замены параметров для защиты от SQL-инъекций.
|
||||
*
|
||||
* @return Tuple (string, array) Сформированное условие: SQL-запрос и параметры запроса.
|
||||
*/
|
||||
public function Get (int $index = 0): Tuple
|
||||
public function Get (DBDriver $driver, int $index = 0): Tuple
|
||||
{
|
||||
// Получаю знаки открытия и закрытия
|
||||
[$signOpen, $signClose] = $driver->GetSigns($driver);
|
||||
|
||||
// Начинаю формировать SQL
|
||||
$sql = (!str_starts_with($this->ColumnName, "FUNC:"))
|
||||
? "`$this->ColumnName`"
|
||||
? "$signOpen$this->ColumnName$signClose"
|
||||
: StringExtension::Replace('FUNC:', '', $this->ColumnName);
|
||||
|
||||
// Добавляю оператор
|
||||
|
@@ -5,6 +5,7 @@
|
||||
use goodboyalex\php_components_pack\classes\Tuple;
|
||||
use goodboyalex\php_components_pack\extensions\ArrayExtension;
|
||||
use goodboyalex\php_components_pack\interfaces\IArrayable;
|
||||
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
||||
use goodboyalex\php_db_components_pack\traits\ConditionBuilder\ConditionBuilderConditionsSet;
|
||||
use goodboyalex\php_db_components_pack\traits\ConditionBuilder\ConditionBuilderLogicalOperationSet;
|
||||
use InvalidArgumentException;
|
||||
@@ -64,7 +65,9 @@
|
||||
// --- если ошибка
|
||||
if ($builder === false)
|
||||
// ---- то выбрасываем исключение
|
||||
throw new InvalidArgumentException("Неверный логический оператор: $condition / The logical operator is invalid: $condition.");
|
||||
throw new InvalidArgumentException(
|
||||
"Неверный логический оператор: $condition / The logical operator is invalid: $condition."
|
||||
);
|
||||
|
||||
// --- идём к следующему элементу
|
||||
continue;
|
||||
@@ -178,6 +181,81 @@
|
||||
return new ConditionGroup($operator, $conditionItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Собирает условие в виде, пригодном для SQL.
|
||||
*
|
||||
* @param DBDriver $driver Тип драйвера СУБД.
|
||||
* @param array $conditions Условия.
|
||||
*
|
||||
* @return Tuple (string, array) Возвращает условие в виде, пригодном для SQL и массив параметров для защиты
|
||||
* от SQL-инъекций.
|
||||
*/
|
||||
private static function ProcessConditions (DBDriver $driver, 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($driver, $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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет группу условий.
|
||||
*
|
||||
@@ -194,16 +272,18 @@
|
||||
/**
|
||||
* Собирает условие в виде пригодном для SQL. Также возвращает массив параметров защиты от SQL-инъекций.
|
||||
*
|
||||
* @param DBDriver $driver Тип драйвера СУБД.
|
||||
*
|
||||
* @return Tuple (string, array) Возвращает условие в виде, пригодном для SQL и массив параметров для защиты от
|
||||
* SQL-инъекций.
|
||||
*/
|
||||
public function Build (): Tuple
|
||||
public function Build (DBDriver $driver): Tuple
|
||||
{
|
||||
// Очищаем цепочку от пустых элементов
|
||||
ArrayExtension::RemoveEmpties($this->Conditions);
|
||||
|
||||
// Парсим цепочку и возвращаем результат
|
||||
return $this->ProcessConditions($this->Conditions);
|
||||
return self::ProcessConditions($driver, $this->Conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,78 +417,4 @@
|
||||
// Возвращаем объект
|
||||
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);
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@
|
||||
|
||||
use goodboyalex\php_components_pack\classes\Tuple;
|
||||
use goodboyalex\php_components_pack\interfaces\IArrayable;
|
||||
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
@@ -45,11 +46,12 @@
|
||||
/**
|
||||
* Формирует массив условий.
|
||||
*
|
||||
* @param DBDriver $driver Тип драйвера СУБД.
|
||||
* @param int $index Индекс замены параметров для защиты от SQL-инъекций.
|
||||
*
|
||||
* @return Tuple (string, array) Массив условий (строка SQL, параметры SQL).
|
||||
*/
|
||||
public function GetConditions (int $index = 0): Tuple
|
||||
public function GetConditions (DBDriver $driver, int $index = 0): Tuple
|
||||
{
|
||||
// Создаём результирующую строку
|
||||
$resultString = "";
|
||||
@@ -66,7 +68,7 @@
|
||||
$count++;
|
||||
|
||||
// - получаем условие
|
||||
$result = $this->WriteCondition($this->Conditions[$i], $count);
|
||||
$result = $this->WriteCondition($driver, $this->Conditions[$i], $count);
|
||||
|
||||
// - записываем условие в строку
|
||||
$resultString .= $result->Get(0);
|
||||
@@ -182,22 +184,23 @@
|
||||
/**
|
||||
* Формирует условие.
|
||||
*
|
||||
* @param DBDriver $driver Тип драйвера СУБД.
|
||||
* @param mixed $condition Условие.
|
||||
* @param int $index Индекс замены параметров для защиты от SQL-инъекций.
|
||||
*
|
||||
* @return string Возвращает условие в виде строки SQL.
|
||||
* @return Tuple Возвращает условие в виде строки SQL.
|
||||
*/
|
||||
private function WriteCondition (mixed $condition, int $index = 0): Tuple
|
||||
private function WriteCondition (DBDriver $driver, mixed $condition, int $index = 0): Tuple
|
||||
{
|
||||
// Проверяем, является ли условие объектом класса Condition
|
||||
if ($condition instanceof Condition)
|
||||
// - если да, то возвращаем его значение
|
||||
return $condition->Get($index);
|
||||
return $condition->Get($driver, $index);
|
||||
|
||||
// Проверяем, является ли условие объектом класса ConditionGroup
|
||||
if ($condition instanceof ConditionGroup)
|
||||
// - если да, то возвращаем его значения
|
||||
return $condition->GetConditions($index);
|
||||
return $condition->GetConditions($driver, $index);
|
||||
|
||||
// Если условие не является ни классом Condition, ни классом ConditionGroup, то это ошибка. Выбрасываем
|
||||
// исключение.
|
||||
|
@@ -36,16 +36,6 @@
|
||||
*/
|
||||
private ?PDO $DataBaseHandle;
|
||||
|
||||
/**
|
||||
* @var string $DBSignOpen Символ открытия для подстановки в запросы к базе.
|
||||
*/
|
||||
private string $DBSignOpen;
|
||||
|
||||
/**
|
||||
* @var string $DBSignСlose Символ закрытия для подстановки в запросы к базе.
|
||||
*/
|
||||
private string $DBSignClose;
|
||||
|
||||
/**
|
||||
* @var Closure $OnException Обработчик исключений. Анонимная функция формата:
|
||||
*
|
||||
@@ -96,21 +86,6 @@
|
||||
DBDriver::SQLite => "sqlite:$dbname"
|
||||
};
|
||||
|
||||
// Задаю DBSign
|
||||
// - Open
|
||||
$this->DBSignOpen = match ($this->Config->Driver) {
|
||||
DBDriver::MySQL, DBDriver::SQLite => '`',
|
||||
DBDriver::MSSQL => '[',
|
||||
DBDriver::PostgreSQL, DBDriver::OracleDB => '"'
|
||||
};
|
||||
|
||||
// - Close
|
||||
$this->DBSignClose = match ($this->Config->Driver) {
|
||||
DBDriver::MySQL, DBDriver::SQLite => '`',
|
||||
DBDriver::MSSQL => ']',
|
||||
DBDriver::PostgreSQL, DBDriver::OracleDB => '"'
|
||||
};
|
||||
|
||||
// Создаю объект для связи с базой данных
|
||||
$this->DataBaseHandle = new PDO($dsn, username: $user, password: $password);
|
||||
|
||||
|
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace goodboyalex\php_db_components_pack\enums;
|
||||
|
||||
use goodboyalex\php_components_pack\classes\Tuple;
|
||||
use goodboyalex\php_components_pack\traits\EnumExtensionsTrait;
|
||||
|
||||
/**
|
||||
@@ -42,4 +43,20 @@
|
||||
* SQLite
|
||||
*/
|
||||
case SQLite = 4;
|
||||
|
||||
/**
|
||||
* Получить знаки открытия/закрытия полей для СУБД.
|
||||
*
|
||||
* @param DBDriver $driver Драйвер СУБД.
|
||||
*
|
||||
* @return Tuple Возвращает кортеж [знак открытия, знак закрытия].
|
||||
*/
|
||||
public static function GetSigns (DBDriver $driver): Tuple
|
||||
{
|
||||
return match ($driver) {
|
||||
DBDriver::MySQL, DBDriver::SQLite => new Tuple('`', '`'),
|
||||
DBDriver::MSSQL => new Tuple('[', ']'),
|
||||
DBDriver::PostgreSQL, DBDriver::OracleDB => new Tuple('"', '"'),
|
||||
};
|
||||
}
|
||||
}
|
@@ -38,8 +38,11 @@
|
||||
*/
|
||||
[$sql_where, $params] = $where->Build();
|
||||
|
||||
// Подготавливаю имя таблицы
|
||||
$table = $this->PrepareTableName($table);
|
||||
|
||||
// Создаю запрос
|
||||
$sql = "SELECT COUNT(*) FROM $this->DBSignOpen$table$this->DBSignClose";
|
||||
$sql = "SELECT COUNT(*) FROM $table";
|
||||
|
||||
// Если заданы where-параметры
|
||||
if ($where->Count() > 0)
|
||||
|
@@ -11,6 +11,7 @@
|
||||
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
||||
use goodboyalex\php_db_components_pack\enums\DBOperation;
|
||||
use goodboyalex\php_db_components_pack\interfaces\IDBItem;
|
||||
use goodboyalex\php_db_components_pack\models\DBItemProperty;
|
||||
use PDO;
|
||||
|
||||
/**
|
||||
@@ -36,7 +37,7 @@
|
||||
public function Insert (string $table, IDBItem $row): mixed
|
||||
{
|
||||
// Подготавливаю запрос
|
||||
[$sql, $params] = $this->PrepareInsertSQL($table, $row);
|
||||
[$sql, $params, $primaryKeyValue] = $this->PrepareInsertSQL($table, $row);
|
||||
|
||||
// Выполняю запрос
|
||||
$count = $this->Execute($sql, $params);
|
||||
@@ -48,7 +49,7 @@
|
||||
|
||||
// Задаю переменную для последнего id
|
||||
$lastId = match ($this->Config->Driver) {
|
||||
DBDriver::MSSQL => $this->DataBaseHandle->query('SELECT SCOPE_IDENTITY()')->fetchColumn(),
|
||||
DBDriver::MSSQL => $this->Query('SELECT SCOPE_IDENTITY() AS last_inserted_id;')['last_inserted_id'],
|
||||
DBDriver::MySQL, DBDriver::SQLite => $this->DataBaseHandle->lastInsertId(),
|
||||
DBDriver::PostgreSQL, DBDriver::OracleDB => $this->DataBaseHandle->lastInsertId('sequence_name')
|
||||
};
|
||||
@@ -58,6 +59,11 @@
|
||||
// - то вывожу просто true
|
||||
$lastId = true;
|
||||
|
||||
// Если id не генерировался
|
||||
if ($lastId === null)
|
||||
// - то вывожу -1
|
||||
$lastId = $primaryKeyValue !== "NULL" ? $primaryKeyValue : true;
|
||||
|
||||
// Вывожу последний id
|
||||
return $lastId;
|
||||
}
|
||||
@@ -106,12 +112,35 @@
|
||||
* @param string $table Имя таблицы.
|
||||
* @param IDBItem $row Элемент.
|
||||
*
|
||||
* @return Tuple Возвращает [запрос, параметры запроса].
|
||||
* @return Tuple Возвращает [запрос, параметры запроса, значение первичного ключа].
|
||||
*/
|
||||
private function PrepareInsertSQL (string $table, IDBItem $row): Tuple
|
||||
{
|
||||
// Подготавливаю массив параметров
|
||||
$params = $this->PrepareParamsArray(source: $row, operation: DBOperation::Insert);
|
||||
$params = [];
|
||||
|
||||
// Получаю массив свойств
|
||||
$properties = self::GetProperties($row, DBOperation::Insert);
|
||||
|
||||
/**
|
||||
* Для каждого свойства...
|
||||
*
|
||||
* @var DBItemProperty $property Свойство.
|
||||
*/
|
||||
foreach ($properties as $property) {
|
||||
// - пропускаю игнорируемые поля
|
||||
if ($property->IsIgnored)
|
||||
continue;
|
||||
|
||||
// - получаю значение имени поля
|
||||
$fieldName = $property->Column->Name;
|
||||
|
||||
// - преобразую тип
|
||||
$value = call_user_func($property->ConvertToDB, $property->Value);
|
||||
|
||||
// - добавляю в массив
|
||||
$params[$fieldName] = $value;
|
||||
}
|
||||
|
||||
// Получаю ключи параметров
|
||||
$keys = array_keys($params);
|
||||
@@ -140,10 +169,26 @@
|
||||
// Значения sql запроса
|
||||
$sql_values = implode(', ', $keys_values);
|
||||
|
||||
// Подготавливаю имя таблицы
|
||||
$table = $this->PrepareTableName($table);
|
||||
|
||||
// Создаю запрос
|
||||
$sql = "INSERT INTO $this->DBSignOpen$table$this->DBSignClose ($sql_keys) VALUES ($sql_values);";
|
||||
$sql = "INSERT INTO $table ($sql_keys) VALUES ($sql_values);";
|
||||
|
||||
/**
|
||||
* Получаю первичный ключ таблицы.
|
||||
*
|
||||
* @var false|DBItemProperty $key Первичный ключ таблицы.
|
||||
*/
|
||||
$key = $properties->GetRow(
|
||||
selectCondition: fn (DBItemProperty $property): bool => $property->Column->IsPrimaryKey
|
||||
);
|
||||
|
||||
// Передаю первичный ключ в переменную
|
||||
$pKey = $key === false ? 'NULL' : $key->Value ?? "NULL";
|
||||
|
||||
|
||||
// Возвращаю результат
|
||||
return new Tuple($sql, $params);
|
||||
return new Tuple($sql, $params, $pKey);
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@
|
||||
use goodboyalex\php_db_components_pack\attributes\PrimaryKey;
|
||||
use goodboyalex\php_db_components_pack\attributes\Unique;
|
||||
use goodboyalex\php_db_components_pack\classes\ConditionBuilder;
|
||||
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
||||
use goodboyalex\php_db_components_pack\enums\DBOperation;
|
||||
use goodboyalex\php_db_components_pack\enums\DBType;
|
||||
use goodboyalex\php_db_components_pack\interfaces\IDBItem;
|
||||
@@ -283,45 +284,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Подготавливает массив параметров
|
||||
*
|
||||
* @param IDBItem $source Объект со свойствами.
|
||||
* @param DBOperation $operation Текущая операция.
|
||||
*
|
||||
* @return array|false Подготовленный массив параметров или false в случае ошибки
|
||||
*/
|
||||
private function PrepareParamsArray (IDBItem $source, DBOperation $operation): array | false
|
||||
{
|
||||
$result = [];
|
||||
|
||||
// Получаю массив свойств
|
||||
$properties = self::GetProperties($source, $operation);
|
||||
|
||||
/**
|
||||
* Для каждого свойства...
|
||||
*
|
||||
* @var DBItemProperty $property Свойство.
|
||||
*/
|
||||
foreach ($properties as $property) {
|
||||
// - пропускаю игнорируемые поля
|
||||
if ($property->IsIgnored)
|
||||
continue;
|
||||
|
||||
// - получаю значение имени поля
|
||||
$fieldName = $property->Column->Name;
|
||||
|
||||
// - преобразую тип
|
||||
$value = call_user_func($property->ConvertToDB, $property->Value);
|
||||
|
||||
// - добавляю в массив
|
||||
$result[$fieldName] = $value;
|
||||
}
|
||||
|
||||
// Возвращаю результат
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Восстанавливает объект из БД.
|
||||
*
|
||||
@@ -492,4 +454,20 @@
|
||||
// Возвращаю запрос
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерирует имя таблицы для использования в запросах.
|
||||
*
|
||||
* @param string $table Имя таблицы.
|
||||
*
|
||||
* @return string Готовое имя таблицы для использования в запросах.
|
||||
*/
|
||||
private function PrepareTableName (string $table): string
|
||||
{
|
||||
return match ($this->Config->Driver) {
|
||||
DBDriver::MySQL, DBDriver::SQLite, DBDriver::OracleDB, DBDriver::PostgreSQL => $this->DBSignOpen
|
||||
. $table . $this->DBSignClose,
|
||||
DBDriver::MSSQL => "[dbo].[$table]"
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user