From 2f3dd81d0a484e88524bdcb9d73e6ed0c65038f2 Mon Sep 17 00:00:00 2001 From: babaev-an Date: Thu, 31 Jul 2025 17:58:22 +0300 Subject: [PATCH] 20250731 --- sources/attributes/FieldName.php | 37 ++++ sources/traits/Database/DatabaseInsert.php | 17 +- sources/traits/Database/DatabaseSpecial.php | 226 ++++++++++++-------- 3 files changed, 182 insertions(+), 98 deletions(-) create mode 100644 sources/attributes/FieldName.php diff --git a/sources/attributes/FieldName.php b/sources/attributes/FieldName.php new file mode 100644 index 0000000..96fe632 --- /dev/null +++ b/sources/attributes/FieldName.php @@ -0,0 +1,37 @@ +FieldName = $fieldName; + } + } \ No newline at end of file diff --git a/sources/traits/Database/DatabaseInsert.php b/sources/traits/Database/DatabaseInsert.php index 0e285a7..8d308bc 100644 --- a/sources/traits/Database/DatabaseInsert.php +++ b/sources/traits/Database/DatabaseInsert.php @@ -8,6 +8,7 @@ use Exception; use goodboyalex\php_components_pack\classes\Tuple; + use goodboyalex\php_db_components_pack\enums\DBOperation; use goodboyalex\php_db_components_pack\interfaces\IDBItem; use PDO; use PDOException; @@ -28,20 +29,14 @@ * * @param string $table Имя таблицы. * @param IDBItem $row Модель или класс, реализующий интерфейс IDBItem, для вставки. - * @param array $options Массив дополнительных параметров. Может содержать следующие ключи: * - * - ignore: array - игнорировать перечисленные поля. Когда массив пуст, то ничего - * игнорироваться не будет. По умолчанию - пустой массив. - * - allow: array - включать только перечисленные поля. Когда массив пуст, то все поля будут - * включены. По умолчанию - пустой массив. - * - * @return string|false В случае успеха выведет: id созданной записи, -1, если запись создана, но id не получен + * @return mixed В случае успеха выведет: id созданной записи, -1, если запись создана, но id не получен * (глюк?) и false, если ошибка */ - public function Insert (string $table, IDBItem $row, array $options = []): string|false + public function Insert (string $table, IDBItem $row): mixed { // Подготавливаю запрос - [$sql, $params] = $this->PrepareInsertSQL($table, $row, $options); + [$sql, $params] = $this->PrepareInsertSQL($table, $row); // Выполняю запрос $count = $this->Execute($sql, $params); @@ -115,10 +110,10 @@ * * @return Tuple Возвращает [запрос, параметры запроса]. */ - private function PrepareInsertSQL (string $table, IDBItem $row, array $options = []): Tuple + private function PrepareInsertSQL (string $table, IDBItem $row): Tuple { // Подготавливаю массив параметров - $params = $this->PrepareParamsArray(source: $row, options: $options); + $params = $this->PrepareParamsArray(source: $row, operation: DBOperation::Insert); // Получаю ключи параметров $keys = array_keys($params); diff --git a/sources/traits/Database/DatabaseSpecial.php b/sources/traits/Database/DatabaseSpecial.php index 928a0c9..901ad57 100644 --- a/sources/traits/Database/DatabaseSpecial.php +++ b/sources/traits/Database/DatabaseSpecial.php @@ -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 Объект атрибута или null + */ + 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 Возвращает true, если свойство публичное, иначе false. + */ + 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; - } - /** * Подготавливает массив столбцов для использования в базе данных *