8 Commits

Author SHA1 Message Date
2d9fceb0d6 20250820 1.0.2 2025-08-20 21:19:52 +03:00
95aa7d9411 20250820 v1.0.2-b2 2025-08-20 18:09:37 +03:00
cc3b1ef41b 20250819 2025-08-19 23:38:52 +03:00
5074629e40 20250818-1 2025-08-18 23:51:45 +03:00
21b664bdfc Очистка данных 2025-08-18 23:17:12 +03:00
770f826e7e 20250818 2025-08-18 23:12:21 +03:00
f20d82a6c5 20250818 2025-08-18 23:00:17 +03:00
2cf87d86d2 20250818 2025-08-18 22:55:30 +03:00
24 changed files with 6004 additions and 223 deletions

3
.gitignore vendored
View File

@@ -2233,5 +2233,4 @@ FodyWeavers.xsd
/vendor/phpunit/phpunit/src/Framework/Exception/ErrorLogNotWritableException.php /vendor/phpunit/phpunit/src/Framework/Exception/ErrorLogNotWritableException.php
/vendor/phpunit/phpunit/src/Runner/ShutdownHandler.php /vendor/phpunit/phpunit/src/Runner/ShutdownHandler.php
/.idea/codeStyles/ /.idea/codeStyles/
/tests/database_config.php
/tests/secret_not_in_git/

4989
git-filter-repo Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
# Пороверка существования данных и их количество # Проверка существования данных и их количество
Итак, в [прошлой статье](get_data.md) мы получали данные из таблицы `users`. Напомним, что она имеет вид: Итак, в [прошлой статье](get_data.md) мы получали данные из таблицы `users`. Напомним, что она имеет вид:

View File

