From dfbcfdf321acf0dcfd9cc2cd6616134f71ef6ffc Mon Sep 17 00:00:00 2001 From: User Date: Sat, 22 Feb 2025 16:22:21 +0300 Subject: [PATCH] 20250222#1 --- composer.json | 2 +- src/AbstractMysqlRepository.php | 4 +- src/AbstractRepository.php | 113 ----------------- src/Component/AbstractDataObject.php | 124 +++++++++++++++++++ src/Mysql/MysqlRepositoryInterface.php | 4 +- src/Repository/AbstractRepository.php | 93 ++++++++++++++ src/{ => Repository}/RepositoryException.php | 2 +- src/{ => Repository}/RepositoryInterface.php | 46 +++---- 8 files changed, 249 insertions(+), 139 deletions(-) delete mode 100644 src/AbstractRepository.php create mode 100644 src/Component/AbstractDataObject.php create mode 100644 src/Repository/AbstractRepository.php rename src/{ => Repository}/RepositoryException.php (88%) rename src/{ => Repository}/RepositoryInterface.php (58%) diff --git a/composer.json b/composer.json index e9340c0..2a3c8d5 100644 --- a/composer.json +++ b/composer.json @@ -16,5 +16,5 @@ "Rmphp\\Storage\\": "src/" } } - + } diff --git a/src/AbstractMysqlRepository.php b/src/AbstractMysqlRepository.php index 5151cbd..5a4b70f 100644 --- a/src/AbstractMysqlRepository.php +++ b/src/AbstractMysqlRepository.php @@ -6,6 +6,8 @@ use Rmphp\Storage\Entity\EntityInterface; use Rmphp\Storage\Mysql\MysqlRepositoryInterface; use Rmphp\Storage\Mysql\MysqlResultData; use Rmphp\Storage\Mysql\MysqlStorageInterface; +use Rmphp\Storage\Repository\AbstractRepository; +use Rmphp\Storage\Repository\RepositoryException; abstract class AbstractMysqlRepository extends AbstractRepository implements MysqlRepositoryInterface { @@ -58,7 +60,7 @@ abstract class AbstractMysqlRepository extends AbstractRepository implements Mys $in = $this->getProperties($object, function ($value){ return (is_string($value)) ? $this->mysql->escapeStr($value) : $value; }); - if($this->getDebug()) {$this->debug($object, $in, $table); exit;} + if($this->getDebug()) {$this->debug($object, $in, $table, $this->getClassesCash(), $this->getRepositoryStack()); exit;} try { if (!empty($object->getId()) && !empty($this->mysql->findById($table, $object->getId()))) { $this->mysql->updateById($table, $in, $object->getId()); diff --git a/src/AbstractRepository.php b/src/AbstractRepository.php deleted file mode 100644 index 7b893e6..0000000 --- a/src/AbstractRepository.php +++ /dev/null @@ -1,113 +0,0 @@ -fillObject(static::$classes[$class], new $class, $data); - } - catch (ReflectionException $exception) { - throw new RepositoryException($exception->getMessage()); - } - } - - - /** @inheritDoc */ - public function updateFromData(object $object, array $data) : object { - try { - $class = get_class($object); - if(!isset(static::$classes[$class])) static::$classes[$class] = new ReflectionClass($class); - return $this->fillObject(static::$classes[$class], clone $object, $data, true); - } - catch (RepositoryException|ReflectionException $exception) { - throw new RepositoryException($exception->getMessage()); - } - } - - - /** @inheritDoc */ - public function getProperties(object $object, callable $method = null) : array { - try{ - $class = get_class($object); - if(!isset(static::$classes[$class])) static::$classes[$class] = new ReflectionClass($class); - - /** @var ReflectionProperty $property */ - foreach(static::$classes[$class]->getProperties() as $property){ - if(!$property->isInitialized($object) || is_array($property->getValue($object))) continue; - - if(static::$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()) && $property->getValue($object) instanceof ValueObjectInterface){ - $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(false !== $fieldValue[$property->getName()]) { - $out[strtolower(preg_replace("'([A-Z])'", "_$1", $property->getName()))] = $fieldValue[$property->getName()]; - } - } - return (isset($method)) ? array_map($method, $out ?? []) : $out ?? []; - } - catch (ReflectionException $exception) { - throw new RepositoryException($exception->getMessage()); - } - } - - - /** - * @param ReflectionClass $class - * @param object $object - * @param array $data - * @param bool $update - * @return mixed - * @throws RepositoryException - */ - private function fillObject(ReflectionClass $class, object $object, array $data, bool $update = false) : mixed { - try { - foreach($class->getProperties() as $property){ - if($update && !array_key_exists($property->getName(), $data) && !array_key_exists(strtolower(preg_replace("'([A-Z])'", "_$1", $property->getName())), $data)) continue; - - // data[propertyName] ?? data[property_name] ?? null - $value = $data[$property->getName()] ?? $data[strtolower(preg_replace("'([A-Z])'", "_$1", $property->getName()))] ?? null; - - // если есть внутренний метод (приоритетная обработка) - if($class->hasMethod('set'.ucfirst($property->getName()))) { - $object->{'set'.ucfirst($property->getName())}($value); - } - // Если тип свойства класс (valueObject) - elseif($property->hasType() && class_exists($property->getType()->getName())) { - $object->{$property->getName()} = (is_object($value)) ? $value : new ($property->getType()->getName())($value); - } - // если значения не пустое - elseif(isset($value)) { - $object->{$property->getName()} = $value; - } - // если значения может быть пустое - elseif($property->getType()->allowsNull()) { - $object->{$property->getName()} = null; - } - } - return $object; - } - catch (ReflectionException $exception) { - throw new RepositoryException($exception->getMessage()); - } - } -} diff --git a/src/Component/AbstractDataObject.php b/src/Component/AbstractDataObject.php new file mode 100644 index 0000000..7e44821 --- /dev/null +++ b/src/Component/AbstractDataObject.php @@ -0,0 +1,124 @@ +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); + } + // Если тип свойства класс (valueObject) + elseif($property->hasType() && class_exists($property->getType()->getName())) { + if(is_object($value[$property->getName()])){ + $object->{$property->getName()} = $value[$property->getName()]; + } + elseif(isset($value[$property->getName()])){ + $object->{$property->getName()} = new ($property->getType()->getName())($value[$property->getName()]); + } + // Значение NULL и VO может принимать null в виде единственного значения + elseif(array_key_exists($property->getName(), $value) && self::isNullAvailable($property->getType()->getName())) { + $object->{$property->getName()} = new ($property->getType()->getName())(null); + } + // Значения нет и VO может быть без параметров + elseif(self::isEmptyAvailable($property->getType()->getName())) { + $object->{$property->getName()} = new ($property->getType()->getName())(); + } + } + // Базовые типы при наличии значения + elseif(array_key_exists($property->getName(), $value)){ + if(!$property->hasType()){ + $object->{$property->getName()} = $value[$property->getName()]; + } + elseif(in_array($property->getType()->getName(), ['float', 'int'])){ + if(is_numeric($value[$property->getName()])) $object->{$property->getName()} = $value[$property->getName()]; + } + elseif($property->getType()->getName() == 'bool'){ + $object->{$property->getName()} = (bool)$value[$property->getName()]; + } + elseif(isset($value[$property->getName()])){ + $object->{$property->getName()} = $value[$property->getName()]; + } + elseif($property->getType()->allowsNull()){ + $object->{$property->getName()} = 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)]['object'] = $object; + return $object; + } + catch (ReflectionException $exception) { + throw new Exception($exception->getMessage()); + } + } + + + /** + * @throws ReflectionException + */ + private static function isNullAvailable(string $class) : bool { + if(isset(self::$constructorNullAvailableClasses[$class])) return self::$constructorNullAvailableClasses[$class]; + if(!$constructor = (new \ReflectionClass($class))->getConstructor()) return self::$constructorNullAvailableClasses[$class] = false; + $parameters = $constructor->getParameters(); + if(count($parameters) == 1 && $parameters[0]->allowsNull()) return self::$constructorNullAvailableClasses[$class] = true; + return self::$constructorNullAvailableClasses[$class] = false; + } + + + /** + * @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/Mysql/MysqlRepositoryInterface.php b/src/Mysql/MysqlRepositoryInterface.php index 6374fb1..eda905f 100644 --- a/src/Mysql/MysqlRepositoryInterface.php +++ b/src/Mysql/MysqlRepositoryInterface.php @@ -9,8 +9,8 @@ namespace Rmphp\Storage\Mysql; use Rmphp\Storage\Entity\EntityInterface; -use Rmphp\Storage\RepositoryException; -use Rmphp\Storage\RepositoryInterface; +use Rmphp\Storage\Repository\RepositoryException; +use Rmphp\Storage\Repository\RepositoryInterface; interface MysqlRepositoryInterface extends RepositoryInterface { diff --git a/src/Repository/AbstractRepository.php b/src/Repository/AbstractRepository.php new file mode 100644 index 0000000..a24c72c --- /dev/null +++ b/src/Repository/AbstractRepository.php @@ -0,0 +1,93 @@ +getProperties() as $property){ + if(!$property->isInitialized($object) || 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()) && $property->getValue($object) instanceof ValueObjectInterface){ + $voClass = $property->getType()->getName(); + if(!isset(self::$classes[$voClass])) self::$classes[$voClass] = new ReflectionClass($voClass); + $dd[$property->getName()] = self::$classes[$voClass]; + $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(array_key_exists($property->getName(), $fieldValue)) { + $out[strtolower(preg_replace("'([A-Z])'", "_$1", $property->getName()))] = $fieldValue[$property->getName()]; + } + } + //dd($fieldValue); + //dd($dd, self::$classes); + return (isset($method)) ? array_map($method, $out ?? []) : $out ?? []; + } + catch (ReflectionException $exception) { + throw new RepositoryException($exception->getMessage()); + } + } + + + /** @inheritDoc */ + public function createFromData(string $class, array|object $data) : 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); + } + catch (Exception $exception) { + throw new RepositoryException($exception->getMessage()); + } + } + + + /** @inheritDoc */ + public function updateFromData(object $object, array|object $data) : 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); + } + catch (Exception $exception) { + throw new RepositoryException($exception->getMessage()); + } + } + + /** + * @return array + */ + public function getRepositoryStack() : array { + return $this->getFillObjectStack(); + } + + /** + * @return array + */ + public function getClassesCash() : array { + return self::$classes; + } + +} diff --git a/src/RepositoryException.php b/src/Repository/RepositoryException.php similarity index 88% rename from src/RepositoryException.php rename to src/Repository/RepositoryException.php index 16b6250..8adfb77 100644 --- a/src/RepositoryException.php +++ b/src/Repository/RepositoryException.php @@ -1,6 +1,6 @@