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;
- }
-
/**
* Подготавливает массив столбцов для использования в базе данных
*