2025-07-31 17:58:22 +03:00

300 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* @noinspection SqlNoDataSourceInspection
*/
namespace goodboyalex\php_db_components_pack\traits\Database;
use Exception;
use goodboyalex\php_db_components_pack\attributes\ConvertToDB;
use goodboyalex\php_db_components_pack\attributes\FieldName;
use goodboyalex\php_db_components_pack\attributes\IgnoredInDB;
use goodboyalex\php_db_components_pack\enums\DBOperation;
use goodboyalex\php_db_components_pack\interfaces\IDBItem;
use PDO;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
/**
* Трейт для функций поддержки для работы с базой данных.
*
* @author Александр Бабаев
* @package php_components_pack
* @version 1.0
* @since 1.0
* @see PDO
*/
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;
}
/**
* Находит атрибут в массиве.
*
* @param array $attrs Массив атрибутов.
* @param string $className Имя класса.
*
* @return object|null Объект атрибута или <code>null</code>
*/
private static function FindAttribute (array $attrs, string $className): ?object
{
return array_find($attrs, fn ($attr) => $attr->getName() === $className);
}
/**
* Проверяет, является ли свойство публичным.
*
* @param object $obj Объект.
* @param string $propertyName Имя свойства.
*
* @return bool Возвращает <code>true</code>, если свойство публичное, иначе <code>false</code>.
*/
private static function IsPublicProperty (object $obj, string $propertyName): bool
{
return property_exists($obj, $propertyName) && new ReflectionProperty($obj, $propertyName)->isPublic();
}
/**
* Подготавливает массив параметров
*
* @param IDBItem $source Объект со свойствами.
* @param DBOperation $operation Текущая операция.
*
* @return array|false Подготовленный массив параметров или false в случае ошибки
*/
private function PrepareParamsArray (IDBItem $source, DBOperation $operation): array|false
{
$result = [];
// Получаю массив свойств
$properties = get_class_vars(get_class($source));
// Для каждого свойства
foreach ($properties as $key => $value) {
// - пропускаю не публичные свойства
if (!self::IsPublicProperty($source, $key))
continue;
// Получаю рефлексию
// - для класса
$reflectedClass = new ReflectionClass(get_class($source));
try {
// - для свойства
$reflectionProperty = $reflectedClass->getProperty($key);
}
catch (ReflectionException $e) {
// - если ошибка, то вывожу и выходим
$this->HandleException($e);
}
// - получаю атрибуты
$attributes = $reflectionProperty->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 : $key;
/**
* Преобразование типа.
*
* @var ConvertToDB|null $convertAttr Атрибут конвертации.
*/
$convertAttr = self::FindAttribute($attributes, ConvertToDB::class);
// Если есть атрибут конвертации
if ($convertAttr) {
// - то получаю значение функции конвертации
$converter = $convertAttr->ConvertToDB;
// - выполняю функцию конвертации
$value = call_user_func($converter, $value);
}
// Добавляю в массив
$result[$fieldName] = $value;
}
// Возвращаю результат
return $result;
}
/**
* Обрабатывает исключение.
*
* @param Exception $exception Исключение.
*
* @return void
*/
private function HandleException (Exception $exception): void
{
// Выбираю обработчик исключений
$onException = $this->OnException ?? fn (Exception $e) => die($e->getMessage());
// Выполняю обработчик исключений
$onException($exception);
}
/**
* Подготавливает массив столбцов для использования в базе данных
*
* @param array $columns Массив колонок.
*
* @return array Массив преобразованных колонок.
*/
private function PrepareColumn (array $columns): array
{
return array_map(function ($item)
{
// Результирующая строка
$result = "";
// Если длинна строки > 0
if (strlen($item) > 0) {
// - первый символ
$firstLetter = substr($item, 0, 1);
// - последний символ
$lastLetter = substr($item, -1);
// - если первый символ не $this->DBSignOpen
if ($firstLetter !== $this->DBSignOpen)
// -- то добавляем
$result .= $this->DBSignOpen;
// - добавляем строку
$result .= $item;
// - если последний символ не $this->DBSignClose
if ($lastLetter !== $this->DBSignClose)
// -- то добавляем
$result .= $this->DBSignClose;
}
// Возвращаем результат
return $result;
}, $columns);
}
/**
* Генерирует SQL запрос выборки строк.
*
* @param string $table Имя таблицы
* @param array $columns Колонки, которые нужно включить в запрос
* @param array $where Параметры выборки
* @param array $params Параметры и их значения
*
* @return string SQL-запрос
*/
private function PrepareSQLForRowsQuery (string $table, array $columns = [], array $where = [],
array &$params = []): string
{
// Очищаю параметры
$params = [];
// Строковая интерпретация массива условий
$sql_where = $this->PrepareQueryWhere($where, $params);
// Колонки
$sql_columns = count($columns) > 0 ? implode(', ', $this->PrepareColumn($columns)) : "*";
// Создаю запрос
$sql = "SELECT $sql_columns FROM $this->DBSignOpen$table$this->DBSignClose";
// Если заданы where-параметры
if (count($where) > 0) {
// - то добавляю их
$sql .= " WHERE $sql_where";
}
// Возвращаю запрос
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;
}
}