From 75214a71d33883dfc264cb45c75360a238748263 Mon Sep 17 00:00:00 2001 From: User Date: Sun, 22 Jun 2025 01:05:13 +0300 Subject: [PATCH] 20250622#1 --- src/AbstractDTO.php | 39 +++++ src/AbstractObjectData.php | 134 ++++++++++++++++ src/Attribute/Data.php | 14 ++ src/Attribute/DataIgnorEmpty.php | 8 + src/Attribute/Entity.php | 14 ++ src/Attribute/EntityNoReturnIfNull.php | 8 + src/Attribute/Property.php | 16 ++ src/Attribute/PropertyNoReturn.php | 8 + src/Attribute/PropertyNoReturnIfNull.php | 8 + src/Attribute/ValueObject.php | 15 ++ src/Attribute/ValueObjectFirstProperty.php | 8 + src/Attribute/ValueObjectPropertyName.php | 14 ++ src/ODMException.php | 16 ++ src/ObjectDataMapper.php | 176 +++++++++++++++++++++ 14 files changed, 478 insertions(+) create mode 100644 src/AbstractDTO.php create mode 100644 src/AbstractObjectData.php create mode 100644 src/Attribute/Data.php create mode 100644 src/Attribute/DataIgnorEmpty.php create mode 100644 src/Attribute/Entity.php create mode 100644 src/Attribute/EntityNoReturnIfNull.php create mode 100644 src/Attribute/Property.php create mode 100644 src/Attribute/PropertyNoReturn.php create mode 100644 src/Attribute/PropertyNoReturnIfNull.php create mode 100644 src/Attribute/ValueObject.php create mode 100644 src/Attribute/ValueObjectFirstProperty.php create mode 100644 src/Attribute/ValueObjectPropertyName.php create mode 100644 src/ODMException.php create mode 100644 src/ObjectDataMapper.php diff --git a/src/AbstractDTO.php b/src/AbstractDTO.php new file mode 100644 index 0000000..3b7c2d5 --- /dev/null +++ b/src/AbstractDTO.php @@ -0,0 +1,39 @@ +getName()][0])){ + self::$attributeObjects[$class->getName()][0] = !empty($class->getAttributes(Data::class)) + ? $class->getAttributes(Data::class)[0]->newInstance() + : new Data(); + } + /** @var Data $dataAttributes */ + $dataAttributes = self::$attributeObjects[$class->getName()][0]; + if(!empty($class->getAttributes(DataIgnorEmpty::class))) $dataAttributes->ignorEmpty = true; + + $value = []; + foreach($class->getProperties() as $property){ + $prop[$property->getName()] = ($property->hasType()) ? $property->getType()->getName() : ""; + + // значение в массиве по ключю с именем свойства + if(array_key_exists($property->getName(), $data)){ + $value[$property->getName()] = $data[$property->getName()]; + } + // значение в массиве по ключю с именем свойства в snake case + elseif(array_key_exists(strtolower(preg_replace("'([A-Z])'", "_$1", $property->getName())), $data)){ + $value[$property->getName()] = $data[strtolower(preg_replace("'([A-Z])'", "_$1", $property->getName()))]; + } + elseif($update) { + continue; + } + + // если есть внутренний метод (приоритетная обработка) + if($class->hasMethod('set'.ucfirst($property->getName()))) { + $object->{'set'.ucfirst($property->getName())}($value[$property->getName()] ?? null); + $case[$property->getName()] = 'Method set'.ucfirst($property->getName()); + } + // Если тип свойства класс (valueObject) + elseif($property->hasType() && class_exists($property->getType()->getName())) { + // значение объект + if(isset($value[$property->getName()]) && is_object($value[$property->getName()])){ + $object->{$property->getName()} = $value[$property->getName()]; + $case[$property->getName()] = 'VO: Object'; + } + // значение не пустое + elseif(isset($value[$property->getName()]) && $value[$property->getName()] !== ""){ + $object->{$property->getName()} = new ($property->getType()->getName())($value[$property->getName()]); + $case[$property->getName()] = 'VO: NewInstance'; + } + // Значения нет и VO может быть без параметров + elseif(($withEmpty && empty($dataAttributes->ignorEmpty)) && self::isEmptyAvailable($property->getType()->getName())) { + $object->{$property->getName()} = new ($property->getType()->getName())(); + $case[$property->getName()] = 'VO: Without params'; + } + } + // Базовые типы при наличии значения + elseif(array_key_exists($property->getName(), $value)){ + if(!$property->hasType()){ + $object->{$property->getName()} = $value[$property->getName()]; + $case[$property->getName()] = 'Base: Hasn`t type'; + } + elseif(in_array($property->getType()->getName(), ['float', 'int'])){ + if(is_numeric($value[$property->getName()])) $object->{$property->getName()} = $value[$property->getName()]; + $case[$property->getName()] = 'Base: Number'; + } + elseif($property->getType()->getName() == 'bool'){ + $object->{$property->getName()} = (bool)$value[$property->getName()]; + $case[$property->getName()] = 'Base: Boolean'; + } + elseif(isset($value[$property->getName()])){ + $object->{$property->getName()} = $value[$property->getName()]; + $case[$property->getName()] = 'Base: NotNull'; + } + elseif($property->getType()->allowsNull()){ + $object->{$property->getName()} = $value[$property->getName()]; + $case[$property->getName()] = 'Base: Null'; + } + } + } + self::$stack[$object::class." #".spl_object_id($object)]['properties'] = $prop ?? []; + self::$stack[$object::class." #".spl_object_id($object)]['values'] = $value; + self::$stack[$object::class." #".spl_object_id($object)]['matchCase'] = $case ?? []; + self::$stack[$object::class." #".spl_object_id($object)]['object'] = $object; + return $object; + } + catch (ReflectionException $exception) { + throw new Exception($exception->getMessage()); + } + } + + + /** + * @throws ReflectionException + */ + private static function isEmptyAvailable(string $class) : bool { + if(isset(self::$constructorEmptyAvailableClasses[$class])) return self::$constructorEmptyAvailableClasses[$class]; + if(!$constructor = (new \ReflectionClass($class))->getConstructor()) return self::$constructorEmptyAvailableClasses[$class] = false; + foreach($constructor->getParameters() as $param){ + if(!$param->isDefaultValueAvailable()){ + return self::$constructorEmptyAvailableClasses[$class] = false; + } + } + return self::$constructorEmptyAvailableClasses[$class] = true; + } + + /** + * @return array + */ + protected function getFillObjectStack() : array { + return self::$stack; + } + +} diff --git a/src/Attribute/Data.php b/src/Attribute/Data.php new file mode 100644 index 0000000..e1ae54e --- /dev/null +++ b/src/Attribute/Data.php @@ -0,0 +1,14 @@ +data = $data; + } + +} diff --git a/src/ObjectDataMapper.php b/src/ObjectDataMapper.php new file mode 100644 index 0000000..6a9fe33 --- /dev/null +++ b/src/ObjectDataMapper.php @@ -0,0 +1,176 @@ +getAttributes(Entity::class)) + ? self::$classes[$class]->getAttributes(Entity::class)[0]->newInstance() + : new Entity(); + } + /** @var Entity $entityAttributes */ + $entityAttributes = self::$attributeObjects[$class][0]; + if(!empty(self::$classes[$class]->getAttributes(EntityNoReturnIfNull::class))) $entityAttributes->noReturnIfNull = true; + + $fieldValue = []; + foreach(self::$classes[$class]->getProperties() as $property){ + + if(!isset(self::$attributeObjects[$class][$property->getName()])){ + self::$attributeObjects[$class][$property->getName()] = !empty($property->getAttributes(Property::class)) + ? $property->getAttributes(Property::class)[0]->newInstance() + : new Property(); + } + /** @var Property $propertyAttributes */ + $propertyAttributes = self::$attributeObjects[$class][$property->getName()]; + if(!empty($property->getAttributes(PropertyNoReturnIfNull::class))) $propertyAttributes->noReturnIfNull = true; + + if(!empty($property->getAttributes(PropertyNoReturn::class)) || !empty($propertyAttributes->noReturn)) continue; + + if($property->isInitialized($object)) { + + if(is_array($property->getValue($object))) continue; + + if(self::$classes[$class]->hasMethod('get'.ucfirst($property->getName()))){ + $fieldValue[$property->getName()] = $object->{'get'.ucfirst($property->getName())}($property->getValue($object)); + } + elseif($property->hasType() && class_exists($property->getType()->getName())){ + $valueObjectClass = get_class($property->getValue($object)); + if(!isset(self::$classes[$valueObjectClass])) self::$classes[$valueObjectClass] = new ReflectionClass($valueObjectClass); + + if(!isset(self::$attributeObjects[$valueObjectClass])){ + self::$attributeObjects[$valueObjectClass] = !empty(self::$classes[$valueObjectClass]->getAttributes(ValueObject::class)) + ? self::$classes[$valueObjectClass]->getAttributes(ValueObject::class)[0]->newInstance() + : new ValueObject(); + if(!empty(self::$classes[$valueObjectClass]->getAttributes(ValueObjectFirstProperty::class))) { + self::$attributeObjects[$valueObjectClass]->firstProperty = true; + } + if(!empty(self::$classes[$valueObjectClass]->getAttributes(ValueObjectPropertyName::class))) { + $propertyName = self::$classes[$valueObjectClass]->getAttributes(ValueObjectPropertyName::class)[0]->newInstance(); + if(isset($propertyName->name)) self::$attributeObjects[$valueObjectClass]->propertyName = $propertyName->name; + } + } + $valueObjectAttributes = self::$attributeObjects[$valueObjectClass]; + + if(!empty($valueObjectAttributes->propertyName) && self::$classes[$valueObjectClass]->hasProperty($valueObjectAttributes->propertyName)){ + if(self::$classes[$valueObjectClass]->getProperty($valueObjectAttributes->propertyName)->isInitialized($property->getValue($object))){ + $fieldValue[$property->getName()] = self::$classes[$valueObjectClass]->getProperty($valueObjectAttributes->propertyName)->getValue($property->getValue($object)); + } + } + elseif(!empty($valueObjectAttributes->firstProperty) && count(self::$classes[$valueObjectClass]->getProperties()) > 0){ + if(self::$classes[$valueObjectClass]->getProperties()[0]->isInitialized($property->getValue($object))){ + $fieldValue[$property->getName()] = self::$classes[$valueObjectClass]->getProperties()[0]->getValue($property->getValue($object)); + } + } + elseif(self::$classes[$valueObjectClass]->hasMethod('getValue')){ + $fieldValue[$property->getName()] = $property->getValue($object)->getValue(); + } + } + elseif(is_bool($property->getValue($object))){ + $fieldValue[$property->getName()] = (int)$property->getValue($object); + } + else{ + $fieldValue[$property->getName()] = $property->getValue($object); + } + + if(!isset($fieldValue[$property->getName()]) && (!empty($propertyAttributes->noReturnIfNull) || !empty($entityAttributes->noReturnIfNull))) continue; + + if(array_key_exists($property->getName(), $fieldValue) && false !== $fieldValue[$property->getName()]) { + $columnName = !empty($propertyAttributes->keyName) ? $propertyAttributes->keyName : strtolower(preg_replace("'([A-Z])'", "_$1", $property->getName())); + $out[$columnName] = $fieldValue[$property->getName()]; + } + } + } + return (isset($method)) ? array_map($method, $out ?? []) : $out ?? []; + } + catch (\ReflectionException $exception) { + throw new ODMException($exception->getMessage()); + } + } + + /** + * @param string $class + * @param array|object $data + * @param bool $withEmpty + * @return object + * @throws ODMException + */ + public function createObjectFromData(string $class, array|object $data, bool $withEmpty = true) : object { + try { + if(!isset(self::$classes[$class])) self::$classes[$class] = new ReflectionClass($class); + return self::fillObject(self::$classes[$class], new $class, (is_object($data)) ? get_object_vars($data) : $data, false, $withEmpty); + } + catch (Exception $exception) { + throw new ODMException($exception->getMessage()); + } + } + + /** + * @param object $object + * @param array|object $data + * @param bool $withEmpty + * @return object + * @throws ODMException + */ + public function updateObjectFromData(object $object, array|object $data, bool $withEmpty = true) : object { + try { + $class = get_class($object); + if(!isset(self::$classes[$class])) self::$classes[$class] = new ReflectionClass($class); + return self::fillObject(self::$classes[$class], clone $object, (is_object($data)) ? get_object_vars($data) : $data, true, $withEmpty); + } + catch (Exception $exception) { + throw new ODMException($exception->getMessage()); + } + } + + /** + * @return array + */ + public function getRepositoryStack() : array { + return $this->getFillObjectStack(); + } + + /** + * @return array + */ + public function getClassesCache() : array { + return self::$classes; + } + + /** + * @return array + */ + public function getAttributesObjectsCache() : array { + return self::$attributeObjects; + } + + /** + * @return array + */ + public function getConstructorEmptyAvailableClassesCache() : array { + return self::$constructorEmptyAvailableClasses; + } + +}