From 95aa7d9411f0c0216a6f62244eb1d6635ee51495 Mon Sep 17 00:00:00 2001 From: babaev-an Date: Wed, 20 Aug 2025 18:09:37 +0300 Subject: [PATCH] 20250820 v1.0.2-b2 --- .gitignore | 2 +- help/basic_usage/count_exist_data.md | 2 +- help/class_desc/classes/Condition.md | 16 +- help/class_desc/classes/ConditionBuilder.md | 15 +- help/class_desc/classes/ConditionGroup.md | 2 +- help/class_desc/enums/DBDriver.md | 60 ++- help/index.md | 4 +- sources/classes/Condition.php | 2 +- sources/classes/ConditionBuilder.php | 2 +- sources/classes/ConditionGroup.php | 2 +- sources/enums/DBDriver.php | 2 +- .../traits/Database/DatabaseCountExist.php | 2 +- sources/traits/Database/DatabaseDelete.php | 7 +- sources/traits/Database/DatabaseGet.php | 31 +- sources/traits/Database/DatabaseInsert.php | 5 +- sources/traits/Database/DatabaseSpecial.php | 36 +- .../Database/DatabaseTableManagement.php | 7 +- sources/traits/Database/DatabaseUpdate.php | 22 +- tests/classes/DatabaseTest.php | 408 ++++++++++++++++++ .../Converters.php | 2 +- .../{secret_not_in_git => data}/UserModel.php | 20 +- tests/database_config_sample.php | 13 + 22 files changed, 594 insertions(+), 68 deletions(-) create mode 100644 tests/classes/DatabaseTest.php rename tests/{secret_not_in_git => data}/Converters.php (98%) rename tests/{secret_not_in_git => data}/UserModel.php (90%) create mode 100644 tests/database_config_sample.php diff --git a/.gitignore b/.gitignore index 217f5dc..4d7719a 100644 --- a/.gitignore +++ b/.gitignore @@ -2233,4 +2233,4 @@ FodyWeavers.xsd /vendor/phpunit/phpunit/src/Framework/Exception/ErrorLogNotWritableException.php /vendor/phpunit/phpunit/src/Runner/ShutdownHandler.php /.idea/codeStyles/ -/tests/secret_not_in_git/ \ No newline at end of file +/tests/database_config.php \ No newline at end of file diff --git a/help/basic_usage/count_exist_data.md b/help/basic_usage/count_exist_data.md index 6298863..4ca62e4 100644 --- a/help/basic_usage/count_exist_data.md +++ b/help/basic_usage/count_exist_data.md @@ -1,4 +1,4 @@ -# Пороверка существования данных и их количество +# Проверка существования данных и их количество Итак, в [прошлой статье](get_data.md) мы получали данные из таблицы `users`. Напомним, что она имеет вид: diff --git a/help/class_desc/classes/Condition.md b/help/class_desc/classes/Condition.md index 5bfd002..4c248c5 100644 --- a/help/class_desc/classes/Condition.md +++ b/help/class_desc/classes/Condition.md @@ -12,7 +12,7 @@ namespace goodboyalex\php_db_components_pack; ## Автор и версия - **Автор**: Александр Бабаев -- **Версия**: 1.0 +- **Версия**: 1.0.2 - **Дата начала поддержки**: с версии 1.0 ## Назначение @@ -70,7 +70,7 @@ $condition = new Condition('age', '>', 18); #### Задание условий с помощью функции (`FUNC:`) -Если в имени колонки передаётся какая-либо функция (начинать такую функцию следует с `FUNC:`), то при обработке она +Если в имени колонки передаётся какая-либо функция (начинать такую функцию следует с `FUNC:`), то при обработке она не обрабатывается как столбец. Это же касается и значения. Например, давайте добавим условие, что каждое новое имя пользователя начинается с заглавной буквы @@ -82,8 +82,10 @@ $condition = new Condition('FUNC:SUBSTR(name, 1, 1)', "=", 'FUNC:UPPER(SUBSTR(na ``` в результате запрос будет содержать: + ```sql -SUBSTR(name, 1, 1) = UPPER(SUBSTR(name, 1, 1) +SUBSTR +(name, 1, 1) = UPPER(SUBSTR(name, 1, 1) ``` ### 2. Метод `Parse` @@ -114,11 +116,12 @@ $parsedCondition = Condition::Parse(['age', '>', 18]); **Синтаксис**: ```php -public function Get(int $index = 0): Tuple; +public function Get(DBDriver $driver, int $index = 0): Tuple; ``` **Описание**: +- **$driver**: Тип драйвера СУБД. - **$index**: Индексация для замены переменных (для предотвращения SQL-инъекций). - Метод возвращает кортеж (tuple), состоящий из двух частей: - SQL-строку с замещаемым параметром (`:paramX`), где X — номер индекса. @@ -127,7 +130,7 @@ public function Get(int $index = 0): Tuple; **Пример использования**: ```php -[$sqlPart, $params] = $condition->Get(); +[$sqlPart, $params] = $condition->Get(\goodboyalex\php_db_components_pack\enums\DBDriver::MySQL); // $sqlPart будет выглядеть примерно так: "`age` > :param0" // $params будет содержать массив с фактическим значением: ["param0" => 18] ``` @@ -136,12 +139,13 @@ public function Get(int $index = 0): Tuple; ```php use goodboyalex\php_db_components_pack\Condition; +use goodboyalex\php_db_components_pack\enums\DBDriver; // Создаём условие для поиска пользователей по возрасту $condition = new Condition('age', '>', 18); // Формируем условие для использования в SQL-запросе -list($sql, $parameters) = $condition->Get(); +[$sql, $parameters] = $condition->Get(DBDriver::MySQL); // Теперь можем использовать $sql и $parameters в запросе $query = "SELECT * FROM users WHERE $sql"; diff --git a/help/class_desc/classes/ConditionBuilder.md b/help/class_desc/classes/ConditionBuilder.md index fb380f0..ea517f8 100644 --- a/help/class_desc/classes/ConditionBuilder.md +++ b/help/class_desc/classes/ConditionBuilder.md @@ -13,7 +13,7 @@ namespace goodboyalex\php_db_components_pack\classes; ## Автор и версия - **Автор**: Александр Бабаев -- **Версия**: 1.0 +- **Версия**: 1.0.2 - **Дата начала поддержки**: с версии 1.0 ## Назначение @@ -125,11 +125,12 @@ $builder = new ConditionBuilder()->AddGroup(Condition::LOGIC_OR, [new Condition( **Синтаксис**: ```php -public function Build(): Tuple; +public function Build(DBDriver $driver): Tuple; ``` **Описание**: +- **$driver**: Тип драйвера СУБД. - Метод возвращает кортеж (Tuple), состоящий из двух элементов: - SQL-строка с условием, готовым к выполнению. - Массив параметров, необходимых для последующей передачи в подготовленный SQL-запрос. @@ -139,7 +140,7 @@ public function Build(): Tuple; ```php // $sql будет (`age` >= :age), а params - [':age' = 18] -[$sql, $params] = new ConditionBuilder()->AddCondition('age', '>=', 18)->Build(); +[$sql, $params] = new ConditionBuilder()->AddCondition('age', '>=', 18)->Build(\goodboyalex\php_db_components_pack\enums\DBDriver::MySQL); ``` --- @@ -317,9 +318,11 @@ public function WhereLike(string $column, string $value): ConditionBuilder; ``` **Описание**: + - **$column**: Имя колонки, по которой проводится поиск. - **$value**: Шаблон для поиска (обычно включает символы `%` для указания маски). -- Метод добавляет условие в виде `WHERE column LIKE '%value%'` и возвращает объект для продолжительного построения условий. +- Метод добавляет условие в виде `WHERE column LIKE '%value%'` и возвращает объект для продолжительного построения + условий. **Пример использования**: @@ -341,10 +344,12 @@ public function AddCondition(string $column, string $operator, mixed $value): Co ``` **Описание**: + - **$column**: Имя колонки, участвующей в условии. - **$operator**: Любой оператор сравнения (например, `=`, `!=`, `<`, `>`, `IN`, `BETWEEN`, и т.д.). - **$value**: Значение, с которым производится сравнение. -- Метод добавляет условие в форме `WHERE column OPERATOR value` и возвращает объект для продолжения построения цепочки условий. +- Метод добавляет условие в форме `WHERE column OPERATOR value` и возвращает объект для продолжения построения цепочки + условий. **Пример использования**: diff --git a/help/class_desc/classes/ConditionGroup.md b/help/class_desc/classes/ConditionGroup.md index 343fa2a..6d7164e 100644 --- a/help/class_desc/classes/ConditionGroup.md +++ b/help/class_desc/classes/ConditionGroup.md @@ -13,7 +13,7 @@ namespace goodboyalex\php_db_components_pack\classes; ## Автор и версия - **Автор**: Александр Бабаев -- **Версия**: 1.0 +- **Версия**: 1.0.2 - **Дата начала поддержки**: с версии 1.0 ## Назначение diff --git a/help/class_desc/enums/DBDriver.md b/help/class_desc/enums/DBDriver.md index f2fb88d..6a9f535 100644 --- a/help/class_desc/enums/DBDriver.md +++ b/help/class_desc/enums/DBDriver.md @@ -6,7 +6,7 @@ ## Основная информация - **Автор**: Александр Бабаев -- **Версия**: 1.0 +- **Версия**: 1.0.2 - **Дата начала поддержки**: с версии 1.0 ## Пространство имен и зависимости @@ -27,6 +27,64 @@ use goodboyalex\php_components_pack\traits\EnumExtensionsTrait; | `OracleDB` | `3` | Драйвер Oracle | | `SQLite` | `4` | Драйвер SQLite | +## Методы + +### Метод `GetSigns` + +Метод `GetSigns` предназначен для получения знаков, которыми принято заключать имена полей и таблиц в различных системах +управления базами данных (СУБД). Некоторые СУБД используют двойные кавычки `"`, квадратные скобки `[]`, обратные +апострофы `` ` `` или другие специальные символы для экранирования идентификаторов (имен таблиц, колонок и других +объектов базы данных). + +#### Синтаксис + +```php +public static function GetSigns(DBDriver $driver): Tuple +``` + +#### Аргументы + +- **$driver**: Переменная типа `DBDriver`, указывающая на используемую СУБД (например, MySQL, PostgreSQL, SQLite и + т.д.). + +#### Возвращаемое значение + +Метод возвращает кортеж `Tuple`, содержащий два элемента: + +- Первый элемент — знак открывающий. +- Второй элемент — знак закрывающий. + +Например, для `MySQL` и `PostgreSQL` чаще всего возвращаются символы обратной косой черты и прямой (```"``, ```"``) +соответственно, а для `MS SQL Server` — квадратные скобки (`[`, `]`). + +#### Пример использования + +Допустим, у вас есть код, который выводит SQL-запрос с экранированием идентификатора в зависимости от используемой СУБД: + +```php +use goodboyalex\php_db_components_pack\enums\DBDriver; + +// Получаем знаки для MySQL +[$open, $close] = DBDriver::GetSigns(DBDriver::MYSQL); + +// Использование полученных знаков +$sql = sprintf("SELECT %s%s%s FROM users;", $open, 'username', $close); +``` + +В результате для MySQL получится: + +```sql +SELECT `username` +FROM users; +``` + +Аналогично, для PostgreSQL: + +```sql +SELECT "username" +FROM users; +``` + ## Особенности реализации Используется trait `EnumExtensionsTrait`, предоставляющий дополнительные методы для работы с перечислениями, такие как diff --git a/help/index.md b/help/index.md index e92b35c..ed4d990 100644 --- a/help/index.md +++ b/help/index.md @@ -1,6 +1,6 @@ # Добро пожаловать в справочное руководство по компонентам PHP DB COMPONENTS PACK! -**Руководство актуально для версии v1.0** +**Руководство актуально для версии v1.0.2** **Автор: Александр Бабаев** @@ -12,7 +12,7 @@ 2. [x] [Создание таблицы](basic_usage/create_table.md) 3. [x] [Вставка данных](basic_usage/insert_data.md) 4. [x] [Получение данных](basic_usage/get_data.md) -5. [x] [Пороверка существования данных и их количество](basic_usage/count_exist_data.md) +5. [x] [Проверка существования данных и их количество](basic_usage/count_exist_data.md) 6. [x] [Обновление данных](basic_usage/update_data.md) 7. [x] [Удаление данных](basic_usage/delete_data.md) 8. [x] [Удаление таблицы](basic_usage/drop_table.md) diff --git a/sources/classes/Condition.php b/sources/classes/Condition.php index e22ac1b..a3ffdd0 100644 --- a/sources/classes/Condition.php +++ b/sources/classes/Condition.php @@ -13,7 +13,7 @@ * * @author Александр Бабаев * @package php_db_components_pack - * @version 1.0 + * @version 1.0.2 * @since 1.0 */ final class Condition implements IArrayable diff --git a/sources/classes/ConditionBuilder.php b/sources/classes/ConditionBuilder.php index 30b6748..a1f0fe0 100644 --- a/sources/classes/ConditionBuilder.php +++ b/sources/classes/ConditionBuilder.php @@ -15,7 +15,7 @@ * * @author Александр Бабаев * @package php_components_pack - * @version 1.0 + * @version 1.0.2 * @since 1.0 */ final class ConditionBuilder implements IArrayable diff --git a/sources/classes/ConditionGroup.php b/sources/classes/ConditionGroup.php index 8d393d8..4d0ebd2 100644 --- a/sources/classes/ConditionGroup.php +++ b/sources/classes/ConditionGroup.php @@ -16,7 +16,7 @@ * * @author Александр Бабаев * @package php_components_pack - * @version 1.0 + * @version 1.0.2 * @since 1.0 */ final class ConditionGroup implements IArrayable diff --git a/sources/enums/DBDriver.php b/sources/enums/DBDriver.php index 243805e..85f9dd9 100644 --- a/sources/enums/DBDriver.php +++ b/sources/enums/DBDriver.php @@ -10,7 +10,7 @@ * * @author Александр Бабаев * @package php_db_components_pack - * @version 1.0 + * @version 1.0.2 * @since 1.0 * @see \PDO */ diff --git a/sources/traits/Database/DatabaseCountExist.php b/sources/traits/Database/DatabaseCountExist.php index 3f98fdd..2dda8ce 100644 --- a/sources/traits/Database/DatabaseCountExist.php +++ b/sources/traits/Database/DatabaseCountExist.php @@ -36,7 +36,7 @@ * @var string $sql_where Строка WHERE условий. * @var array $params Параметры условий. */ - [$sql_where, $params] = $where->Build(); + [$sql_where, $params] = $where->Build($this->Config->Driver); // Подготавливаю имя таблицы $table = $this->PrepareTableName($table); diff --git a/sources/traits/Database/DatabaseDelete.php b/sources/traits/Database/DatabaseDelete.php index a2164ad..174380a 100644 --- a/sources/traits/Database/DatabaseDelete.php +++ b/sources/traits/Database/DatabaseDelete.php @@ -29,16 +29,19 @@ */ public function Delete (string $table, ConditionBuilder $where = new ConditionBuilder()): bool { + // Подготавливаю имя таблицы + $table = $this->PrepareTableName($table); + /** * Получаю SQL-запрос и параметры для where. * * @var string $sql_where SQL-запрос для where. * @var array $params Параметры запроса. */ - [$sql_where, $params] = $where->Build(); + [$sql_where, $params] = $where->Build($this->Config->Driver); // Создаю запрос - $sql = "DELETE FROM $this->DBSignOpen$table$this->DBSignClose"; + $sql = "DELETE FROM $table"; // Если заданы where-параметры if ($where->Count() > 0) diff --git a/sources/traits/Database/DatabaseGet.php b/sources/traits/Database/DatabaseGet.php index e18b4d8..1933621 100644 --- a/sources/traits/Database/DatabaseGet.php +++ b/sources/traits/Database/DatabaseGet.php @@ -7,6 +7,7 @@ use goodboyalex\php_components_pack\classes\ObjectArray; 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\interfaces\IDBItem; use PDO; @@ -33,22 +34,22 @@ * @return IDBItem|false Заполненный объект класса или false в случае ошибки. */ public function GetRow (string $table, array $columns = [], ConditionBuilder $where = new ConditionBuilder(), - string $className = "\\StdClass"): IDBItem|false + string $className = "\\StdClass"): IDBItem | false { // Задаю массив параметров $params = []; + // Подготавливаю имя таблицы + $table = $this->PrepareTableName($table); + // Формируем SQL-запрос $sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params); - // Добавляю лимит - $sql .= " LIMIT 1"; - // Получаю строку - $row = $this->Query($sql, $params); + $row = $this->QueryFirst($sql, $params); // Если не получено - if ($row === false) + if ($row === false || count($row) == 0) // - то вывожу false return false; @@ -75,11 +76,14 @@ * @return false|ObjectArray Массив найденных классов или false в случае ошибки. */ public function GetRows (string $table, array $columns = [], ConditionBuilder $where = new ConditionBuilder(), - string $className = "\\StdClass"): false|ObjectArray + string $className = "\\StdClass"): false | ObjectArray { // Задаю массив параметров $params = []; + // Подготавливаю имя таблицы + $table = $this->PrepareTableName($table); + // Получаю SQL запрос $sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params); @@ -124,18 +128,23 @@ * @see Query */ public function GetCol (string $table, string $column, ConditionBuilder $where = new ConditionBuilder()): - false|array - { + false | array { /** * Интерпретирую условия. * * @var string $sql_where Строка запроса. * @var array $params Массив параметров строки запроса. */ - [$sql_where, $params] = $where->Build(); + [$sql_where, $params] = $where->Build($this->Config->Driver); + + // Получаю знаки открытия параметра и закрытия его + [$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver); + + // Подготавливаю имя таблицы + $table = $this->PrepareTableName($table); // Создаю запрос - $sql = "SELECT $this->DBSignOpen$column$this->DBSignClose FROM $this->DBSignOpen$table$this->DBSignClose"; + $sql = "SELECT $signOpen$column$signClose FROM $table"; // Если заданы where-параметры if ($where->Count() > 0) diff --git a/sources/traits/Database/DatabaseInsert.php b/sources/traits/Database/DatabaseInsert.php index 92ae7d9..831f4d7 100644 --- a/sources/traits/Database/DatabaseInsert.php +++ b/sources/traits/Database/DatabaseInsert.php @@ -119,6 +119,9 @@ // Подготавливаю массив параметров $params = []; + // Получаю знаки открытия параметра и закрытия его + [$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver); + // Получаю массив свойств $properties = self::GetProperties($row, DBOperation::Insert); @@ -157,7 +160,7 @@ $keyReal = $key[0] == ":" ? substr($key, 1) : $key; // - добавляю в результирующий массив ключей - $keys_params[] = "$this->DBSignOpen$keyReal$this->DBSignClose"; + $keys_params[] = "$signOpen$keyReal$signClose"; // - добавляю в результирующий массив значений $keys_values[] = ":$keyReal"; diff --git a/sources/traits/Database/DatabaseSpecial.php b/sources/traits/Database/DatabaseSpecial.php index 6659386..c2557cf 100644 --- a/sources/traits/Database/DatabaseSpecial.php +++ b/sources/traits/Database/DatabaseSpecial.php @@ -291,12 +291,12 @@ * @param string $className Имя класса объекта. * @param DBOperation $operation Операция. * - * @return object|null Экземпляр класса объекта или null, если ошибка. + * @return IDBItem|null Экземпляр класса объекта или null, если ошибка. */ - private function RestoreItem (array $row, string $className, DBOperation $operation): ?object + private function RestoreItem (array $row, string $className, DBOperation $operation): ?IDBItem { // Если целевой класс не реализует интерфейс IDBItem - if (in_array(IDBItem::class, class_implements($className))) + if (!is_a($className, IDBItem::class, true)) // - то прерываем и возвращаем null return null; @@ -385,8 +385,12 @@ */ private function PrepareColumn (array $columns): array { + // Получаю знаки открытия параметра и закрытия его + [$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver); + + // Возвращаю результат return array_map( - function ($item) + function ($item) use ($signOpen, $signClose) { // Результирующая строка $result = ""; @@ -399,18 +403,18 @@ // - последний символ $lastLetter = substr($item, -1); - // - если первый символ не $this->DBSignOpen - if ($firstLetter !== $this->DBSignOpen) + // - если первый символ не $signOpen + if ($firstLetter !== $signOpen) // -- то добавляем - $result .= $this->DBSignOpen; + $result .= $signOpen; // - добавляем строку $result .= $item; - // - если последний символ не $this->DBSignClose - if ($lastLetter !== $this->DBSignClose) + // - если последний символ не $signClose + if ($lastLetter !== $signClose) // -- то добавляем - $result .= $this->DBSignClose; + $result .= $signClose; } // Возвращаем результат @@ -438,13 +442,13 @@ * @var string $sql_where where-запрос SQL * @var array $params Параметры и их значения (для защиты от SQL-инъекции) */ - [$sql_where, $params] = $whereConditions->Build(); + [$sql_where, $params] = $whereConditions->Build($this->Config->Driver); // Колонки $sql_columns = count($columns) > 0 ? implode(', ', $this->PrepareColumn($columns)) : "*"; // Создаю запрос - $sql = "SELECT $sql_columns FROM $this->DBSignOpen$table$this->DBSignClose"; + $sql = "SELECT $sql_columns FROM $table"; // Если заданы where-параметры if ($whereConditions->Count() > 0) @@ -464,9 +468,13 @@ */ private function PrepareTableName (string $table): string { + // Получаю знаки открытия параметра и закрытия его + [$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver); + + // Возвращаю результат return match ($this->Config->Driver) { - DBDriver::MySQL, DBDriver::SQLite, DBDriver::OracleDB, DBDriver::PostgreSQL => $this->DBSignOpen - . $table . $this->DBSignClose, + DBDriver::MySQL, DBDriver::SQLite, DBDriver::OracleDB, DBDriver::PostgreSQL => + "$signOpen$table$signClose", DBDriver::MSSQL => "[dbo].[$table]" }; } diff --git a/sources/traits/Database/DatabaseTableManagement.php b/sources/traits/Database/DatabaseTableManagement.php index f405ade..70e7f7c 100644 --- a/sources/traits/Database/DatabaseTableManagement.php +++ b/sources/traits/Database/DatabaseTableManagement.php @@ -135,8 +135,11 @@ // - то возвращаем true return true; + // Обрабатываю имя таблицы + $tableName = $this->PrepareTableName($tableName); + // SQL-запрос - $sql = "DROP TABLE dbo.`$tableName`"; + $sql = "DROP TABLE $tableName"; // Инициализирую транзакцию $this->InitTransaction(); @@ -169,6 +172,6 @@ } // Возвращаю, существует ли теперь таблица - return $this->IsTableExist($tableName); + return !$this->IsTableExist($tableName); } } \ No newline at end of file diff --git a/sources/traits/Database/DatabaseUpdate.php b/sources/traits/Database/DatabaseUpdate.php index a216f04..65bcb3b 100644 --- a/sources/traits/Database/DatabaseUpdate.php +++ b/sources/traits/Database/DatabaseUpdate.php @@ -7,6 +7,7 @@ use Exception; 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\interfaces\IDBItem; use goodboyalex\php_db_components_pack\models\DBItemProperty; @@ -33,6 +34,9 @@ */ public function Update (string $table, IDBItem $item): bool { + // Получаю знаки открытия параметра и закрытия его + [$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver); + // Получаю свойства объекта $properties = self::GetProperties($item, DBOperation::Update); @@ -124,14 +128,19 @@ // Для каждого свойства для обновления foreach ($propertyToSet as $key => $value) { // - добавляю его в массив set - $set[] = "$this->DBSignOpen$key$this->DBSignClose=:$key"; + $set[] = "$key=:$key";//"$signOpen$key$signClose=:$key"; // - добавляю его в массив параметров set для обновления $set_params[":$key"] = $value; } - // Строковая интерпретация массива set - $sql_set = count($set) > 0 ? '(' . implode(", ", $set) . ')' : ""; + // Если нечего изменять + if (count($set) == 0) + // - то прерываем с успехом + return true; + + // Устанавливаю параметры обновления + $sql_set = implode(", ", $set); /** * Получаю WHERE-запрос и параметры. @@ -139,13 +148,16 @@ * @var string $sql_where WHERE-запрос. * @var array $where_params Параметры WHERE-запроса. */ - [$sql_where, $where_params] = $where->Build(); + [$sql_where, $where_params] = $where->Build($this->Config->Driver); // Объединяю все параметры в один массив $params = array_merge($set_params, $where_params); + // Подготавливаю имя таблицы + $table = $this->PrepareTableName($table); + // Создаю запрос - $sql = "UPDATE $this->DBSignOpen$table$this->DBSignClose SET $sql_set"; + $sql = "UPDATE $table SET $sql_set"; // Если заданы where-параметры if ($where->Count() > 0) diff --git a/tests/classes/DatabaseTest.php b/tests/classes/DatabaseTest.php new file mode 100644 index 0000000..060a7d1 --- /dev/null +++ b/tests/classes/DatabaseTest.php @@ -0,0 +1,408 @@ + $terminate + ? die($exception->getMessage()) + : print $exception->getMessage(); + + // Подключаем базу данных + $db = new Database($testConfig, $onException); + + /* ТЕСТ 1: Создание таблицы */ + + // Задаём имя таблицы + $tableName = 'users'; + + // Создаю таблицу + $db->CreateTable( + tableName: $tableName, + dbItemClass: '\goodboyalex\php_db_components_pack\tests\data\UserModel' + ); + + // Проверяю создание + $this->assertTrue($db->IsTableExist($tableName)); + + /* 2. Добавление данных */ + + /* 2.1. Модели */ + + // Модель 1 + $model1 = new UserModel( + id: GUID::Parse('8b5dab25-4445-436c-8f25-0f5352cb8500', true), + login: "account1", + password: "password1", + salt: GUID_EMPTY, + firstName: "firstName1", + middleName: "middleName1", + lastName: "lastName1", + email: "email1@ya.ru", + isEmailVerified: false, + groupName: "default", + createdAt: time(), + updatedAt: 0, + userData: ['key1' => 'value1', 'key2' => 'value2'] + ); + + // Модель 2 + $model2 = new UserModel( + id: GUID::Parse('3552914c-9ffc-4c73-8946-97c40b0d81b5'), + login: "account2", + password: "password2", + salt: GUID_EMPTY, + firstName: "firstName2", + middleName: "middleName2", + lastName: "lastName2", + email: "email2@ya.ru", + isEmailVerified: false, + groupName: "default", + createdAt: time(), + updatedAt: 0, + userData: ['key1' => 'value3', 'key2' => 4] + ); + + // Модель 3 + $model3 = new UserModel( + id: GUID::Parse('73828df6-89a3-4feb-ae9a-ef79d0e67cb0'), + login: "account3", + password: "password3", + salt: GUID_EMPTY, + firstName: "firstName3", + middleName: "middleName3", + lastName: "lastName3", + email: "email3@ya.ru", + isEmailVerified: true, + groupName: "default1", + createdAt: time(), + updatedAt: time(), + userData: ['key1' => 1, 'key2' => 2] + ); + + // Модель 4 + $model4 = new UserModel( + id: GUID::Parse('df39f53f-faff-42cf-bf00-f5855c3e11ec'), + login: "account4", + password: "password4", + salt: GUID_EMPTY, + firstName: "firstName4", + middleName: "middleName4", + lastName: "lastName4", + email: "email4@ya.ru", + isEmailVerified: true, + groupName: "default1", + createdAt: time(), + updatedAt: time(), + userData: ['key1' => 3, 'key2' => '2'] + ); + + /* 2.2. Добавление 1 элемента */ + // Вставляю $model1 + $result = $db->Insert($tableName, $model1); + + // Проверяю, успешно ли прошло + $this->assertTrue($result !== false); + + // - и совпадает ли идентификатор + $this->assertTrue($model1->Id->IsEqualsTo($result)); + + /* 2.3. Добавление более 1 элемента */ + // Массив ожидаемых результатов + $expectedResult = [ + GUID::Parse('3552914c-9ffc-4c73-8946-97c40b0d81b5'), + GUID::Parse('73828df6-89a3-4feb-ae9a-ef79d0e67cb0'), + GUID::Parse('df39f53f-faff-42cf-bf00-f5855c3e11ec') + ]; + + // Вставляю несколько + $result = $db->InsertMany('users', $model2, $model3, $model4); + + // Убеждаюсь, что вставка успешна + $this->assertTrue($result !== false); + + // - и все идентификаторы совпадают + $this->assertEqualsCanonicalizing($expectedResult, $result); + } + + public function testGetRow () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + if (!$db->IsTableExist('users')) + die('Таблица не существует. Сперва запустите тест testCreateTable / Table does not exist. Run testCreateTable first'); + + if (!$db->IsExist( + table: 'users', + where: new ConditionBuilder()->WhereEquals('login', 'account1') + )) + die('Записи с логином account1 не существует в таблице users / Record with login account1 is not exists in table users'); + + /** + * @var false|UserModel $model + */ + $model = $db->GetRow( + table: 'users', + where: new ConditionBuilder()->WhereEquals('login', 'account1'), + className: '\goodboyalex\php_db_components_pack\tests\data\UserModel' + ); + + $this->assertTrue($model !== false); + $this->assertEquals('account1', $model->Login); + $this->assertEquals( + 'account1', $model->Id->IsEqualsTo(GUID::Parse('8b5dab25-4445-436c-8f25-0f5352cb8500')) + ); + } + + public function testGetCol () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + if (!$db->IsTableExist('users')) + die('Таблица не существует. Сперва запустите тест testCreateTable / Table does not exist. Run testCreateTable first'); + + if (!$db->IsExist( + table: 'users', + where: new ConditionBuilder() + ->WhereEquals('login', 'account2') + ->Or() + ->WhereEquals('login', 'account3') + ->Or() + ->WhereEquals('login', 'account4') + )) + die('Записи с логином account1 не существует в таблице users / Record with login account1 is not exists in table users'); + + $models = $db->GetCol( + table: 'users', + column: 'id', + where: new ConditionBuilder()->WhereNotEquals('login', 'account1') + ); + + $this->assertTrue($models !== false); + + $expected = [ + "3552914c-9ffc-4c73-8946-97c40b0d81b5", + "73828df6-89a3-4feb-ae9a-ef79d0e67cb0", + "df39f53f-faff-42cf-bf00-f5855c3e11ec" + ]; + + $this->assertEqualsCanonicalizing($expected, $models); + } + + public function testGetRows () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + if (!$db->IsTableExist('users')) + die('Таблица не существует. Сперва запустите тест testCreateTable / Table does not exist. Run testCreateTable first'); + + if (!$db->IsExist( + table: 'users', + where: new ConditionBuilder() + ->WhereEquals('login', 'account2') + ->Or() + ->WhereEquals('login', 'account3') + ->Or() + ->WhereEquals('login', 'account4') + )) + die('Записи с логином account1 не существует в таблице users / Record with login account1 is not exists in table users'); + + /** + * @var false|ObjectArray $model + */ + $models = $db->GetRows( + table: 'users', + where: new ConditionBuilder()->WhereNotEquals('login', 'account1'), + className: '\goodboyalex\php_db_components_pack\tests\data\UserModel' + ); + + $this->assertTrue($models !== false); + $this->assertTrue($models->IsExist(fn (UserModel $model) => $model->Login == 'account2')); + } + + public function testDropTable () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + $result = $db->DropTable('users'); + + $this->assertTrue($result); + } + + public function testGetValue () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + if (!$db->IsTableExist('users')) + die('Таблица не существует. Сперва запустите тест testCreateTable / Table does not exist. Run testCreateTable first'); + + if (!$db->IsExist( + table: 'users', + where: new ConditionBuilder() + ->WhereEquals('login', 'account2') + ->Or() + ->WhereEquals('login', 'account3') + ->Or() + ->WhereEquals('login', 'account4') + )) + die('Записи с логином account1 не существует в таблице users / Record with login account1 is not exists in table users'); + + $id = $db->GetValue( + table: 'users', + column: 'id', + where: new ConditionBuilder()->WhereEquals('login', 'account2') + ); + + $this->assertEquals("3552914c-9ffc-4c73-8946-97c40b0d81b5", $id); + } + + public function testUpdate () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + if (!$db->IsTableExist('users')) + die('Таблица не существует. Сперва запустите тест testCreateTable / Table does not exist. Run testCreateTable first'); + + if (!$db->IsExist( + table: 'users', + where: new ConditionBuilder()->WhereEquals('login', 'account1') + )) + die('Запись с логином account1 не существует в таблице users. Сперва удалите запись с логином account1 (можно использовать метод testDelete) / Record with login account1 already exists in table users. Delete record with login account1 first'); + + $model = $db->GetRow( + 'users', + where: new ConditionBuilder()->WhereEquals('login', 'account1'), + className: '\goodboyalex\php_db_components_pack\tests\data\UserModel' + ); + + $this->assertTrue($model !== false); + + $model->Email = 'account1@yandex.ru'; + + $result = $db->Update('users', $model); + + $this->assertTrue($result); + } + + public function testUpdateMany () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + $model1 = $db->GetRow( + 'users', + where: new ConditionBuilder()->WhereEquals('login', 'account2'), + className: '\goodboyalex\php_db_components_pack\tests\data\UserModel' + ); + + $this->assertTrue($model1 !== false); + + $model1->Email = 'account2@yandex.ru'; + $model1->IsEmailVerified = true; + + $model2 = $db->GetRow( + 'users', + where: new ConditionBuilder()->WhereEquals('login', 'account3'), + className: '\goodboyalex\php_db_components_pack\tests\data\UserModel' + ); + + $this->assertTrue($model2 !== false); + + + $model2->Email = 'account3@yandex.ru'; + $model2->IsEmailVerified = true; + + $model3 = $db->GetRow( + 'users', + where: new ConditionBuilder()->WhereEquals('login', 'account4'), + className: '\goodboyalex\php_db_components_pack\tests\data\UserModel' + ); + + $this->assertTrue($model3 !== false); + + $model3->Email = 'account4@yandex.ru'; + $model3->IsEmailVerified = true; + + + $result = $db->UpdateMany('users', $model1, $model2, $model3); + + $this->assertTrue($result); + } + + public function testDelete () + { + $this->IncludeReq(); + + $db = $this->GetDatabase(); + + $result = $db->Delete('users', new ConditionBuilder()->WhereEquals('email_verified', 0)); + + $this->assertTrue($result); + + $count = $db->Count('users'); + $this->assertEquals(3, $count); + + $result = $db->Delete('users'); + + $this->assertTrue($result); + + $count = $db->Count('users'); + $this->assertEquals(0, $count); + } + + private function IncludeReq (): void {} + + private function GetDatabase (): Database {} + } \ No newline at end of file diff --git a/tests/secret_not_in_git/Converters.php b/tests/data/Converters.php similarity index 98% rename from tests/secret_not_in_git/Converters.php rename to tests/data/Converters.php index 74b319c..5c11776 100644 --- a/tests/secret_not_in_git/Converters.php +++ b/tests/data/Converters.php @@ -1,6 +1,6 @@