@@ -12,7 +12,7 @@ namespace goodboyalex\php_db_components_pack;
## Автор и версия ## Автор и версия
- **Автор**: Александр Бабаев - **Автор**: Александр Бабаев
- **Версия**: 1.0 - **Версия**: 1.0.2
- **Дата начала поддержки**: с версии 1.0 - **Дата начала поддержки**: с версии 1.0
## Назначение ## Назначение
@@ -70,7 +70,7 @@ $condition = new Condition('age', '>', 18);
#### Задание условий с помощью функции (`FUNC:`) #### Задание условий с помощью функции (`FUNC:`)
Если в имени колонки передаётся какая-либо функция (начинать такую функцию следует с `FUNC:`), то при обработке она Если в имени колонки передаётся какая-либо функция (начинать такую функцию следует с `FUNC:`), то при обработке она
не обрабатывается как столбец. Это же касается и значения. не обрабатывается как столбец. Это же касается и значения.
Например, давайте добавим условие, что каждое новое имя пользователя начинается с заглавной буквы Например, давайте добавим условие, что каждое новое имя пользователя начинается с заглавной буквы
@@ -82,8 +82,10 @@ $condition = new Condition('FUNC:SUBSTR(name, 1, 1)', "=", 'FUNC:UPPER(SUBSTR(na
``` ```
в результате запрос будет содержать: в результате запрос будет содержать:
```sql ```sql
SUBSTR(name, 1, 1) = UPPER(SUBSTR(name, 1, 1) SUBSTR
(name, 1, 1) = UPPER(SUBSTR(name, 1, 1)
``` ```
### 2. Метод `Parse` ### 2. Метод `Parse`
@@ -114,11 +116,12 @@ $parsedCondition = Condition::Parse(['age', '>', 18]);
**Синтаксис**: **Синтаксис**:
```php ```php
public function Get(int $index = 0): Tuple; public function Get(DBDriver $driver, int $index = 0): Tuple;
``` ```
**Описание**: **Описание**:
- **$driver**: Тип драйвера СУБД.
- **$index**: Индексация для замены переменных (для предотвращения SQL-инъекций). - **$index**: Индексация для замены переменных (для предотвращения SQL-инъекций).
- Метод возвращает кортеж (tuple), состоящий из двух частей: - Метод возвращает кортеж (tuple), состоящий из двух частей:
- SQL-строку с замещаемым параметром (`:paramX`), где X — номер индекса. - SQL-строку с замещаемым параметром (`:paramX`), где X — номер индекса.
@@ -127,7 +130,7 @@ public function Get(int $index = 0): Tuple;
**Пример использования**: **Пример использования**:
```php ```php
[$sqlPart, $params] = $condition->Get(); [$sqlPart, $params] = $condition->Get(\goodboyalex\php_db_components_pack\enums\DBDriver::MySQL);
// $sqlPart будет выглядеть примерно так: "`age` > :param0" // $sqlPart будет выглядеть примерно так: "`age` > :param0"
// $params будет содержать массив с фактическим значением: ["param0" => 18] // $params будет содержать массив с фактическим значением: ["param0" => 18]
``` ```
@@ -136,12 +139,13 @@ public function Get(int $index = 0): Tuple;
```php ```php
use goodboyalex\php_db_components_pack\Condition; use goodboyalex\php_db_components_pack\Condition;
use goodboyalex\php_db_components_pack\enums\DBDriver;
// Создаём условие для поиска пользователей по возрасту // Создаём условие для поиска пользователей по возрасту
$condition = new Condition('age', '>', 18); $condition = new Condition('age', '>', 18);
// Формируем условие для использования в SQL-запросе // Формируем условие для использования в SQL-запросе
list($sql, $parameters) = $condition->Get(); [$sql, $parameters] = $condition->Get(DBDriver::MySQL);
// Теперь можем использовать $sql и $parameters в запросе // Теперь можем использовать $sql и $parameters в запросе
$query = "SELECT * FROM users WHERE $sql"; $query = "SELECT * FROM users WHERE $sql";

View File

@@ -13,7 +13,7 @@ namespace goodboyalex\php_db_components_pack\classes;
## Автор и версия ## Автор и версия
- **Автор**: Александр Бабаев - **Автор**: Александр Бабаев
- **Версия**: 1.0 - **Версия**: 1.0.2
- **Дата начала поддержки**: с версии 1.0 - **Дата начала поддержки**: с версии 1.0
## Назначение ## Назначение
@@ -125,11 +125,12 @@ $builder = new ConditionBuilder()->AddGroup(Condition::LOGIC_OR, [new Condition(
**Синтаксис**: **Синтаксис**:
```php ```php
public function Build(): Tuple; public function Build(DBDriver $driver): Tuple;
``` ```
**Описание**: **Описание**:
- **$driver**: Тип драйвера СУБД.
- Метод возвращает кортеж (Tuple), состоящий из двух элементов: - Метод возвращает кортеж (Tuple), состоящий из двух элементов:
- SQL-строка с условием, готовым к выполнению. - SQL-строка с условием, готовым к выполнению.
- Массив параметров, необходимых для последующей передачи в подготовленный SQL-запрос. - Массив параметров, необходимых для последующей передачи в подготовленный SQL-запрос.
@@ -139,7 +140,7 @@ public function Build(): Tuple;
```php ```php
// $sql будет (`age` >= :age), а params - [':age' = 18] // $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**: Имя колонки, по которой проводится поиск. - **$column**: Имя колонки, по которой проводится поиск.
- **$value**: Шаблон для поиска (обычно включает символы `%` для указания маски). - **$value**: Шаблон для поиска (обычно включает символы `%` для указания маски).
- Метод добавляет условие в виде `WHERE column LIKE '%value%'` и возвращает объект для продолжительного построения условий. - Метод добавляет условие в виде `WHERE column LIKE '%value%'` и возвращает объект для продолжительного построения
условий.
**Пример использования**: **Пример использования**:
@@ -341,10 +344,12 @@ public function AddCondition(string $column, string $operator, mixed $value): Co
``` ```
**Описание**: **Описание**:
- **$column**: Имя колонки, участвующей в условии. - **$column**: Имя колонки, участвующей в условии.
- **$operator**: Любой оператор сравнения (например, `=`, `!=`, `<`, `>`, `IN`, `BETWEEN`, и т.д.). - **$operator**: Любой оператор сравнения (например, `=`, `!=`, `<`, `>`, `IN`, `BETWEEN`, и т.д.).
- **$value**: Значение, с которым производится сравнение. - **$value**: Значение, с которым производится сравнение.
- Метод добавляет условие в форме `WHERE column OPERATOR value` и возвращает объект для продолжения построения цепочки условий. - Метод добавляет условие в форме `WHERE column OPERATOR value` и возвращает объект для продолжения построения цепочки
условий.
**Пример использования**: **Пример использования**:

View File

@@ -13,7 +13,7 @@ namespace goodboyalex\php_db_components_pack\classes;
## Автор и версия ## Автор и версия
- **Автор**: Александр Бабаев - **Автор**: Александр Бабаев
- **Версия**: 1.0 - **Версия**: 1.0.2
- **Дата начала поддержки**: с версии 1.0 - **Дата начала поддержки**: с версии 1.0
## Назначение ## Назначение

View File

@@ -6,7 +6,7 @@
## Основная информация ## Основная информация
- **Автор**: Александр Бабаев - **Автор**: Александр Бабаев
- **Версия**: 1.0 - **Версия**: 1.0.2
- **Дата начала поддержки**: с версии 1.0 - **Дата начала поддержки**: с версии 1.0
## Пространство имен и зависимости ## Пространство имен и зависимости
@@ -27,6 +27,64 @@ use goodboyalex\php_components_pack\traits\EnumExtensionsTrait;
| `OracleDB` | `3` | Драйвер Oracle | | `OracleDB` | `3` | Драйвер Oracle |
| `SQLite` | `4` | Драйвер SQLite | | `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`, предоставляющий дополнительные методы для работы с перечислениями, такие как Используется trait `EnumExtensionsTrait`, предоставляющий дополнительные методы для работы с перечислениями, такие как

View File

@@ -1,6 +1,6 @@
# Добро пожаловать в справочное руководство по компонентам PHP DB COMPONENTS PACK! # Добро пожаловать в справочное руководство по компонентам PHP DB COMPONENTS PACK!
**Руководство актуально для версии v1.0** **Руководство актуально для версии v1.0.2**
**Автор: Александр Бабаев** **Автор: Александр Бабаев**
@@ -12,7 +12,7 @@
2. [x] [Создание таблицы](basic_usage/create_table.md) 2. [x] [Создание таблицы](basic_usage/create_table.md)
3. [x] [Вставка данных](basic_usage/insert_data.md) 3. [x] [Вставка данных](basic_usage/insert_data.md)
4. [x] [Получение данных](basic_usage/get_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) 6. [x] [Обновление данных](basic_usage/update_data.md)
7. [x] [Удаление данных](basic_usage/delete_data.md) 7. [x] [Удаление данных](basic_usage/delete_data.md)
8. [x] [Удаление таблицы](basic_usage/drop_table.md) 8. [x] [Удаление таблицы](basic_usage/drop_table.md)

View File

@@ -5,6 +5,7 @@
use goodboyalex\php_components_pack\classes\Tuple; use goodboyalex\php_components_pack\classes\Tuple;
use goodboyalex\php_components_pack\extensions\StringExtension; use goodboyalex\php_components_pack\extensions\StringExtension;
use goodboyalex\php_components_pack\interfaces\IArrayable; use goodboyalex\php_components_pack\interfaces\IArrayable;
use goodboyalex\php_db_components_pack\enums\DBDriver;
use PHPUnit\Event\InvalidArgumentException; use PHPUnit\Event\InvalidArgumentException;
/** /**
@@ -12,7 +13,7 @@
* *
* @author Александр Бабаев * @author Александр Бабаев
* @package php_db_components_pack * @package php_db_components_pack
* @version 1.0 * @version 1.0.2
* @since 1.0 * @since 1.0
*/ */
final class Condition implements IArrayable final class Condition implements IArrayable
@@ -101,15 +102,19 @@
/** /**
* Формирует условие. * Формирует условие.
* *
* @param DBDriver $driver Тип драйвера СУБД.
* @param int $index Индекс замены параметров для защиты от SQL-инъекций. * @param int $index Индекс замены параметров для защиты от SQL-инъекций.
* *
* @return Tuple (string, array) Сформированное условие: 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
$sql = (!str_starts_with($this->ColumnName, "FUNC:")) $sql = (!str_starts_with($this->ColumnName, "FUNC:"))
? "`$this->ColumnName`" ? "$signOpen$this->ColumnName$signClose"
: StringExtension::Replace('FUNC:', '', $this->ColumnName); : StringExtension::Replace('FUNC:', '', $this->ColumnName);
// Добавляю оператор // Добавляю оператор

View File

@@ -5,6 +5,7 @@
use goodboyalex\php_components_pack\classes\Tuple; use goodboyalex\php_components_pack\classes\Tuple;
use goodboyalex\php_components_pack\extensions\ArrayExtension; use goodboyalex\php_components_pack\extensions\ArrayExtension;
use goodboyalex\php_components_pack\interfaces\IArrayable; 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\ConditionBuilderConditionsSet;
use goodboyalex\php_db_components_pack\traits\ConditionBuilder\ConditionBuilderLogicalOperationSet; use goodboyalex\php_db_components_pack\traits\ConditionBuilder\ConditionBuilderLogicalOperationSet;
use InvalidArgumentException; use InvalidArgumentException;
@@ -14,7 +15,7 @@
* *
* @author Александр Бабаев * @author Александр Бабаев
* @package php_components_pack * @package php_components_pack
* @version 1.0 * @version 1.0.2
* @since 1.0 * @since 1.0
*/ */
final class ConditionBuilder implements IArrayable final class ConditionBuilder implements IArrayable
@@ -64,7 +65,9 @@
// --- если ошибка // --- если ошибка
if ($builder === false) if ($builder === false)
// ---- то выбрасываем исключение // ---- то выбрасываем исключение
throw new InvalidArgumentException("Неверный логический оператор: $condition / The logical operator is invalid: $condition."); throw new InvalidArgumentException(
"Неверный логический оператор: $condition / The logical operator is invalid: $condition."
);
// --- идём к следующему элементу // --- идём к следующему элементу
continue; continue;
@@ -93,7 +96,7 @@
* @return false|string Возвращает правильно отформатированный оператор или <code>false</code>, если оператор * @return false|string Возвращает правильно отформатированный оператор или <code>false</code>, если оператор
* не является логическим. * не является логическим.
*/ */
private static function PrepareLogicalOperator (string $operator): false|string private static function PrepareLogicalOperator (string $operator): false | string
{ {
// Задаем массив логических операторов // Задаем массив логических операторов
$logicalOperators = ['AND', 'OR', 'NOT', 'XOR', 'NAND', 'NOR']; $logicalOperators = ['AND', 'OR', 'NOT', 'XOR', 'NAND', 'NOR'];
@@ -178,6 +181,81 @@
return new ConditionGroup($operator, $conditionItems); 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-инъекций. * Собирает условие в виде пригодном для SQL. Также возвращает массив параметров защиты от SQL-инъекций.
* *
* @param DBDriver $driver Тип драйвера СУБД.
*
* @return Tuple (string, array) Возвращает условие в виде, пригодном для SQL и массив параметров для защиты от * @return Tuple (string, array) Возвращает условие в виде, пригодном для SQL и массив параметров для защиты от
* SQL-инъекций. * SQL-инъекций.
*/ */
public function Build (): Tuple public function Build (DBDriver $driver): Tuple
{ {
// Очищаем цепочку от пустых элементов // Очищаем цепочку от пустых элементов
ArrayExtension::RemoveEmpties($this->Conditions); ArrayExtension::RemoveEmpties($this->Conditions);
// Парсим цепочку и возвращаем результат // Парсим цепочку и возвращаем результат
return $this->ProcessConditions($this->Conditions); return self::ProcessConditions($driver, $this->Conditions);
} }
/** /**
@@ -337,78 +417,4 @@
// Возвращаем объект // Возвращаем объект
return $this; 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);
}
} }

View File

@@ -8,6 +8,7 @@
use goodboyalex\php_components_pack\classes\Tuple; use goodboyalex\php_components_pack\classes\Tuple;
use goodboyalex\php_components_pack\interfaces\IArrayable; use goodboyalex\php_components_pack\interfaces\IArrayable;
use goodboyalex\php_db_components_pack\enums\DBDriver;
use InvalidArgumentException; use InvalidArgumentException;
/** /**
@@ -15,7 +16,7 @@
* *
* @author Александр Бабаев * @author Александр Бабаев
* @package php_components_pack * @package php_components_pack
* @version 1.0 * @version 1.0.2
* @since 1.0 * @since 1.0
*/ */
final class ConditionGroup implements IArrayable final class ConditionGroup implements IArrayable
@@ -45,11 +46,12 @@
/** /**
* Формирует массив условий. * Формирует массив условий.
* *
* @param DBDriver $driver Тип драйвера СУБД.
* @param int $index Индекс замены параметров для защиты от SQL-инъекций. * @param int $index Индекс замены параметров для защиты от SQL-инъекций.
* *
* @return Tuple (string, array) Массив условий (строка SQL, параметры SQL). * @return Tuple (string, array) Массив условий (строка SQL, параметры SQL).
*/ */
public function GetConditions (int $index = 0): Tuple public function GetConditions (DBDriver $driver, int $index = 0): Tuple
{ {
// Создаём результирующую строку // Создаём результирующую строку
$resultString = ""; $resultString = "";
@@ -66,7 +68,7 @@
$count++; $count++;
// - получаем условие // - получаем условие
$result = $this->WriteCondition($this->Conditions[$i], $count); $result = $this->WriteCondition($driver, $this->Conditions[$i], $count);
// - записываем условие в строку // - записываем условие в строку
$resultString .= $result->Get(0); $resultString .= $result->Get(0);
@@ -182,22 +184,23 @@
/** /**
* Формирует условие. * Формирует условие.
* *
* @param DBDriver $driver Тип драйвера СУБД.
* @param mixed $condition Условие. * @param mixed $condition Условие.
* @param int $index Индекс замены параметров для защиты от SQL-инъекций. * @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 // Проверяем, является ли условие объектом класса Condition
if ($condition instanceof Condition) if ($condition instanceof Condition)
// - если да, то возвращаем его значение // - если да, то возвращаем его значение
return $condition->Get($index); return $condition->Get($driver, $index);
// Проверяем, является ли условие объектом класса ConditionGroup // Проверяем, является ли условие объектом класса ConditionGroup
if ($condition instanceof ConditionGroup) if ($condition instanceof ConditionGroup)
// - если да, то возвращаем его значения // - если да, то возвращаем его значения
return $condition->GetConditions($index); return $condition->GetConditions($driver, $index);
// Если условие не является ни классом Condition, ни классом ConditionGroup, то это ошибка. Выбрасываем // Если условие не является ни классом Condition, ни классом ConditionGroup, то это ошибка. Выбрасываем
// исключение. // исключение.

View File

@@ -36,16 +36,6 @@
*/ */
private ?PDO $DataBaseHandle; private ?PDO $DataBaseHandle;
/**
* @var string $DBSignOpen Символ открытия для подстановки в запросы к базе.
*/
private string $DBSignOpen;
/**
* @var string $DBSignСlose Символ закрытия для подстановки в запросы к базе.
*/
private string $DBSignClose;
/** /**
* @var Closure $OnException Обработчик исключений. Анонимная функция формата: * @var Closure $OnException Обработчик исключений. Анонимная функция формата:
* *
@@ -96,21 +86,6 @@
DBDriver::SQLite => "sqlite:$dbname" 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); $this->DataBaseHandle = new PDO($dsn, username: $user, password: $password);

View File

@@ -2,6 +2,7 @@
namespace goodboyalex\php_db_components_pack\enums; namespace goodboyalex\php_db_components_pack\enums;
use goodboyalex\php_components_pack\classes\Tuple;
use goodboyalex\php_components_pack\traits\EnumExtensionsTrait; use goodboyalex\php_components_pack\traits\EnumExtensionsTrait;
/** /**
@@ -9,7 +10,7 @@
* *
* @author Александр Бабаев * @author Александр Бабаев
* @package php_db_components_pack * @package php_db_components_pack
* @version 1.0 * @version 1.0.2
* @since 1.0 * @since 1.0
* @see \PDO * @see \PDO
*/ */
@@ -42,4 +43,20 @@
* SQLite * SQLite
*/ */
case SQLite = 4; 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('"', '"'),
};
}
} }

View File

@@ -36,10 +36,13 @@
* @var string $sql_where Строка WHERE условий. * @var string $sql_where Строка WHERE условий.
* @var array $params Параметры условий. * @var array $params Параметры условий.
*/ */
[$sql_where, $params] = $where->Build(); [$sql_where, $params] = $where->Build($this->Config->Driver);
// Подготавливаю имя таблицы
$table = $this->PrepareTableName($table);
// Создаю запрос // Создаю запрос
$sql = "SELECT COUNT(*) FROM $this->DBSignOpen$table$this->DBSignClose"; $sql = "SELECT COUNT(*) FROM $table";
// Если заданы where-параметры // Если заданы where-параметры
if ($where->Count() > 0) if ($where->Count() > 0)

View File

@@ -29,16 +29,19 @@
*/ */
public function Delete (string $table, ConditionBuilder $where = new ConditionBuilder()): bool public function Delete (string $table, ConditionBuilder $where = new ConditionBuilder()): bool
{ {
// Подготавливаю имя таблицы
$table = $this->PrepareTableName($table);
/** /**
* Получаю SQL-запрос и параметры для where. * Получаю SQL-запрос и параметры для where.
* *
* @var string $sql_where SQL-запрос для where. * @var string $sql_where SQL-запрос для where.
* @var array $params Параметры запроса. * @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-параметры // Если заданы where-параметры
if ($where->Count() > 0) if ($where->Count() > 0)

View File

@@ -7,6 +7,7 @@
use goodboyalex\php_components_pack\classes\ObjectArray; use goodboyalex\php_components_pack\classes\ObjectArray;
use goodboyalex\php_db_components_pack\classes\ConditionBuilder; 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\DBOperation;
use goodboyalex\php_db_components_pack\interfaces\IDBItem; use goodboyalex\php_db_components_pack\interfaces\IDBItem;
use PDO; use PDO;
@@ -33,22 +34,22 @@
* @return IDBItem|false Заполненный объект класса или <code>false</code> в случае ошибки. * @return IDBItem|false Заполненный объект класса или <code>false</code> в случае ошибки.
*/ */
public function GetRow (string $table, array $columns = [], ConditionBuilder $where = new ConditionBuilder(), public function GetRow (string $table, array $columns = [], ConditionBuilder $where = new ConditionBuilder(),
string $className = "\\StdClass"): IDBItem|false string $className = "\\StdClass"): IDBItem | false
{ {
// Задаю массив параметров // Задаю массив параметров
$params = []; $params = [];
// Подготавливаю имя таблицы
$table = $this->PrepareTableName($table);
// Формируем SQL-запрос // Формируем SQL-запрос
$sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params); $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 // - то вывожу false
return false; return false;
@@ -75,11 +76,14 @@
* @return false|ObjectArray Массив найденных классов или <code>false</code> в случае ошибки. * @return false|ObjectArray Массив найденных классов или <code>false</code> в случае ошибки.
*/ */
public function GetRows (string $table, array $columns = [], ConditionBuilder $where = new ConditionBuilder(), public function GetRows (string $table, array $columns = [], ConditionBuilder $where = new ConditionBuilder(),
string $className = "\\StdClass"): false|ObjectArray string $className = "\\StdClass"): false | ObjectArray
{ {
// Задаю массив параметров // Задаю массив параметров
$params = []; $params = [];
// Подготавливаю имя таблицы
$table = $this->PrepareTableName($table);
// Получаю SQL запрос // Получаю SQL запрос
$sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params); $sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params);
@@ -124,18 +128,23 @@
* @see Query * @see Query
*/ */
public function GetCol (string $table, string $column, ConditionBuilder $where = new ConditionBuilder()): public function GetCol (string $table, string $column, ConditionBuilder $where = new ConditionBuilder()):
false|array false | array {
{
/** /**
* Интерпретирую условия. * Интерпретирую условия.
* *
* @var string $sql_where Строка запроса. * @var string $sql_where Строка запроса.
* @var array $params Массив параметров строки запроса. * @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-параметры // Если заданы where-параметры
if ($where->Count() > 0) if ($where->Count() > 0)

View File

@@ -11,6 +11,7 @@
use goodboyalex\php_db_components_pack\enums\DBDriver; use goodboyalex\php_db_components_pack\enums\DBDriver;
use goodboyalex\php_db_components_pack\enums\DBOperation; use goodboyalex\php_db_components_pack\enums\DBOperation;
use goodboyalex\php_db_components_pack\interfaces\IDBItem; use goodboyalex\php_db_components_pack\interfaces\IDBItem;
use goodboyalex\php_db_components_pack\models\DBItemProperty;
use PDO; use PDO;
/** /**
@@ -36,7 +37,7 @@
public function Insert (string $table, IDBItem $row): mixed 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); $count = $this->Execute($sql, $params);
@@ -48,7 +49,7 @@
// Задаю переменную для последнего id // Задаю переменную для последнего id
$lastId = match ($this->Config->Driver) { $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::MySQL, DBDriver::SQLite => $this->DataBaseHandle->lastInsertId(),
DBDriver::PostgreSQL, DBDriver::OracleDB => $this->DataBaseHandle->lastInsertId('sequence_name') DBDriver::PostgreSQL, DBDriver::OracleDB => $this->DataBaseHandle->lastInsertId('sequence_name')
}; };
@@ -58,6 +59,11 @@
// - то вывожу просто true // - то вывожу просто true
$lastId = true; $lastId = true;
// Если id не генерировался
if ($lastId === null)
// - то вывожу -1
$lastId = $primaryKeyValue !== "NULL" ? $primaryKeyValue : true;
// Вывожу последний id // Вывожу последний id
return $lastId; return $lastId;
} }
@@ -70,7 +76,7 @@
* *
* @return false|array Возвращает массив id созданных записей и <code>false</code>, если ошибка. * @return false|array Возвращает массив id созданных записей и <code>false</code>, если ошибка.
*/ */
public function InsertMany (string $table, IDBItem ...$sources): false|array public function InsertMany (string $table, IDBItem ...$sources): false | array
{ {
// Инициализирую транзакцию // Инициализирую транзакцию
$this->InitTransaction(); $this->InitTransaction();
@@ -106,41 +112,86 @@
* @param string $table Имя таблицы. * @param string $table Имя таблицы.
* @param IDBItem $row Элемент. * @param IDBItem $row Элемент.
* *
* @return Tuple Возвращает [запрос, параметры запроса]. * @return Tuple Возвращает [запрос, параметры запроса, значение первичного ключа].
*/ */
private function PrepareInsertSQL (string $table, IDBItem $row): Tuple private function PrepareInsertSQL (string $table, IDBItem $row): Tuple
{ {
// Подготавливаю массив параметров // Подготавливаю массив параметров
$params = $this->PrepareParamsArray(source: $row, operation: DBOperation::Insert); $params = [];
// Получаю знаки открытия параметра и закрытия его
[$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver);
// Получаю массив свойств
$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); $keys = array_keys($params);
// Создаю результирующий массив имён ключей параметров // Создаю результирующий массив имён ключей параметров
$keysReal = []; $keys_params = [];
// Создаю результирующий массив значений параметров
$keys_values = [];
// Для каждого ключа параметра // Для каждого ключа параметра
foreach ($keys as $key) { foreach ($keys as $key) {
// - получаю его имя // - получаю его имя
$keyResult = $key[0] == ":" ? substr($key, 1) : $key; $keyReal = $key[0] == ":" ? substr($key, 1) : $key;
// - заключаю в кавычки
$keyResult = "$this->DBSignOpen$keyResult$this->DBSignClose";
// - добавляю в результирующий массив ключей // - добавляю в результирующий массив ключей
$keysReal[] = $keyResult; $keys_params[] = "$signOpen$keyReal$signClose";
// - добавляю в результирующий массив значений
$keys_values[] = ":$keyReal";
} }
// Ключи sql запроса // Ключи sql запроса
$sql_keys = implode(', ', $keysReal); $sql_keys = implode(', ', $keys_params);
// Значения sql запроса // Значения sql запроса
$sql_values = implode(', ', $keys); $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);
} }
} }

View File

@@ -23,6 +23,7 @@
use goodboyalex\php_db_components_pack\attributes\PrimaryKey; use goodboyalex\php_db_components_pack\attributes\PrimaryKey;
use goodboyalex\php_db_components_pack\attributes\Unique; use goodboyalex\php_db_components_pack\attributes\Unique;
use goodboyalex\php_db_components_pack\classes\ConditionBuilder; 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\DBOperation;
use goodboyalex\php_db_components_pack\enums\DBType; use goodboyalex\php_db_components_pack\enums\DBType;
use goodboyalex\php_db_components_pack\interfaces\IDBItem; use goodboyalex\php_db_components_pack\interfaces\IDBItem;
@@ -244,8 +245,13 @@
// - создаю объект свойства // - создаю объект свойства
$item = new DBItemProperty( $item = new DBItemProperty(
$key, $value, $columnHeader, $isIgnore, $converterToDB, name: $key,
$converterFromDB, $compareFunc value: $source->$key,
column: $columnHeader,
isIgnored: $isIgnore,
ConvertToDB: $converterToDB,
ConvertFromDB: $converterFromDB,
Compare: $compareFunc
); );
// - добавляю в массив // - добавляю в массив
@@ -278,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;
}
/** /**
* Восстанавливает объект из БД. * Восстанавливает объект из БД.
* *
@@ -324,12 +291,12 @@
* @param string $className Имя класса объекта. * @param string $className Имя класса объекта.
* @param DBOperation $operation Операция. * @param DBOperation $operation Операция.
* *
* @return object|null Экземпляр класса объекта или <code>null</code>, если ошибка. * @return IDBItem|null Экземпляр класса объекта или <code>null</code>, если ошибка.
*/ */
private function RestoreItem (array $row, string $className, DBOperation $operation): ?object private function RestoreItem (array $row, string $className, DBOperation $operation): ?IDBItem
{ {
// Если целевой класс не реализует интерфейс IDBItem // Если целевой класс не реализует интерфейс IDBItem
if (in_array(IDBItem::class, class_implements($className))) if (!is_a($className, IDBItem::class, true))
// - то прерываем и возвращаем null // - то прерываем и возвращаем null
return null; return null;
@@ -418,8 +385,12 @@
*/ */
private function PrepareColumn (array $columns): array private function PrepareColumn (array $columns): array
{ {
// Получаю знаки открытия параметра и закрытия его
[$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver);
// Возвращаю результат
return array_map( return array_map(
function ($item) function ($item) use ($signOpen, $signClose)
{ {
// Результирующая строка // Результирующая строка
$result = ""; $result = "";
@@ -432,18 +403,18 @@
// - последний символ // - последний символ
$lastLetter = substr($item, -1); $lastLetter = substr($item, -1);
// - если первый символ не $this->DBSignOpen // - если первый символ не $signOpen
if ($firstLetter !== $this->DBSignOpen) if ($firstLetter !== $signOpen)
// -- то добавляем // -- то добавляем
$result .= $this->DBSignOpen; $result .= $signOpen;
// - добавляем строку // - добавляем строку
$result .= $item; $result .= $item;
// - если последний символ не $this->DBSignClose // - если последний символ не $signClose
if ($lastLetter !== $this->DBSignClose) if ($lastLetter !== $signClose)
// -- то добавляем // -- то добавляем
$result .= $this->DBSignClose; $result .= $signClose;
} }
// Возвращаем результат // Возвращаем результат
@@ -471,13 +442,13 @@
* @var string $sql_where where-запрос SQL * @var string $sql_where where-запрос SQL
* @var array $params Параметры и их значения (для защиты от 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_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-параметры // Если заданы where-параметры
if ($whereConditions->Count() > 0) if ($whereConditions->Count() > 0)
@@ -487,4 +458,24 @@
// Возвращаю запрос // Возвращаю запрос
return $sql; return $sql;
} }
/**
* Генерирует имя таблицы для использования в запросах.
*
* @param string $table Имя таблицы.
*
* @return string Готовое имя таблицы для использования в запросах.
*/
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 =>
"$signOpen$table$signClose",
DBDriver::MSSQL => "[dbo].[$table]"
};
}
} }

View File

@@ -135,8 +135,11 @@
// - то возвращаем true // - то возвращаем true
return true; return true;
// Обрабатываю имя таблицы
$tableName = $this->PrepareTableName($tableName);
// SQL-запрос // SQL-запрос
$sql = "DROP TABLE dbo.`$tableName`"; $sql = "DROP TABLE $tableName";
// Инициализирую транзакцию // Инициализирую транзакцию
$this->InitTransaction(); $this->InitTransaction();
@@ -169,6 +172,6 @@
} }
// Возвращаю, существует ли теперь таблица // Возвращаю, существует ли теперь таблица
return $this->IsTableExist($tableName); return !$this->IsTableExist($tableName);
} }
} }

View File

@@ -7,6 +7,7 @@
use Exception; use Exception;
use goodboyalex\php_db_components_pack\classes\ConditionBuilder; 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\DBOperation;
use goodboyalex\php_db_components_pack\interfaces\IDBItem; use goodboyalex\php_db_components_pack\interfaces\IDBItem;
use goodboyalex\php_db_components_pack\models\DBItemProperty; use goodboyalex\php_db_components_pack\models\DBItemProperty;
@@ -33,6 +34,9 @@
*/ */
public function Update (string $table, IDBItem $item): bool public function Update (string $table, IDBItem $item): bool
{ {
// Получаю знаки открытия параметра и закрытия его
[$signOpen, $signClose] = DBDriver::GetSigns($this->Config->Driver);
// Получаю свойства объекта // Получаю свойства объекта
$properties = self::GetProperties($item, DBOperation::Update); $properties = self::GetProperties($item, DBOperation::Update);
@@ -124,14 +128,19 @@
// Для каждого свойства для обновления // Для каждого свойства для обновления
foreach ($propertyToSet as $key => $value) { foreach ($propertyToSet as $key => $value) {
// - добавляю его в массив set // - добавляю его в массив set
$set[] = "$this->DBSignOpen$key$this->DBSignClose=:$key"; $set[] = "$key=:$key";//"$signOpen$key$signClose=:$key";
// - добавляю его в массив параметров set для обновления // - добавляю его в массив параметров set для обновления
$set_params[":$key"] = $value; $set_params[":$key"] = $value;
} }
// Строковая интерпретация массива set // Если нечего изменять
$sql_set = count($set) > 0 ? '(' . implode(", ", $set) . ')' : ""; if (count($set) == 0)
// - то прерываем с успехом
return true;
// Устанавливаю параметры обновления
$sql_set = implode(", ", $set);
/** /**
* Получаю WHERE-запрос и параметры. * Получаю WHERE-запрос и параметры.
@@ -139,13 +148,16 @@
* @var string $sql_where WHERE-запрос. * @var string $sql_where WHERE-запрос.
* @var array $where_params Параметры 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); $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-параметры // Если заданы where-параметры
if ($where->Count() > 0) if ($where->Count() > 0)

View File

@@ -0,0 +1,310 @@
<?php
namespace goodboyalex\php_db_components_pack\tests\classes;
use Exception;
use goodboyalex\php_components_pack\classes\ObjectArray;
use goodboyalex\php_components_pack\types\GUID;
use goodboyalex\php_db_components_pack\classes\ConditionBuilder;
use goodboyalex\php_db_components_pack\classes\Database;
use goodboyalex\php_db_components_pack\models\DBConfig;
use goodboyalex\php_db_components_pack\tests\data\UserModel;
use PHPUnit\Framework\TestCase;
class DatabaseTest extends TestCase
{
/**
* Тестирование базы данных.
*
* ВНИМАТЕЛЬНО ПРОЧТИТЕ, ПЕРЕД ВЫПОЛНЕНИЕМ ТЕСТА.
*
* Подготовка к тесту:
*
* - создайте тестовую базу данных;
* - создайте пользователя и его пароль;
* - сделайте его создателем с правами управления созданной в п. 1 БД;
* - в корне папки "tests" создайте файл "database_config.php";
* - убедитесь, что он добавлен в .gitignore и НИКАКИМ ОБРАЗОМ НЕ ПОПАДЁТ В РЕПОЗИТАРИЙ (это
* скомпрометирует вшу БД!);
* - в этом файле задайте переменной $testConfig класс настроек с необходимыми настройками или можете
* воспользоваться файлом "database_config_sample.php", переименовав его в "database_config.php".
*
* @return void
*/
public function testDatabase ()
{
/**
* Подключаем настройки базы данных.
*
* @var DBConfig $testConfig Конфигурация СУБД.
*/
require_once "../database_config.php";
// Подключаем данные
require_once '../data/Converters.php';
require_once '../data/UserModel.php';
// Задаём действие при ошибке
$onException = fn (Exception $exception, bool $terminate)
=> $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($tableName, $model2, $model3, $model4);
// Убеждаюсь, что вставка успешна
$this->assertTrue($result !== false);
// - и все идентификаторы совпадают
$this->assertEqualsCanonicalizing($expectedResult, $result);
/* 3. Получение данных. */
/* 3.1. Получение одной записи. */
/**
* Получаю запись.
*
* @var false|UserModel $model Модель записи.
*/
$model = $db->GetRow(
table: $tableName,
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'))
);
/* 3.2. Получение нескольких записей. */
/**
* Получаю записи.
*
* @var false|ObjectArray $model Набор моделей записей.
*/
$models = $db->GetRows(
table: $tableName,
where: new ConditionBuilder()->WhereNotEquals('login', 'account1'),
className: '\goodboyalex\php_db_components_pack\tests\data\UserModel'
);
// Убеждаюсь, что записи получены
$this->assertTrue($models !== false);
// - и запись с логином account2 находится в списке
$this->assertTrue($models->IsExist(fn (UserModel $model) => $model->Login == 'account2'));
/* 3.3. Получение колонки. */
// Получаю колонку id
$models = $db->GetCol(
table: $tableName,
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);
/* 3.4. Получение значения. */
// Получаю значение id
$id = $db->GetValue(
table: $tableName,
column: 'id',
where: new ConditionBuilder()->WhereEquals('login', 'account2')
);
// Убеждаюсь, что значение получено и оно совпадает с ожидаемым
$this->assertEquals("3552914c-9ffc-4c73-8946-97c40b0d81b5", $id);
/* 4. Обновление данных. */
/* 4.1. Обновление одной записи. */
// Обновляю запись model1
$model1->Email = 'account1@yandex.ru';
// Обновляю запись
$result = $db->Update($tableName, $model);
// Убеждаюсь, что запись обновлена
$this->assertTrue($result);
/* 4.2. Обновление нескольких записей. */
// Обновляю записи
$model2->Email = 'account2@yandex.ru';
$model2->IsEmailVerified = true;
$model3->Email = 'account3@yandex.ru';
$model3->IsEmailVerified = true;
$model4->Email = 'account4@yandex.ru';
$model4->IsEmailVerified = true;
// Обновляю записи
$result = $db->UpdateMany($tableName, $model1, $model2, $model3);
// Убеждаюсь, что записи обновлены
$this->assertTrue($result);
/* 5. Удаление данных. */
/* 5.1. Удаление одной записи. */
// Удаляю запись
$result = $db->Delete($tableName, new ConditionBuilder()->WhereEquals('email_verified', 0));
// Убеждаюсь, что запись удалена
$this->assertTrue($result);
// - и количество записей в таблице
$count = $db->Count($tableName);
// - равно 3
$this->assertEquals(3, $count);
/* 5.2. Удаление нескольких записей. */
// Удаляю записи
$result = $db->Delete($tableName);
// Убеждаюсь, что записи удалены
$this->assertTrue($result);
// - и количество записей в таблице
$count = $db->Count($tableName);
// - равно 0
$this->assertEquals(0, $count);
/* 6. Удаление таблицы. */
// Удаляю таблицу
$result = $db->DropTable($tableName);
// Убеждаюсь, что таблица удалена
$this->assertTrue($result);
// - и таблица не существует
$this->assertFalse($db->IsTableExist($tableName));
/* КОНЕЦ ТЕСТА */
}
}

168
tests/data/Converters.php Normal file
View File

@@ -0,0 +1,168 @@
<?php
namespace goodboyalex\php_db_components_pack\tests\data;
use BackedEnum;
use goodboyalex\php_components_pack\types\GUID;
/**
* Класс конвертации для моделей.
*
* @author Александр Бабаев
* @package freecms
* @version 0.1
* @since 0.1
*/
final class Converters
{
/**
* Преобразует GUID в строковое представление.
*
* @param GUID $guid GUID.
*
* @return string GUID в строковом представлении.
*/
public static function GUIDToString (GUID $guid): string
{
return $guid->ToString();
}
/**
* Преобразует строковое представление GUID в объект GUID.
*
* @param string $guid Строковое представление GUID.
*
* @return GUID Преобразует строковое представление GUID в объект GUID или GUID_EMPTY.
*/
public static function GUIDFromString (string $guid): GUID
{
return GUID::Parse($guid, true);
}
/**
* Проверяет равенство двух GUID.
*
* @param GUID $guid1 GUID 1.
* @param GUID $guid2 GUID 2.
*
* @return bool true, если GUID равны, false - иначе.
*/
public static function IsGUIDEqual (GUID $guid1, GUID $guid2): bool
{
return $guid1->IsEqualsTo($guid2);
}
/**
* Преобразует объект перечисления в значение.
*
* @param BackedEnum $enum Перечисление.
*
* @return int Значение перечисления.
*/
public static function BackedEnumToInt (BackedEnum $enum): int
{
return $enum->value;
}
/**
*
* @param int $value Значение перечисления.
*
* @return BackedEnum Перечисление.
*/
public static function BackedEnumFromInt (int $value): BackedEnum
{
return BackedEnum::tryFrom($value);
}
/**
* Сравнивает два значения перечисления.
*
* @param BackedEnum $enum1 Перечисление 1.
* @param BackedEnum $enum2 Перечисление 2.
*
* @return bool true, если значения равны, false - иначе.
*/
public static function IsBackedEnumEqual (BackedEnum $enum1, BackedEnum $enum2): bool
{
return $enum1->value === $enum2->value;
}
/**
* Преобразует логическое значение в целочисленное.
*
* @param bool $boolValue Значение.
*
* @return int Значение 0 или 1.
*/
public static function BoolToInt (bool $boolValue): int
{
return $boolValue ? 1 : 0;
}
/**
* Преобразует целочисленное значение в логическое.
*
* @param int $value Значение 0 или 1.
*
* @return bool Логическое значение.
*/
public static function BoolFromInt (int $value): bool
{
return $value > 0;
}
/**
* Сравнивает два логических значения.
*
* @param bool $bool1 Значение 1.
* @param bool $bool2 Значение 2.
*
* @return bool true, если значения равны, false - иначе.
*/
public static function IsBoolEqual (bool $bool1, bool $bool2): bool
{
return ($bool1 == true && $bool2 == true) || ($bool1 == false && $bool2 == false);
}
/**
* Преобразует массив в строку.
*
* @param array $array Массив.
*
* @return string Сериализованный массив.
*/
public static function ArrayToString (array $array): string
{
return serialize($array);
}
/**
* Преобразует строковое представление массива в массив.
*
* @param string $value Строковое представление массива.
*
* @return array Массив.
*/
public static function ArrayFromString (string $value): array
{
return unserialize($value);
}
/**
* Сравнивает два массива.
*
* @param array $array1 Массив 1.
* @param array $array2 Массив 2.
*
* @return bool true, если значения равны, false - иначе.
*/
public static function IsArrayEqual (array $array1, array $array2): bool
{
// Получаем разницу между массивами
$difference = array_diff($array1, $array2);
// Если разница пуста, значит массивы равны
return empty($difference);
}
}

157
tests/data/UserModel.php Normal file
View File

@@ -0,0 +1,157 @@
<?php
namespace goodboyalex\php_db_components_pack\tests\data;
use goodboyalex\php_components_pack\types\GUID;
use goodboyalex\php_db_components_pack\attributes\Compare;
use goodboyalex\php_db_components_pack\attributes\ConvertToDB;
use goodboyalex\php_db_components_pack\attributes\DataType;
use goodboyalex\php_db_components_pack\attributes\DefaultValue;
use goodboyalex\php_db_components_pack\attributes\FieldName;
use goodboyalex\php_db_components_pack\attributes\NotNull;
use goodboyalex\php_db_components_pack\attributes\PrimaryKey;
use goodboyalex\php_db_components_pack\enums\DBType;
use goodboyalex\php_db_components_pack\interfaces\IDBItem;
/**
* Модель информации о пользователе.
*
* Структура таблицы:
*
* -- id - string(36) - Уникальный идентификатор пользователя (индекс).
* -- login - varchar(50) - Имя пользователя.
* -- password - varchar(255) - Пароль пользователя.
* -- salt - varchar(36) - Ключ шифровки пароля.
* -- first_name - varchar(50) - Имя пользователя.
* -- middle_name - varchar(50) - Отчество пользователя.
* -- last_name - varchar(50) - Фамилия пользователя.
* -- email - varchar(100) - Электронная почта пользователя.
* -- email_verified - TinyInt - Статус подтверждения электронной почты.
* -- group - varchar(50) - Имя группы пользователя.
* -- date_created - bigint - Временной штамп даты создания пользователя.
* -- date_updated - bigint - Временной штамп даты обновления пользователя.
* -- data - LONGTEXT - NULL - Массив разных данных о пользователе.
*
* @author Александр Бабаев
* @package php_db_components_pack
* @version 1.0
* @since 1.0.2
*/
final class UserModel implements IDBItem
{
/**
* @var GUID $Id Идентификатор пользователя
*/
#[PrimaryKey, NotNull, DataType(DBType::STRING, 36), ConvertToDB(
fromType: ['\goodboyalex\php_db_components_pack\tests\data\Converters', 'GUIDToString'],
toType: [
'\goodboyalex\php_db_components_pack\tests\data\Converters', 'GUIDFromString'
]), Compare(['\goodboyalex\php_db_components_pack\tests\data\Converters', 'IsGUIDEqual']),
FieldName('id'), DefaultValue(GUID_EMPTY)]
public GUID $Id;
/**
* @var string $Login Логин пользователя (на английском языке)
*/
#[NotNull, FieldName('login'), DataType(DBType::STRING, 50), DefaultValue("")]
public string $Login = "";
/**
* @var string $Password Пароль пользователя
*/
#[NotNull, FieldName('password'), DataType(DBType::STRING, 255), DefaultValue("")]
public string $Password = '';
/**
* @var string $Salt Кодовое слово для пароля (обычно Guid)
*/
#[NotNull, FieldName('salt'), DataType(DBType::STRING, 36), DefaultValue(GUID_EMPTY)]
public string $Salt = '';
/**
* @var string $FirstName Имя пользователя
*/
#[NotNull, FieldName('first_name'), DataType(DBType::STRING, 50), DefaultValue("")]
public string $FirstName = "";
/**
* @var string $MiddleName Отчество пользователя
*/
#[NotNull, FieldName('middle_name'), DataType(DBType::STRING, 50), DefaultValue("")]
public string $MiddleName = "";
/**
* @var string $LastName Фамилия пользователя
*/
#[NotNull, FieldName('last_name'), DataType(DBType::STRING, 50), DefaultValue("")]
public string $LastName = "";
/**
* @var string $Email Электронная почта пользователя
*/
#[NotNull, FieldName('email'), DataType(DBType::STRING, 100), DefaultValue("")]
public string $Email = "";
/**
* @var bool $IsEmailVerified Статус подтверждения электронной почты
*/
#[NotNull, FieldName('email_verified'), DataType(DBType::INT), ConvertToDB(
fromType: ['\goodboyalex\php_db_components_pack\tests\data\Converters', 'BoolToInt'],
toType: ['\goodboyalex\php_db_components_pack\tests\data\Converters', 'BoolFromInt']),
Compare([
'\goodboyalex\php_db_components_pack\tests\data\Converters', 'IsBoolEqual'
]), DefaultValue(0)]
public bool $IsEmailVerified = false;
/**
* @var string $GroupName Имя группы пользователя
*/
#[NotNull, FieldName('group'), DataType(DBType::STRING, 50), DefaultValue('')]
public string $GroupName = '';
/**
* @var int $CreatedAt Штамп даты создания пользователя
*/
#[NotNull, FieldName('date_created'), DataType(DBType::INT), DefaultValue(0)]
public int $CreatedAt = 0;
/**
* @var int $ModifiedAt Штамп даты изменения пользователя
*/
#[NotNull, FieldName('date_updated'), DataType(DBType::INT), DefaultValue(0)]
public int $ModifiedAt = 0;
/**
* @var array $UserData Массив разных данных о пользователе
*/
#[NotNull, FieldName('data'), DataType(DBType::STRING, 0), ConvertToDB(
fromType: ['\goodboyalex\php_db_components_pack\tests\data\Converters', 'ArrayToString'],
toType: ['\goodboyalex\php_db_components_pack\tests\data\Converters', 'ArrayFromString']),
Compare([
'\goodboyalex\php_db_components_pack\tests\data\Converters', 'IsArrayEqual'
]), DefaultValue("[]")]
public array $UserData = [];
/**
* Конструктор.
*/
public function __construct (GUID $id = new GUID(), string $login = "", string $password = "",
string $salt = "", string $firstName = "", string $middleName = "", string $lastName = "",
string $email = "", bool $isEmailVerified = false, string $groupName = "", int $createdAt = 0,
int $updatedAt = 0, array $userData = [])
{
$this->Id = $id;
$this->Login = $login;
$this->Password = $password;
$this->Salt = $salt;
$this->FirstName = $firstName;
$this->MiddleName = $middleName;
$this->LastName = $lastName;
$this->Email = $email;
$this->IsEmailVerified = $isEmailVerified;
$this->GroupName = $groupName;
$this->CreatedAt = $createdAt;
$this->ModifiedAt = $updatedAt;
$this->UserData = $userData;
}
}

View File

@@ -0,0 +1,13 @@
<?php
use goodboyalex\php_db_components_pack\enums\DBDriver;
use goodboyalex\php_db_components_pack\models\DBConfig;
$testConfig = new DBConfig(
driver: DBDriver::MySQL, # ВЫБЕРЕТЕ НУЖНЫЙ ДРАЙВЕР
host: 'ХОСТ БАЗЫ ДАННЫХ',
port: 0, # ПОРТ
name: 'ИМЯ БАЗЫ ДАННЫХ',
userName: "ПОЛЬЗОВАТЕЛЬ БАЗЫ ДАННЫХ",
password: 'ПАРОЛЬ ПОЛЬЗОВАТЕЛЯ'
);