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

@ -0,0 +1,37 @@
<?php
/**
* Отключаю несущественные инспекции (из-за Attribute)
*
* @noinspection PhpMultipleClassDeclarationsInspection
*/
namespace goodboyalex\php_db_components_pack\attributes;
use Attribute;
/**
* Атрибут указывающий имя поля в БД.
*
* @author Александр Бабаев
* @package php_db_components_pack
* @version 1.0
* @since 1.0
*/
#[Attribute(flags: Attribute::TARGET_PROPERTY)]
final readonly class FieldName
{
/**
* @var string $FieldName Имя поля.
*/
public string $FieldName;
/**
* Конструктор.
*
* @param string $fieldName Имя поля.
*/
public function __construct (string $fieldName)
{
$this->FieldName = $fieldName;
}
}

View File

@ -8,6 +8,7 @@
use Exception; use Exception;
use goodboyalex\php_components_pack\classes\Tuple; use goodboyalex\php_components_pack\classes\Tuple;
use goodboyalex\php_db_components_pack\enums\DBOperation;
use goodboyalex\php_db_components_pack\interfaces\IDBItem; use goodboyalex\php_db_components_pack\interfaces\IDBItem;
use PDO; use PDO;
use PDOException; use PDOException;
@ -28,20 +29,14 @@
* *
* @param string $table Имя таблицы. * @param string $table Имя таблицы.
* @param IDBItem $row Модель или класс, реализующий интерфейс IDBItem, для вставки. * @param IDBItem $row Модель или класс, реализующий интерфейс IDBItem, для вставки.
* @param array $options Массив дополнительных параметров. Может содержать следующие ключи:
* *
* - <code>ignore: array</code> - игнорировать перечисленные поля. Когда массив пуст, то ничего * @return mixed В случае успеха выведет: id созданной записи, -1, если запись создана, но id не получен
* игнорироваться не будет. По умолчанию - пустой массив.
* - <code>allow: array</code> - включать только перечисленные поля. Когда массив пуст, то все поля будут
* включены. По умолчанию - пустой массив.
*
* @return string|false В случае успеха выведет: id созданной записи, -1, если запись создана, но id не получен
* (глюк?) и false, если ошибка * (глюк?) и 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); $count = $this->Execute($sql, $params);
@ -115,10 +110,10 @@
* *
* @return Tuple Возвращает [запрос, параметры запроса]. * @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); $keys = array_keys($params);

View File

@ -7,10 +7,15 @@
namespace goodboyalex\php_db_components_pack\traits\Database; namespace goodboyalex\php_db_components_pack\traits\Database;
use Exception; use Exception;
use goodboyalex\php_components_pack\exceptions\TypeException; use goodboyalex\php_db_components_pack\attributes\ConvertToDB;
use goodboyalex\php_components_pack\extensions\TypeExtension; 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 goodboyalex\php_db_components_pack\interfaces\IDBItem;
use PDO; use PDO;
use ReflectionClass;
use ReflectionException;
use ReflectionProperty;
/** /**
* Трейт для функций поддержки для работы с базой данных. * Трейт для функций поддержки для работы с базой данных.
@ -24,23 +29,143 @@
trait DatabaseSpecial trait DatabaseSpecial
{ {
/** /**
* Добавляет параметр в массив. * Подготавливает массив параметров для запроса.
* *
* @param array $array Массив. * @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] == ":") $result = [];
// - то сразу добавляем его в результирующий массив
$array[$key] = $value; // Для каждого элемента массива
else foreach ($array as $key => $value)
// - в противном случае, предварительно добавим в имя ключа ":" // - если ключ начинается с ":"
$array[':' . $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); $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;
}
/** /**
* Подготавливает массив столбцов для использования в базе данных * Подготавливает массив столбцов для использования в базе данных
* *