20250802
This commit is contained in:
parent
906ed15c58
commit
4ec8ccc089
1
.gitignore
vendored
1
.gitignore
vendored
@ -2220,3 +2220,4 @@ FodyWeavers.xsd
|
|||||||
/composer.lock
|
/composer.lock
|
||||||
/vendor/goodboyalex/php_components_pack/
|
/vendor/goodboyalex/php_components_pack/
|
||||||
/.idea/php-test-framework.xml
|
/.idea/php-test-framework.xml
|
||||||
|
/.idea/inspectionProfiles/Project_Default.xml
|
||||||
|
@ -194,9 +194,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Собирает условие в виде строки.
|
* Собирает условие в виде пригодном для SQL. Также возвращает массив параметров защиты от SQL-инъекций.
|
||||||
*
|
*
|
||||||
* @return string Возвращает условие в виде, пригодном для SQL.
|
* @return Tuple (string, array) Возвращает условие в виде, пригодном для SQL и массив параметров для защиты от
|
||||||
|
* SQL-инъекций.
|
||||||
*/
|
*/
|
||||||
public function Build (): Tuple
|
public function Build (): Tuple
|
||||||
{
|
{
|
||||||
|
@ -9,11 +9,13 @@
|
|||||||
use Closure;
|
use Closure;
|
||||||
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
||||||
use goodboyalex\php_db_components_pack\models\DBConfig;
|
use goodboyalex\php_db_components_pack\models\DBConfig;
|
||||||
|
use goodboyalex\php_db_components_pack\traits\Database\DatabaseCountExist;
|
||||||
use goodboyalex\php_db_components_pack\traits\Database\DatabaseGet;
|
use goodboyalex\php_db_components_pack\traits\Database\DatabaseGet;
|
||||||
use goodboyalex\php_db_components_pack\traits\Database\DatabaseInsert;
|
use goodboyalex\php_db_components_pack\traits\Database\DatabaseInsert;
|
||||||
use goodboyalex\php_db_components_pack\traits\Database\DatabaseQueryExecute;
|
use goodboyalex\php_db_components_pack\traits\Database\DatabaseQueryExecute;
|
||||||
use goodboyalex\php_db_components_pack\traits\Database\DatabaseSpecial;
|
use goodboyalex\php_db_components_pack\traits\Database\DatabaseSpecial;
|
||||||
use goodboyalex\php_db_components_pack\traits\Database\DatabaseTransactions;
|
use goodboyalex\php_db_components_pack\traits\Database\DatabaseTransactions;
|
||||||
|
use goodboyalex\php_db_components_pack\traits\Database\DatabaseUpdate;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
|
|
||||||
@ -23,7 +25,7 @@
|
|||||||
* Используется класс PDO для подключения к базе данных.
|
* Используется класс PDO для подключения к базе данных.
|
||||||
*
|
*
|
||||||
* @author Александр Бабаев
|
* @author Александр Бабаев
|
||||||
* @package php_components_pack
|
* @package php_db_components_pack
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @see PDO
|
* @see PDO
|
||||||
@ -47,7 +49,9 @@
|
|||||||
private string $DBSignClose;
|
private string $DBSignClose;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Closure $OnException Обработчик исключений.
|
* @var Closure $OnException Обработчик исключений. Анонимная функция формата:
|
||||||
|
*
|
||||||
|
* function (Exception $e, bool $isTerminate): void
|
||||||
*/
|
*/
|
||||||
private Closure $OnException;
|
private Closure $OnException;
|
||||||
|
|
||||||
@ -57,10 +61,12 @@
|
|||||||
private DBConfig $Config;
|
private DBConfig $Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Конструктор. Подключает базу данных
|
* Конструктор.
|
||||||
*
|
*
|
||||||
* @param DBConfig $config Конфигурация подключения к базе данных.
|
* @param DBConfig $config Конфигурация подключения к базе данных.
|
||||||
* @param callable $onException Обработчик исключений.
|
* @param callable $onException Обработчик исключений. Анонимная функция формата:
|
||||||
|
*
|
||||||
|
* function (Exception $e, bool $isTerminate): void
|
||||||
*/
|
*/
|
||||||
public function __construct (DBConfig $config, callable $onException)
|
public function __construct (DBConfig $config, callable $onException)
|
||||||
{
|
{
|
||||||
@ -126,40 +132,6 @@
|
|||||||
$this->DataBaseHandle = null;
|
$this->DataBaseHandle = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Получает набор строк в массиве данных, удовлетворяющий выборке
|
|
||||||
*
|
|
||||||
* @param string $table Имя таблицы
|
|
||||||
* @param array $columns Колонки, которые нужно включить в запрос
|
|
||||||
* @param array $where Параметры выборки
|
|
||||||
*
|
|
||||||
* @return false|array Строка в формате массива или false в случае ошибки
|
|
||||||
*
|
|
||||||
* @see Query
|
|
||||||
* @see QueryFirst
|
|
||||||
* @see QueryLast
|
|
||||||
* @see GetRow
|
|
||||||
*/
|
|
||||||
public function GetRows (string $table, array $columns = [], array $where = []): false|array
|
|
||||||
{
|
|
||||||
// Задаю массив параметров
|
|
||||||
$params = [];
|
|
||||||
|
|
||||||
// Получаю SQL запрос
|
|
||||||
$sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params);
|
|
||||||
|
|
||||||
// Получаю строки на основании запроса
|
|
||||||
$queryResult = $this->Query($sql, $params);
|
|
||||||
|
|
||||||
// Если строки не получены
|
|
||||||
if ($queryResult === false)
|
|
||||||
// - то выдаю ошибку
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Получаю значение строк
|
|
||||||
return $queryResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Транзакции
|
// Транзакции
|
||||||
use DatabaseTransactions;
|
use DatabaseTransactions;
|
||||||
|
|
||||||
@ -172,220 +144,15 @@
|
|||||||
// Получение данных
|
// Получение данных
|
||||||
use DatabaseGet;
|
use DatabaseGet;
|
||||||
|
|
||||||
|
// Проверка существования и получение количества строк
|
||||||
|
use DatabaseCountExist;
|
||||||
|
|
||||||
|
// Обновление записей
|
||||||
|
use DatabaseUpdate;
|
||||||
|
|
||||||
// Приватные методы
|
// Приватные методы
|
||||||
use DatabaseSpecial;
|
use DatabaseSpecial;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Получает колонку в массиве данных
|
|
||||||
*
|
|
||||||
* @param string $table Имя таблицы
|
|
||||||
* @param string $column Имя колонки
|
|
||||||
* @param array $where Параметры запроса
|
|
||||||
*
|
|
||||||
* @return false|array Ассоциированный массив с результатом запроса или false в случае ошибки
|
|
||||||
*
|
|
||||||
* @see Query
|
|
||||||
*/
|
|
||||||
public function GetCol (string $table, string $column, array $where = []): false|array
|
|
||||||
{
|
|
||||||
// Задаю параметры
|
|
||||||
$params = [];
|
|
||||||
|
|
||||||
// Строковая интерпретация массива условий
|
|
||||||
$sql_where = $this->PrepareQueryWhere($where, $params);
|
|
||||||
|
|
||||||
// Создаю запрос
|
|
||||||
$sql = "SELECT $this->DBSignOpen$column$this->DBSignClose FROM $this->DBSignOpen$table$this->DBSignClose";
|
|
||||||
|
|
||||||
// Если заданы where-параметры
|
|
||||||
if (count($where) > 0) {
|
|
||||||
// - то добавляю их
|
|
||||||
$sql .= " WHERE $sql_where";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаю столбец на основании запроса
|
|
||||||
$queryResult = $this->Query($sql, $params);
|
|
||||||
|
|
||||||
// Если строка не получена или пуста
|
|
||||||
if ($queryResult === false)
|
|
||||||
// - то выдаю ошибку
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Создаю результат
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
// Для каждого результата запроса
|
|
||||||
foreach ($queryResult as $row)
|
|
||||||
// - передаю его в результат
|
|
||||||
$result[] = $row[$column];
|
|
||||||
|
|
||||||
// Вывожу результат
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Получение значение единичного поля
|
|
||||||
*
|
|
||||||
* @param string $table Имя таблицы
|
|
||||||
* @param string $column Требуемый столбец
|
|
||||||
* @param array $where Параметры запроса
|
|
||||||
*
|
|
||||||
* @return mixed|null Результат запроса или null в случае ошибки
|
|
||||||
*/
|
|
||||||
public function GetValue (string $table, string $column, array $where = []): mixed
|
|
||||||
{
|
|
||||||
// Задаю параметры
|
|
||||||
$params = [];
|
|
||||||
|
|
||||||
// Строковая интерпретация массива условий
|
|
||||||
$sql_where = $this->PrepareQueryWhere($where, $params);
|
|
||||||
|
|
||||||
// Создаю запрос
|
|
||||||
$sql = "SELECT $this->DBSignOpen$column$this->DBSignClose FROM $this->DBSignOpen$table$this->DBSignClose";
|
|
||||||
|
|
||||||
// Если заданы where-параметры
|
|
||||||
if (count($where) > 0) {
|
|
||||||
// - то добавляю их
|
|
||||||
$sql .= " WHERE $sql_where";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаю строку на основании запроса
|
|
||||||
$queryResult = $this->QueryScalar($sql, $params);
|
|
||||||
|
|
||||||
// Если строка не получена или пуста
|
|
||||||
if ($queryResult === false || count($queryResult) == 0)
|
|
||||||
// - то выдаю результат null
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Получаю значение колонки
|
|
||||||
return $queryResult[$column];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Заменяет данные в строке базы данных
|
|
||||||
*
|
|
||||||
* @param string $table Имя таблицы
|
|
||||||
* @param array $set Массив данных для замены
|
|
||||||
* @param array $where Массив условий
|
|
||||||
*
|
|
||||||
* @return bool Результат выполнения
|
|
||||||
*/
|
|
||||||
public function Update (string $table, array $set, array $where = []): bool
|
|
||||||
{
|
|
||||||
// Создаю массив параметров
|
|
||||||
$params_set = [];
|
|
||||||
|
|
||||||
// Строковая интерпретация массива для изменения
|
|
||||||
$sql_set = "";
|
|
||||||
|
|
||||||
// Для каждых данных для изменения
|
|
||||||
foreach ($set as $key => $value) {
|
|
||||||
// - получаю ключ 100%-но без ":" в начале
|
|
||||||
$set_key = $key[0] == ":" ? substr($key, 1) : $key;
|
|
||||||
|
|
||||||
// - добавляю префикс для 2 или более итерации
|
|
||||||
$prefix = $sql_set == "" ? "" : ", ";
|
|
||||||
|
|
||||||
// - добавляю данные в sql_set
|
|
||||||
$sql_set .= "$prefix$this->DBSignOpen$set_key$this->DBSignClose=:$set_key";
|
|
||||||
|
|
||||||
// - добавляю данные в параметры
|
|
||||||
$params_set[":" . $set_key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработанные параметры
|
|
||||||
$params_where = [];
|
|
||||||
|
|
||||||
// Строковая интерпретация массива условий
|
|
||||||
$sql_where = $this->PrepareQueryWhere(where: $where, params: $params_where);
|
|
||||||
|
|
||||||
// Создаю параметры
|
|
||||||
$params = array_merge($params_set, $params_where);
|
|
||||||
|
|
||||||
// Создаю запрос
|
|
||||||
$sql = "UPDATE $this->DBSignOpen$table$this->DBSignClose SET $sql_set";
|
|
||||||
|
|
||||||
// Если заданы where-параметры
|
|
||||||
if (count($where) > 0)
|
|
||||||
// - то добавляю их
|
|
||||||
$sql .= " WHERE $sql_where";
|
|
||||||
|
|
||||||
// Выполняю запрос
|
|
||||||
$count = $this->Execute($sql, $params);
|
|
||||||
|
|
||||||
// Если результат - false
|
|
||||||
if ($count === false)
|
|
||||||
// - то и общий результат - false
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Если изменено 0 строк
|
|
||||||
if ($count === 0)
|
|
||||||
// - то и общий результат - false
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Вывожу результат -- успех
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверяет, существует ли запись в таблице.
|
|
||||||
*
|
|
||||||
* @param string $table Имя таблицы
|
|
||||||
* @param array $where Массив условий
|
|
||||||
*
|
|
||||||
* @return bool Результат проверки
|
|
||||||
*/
|
|
||||||
public function IsExist (string $table, array $where = []): bool
|
|
||||||
{
|
|
||||||
// Вывожу результат
|
|
||||||
return $this->Count($table, $where) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Подсчитывает количество строк, удовлетворяющих условию.
|
|
||||||
*
|
|
||||||
* @param string $table Имя таблицы
|
|
||||||
* @param array $where Массив условий выборки
|
|
||||||
*
|
|
||||||
* @return int Количество строк или -1, в случае ошибки
|
|
||||||
*/
|
|
||||||
public function Count (string $table, array $where = []): int
|
|
||||||
{
|
|
||||||
// Параметры
|
|
||||||
$params = [];
|
|
||||||
|
|
||||||
// Строковая интерпретация массива условий
|
|
||||||
$sql_where = $this->PrepareQueryWhere($where, $params);
|
|
||||||
|
|
||||||
// Создаю запрос
|
|
||||||
$sql = "SELECT COUNT(*) FROM $this->DBSignOpen$table$this->DBSignClose";
|
|
||||||
|
|
||||||
// Если заданы where-параметры
|
|
||||||
if (count($where) > 0) {
|
|
||||||
// - то добавляю их
|
|
||||||
$sql .= ' WHERE ' . $sql_where;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Выполняю запрос
|
|
||||||
$countResult = $this->Query($sql, $params);
|
|
||||||
|
|
||||||
// Если запрос выполнен с ошибкой
|
|
||||||
if ($countResult === false)
|
|
||||||
// - то в результат идёт -1
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Получаю секцию
|
|
||||||
$section = match ($this->Config->Driver) {
|
|
||||||
DBDriver::MySQL, DBDriver::SQLite => "COUNT(*)",
|
|
||||||
DBDriver::MSSQL, DBDriver::OracleDB, DBDriver::PostgreSQL => ""
|
|
||||||
};
|
|
||||||
|
|
||||||
// Вывожу количество
|
|
||||||
return isset($countResult[0][$section]) ? (int)$countResult[0][$section] : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Удаляет строки по условию.
|
* Удаляет строки по условию.
|
||||||
*
|
*
|
||||||
|
80
sources/traits/Database/DatabaseCountExist.php
Normal file
80
sources/traits/Database/DatabaseCountExist.php
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @noinspection SqlNoDataSourceInspection
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace goodboyalex\php_db_components_pack\traits\Database;
|
||||||
|
|
||||||
|
use goodboyalex\php_db_components_pack\classes\ConditionBuilder;
|
||||||
|
use goodboyalex\php_db_components_pack\enums\DBDriver;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Трейт для подсчета количества записей в таблице базы данных и проверки существования записи.
|
||||||
|
*
|
||||||
|
* @author Александр Бабаев
|
||||||
|
* @package php_db_components_pack
|
||||||
|
* @version 1.0
|
||||||
|
* @since 1.0
|
||||||
|
* @see PDO
|
||||||
|
*/
|
||||||
|
trait DatabaseCountExist
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Подсчитывает количество строк, удовлетворяющих условию.
|
||||||
|
*
|
||||||
|
* @param string $table Имя таблицы.
|
||||||
|
* @param ConditionBuilder $where Условия выборки.
|
||||||
|
*
|
||||||
|
* @return int Количество строк или -1, в случае ошибки.
|
||||||
|
*/
|
||||||
|
public function Count (string $table, ConditionBuilder $where): int
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Получаем условия.
|
||||||
|
*
|
||||||
|
* @var string $sql_where Строка WHERE условий.
|
||||||
|
* @var array $params Параметры условий.
|
||||||
|
*/
|
||||||
|
[$sql_where, $params] = $where->Build();
|
||||||
|
|
||||||
|
// Создаю запрос
|
||||||
|
$sql = "SELECT COUNT(*) FROM $this->DBSignOpen$table$this->DBSignClose";
|
||||||
|
|
||||||
|
// Если заданы where-параметры
|
||||||
|
if ($where->Count() > 0)
|
||||||
|
// - то добавляю их
|
||||||
|
$sql .= ' WHERE ' . $sql_where;
|
||||||
|
|
||||||
|
// Выполняю запрос
|
||||||
|
$countResult = $this->Query($sql, $params);
|
||||||
|
|
||||||
|
// Если запрос выполнен с ошибкой
|
||||||
|
if ($countResult === false)
|
||||||
|
// - то в результат идёт -1
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Получаю секцию
|
||||||
|
$section = match ($this->Config->Driver) {
|
||||||
|
DBDriver::MySQL, DBDriver::SQLite => "COUNT(*)",
|
||||||
|
DBDriver::MSSQL, DBDriver::OracleDB, DBDriver::PostgreSQL => ""
|
||||||
|
};
|
||||||
|
|
||||||
|
// Вывожу количество
|
||||||
|
return isset($countResult[0][$section]) ? (int)$countResult[0][$section] : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяет, существует ли запись в таблице.
|
||||||
|
*
|
||||||
|
* @param string $table Имя таблицы.
|
||||||
|
* @param ConditionBuilder $where Условия выборки.
|
||||||
|
*
|
||||||
|
* @return bool Результат проверки.
|
||||||
|
*/
|
||||||
|
public function IsExist (string $table, ConditionBuilder $where): bool
|
||||||
|
{
|
||||||
|
// Вывожу результат
|
||||||
|
return $this->Count($table, $where) > 0;
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @noinspection SqlNoDataSourceInspection
|
* @noinspection SqlNoDataSourceInspection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace goodboyalex\php_db_components_pack\traits\Database;
|
namespace goodboyalex\php_db_components_pack\traits\Database;
|
||||||
|
|
||||||
use Exception;
|
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\DBOperation;
|
||||||
use PDO;
|
use PDO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Трейт для работы с получением данных из базы данных.
|
* Трейт для работы с получением данных из базы данных.
|
||||||
*
|
*
|
||||||
* @author Александр Бабаев
|
* @author Александр Бабаев
|
||||||
* @package php_components_pack
|
* @package php_db_components_pack
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @see PDO
|
* @see PDO
|
||||||
@ -22,21 +22,59 @@
|
|||||||
trait DatabaseGet
|
trait DatabaseGet
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Получает первую строку в массиве данных, удовлетворяющую выборке
|
* Извлекает одну запись из базы данных и создает соответствующий объект класса.
|
||||||
*
|
*
|
||||||
* @param string $table Имя таблицы
|
* @param string $table Название таблицы.
|
||||||
* @param array $columns Колонки, которые нужно включить в запрос
|
* @param ConditionBuilder $where Условия выборки.
|
||||||
* @param ConditionBuilder $where Параметры выборки
|
* @param array $columns Колонки, которые нужно включить в запрос.
|
||||||
|
* @param string $className Полное имя класса, реализуемого интерфейсом IDBItem.
|
||||||
*
|
*
|
||||||
* @return false|array Строка в формате массива или false в случае ошибки
|
* @return object|false Заполненный объект класса или <code>false</code> в случае ошибки.
|
||||||
*
|
|
||||||
* @see Query
|
|
||||||
* @see QueryFirst
|
|
||||||
* @see QueryLast
|
|
||||||
* @see QueryScalar
|
|
||||||
* @see GetRows
|
|
||||||
*/
|
*/
|
||||||
public function GetRowColumns (string $table, array $columns, ConditionBuilder $where): false|array
|
public function GetRow (string $table, ConditionBuilder $where, array $columns = [],
|
||||||
|
string $className = "\\StdClass"): object|false
|
||||||
|
{
|
||||||
|
// Задаю массив параметров
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
// Формируем SQL-запрос
|
||||||
|
$sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params);
|
||||||
|
|
||||||
|
// Добавляю лимит
|
||||||
|
$sql .= " LIMIT 1";
|
||||||
|
|
||||||
|
// Получаю строку
|
||||||
|
$row = $this->Query($sql, $params);
|
||||||
|
|
||||||
|
// Если не получено
|
||||||
|
if ($row === false)
|
||||||
|
// - то вывожу false
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Получаю объект
|
||||||
|
$item = $this->RestoreItem($row, $className, DBOperation::Get);
|
||||||
|
|
||||||
|
// Если при получении возникла ошибка
|
||||||
|
if ($item === null)
|
||||||
|
// - то возвращаю false
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Возвращаю элемент
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получает набор строк в массиве данных, удовлетворяющий выборке.
|
||||||
|
*
|
||||||
|
* @param string $table Имя таблицы.
|
||||||
|
* @param ConditionBuilder $where Where-условия выборки.
|
||||||
|
* @param array $columns Колонки, которые нужно включить в запрос.
|
||||||
|
* @param string $className Полное имя класса, реализуемого интерфейсом IDBItem.
|
||||||
|
*
|
||||||
|
* @return false|ObjectArray Массив найденных классов или <code>false</code> в случае ошибки.
|
||||||
|
*/
|
||||||
|
public function GetRows (string $table, ConditionBuilder $where, array $columns = [],
|
||||||
|
string $className = "\\StdClass"): false|ObjectArray
|
||||||
{
|
{
|
||||||
// Задаю массив параметров
|
// Задаю массив параметров
|
||||||
$params = [];
|
$params = [];
|
||||||
@ -44,78 +82,99 @@
|
|||||||
// Получаю SQL запрос
|
// Получаю SQL запрос
|
||||||
$sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params);
|
$sql = $this->PrepareSQLForRowsQuery($table, $columns, $where, $params);
|
||||||
|
|
||||||
// Получаю строку на основании запроса
|
// Получаю строки на основании запроса
|
||||||
return $this->QueryScalar($sql, $params);
|
$queryResult = $this->Query($sql, $params);
|
||||||
|
|
||||||
|
// Если строки не получены
|
||||||
|
if ($queryResult === false)
|
||||||
|
// - то выдаю ошибку
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Создаю массив объектов
|
||||||
|
$result = new ObjectArray();
|
||||||
|
|
||||||
|
// Для каждого элемента
|
||||||
|
foreach ($queryResult as $row) {
|
||||||
|
// - пытаюсь восстановить объект
|
||||||
|
$item = $this->RestoreItem($row, $className, DBOperation::Get);
|
||||||
|
|
||||||
|
// - если не получилось
|
||||||
|
if ($item === null)
|
||||||
|
// -- то пропускаю элемент
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// - добавляю элемент в массив
|
||||||
|
$result[] = $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выдаю массив объектов
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Извлекает одну запись из базы данных и создает соответствующий объект класса
|
* Получает колонку в массиве данных.
|
||||||
*
|
*
|
||||||
* @param string $table Название таблицы
|
* @param string $table Имя таблицы.
|
||||||
* @param ConditionBuilder $condition Условия выборки
|
* @param string $column Имя колонки.
|
||||||
* @param string $className Полное имя класса, реализуемого интерфейсом IDBItem
|
* @param ConditionBuilder $where Параметры запроса.
|
||||||
*
|
*
|
||||||
* @return object Заполненный объект класса
|
* @return false|array Ассоциированный массив с результатом запроса или false в случае ошибки.
|
||||||
|
*
|
||||||
|
* @see Query
|
||||||
*/
|
*/
|
||||||
public function GetRow (string $table, ConditionBuilder $condition, string $className): object
|
public function GetCol (string $table, string $column, ConditionBuilder $where): false|array
|
||||||
{
|
{
|
||||||
try {
|
/**
|
||||||
// Строим условие WHERE для SQL-запроса
|
* Интерпретирую условия.
|
||||||
$whereClause = $condition->Build();
|
*
|
||||||
|
* @var string $sql_where Строка запроса.
|
||||||
// Формируем SQL-запрос
|
* @var array $params Массив параметров строки запроса.
|
||||||
$sql = "SELECT * FROM $table WHERE $whereClause LIMIT 1;";
|
*/
|
||||||
|
[$sql_where, $params] = $where->Build();
|
||||||
// Подготовленное выражение
|
|
||||||
$stmt = $this->pdo->prepare($sql);
|
// Создаю запрос
|
||||||
|
$sql = "SELECT $this->DBSignOpen$column$this->DBSignClose FROM $this->DBSignOpen$table$this->DBSignClose";
|
||||||
// Присваивание параметров
|
|
||||||
foreach ($params as $col => $val) {
|
// Если заданы where-параметры
|
||||||
$stmt->bindParam(":$col", $val);
|
if ($where->Count() > 0)
|
||||||
}
|
// - то добавляю их
|
||||||
|
$sql .= " WHERE $sql_where";
|
||||||
// Выполнение запроса
|
|
||||||
$stmt->execute();
|
// Получаю столбец на основании запроса
|
||||||
|
$queryResult = $this->Query($sql, $params);
|
||||||
// Получаем первую строку результатов
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
// Если строка не получена или пуста
|
||||||
|
if ($queryResult === false)
|
||||||
if (!$row) {
|
// - то выдаю ошибку
|
||||||
throw new Exception("No data found with the given conditions.");
|
return false;
|
||||||
}
|
|
||||||
|
// Создаю результат
|
||||||
// Создание объекта желаемого класса
|
$result = [];
|
||||||
$obj = new $className();
|
|
||||||
|
// Для каждого результата запроса
|
||||||
// Заполняем поля объекта соответствующими значениями из БД
|
foreach ($queryResult as $row)
|
||||||
foreach ($row as $column => $value) {
|
// - передаю его в результат
|
||||||
// Анализируем свойства класса и находим соответствующее свойство
|
$result[] = $row[$column];
|
||||||
$properties = (new ReflectionClass($obj))->getProperties(ReflectionProperty::IS_PUBLIC);
|
|
||||||
foreach ($properties as $prop) {
|
// Вывожу результат
|
||||||
// Проверяем, соответствует ли данное свойство указанному полю
|
return $result;
|
||||||
$attributes = $prop->getAttributes(FieldName::class);
|
}
|
||||||
if ($attributes && $attributes[0]->getArguments()['name'] === $column) {
|
|
||||||
// Применяем процедуру конвертации (если указана)
|
/**
|
||||||
$convertAttrs = $prop->getAttributes(ConvertToDB::class);
|
* Получение значение единичного поля.
|
||||||
if ($convertAttrs) {
|
*
|
||||||
$converter = $convertAttrs[0]->getArguments()['fromDb'];
|
* @param string $table Имя таблицы.
|
||||||
if ($converter !== null) {
|
* @param string $column Требуемый столбец.
|
||||||
$value = call_user_func($converter, $value);
|
* @param ConditionBuilder $where Параметры запроса.
|
||||||
}
|
*
|
||||||
}
|
* @return mixed|null Результат запроса или <code>null</code> в случае ошибки.
|
||||||
|
*/
|
||||||
// Устанавливаем значение свойства
|
public function GetValue (string $table, string $column, ConditionBuilder $where): mixed
|
||||||
$obj->{$prop->getName()} = $value;
|
{
|
||||||
break;
|
// Получаю колонку по условию из таблицы
|
||||||
}
|
$result = $this->GetCol($table, $column, $where);
|
||||||
}
|
|
||||||
}
|
// Если результат получен, то выдаю первый элемент, а если нет, то null
|
||||||
|
return $result[0] ?? null;
|
||||||
return $obj;
|
|
||||||
}
|
|
||||||
catch (\PDOException|\Exception $e) {
|
|
||||||
error_log("Error fetching data: " . $e->getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
namespace goodboyalex\php_db_components_pack\traits\Database;
|
namespace goodboyalex\php_db_components_pack\traits\Database;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
use PDO;
|
use PDO;
|
||||||
use PDOException;
|
use PDOException;
|
||||||
|
|
||||||
@ -13,7 +14,7 @@
|
|||||||
* Трейт для работы с запросами к базе данных типа Query и Execute.
|
* Трейт для работы с запросами к базе данных типа Query и Execute.
|
||||||
*
|
*
|
||||||
* @author Александр Бабаев
|
* @author Александр Бабаев
|
||||||
* @package php_components_pack
|
* @package php_db_components_pack
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
* @see PDO
|
* @see PDO
|
||||||
@ -21,162 +22,138 @@
|
|||||||
trait DatabaseQueryExecute
|
trait DatabaseQueryExecute
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Запрос строк из базы данных.
|
* Выполнить запрос к базе данных и вернуть ассоциированный массив.
|
||||||
*
|
*
|
||||||
* @param string $query Запрос
|
* @param string $query Запрос SQL.
|
||||||
* @param array $params Параметры запроса
|
* @param array $params Параметры запроса.
|
||||||
*
|
*
|
||||||
* @return false|array Ассоциированный массив с результатом запроса или false в случае ошибки
|
* @return array|false Ассоциативный массив с результатом запроса или <code>false</code> в случае ошибки.
|
||||||
*/
|
*/
|
||||||
public function Query (string $query, array $params = []): false|array
|
public function Query (string $query, array $params = []): array|false
|
||||||
{
|
{
|
||||||
// По умолчанию, результат пуст
|
// Проверяем, что это именно запрос SELECT
|
||||||
$result = false;
|
if (!preg_match('/^\s*SELECT\s+/i', trim($query))) {
|
||||||
|
$this->HandleException(new Exception("Некорректный запрос для метода Query() / Incorrect request for the Query() method: '$query'. Используйте метод Execute() для небезопасных операций / Use the Execute() method for unsafe operations."),
|
||||||
|
false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подготавливаем запрос
|
||||||
|
$stmt = $this->DataBaseHandle->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]);
|
||||||
|
|
||||||
|
// Если запрос не подготовлен
|
||||||
|
if ($stmt === false) {
|
||||||
|
// - обрабатываю ошибку
|
||||||
|
$this->HandleException(new Exception("Ошибка подготовки SQL-запроса / SQL query preparation error"),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// - прерываю выполнение
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Подготавливаю запрос
|
// Выполняем запрос
|
||||||
$STH = $this->DataBaseHandle->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]);
|
$stmt->execute($params);
|
||||||
|
|
||||||
// Выполняю запрос
|
|
||||||
$STH->execute($params);
|
|
||||||
|
|
||||||
// Указываю, что данные, которые я хочу получить, должны быть в ассоциативном массиве
|
|
||||||
$STH->setFetchMode(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
// Получаю все данные
|
|
||||||
$result = $STH->fetchAll();
|
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
$this->HandleException($e);
|
// - в случае ошибки, обрабатываю её
|
||||||
|
$this->HandleException(new Exception("Ошибка выполнения SQL-запроса / SQL query execution error: " .
|
||||||
|
$e->getMessage()), false);
|
||||||
|
|
||||||
|
// - прерываю выполнение
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вывожу результат
|
// Если получены данные, то возвращаю их, в противном случае возвращаю пустой массив
|
||||||
return $result;
|
return ($data = $stmt->fetchAll(PDO::FETCH_ASSOC)) !== [] ? $data : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Выполняем запрос на получение одной строки (аналог QueryFirst)
|
* Получить первую строку результата запроса.
|
||||||
*
|
*
|
||||||
* @param string $query Запрос
|
* @param string $query Запрос SQL.
|
||||||
* @param array $params Параметры запроса
|
* @param array $params Параметры запроса.
|
||||||
*
|
*
|
||||||
* @return false|array Строка в формате массива или false в случае ошибки
|
* @return array|false Первая строка результата запроса или <code>false</code> в случае ошибки.
|
||||||
*
|
|
||||||
* @see Query
|
|
||||||
* @see QueryFirst
|
|
||||||
* @see QueryLast
|
|
||||||
* @see GetRow
|
|
||||||
*/
|
*/
|
||||||
public function QueryScalar (string $query, array $params = []): false|array
|
public function QueryFirst (string $query, array $params = []): array|false
|
||||||
{
|
{
|
||||||
return $this->QueryFirst($query, $params);
|
// Выполняем запрос
|
||||||
|
$rows = $this->Query($query, $params);
|
||||||
|
|
||||||
|
// Если получены данные, то возвращаю первую строку, в противном случае возвращаю false
|
||||||
|
return is_array($rows) ? reset($rows) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Выполняем запрос на получение первой строки
|
* Получить последнюю строку результата запроса.
|
||||||
*
|
*
|
||||||
* @param string $query Запрос
|
* @param string $query Запрос SQL.
|
||||||
* @param array $params Параметры запроса
|
* @param array $params Параметры запроса.
|
||||||
*
|
*
|
||||||
* @return false|array Строка в формате массива или false в случае ошибки
|
* @return array|false Последняя строка результата запроса или <code>false</code> в случае ошибки.
|
||||||
*
|
|
||||||
* @see Query
|
|
||||||
* @see QueryLast
|
|
||||||
* @see QueryScalar
|
|
||||||
*/
|
*/
|
||||||
public function QueryFirst (string $query, array $params = []): false|array
|
public function QueryLast (string $query, array $params = []): array|false
|
||||||
{
|
{
|
||||||
// Выполняю запрос
|
// Выполняем запрос
|
||||||
$result = $this->Query($query, $params);
|
$rows = $this->Query($query, $params);
|
||||||
|
|
||||||
// Если в результате запроса получили ошибку или количество строк = 0
|
// Если получены данные, то возвращаю последнюю строку, в противном случае возвращаю false
|
||||||
if ($result === false || count($result) == 0)
|
return is_array($rows) ? end($rows) : false;
|
||||||
// - то возвращаем ошибку
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Получаю первый ключ массива
|
|
||||||
$firstKey = array_key_first($result);
|
|
||||||
|
|
||||||
// Возвращаем первую строку
|
|
||||||
return $result[$firstKey];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Выполняем запрос на получение последней строки
|
* Выполнить запрос, который изменяет данные в базе данных (INSERT, UPDATE, DELETE).
|
||||||
*
|
*
|
||||||
* @param string $query Запрос
|
* @param string $query Запрос SQL.
|
||||||
* @param array $params Параметры запроса
|
* @param array $params Параметры запроса.
|
||||||
*
|
*
|
||||||
* @return false|array Строка в формате массива или false в случае ошибки
|
* @return int|false Количество затронутых строк или <code>false</code> в случае ошибки.
|
||||||
*
|
|
||||||
* @see Query
|
|
||||||
* @see QueryFirst
|
|
||||||
* @see QueryScalar
|
|
||||||
*/
|
|
||||||
public function QueryLast (string $query, array $params = []): false|array
|
|
||||||
{
|
|
||||||
// Выполняю запрос
|
|
||||||
$result = $this->Query($query, $params);
|
|
||||||
|
|
||||||
// Если в результате запроса получили ошибку или количество строк = 0
|
|
||||||
if ($result === false || count($result) == 0)
|
|
||||||
// - то возвращаем ошибку
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Получаю последний ключ массива
|
|
||||||
$lastKey = array_key_last($result);
|
|
||||||
|
|
||||||
// Возвращаем первую строку
|
|
||||||
return $result[$lastKey];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Выполнение запроса. Обычно используется для операций,
|
|
||||||
* которые не возвращают никаких данных, кроме количества
|
|
||||||
* затронутых ими записей. Например,
|
|
||||||
*
|
|
||||||
* <code>$db->Execute('DELETE FROM table WHERE id=1');</code>
|
|
||||||
*
|
|
||||||
* @param string $query Запрос
|
|
||||||
* @param array $params Параметры запроса
|
|
||||||
*
|
|
||||||
* @return int|false Количество затронутых строк или false в случае ошибки
|
|
||||||
*/
|
*/
|
||||||
public function Execute (string $query, array $params = []): int|false
|
public function Execute (string $query, array $params = []): int|false
|
||||||
{
|
{
|
||||||
// По умолчанию результат false
|
// Проверяем, что это не запрос SELECT
|
||||||
$result = false;
|
if (preg_match('/^\s*SELECT\s+/i', trim($query))) {
|
||||||
|
// - выдаю ошибку
|
||||||
|
$this->HandleException(new Exception("Некорректный запрос для метода Execute() / Incorrect request for the Execute() method: '$query'. Используйте метод Query() для безопасного выбора данных / Use the Query() method to select data safely."),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// - аннулирую результат
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Если параметры не заданы
|
// Если это простой запрос без параметров
|
||||||
if (count($params) == 0) {
|
if (empty($params))
|
||||||
// - то выполняю запрос
|
// - то просто выполняю его
|
||||||
$result = $this->DataBaseHandle->exec($query);
|
return $this->DataBaseHandle->exec($query);
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
// - в противном случае
|
// - в противном случае, подготавливаем запрос с параметрами
|
||||||
// -- подготавливаю запрос
|
$stmt = $this->DataBaseHandle->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]);
|
||||||
$STH = $this->DataBaseHandle->prepare($query, [PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY]);
|
|
||||||
|
|
||||||
// -- выполняю запрос
|
// - если подготовили неуспешно
|
||||||
$opResult = $STH->execute($params);
|
if (!$stmt) {
|
||||||
|
// -- то формируем ошибку
|
||||||
|
$this->HandleException(new PDOException("Ошибка подготовки SQL-запроса / SQL query preparation error"),
|
||||||
|
false);
|
||||||
|
|
||||||
|
// -- возвращаем неудачу
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// -- и если выполнение успешное,
|
// - выполняем запрос
|
||||||
if ($opResult)
|
$executed = $stmt->execute($params);
|
||||||
// --- то в результат пойдёт количество строк
|
|
||||||
$result = $STH->rowCount();
|
// - если успешно, то возвращаем количество изменённых строк, иначе - false
|
||||||
|
return ($executed) ? $stmt->rowCount() : false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
$this->HandleException($e);
|
// - в случае ошибки, обрабатываю её
|
||||||
}
|
$this->HandleException($e, false);
|
||||||
|
|
||||||
// Если в результате false
|
// - прерываю выполнение
|
||||||
if ($result === false)
|
|
||||||
// - то возвращаю его
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
// Возвращаю результат
|
|
||||||
return $result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,6 +10,7 @@
|
|||||||
use goodboyalex\php_db_components_pack\attributes\ConvertToDB;
|
use goodboyalex\php_db_components_pack\attributes\ConvertToDB;
|
||||||
use goodboyalex\php_db_components_pack\attributes\FieldName;
|
use goodboyalex\php_db_components_pack\attributes\FieldName;
|
||||||
use goodboyalex\php_db_components_pack\attributes\IgnoredInDB;
|
use goodboyalex\php_db_components_pack\attributes\IgnoredInDB;
|
||||||
|
use goodboyalex\php_db_components_pack\attributes\PrimaryKey;
|
||||||
use goodboyalex\php_db_components_pack\classes\ConditionBuilder;
|
use goodboyalex\php_db_components_pack\classes\ConditionBuilder;
|
||||||
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;
|
||||||
@ -29,32 +30,6 @@
|
|||||||
*/
|
*/
|
||||||
trait DatabaseSpecial
|
trait DatabaseSpecial
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Подготавливает массив параметров для запроса.
|
|
||||||
*
|
|
||||||
* @param array $array Массив.
|
|
||||||
*
|
|
||||||
* @return array Массив с подготовленными параметрами.
|
|
||||||
*/
|
|
||||||
private static function PrepareArray (array $array): array
|
|
||||||
{
|
|
||||||
// Результирующий массив
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
// Для каждого элемента массива
|
|
||||||
foreach ($array as $key => $value)
|
|
||||||
// - если ключ начинается с ":"
|
|
||||||
if ($key[0] == ":")
|
|
||||||
// - то сразу добавляем его в результирующий массив
|
|
||||||
$result[$key] = $value;
|
|
||||||
else
|
|
||||||
// - в противном случае, предварительно добавим в имя ключа ":"
|
|
||||||
$result[':' . $key] = $value;
|
|
||||||
|
|
||||||
// Возвращаем результат
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Находит атрибут в массиве.
|
* Находит атрибут в массиве.
|
||||||
*
|
*
|
||||||
@ -69,16 +44,112 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Проверяет, является ли свойство публичным.
|
* Получает публичные свойства класса и их значения.
|
||||||
*
|
*
|
||||||
* @param object $obj Объект.
|
* @param object $obj Класс.
|
||||||
* @param string $propertyName Имя свойства.
|
|
||||||
*
|
*
|
||||||
* @return bool Возвращает <code>true</code>, если свойство публичное, иначе <code>false</code>.
|
* @return array Массив публичных свойств и их значений.
|
||||||
*/
|
*/
|
||||||
private static function IsPublicProperty (object $obj, string $propertyName): bool
|
private static function GetPublicProperties (object $obj): array
|
||||||
{
|
{
|
||||||
return property_exists($obj, $propertyName) && new ReflectionProperty($obj, $propertyName)->isPublic();
|
// Создаю массив свойств
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
// Получаю массив свойств
|
||||||
|
$properties = get_class_vars(get_class($obj));
|
||||||
|
|
||||||
|
// Для каждого свойства
|
||||||
|
foreach ($properties as $key => $value) {
|
||||||
|
// - пропускаю не свойства
|
||||||
|
if (!property_exists($obj, $key))
|
||||||
|
// -- пропускаю
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// - получаю рефлексию
|
||||||
|
$rProperty = new ReflectionProperty($obj, $key);
|
||||||
|
|
||||||
|
// - пропускаю не публичные свойства
|
||||||
|
if (!$rProperty->isPublic())
|
||||||
|
// -- пропускаю
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// - добавляю в массив свойство и его значение
|
||||||
|
$result[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаю результат
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получает первичные ключи класса.
|
||||||
|
*
|
||||||
|
* @param IDBItem $source Класс.
|
||||||
|
* @param DBOperation $operation Текущая операция.
|
||||||
|
*
|
||||||
|
* @return array Массив первичных ключей и их значений.
|
||||||
|
*/
|
||||||
|
private function FindPrimaryKeys (IDBItem $source, DBOperation $operation): array
|
||||||
|
{
|
||||||
|
// Создаю массив первичных ключей
|
||||||
|
$pKeys = [];
|
||||||
|
|
||||||
|
// Получаю массив свойств
|
||||||
|
$properties = self::GetPublicProperties($source);
|
||||||
|
|
||||||
|
// Для каждого свойства
|
||||||
|
foreach ($properties as $key => $value) {
|
||||||
|
// - получаю рефлексию
|
||||||
|
// - для класса
|
||||||
|
$reflectedClass = new ReflectionClass(get_class($source));
|
||||||
|
try {
|
||||||
|
// - для свойства
|
||||||
|
$reflectionProperty = $reflectedClass->getProperty($key);
|
||||||
|
}
|
||||||
|
catch (ReflectionException) {
|
||||||
|
// - если ошибка, то вывожу пустой массив
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// - получаю атрибуты
|
||||||
|
$attributes = $reflectionProperty->getAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Фильтруем поля, игнорируемые для данной операции
|
||||||
|
*
|
||||||
|
* @var PrimaryKey|null $pkAttr Атрибут первичного ключа.
|
||||||
|
*/
|
||||||
|
$pkAttr = self::FindAttribute($attributes, PrimaryKey::class);
|
||||||
|
|
||||||
|
// - если на свойстве нет атрибута первичного ключа
|
||||||
|
if ($pkAttr === null)
|
||||||
|
// -- то пропускаю
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Фильтруем поля, игнорируемые для данной операции
|
||||||
|
*
|
||||||
|
* @var IgnoredInDB|null $ignoreAttr Атрибут игнорирования.
|
||||||
|
*/
|
||||||
|
$ignoreAttr = self::FindAttribute($attributes, IgnoredInDB::class);
|
||||||
|
|
||||||
|
// - если поле игнорируется
|
||||||
|
if ($ignoreAttr !== null) {
|
||||||
|
// -- то проверяю, игнорируется ли данное поле
|
||||||
|
$isIgnore = $ignoreAttr->IgnoredOperations->IsExist(fn (DBOperation $oper) => $oper == $operation);
|
||||||
|
|
||||||
|
// -- если игнорируется
|
||||||
|
if ($isIgnore)
|
||||||
|
// --- то пропускаю
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - добавляю первичный ключ и его значение
|
||||||
|
$pKeys[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаю найденные ключи
|
||||||
|
return $pKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -94,14 +165,10 @@
|
|||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
// Получаю массив свойств
|
// Получаю массив свойств
|
||||||
$properties = get_class_vars(get_class($source));
|
$properties = self::GetPublicProperties($source);
|
||||||
|
|
||||||
// Для каждого свойства
|
// Для каждого свойства
|
||||||
foreach ($properties as $key => $value) {
|
foreach ($properties as $key => $value) {
|
||||||
// - пропускаю не публичные свойства
|
|
||||||
if (!self::IsPublicProperty($source, $key))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Получаю рефлексию
|
// Получаю рефлексию
|
||||||
// - для класса
|
// - для класса
|
||||||
$reflectedClass = new ReflectionClass(get_class($source));
|
$reflectedClass = new ReflectionClass(get_class($source));
|
||||||
@ -169,20 +236,135 @@
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Восстанавливает объект из БД.
|
||||||
|
*
|
||||||
|
* @param array $row Строка БД.
|
||||||
|
* @param string $className Имя класса объекта.
|
||||||
|
* @param DBOperation $operation Операция.
|
||||||
|
*
|
||||||
|
* @return object|null Экземпляр класса объекта или <code>null</code>, если ошибка.
|
||||||
|
*/
|
||||||
|
private function RestoreItem (array $row, string $className, DBOperation $operation): ?object
|
||||||
|
{
|
||||||
|
// Если целевой класс не реализует интерфейс IDBItem
|
||||||
|
if (in_array(IDBItem::class, class_implements($className)))
|
||||||
|
// - то прерываем и возвращаем null
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Создаём объект желаемого класса
|
||||||
|
$class = new $className();
|
||||||
|
|
||||||
|
// Получаем публичные свойства класса
|
||||||
|
try {
|
||||||
|
$properties = new ReflectionClass($class)->getProperties(ReflectionProperty::IS_PUBLIC);
|
||||||
|
}
|
||||||
|
catch (ReflectionException $exception) {
|
||||||
|
// - в случае ошибки, обрабатываем её
|
||||||
|
$this->HandleException($exception, false);
|
||||||
|
|
||||||
|
// - и возвращаем null
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Массив отношений, содержащий имя поля, имя свойства, метод преобразования
|
||||||
|
$propertiesRelationship = [];
|
||||||
|
|
||||||
|
// Для каждого свойства
|
||||||
|
foreach ($properties as $property) {
|
||||||
|
// - получаю атрибуты
|
||||||
|
$attributes = $property->getAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Фильтруем поля, игнорируемые для данной операции
|
||||||
|
*
|
||||||
|
* @var IgnoredInDB|null $ignoreAttr Атрибут игнорирования.
|
||||||
|
*/
|
||||||
|
$ignoreAttr = self::FindAttribute($attributes, IgnoredInDB::class);
|
||||||
|
|
||||||
|
// - если поле игнорируется
|
||||||
|
if ($ignoreAttr !== null) {
|
||||||
|
// -- то проверяю, игнорируется ли данное поле
|
||||||
|
$isIgnore = $ignoreAttr->IgnoredOperations->IsExist(fn (DBOperation $oper) => $oper == $operation);
|
||||||
|
|
||||||
|
// -- если игнорируется
|
||||||
|
if ($isIgnore)
|
||||||
|
// --- то пропускаю
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получаю значение имени поля
|
||||||
|
*
|
||||||
|
* @var FieldName|null $fieldNameAttr Атрибут имени поля.
|
||||||
|
*/
|
||||||
|
$fieldNameAttr = self::FindAttribute($attributes, FieldName::class);
|
||||||
|
|
||||||
|
// - если есть атрибут имени поля, то беру его имя, иначе беру имя свойства
|
||||||
|
$fieldName = $fieldNameAttr !== null ? $fieldNameAttr->FieldName : $property->getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Преобразование типа.
|
||||||
|
*
|
||||||
|
* @var ConvertToDB|null $convertAttr Атрибут конвертации.
|
||||||
|
*/
|
||||||
|
$convertAttr = self::FindAttribute($attributes, ConvertToDB::class);
|
||||||
|
|
||||||
|
// - если есть атрибут конвертации, то получаю значение функции конвертации, иначе null
|
||||||
|
$converterFunc = ($convertAttr) ? $convertAttr->ConvertFromDB : null;
|
||||||
|
|
||||||
|
// - добавляю в массив отношений
|
||||||
|
$propertiesRelationship[$fieldName] = [$property->getName(), $converterFunc];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проходим элементы массива из БД
|
||||||
|
foreach ($row as $column => $value) {
|
||||||
|
// - если свойство не найдено
|
||||||
|
if (!array_key_exists($column, $propertiesRelationship))
|
||||||
|
// -- то пропускаем
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// - получаю имя свойства и процедуру конвертации
|
||||||
|
[$property, $converter] = $propertiesRelationship[$column];
|
||||||
|
|
||||||
|
// - применяем процедуру конвертации (если указана)
|
||||||
|
if ($converter !== null)
|
||||||
|
// - выполняю функцию конвертации
|
||||||
|
$value = call_user_func($converter, $value);
|
||||||
|
|
||||||
|
// - устанавливаем значение свойства
|
||||||
|
$class->$property = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаю класс
|
||||||
|
return $class;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Обрабатывает исключение.
|
* Обрабатывает исключение.
|
||||||
*
|
*
|
||||||
* @param Exception $exception Исключение.
|
* @param Exception $exception Исключение.
|
||||||
|
* @param bool $terminate Прерывать выполнение (<code>true</code>) или просто зафиксировать
|
||||||
|
* (<code>false</code>).
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function HandleException (Exception $exception): void
|
private function HandleException (Exception $exception, bool $terminate = true): void
|
||||||
{
|
{
|
||||||
// Выбираю обработчик исключений
|
// Выбираю обработчик исключений
|
||||||
$onException = $this->OnException ?? fn (Exception $e) => die($e->getMessage());
|
$onException = $this->OnException ?? function (Exception $e, bool $isTerminate): void
|
||||||
|
{
|
||||||
|
// Если требуется прерывать выполнение
|
||||||
|
if ($isTerminate)
|
||||||
|
// - то прерываем
|
||||||
|
die($e->getMessage());
|
||||||
|
else
|
||||||
|
// - в противном случае, выводим сообщение
|
||||||
|
echo $e->getMessage();
|
||||||
|
};
|
||||||
|
|
||||||
// Выполняю обработчик исключений
|
// Выполняю обработчик исключений
|
||||||
$onException($exception);
|
$onException($exception, $terminate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -231,19 +413,21 @@
|
|||||||
*
|
*
|
||||||
* @param string $table Имя таблицы
|
* @param string $table Имя таблицы
|
||||||
* @param array $columns Колонки, которые нужно включить в запрос
|
* @param array $columns Колонки, которые нужно включить в запрос
|
||||||
* @param array $where Параметры выборки
|
* @param ConditionBuilder $whereConditions Параметры выборки
|
||||||
* @param array $params Параметры и их значения
|
* @param array $params Параметры и их значения
|
||||||
*
|
*
|
||||||
* @return string SQL-запрос
|
* @return string SQL-запрос
|
||||||
*/
|
*/
|
||||||
private function PrepareSQLForRowsQuery (string $table, array $columns, ConditionBuilder $where,
|
private function PrepareSQLForRowsQuery (string $table, array $columns, ConditionBuilder $whereConditions,
|
||||||
array &$params = []): string
|
array &$params = []): string
|
||||||
{
|
{
|
||||||
// Очищаю параметры
|
/**
|
||||||
$params = [];
|
* Собираю условие в виде пригодном для SQL
|
||||||
|
*
|
||||||
// Строковая интерпретация массива условий
|
* @var string $sql_where where-запрос SQL
|
||||||
$sql_where = $this->PrepareQueryWhere($where, $params);
|
* @var array $params Параметры и их значения (для защиты от SQL-инъекции)
|
||||||
|
*/
|
||||||
|
[$sql_where, $params] = $whereConditions->Build();
|
||||||
|
|
||||||
// Колонки
|
// Колонки
|
||||||
$sql_columns = count($columns) > 0 ? implode(', ', $this->PrepareColumn($columns)) : "*";
|
$sql_columns = count($columns) > 0 ? implode(', ', $this->PrepareColumn($columns)) : "*";
|
||||||
@ -252,50 +436,11 @@
|
|||||||
$sql = "SELECT $sql_columns FROM $this->DBSignOpen$table$this->DBSignClose";
|
$sql = "SELECT $sql_columns FROM $this->DBSignOpen$table$this->DBSignClose";
|
||||||
|
|
||||||
// Если заданы where-параметры
|
// Если заданы where-параметры
|
||||||
if (count($where) > 0) {
|
if ($whereConditions->Count() > 0)
|
||||||
// - то добавляю их
|
// - то добавляю их
|
||||||
$sql .= " WHERE $sql_where";
|
$sql .= " WHERE $sql_where";
|
||||||
}
|
|
||||||
|
|
||||||
// Возвращаю запрос
|
// Возвращаю запрос
|
||||||
return $sql;
|
return $sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Готовит выражение для WHERE-запроса
|
|
||||||
*
|
|
||||||
* @param array $where Массив условий
|
|
||||||
* @param array $params Очищенные параметры
|
|
||||||
*
|
|
||||||
* @return string Строка WHERE-запроса
|
|
||||||
*/
|
|
||||||
private function PrepareQueryWhere (array $where, array &$params): string
|
|
||||||
{
|
|
||||||
// Очищаю параметры
|
|
||||||
$params = [];
|
|
||||||
|
|
||||||
// Задаю результат
|
|
||||||
$result = "";
|
|
||||||
|
|
||||||
// Если массив условий не пуст
|
|
||||||
if (count($where) > 0) {
|
|
||||||
// - то для каждого условия
|
|
||||||
foreach ($where as $key => $value) {
|
|
||||||
// -- получаю ключ 100%-но без ":" в начале
|
|
||||||
$where_key = $key[0] == ":" ? substr($key, 1) : $key;
|
|
||||||
|
|
||||||
// -- добавляю префикс для 2 или более итерации
|
|
||||||
$prefix = $result == "" ? "" : " AND ";
|
|
||||||
|
|
||||||
// -- добавляю данные в $sql_where
|
|
||||||
$result .= $prefix . $this->DBSignOpen . $where_key . $this->DBSignClose . " = :" . $where_key;
|
|
||||||
|
|
||||||
// -- добавляю данные в параметры
|
|
||||||
$params[$where_key] = "$value";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Вывожу результат
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
}
|
165
sources/traits/Database/DatabaseUpdate.php
Normal file
165
sources/traits/Database/DatabaseUpdate.php
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @noinspection SqlNoDataSourceInspection
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace goodboyalex\php_db_components_pack\traits\Database;
|
||||||
|
|
||||||
|
use goodboyalex\php_db_components_pack\classes\ConditionBuilder;
|
||||||
|
use goodboyalex\php_db_components_pack\enums\DBOperation;
|
||||||
|
use goodboyalex\php_db_components_pack\interfaces\IDBItem;
|
||||||
|
use PDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Трейт для обновления записей в таблице базы данных.
|
||||||
|
*
|
||||||
|
* @author Александр Бабаев
|
||||||
|
* @package php_db_components_pack
|
||||||
|
* @version 1.0
|
||||||
|
* @since 1.0
|
||||||
|
* @see PDO
|
||||||
|
*/
|
||||||
|
trait DatabaseUpdate
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Заменяет данные в строке базы данных.
|
||||||
|
*
|
||||||
|
* @param string $table Имя таблицы.
|
||||||
|
* @param array $set Массив данных для замены
|
||||||
|
* @param array $where Массив условий
|
||||||
|
*
|
||||||
|
* @return bool Результат выполнения
|
||||||
|
*/
|
||||||
|
public function UpdateOld (string $table, array $set, array $where = []): bool
|
||||||
|
{
|
||||||
|
// Создаю массив параметров
|
||||||
|
$params_set = [];
|
||||||
|
|
||||||
|
// Строковая интерпретация массива для изменения
|
||||||
|
$sql_set = "";
|
||||||
|
|
||||||
|
// Для каждых данных для изменения
|
||||||
|
foreach ($set as $key => $value) {
|
||||||
|
// - получаю ключ 100%-но без ":" в начале
|
||||||
|
$set_key = $key[0] == ":" ? substr($key, 1) : $key;
|
||||||
|
|
||||||
|
// - добавляю префикс для 2 или более итерации
|
||||||
|
$prefix = $sql_set == "" ? "" : ", ";
|
||||||
|
|
||||||
|
// - добавляю данные в sql_set
|
||||||
|
$sql_set .= "$prefix$this->DBSignOpen$set_key$this->DBSignClose=:$set_key";
|
||||||
|
|
||||||
|
// - добавляю данные в параметры
|
||||||
|
$params_set[":" . $set_key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработанные параметры
|
||||||
|
$params_where = [];
|
||||||
|
|
||||||
|
// Строковая интерпретация массива условий
|
||||||
|
$sql_where = $this->PrepareQueryWhere(where: $where, params: $params_where);
|
||||||
|
|
||||||
|
// Создаю параметры
|
||||||
|
$params = array_merge($params_set, $params_where);
|
||||||
|
|
||||||
|
// Создаю запрос
|
||||||
|
$sql = "UPDATE $this->DBSignOpen$table$this->DBSignClose SET $sql_set";
|
||||||
|
|
||||||
|
// Если заданы where-параметры
|
||||||
|
if (count($where) > 0)
|
||||||
|
// - то добавляю их
|
||||||
|
$sql .= " WHERE $sql_where";
|
||||||
|
|
||||||
|
// Выполняю запрос
|
||||||
|
$count = $this->Execute($sql, $params);
|
||||||
|
|
||||||
|
// Если результат - false
|
||||||
|
if ($count === false)
|
||||||
|
// - то и общий результат - false
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Если изменено 0 строк
|
||||||
|
if ($count === 0)
|
||||||
|
// - то и общий результат - false
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Вывожу результат -- успех
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Update (string $table, IDBItem $item): bool
|
||||||
|
{
|
||||||
|
$primaryKeys = $this->FindPrimaryKeys($item, DBOperation::Update);
|
||||||
|
|
||||||
|
$pk_keys = array_keys($primaryKeys);
|
||||||
|
|
||||||
|
$where = new ConditionBuilder();
|
||||||
|
|
||||||
|
for ($i = 0; $i < count($primaryKeys); $i++) {
|
||||||
|
if ($i > 0)
|
||||||
|
$where = $where->And();
|
||||||
|
|
||||||
|
$whereKey = $pk_keys[$i];
|
||||||
|
|
||||||
|
$whereValue = $primaryKeys[$whereKey];
|
||||||
|
|
||||||
|
$where = $where->WhereEquals($whereKey, $whereValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbItem = $this->GetRow($table, $where, className: get_class($item));
|
||||||
|
|
||||||
|
// Создаю массив параметров
|
||||||
|
$params_set = [];
|
||||||
|
|
||||||
|
// Строковая интерпретация массива для изменения
|
||||||
|
$sql_set = "";
|
||||||
|
|
||||||
|
// Для каждых данных для изменения
|
||||||
|
foreach ($set as $key => $value) {
|
||||||
|
// - получаю ключ 100%-но без ":" в начале
|
||||||
|
$set_key = $key[0] == ":" ? substr($key, 1) : $key;
|
||||||
|
|
||||||
|
// - добавляю префикс для 2 или более итерации
|
||||||
|
$prefix = $sql_set == "" ? "" : ", ";
|
||||||
|
|
||||||
|
// - добавляю данные в sql_set
|
||||||
|
$sql_set .= "$prefix$this->DBSignOpen$set_key$this->DBSignClose=:$set_key";
|
||||||
|
|
||||||
|
// - добавляю данные в параметры
|
||||||
|
$params_set[":" . $set_key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обработанные параметры
|
||||||
|
$params_where = [];
|
||||||
|
|
||||||
|
// Строковая интерпретация массива условий
|
||||||
|
$sql_where = $this->PrepareQueryWhere(where: $where, params: $params_where);
|
||||||
|
|
||||||
|
// Создаю параметры
|
||||||
|
$params = array_merge($params_set, $params_where);
|
||||||
|
|
||||||
|
// Создаю запрос
|
||||||
|
$sql = "UPDATE $this->DBSignOpen$table$this->DBSignClose SET $sql_set";
|
||||||
|
|
||||||
|
// Если заданы where-параметры
|
||||||
|
if (count($where) > 0)
|
||||||
|
// - то добавляю их
|
||||||
|
$sql .= " WHERE $sql_where";
|
||||||
|
|
||||||
|
// Выполняю запрос
|
||||||
|
$count = $this->Execute($sql, $params);
|
||||||
|
|
||||||
|
// Если результат - false
|
||||||
|
if ($count === false)
|
||||||
|
// - то и общий результат - false
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Если изменено 0 строк
|
||||||
|
if ($count === 0)
|
||||||
|
// - то и общий результат - false
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Вывожу результат -- успех
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user