This commit is contained in:
2025-07-31 17:58:22 +03:00
parent 45fb57981d
commit 2f3dd81d0a
3 changed files with 182 additions and 98 deletions

View File

@@ -7,10 +7,15 @@
namespace goodboyalex\php_db_components_pack\traits\Database;
use Exception;
use goodboyalex\php_components_pack\exceptions\TypeException;
use goodboyalex\php_components_pack\extensions\TypeExtension;
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;
/**
* Трейт для функций поддержки для работы с базой данных.
@@ -24,23 +29,143 @@
trait DatabaseSpecial
{
/**
* Добавляет параметр в массив.
* Подготавливает массив параметров для запроса.
*
* @param array $array Массив.
* @param string $key Имя параметра.
* @param mixed $value Значение параметра.
*
* @return void
* @return array Массив с подготовленными параметрами.
*/
private static function AddArrayItem (array &$array, string $key, mixed $value): void
private static function PrepareArray (array $array): array
{
// Если ключ параметра начинается с ":"
if ($key[0] == ":")
// - то сразу добавляем его в результирующий массив
$array[$key] = $value;
else
// - в противном случае, предварительно добавим в имя ключа ":"
$array[':' . $key] = $value;
// Результирующий массив
$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;
}
/**
@@ -59,79 +184,6 @@
$onException($exception);
}
/**
* Подготавливает массив параметров
*
* @param IDBItem $source Объект со свойствами.
* @param bool $withId Добавлять ли id в массив.
* @param array $options Опции.
*
* @return array|false Подготовленный массив параметров или false в случае ошибки
*/
private function PrepareParamsArray (IDBItem $source, bool $withId = true, array $options = []): array|false
{
// Создаём результирующий массив
$result = [];
// Если есть игнорируемые или разрешенные свойства
if (!(count($options['ignore']) == 0 && count($options['allow']) == 0))
// -- то для каждого игнорируемого свойства
foreach ($options['ignored'] as $ignoredProperty)
// --- и если оно есть в массиве разрешенных
if (in_array($ignoredProperty, $options['allowed']))
// ---- то исключаю его из массива разрешенных
unset($options['allowed'][array_search($ignoredProperty, $options['allowed'])]);
// Получаю массив свойств
$properties = $source->ToSQL($withId);
// Для каждого элемента массива
foreach ($properties as $name => $value) {
// - если свойство игнорируется
if (in_array($name, $options['ignored']))
// -- пропускаю
continue;
// - если свойство не разрешено
if (count($options['allowed']) > 0 && !in_array($name, $options['allowed']))
// -- пропускаю
continue;
// - если свойство является объектом
if (is_object($source->$name)) {
try {
// -- пытаюсь преобразовать его в массив
self::AddArrayItem($result, $name, json_encode(TypeExtension::ToArray($source->$name),
JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
catch (TypeException) {
// -- если не получилось, то сериализую его
self::AddArrayItem($result, $name,
json_encode($source->$name, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
}
// -- пропускаю
continue;
}
// - если свойство является массивом
if (is_array($source->$name)) {
// -- сериализую его
self::AddArrayItem($result, $name, json_encode($source->$name, JSON_UNESCAPED_UNICODE |
JSON_PRETTY_PRINT));
// -- пропускаю
continue;
}
// - иначе просто добавляю свойство в массив
self::AddArrayItem($result, $name, $source->$name);
}
// Вывожу результирующий массив
return $result;
}
/**
* Подготавливает массив столбцов для использования в базе данных
*