20250712 1.1.1 Beta 4
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -78,4 +78,5 @@ fabric.properties | ||||
| .idea/caches/build_file_checksums.ser | ||||
|  | ||||
| .idea/ | ||||
| vendor/ | ||||
| vendor/ | ||||
| /tests/extensions/class.txt | ||||
|   | ||||
							
								
								
									
										179
									
								
								sources/extensions/TypeExtension.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								sources/extensions/TypeExtension.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| # Описание класса TypeExtension | ||||
|  | ||||
| ## Информация о версии | ||||
|  | ||||
| Версия класса: 1.0 | ||||
|  | ||||
| Впервые введено в пакет с версии: 1.1.1 | ||||
|  | ||||
| Описание класса: Расширение для любого типа. | ||||
|  | ||||
| ## Публичные свойства и константы класса | ||||
|  | ||||
| К публичным свойствам можно отнести следующие статичные функции: `DEFAULT_TO_ARRAY_ON_CLASS` и | ||||
| `DEFAULT_FROM_ARRAY_ON_CLASS`. Они описывают методы обработки классов по умолчанию для методов `ToArray` и `FromArray` | ||||
| соответственно. | ||||
|  | ||||
| ### Метод `DEFAULT_TO_ARRAY_ON_CLASS` | ||||
|  | ||||
| Этот метод возвращает `Closure`, который можно использовать (и он используется при значении параметра `null`) во втором | ||||
| параметре метода `ToArray`. | ||||
|  | ||||
| ### Метод `DEFAULT_FROM_ARRAY_ON_CLASS` | ||||
|  | ||||
| Этот метод возвращает `Closure`, который можно использовать (и он используется при значении параметра `null`) во втором | ||||
| параметре метода `FromArray`. | ||||
|  | ||||
| ## Методы и функции | ||||
|  | ||||
| ### Метод `ToArray` | ||||
|  | ||||
| Этот _статический_ метод переводит класс в массив. Он содержит **1 обязательный параметр**: | ||||
|  | ||||
| * `object $class` - класс, который нужно перевести в массив | ||||
|  | ||||
| и **1 необязательный параметр**: | ||||
|  | ||||
| * `?callable $onClass = null` - метод обработки классов. Если передаётся `null`, то используется | ||||
|   `DEFAULT_TO_ARRAY_ON_CLASS`. По умолчанию, `null`. | ||||
|  | ||||
| В качестве метода обработки классов `onClass` подойдет любое `Closure` вида | ||||
|  | ||||
|     fn (object $class): array => <ДЕЙСТВИЕ>; | ||||
|  | ||||
| Метод возвращает `array` - массив публичных свойств. | ||||
|  | ||||
| Синтаксис: | ||||
|  | ||||
|     public static function ToArray (object $class, ?callable $onClass = null): array | ||||
|  | ||||
| Пример, | ||||
|  | ||||
|     // Создаём тестовый класс (см. TypeExtensionTest) | ||||
|     $class = new D ( | ||||
|       'test_string', | ||||
|       12345, | ||||
|       true, | ||||
|       new A("test_string_A", 6789, false), | ||||
|       new B("test_string_B", 9876, "false"), | ||||
|       new C("test_string_C", 54321, true) | ||||
|     ); | ||||
|  | ||||
|     // Создаём метод обработки классов | ||||
|     // - воспользуемся тем, что класс C реализует интерфейс ISerialize | ||||
|     $c_closure = fn (C $class): array => | ||||
|       [ | ||||
|         "type_class" => C::class, | ||||
|         "type_value" => $class->Serialize() | ||||
|       ]; | ||||
|      | ||||
|     // - зададим метод обработки | ||||
|     $closure = fn (object $class): array => $class instanceof C | ||||
|       ? $c_closure($class) | ||||
|       : TypeExtension::ToArray($class); | ||||
|  | ||||
|     // Преобразуем в массив | ||||
|     try { | ||||
|       $array = TypeExtension::ToArray($class, $closure); | ||||
|     } | ||||
|     catch (TypeException $e) { | ||||
|       // - если ошибка, то выводим её и завершаем работу | ||||
|       die($e->getMessage()); | ||||
|     } | ||||
|  | ||||
|     // Вывожу в файл | ||||
|     file_put_contents(__DIR__ . "/class.txt", json_encode($array, JSON_PRETTY_PRINT)); | ||||
|  | ||||
| В результате содержимое файла: | ||||
|  | ||||
|     { | ||||
|       "type_class": "goodboyalex\\php_components_pack\\tests\\data\\D", | ||||
|       "stringD": "test_string", | ||||
|       "intD": 12345, | ||||
|       "boolD": true, | ||||
|       "a": { | ||||
|         "type_class": "goodboyalex\\php_components_pack\\tests\\data\\A", | ||||
|         "a": "test_string_A", | ||||
|         "b": 6789, | ||||
|         "c": false | ||||
|       }, | ||||
|       "b": { | ||||
|         "type_class": "goodboyalex\\php_components_pack\\tests\\data\\B", | ||||
|         "a": "test_string_B", | ||||
|         "b": 9876, | ||||
|         "d": "false" | ||||
|       }, | ||||
|       "c": { | ||||
|         "type_class": "goodboyalex\\php_components_pack\\tests\\data\\C", | ||||
|         "type_value": "{\"string\":\"test_string_C\",\"int\":54321,\"bool\":true}" | ||||
|       } | ||||
|     } | ||||
|  | ||||
| #### Выбрасываемые исключения | ||||
|  | ||||
| Этот метод выбрасывает исключение типа `TypeException` в случае, если объект `$class` (обязательный параметр метода) не является классом. | ||||
|  | ||||
| ### Метод `FromArray` | ||||
|  | ||||
| Этот _статический_ метод переводит массив в класс. Он содержит **1 обязательный параметр**: | ||||
|  | ||||
| * `array $array` - массив публичных свойств | ||||
|  | ||||
| и **1 необязательный параметр**: | ||||
|  | ||||
| * `?callable $onClass = null` - метод обработки классов. Если передаётся `null`, то используется | ||||
|   `DEFAULT_FROM_ARRAY_ON_CLASS`. По умолчанию, `null`. | ||||
|  | ||||
| В качестве метода обработки классов `onClass` подойдет любое `Closure` вида | ||||
|  | ||||
|     fn (array $classArray): object => <ДЕЙСТВИЕ>; | ||||
|  | ||||
| Метод возвращает `object` - экземпляр класса. | ||||
|  | ||||
| Синтаксис: | ||||
|  | ||||
|     public static function FromArray (array $array, ?callable $onClass = null): object | ||||
|  | ||||
| Пример, | ||||
|  | ||||
|     // Считываем данные из файла и преобразуем в массив | ||||
|     $array = json_decode(file_get_contents(__DIR__ . "/class.txt"), true); | ||||
|  | ||||
|     // Создаём метод обработки классов | ||||
|     // - воспользуемся тем, что класс C реализует интерфейс ISerialize | ||||
|     $c_closure = function (string $serializedC): C | ||||
|     { | ||||
|       $classC = new C(); | ||||
|       $classC->UnSerialize($serializedC); | ||||
|       return $classC; | ||||
|     }; | ||||
|  | ||||
|     // - зададим метод обработки | ||||
|     $closure = fn (array $classArray): object => | ||||
|       $classArray['type_class'] == C::class | ||||
|         ? $c_closure($classArray["type_value"]) | ||||
|         : TypeExtension::FromArray($classArray); | ||||
|  | ||||
|     // Преобразуем в объект | ||||
|     try { | ||||
|       $class = TypeExtension::FromArray($array, D::CLOSURE_FROM_ARRAY()); | ||||
|     } catch (TypeException $e) { | ||||
|       // - если ошибка, то выводим её и завершаем работу | ||||
|       die($e->getMessage()); | ||||
|     } | ||||
|  | ||||
|     // Вывожу тестовую строку | ||||
|     echo $class->c->stringC; | ||||
|  | ||||
| В результате на экране отобразится: | ||||
|  | ||||
|     test_string_C | ||||
|  | ||||
| #### Выбрасываемые исключения | ||||
|  | ||||
| Этот метод выбрасывает исключение типа `TypeException` в случаях, если: | ||||
|  | ||||
| * массив `$array` не создан через метод `ToArray`; | ||||
| * при попытке создать объект, он не создаётся; | ||||
| * дата передана не корректного формата; | ||||
| * пытается передать `null` не `nullable` типу. | ||||
| @@ -28,20 +28,24 @@ final class TypeExtension | ||||
|      */ | ||||
|     public static function DEFAULT_TO_ARRAY_ON_CLASS (): Closure | ||||
|     { | ||||
|         return fn (object $class) => self::ToArray($class, self::DEFAULT_TO_ARRAY_ON_CLASS()); | ||||
|         return fn (object $class): array => self::ToArray($class); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Переводит объект в массив. | ||||
|      * | ||||
|      * @param object $class Объект. | ||||
|      * @param callable $onClass Метод обработки классов. | ||||
|      * @param callable|null $onClass Метод обработки классов. Если передаётся null, то используется | ||||
|      *     <code>DEFAULT_TO_ARRAY_ON_CLASS</code>. По умолчанию, null. | ||||
|      * | ||||
|      * @return array Массив свойств типа. | ||||
|      * @throws TypeException Исключение, если объект не является классом. | ||||
|      */ | ||||
|     public static function ToArray (object $class, callable $onClass): array | ||||
|     public static function ToArray (object $class, ?callable $onClass = null): array | ||||
|     { | ||||
|         // Проверяю, что метод обработки классов не null и если null, то присваиваю дефолтный метод | ||||
|         $onClass ??= self::DEFAULT_TO_ARRAY_ON_CLASS(); | ||||
|  | ||||
|         // Создаю массив результата | ||||
|         $result = []; | ||||
|  | ||||
| @@ -102,16 +106,28 @@ final class TypeExtension | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Дефолтный метод обработки классов для FromArray. | ||||
|      * | ||||
|      * @return Closure Метод обработки классов для FromArray. | ||||
|      */ | ||||
|     public static function DEFAULT_FROM_ARRAY_ON_CLASS (): Closure | ||||
|     { | ||||
|         return fn (array $classArray): object => self::FromArray($classArray); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Переводит массив в объект. | ||||
|      * | ||||
|      * @param array $array Массив свойств. | ||||
|      * @param callable|null $onClass Метод обработки классов. Если передаётся null, то используется | ||||
|      *     <code>DEFAULT_FROM_ARRAY_ON_CLASS</code>. По умолчанию, null. | ||||
|      * | ||||
|      * @return mixed Объект типа $className. | ||||
|      * @return mixed Восстановленный объект. | ||||
|      * @throws TypeException Исключение, если массив не создан через ToArray, объект не создан, дата не корректного | ||||
|      *     формата или пытается передать null не nullable типу. | ||||
|      */ | ||||
|     public function FromArray (array $array): object | ||||
|     public static function FromArray (array $array, ?callable $onClass = null): object | ||||
|     { | ||||
|         // Проверяю, что массив является массивом, созданным через ToArray, то есть содержит ключ type_class | ||||
|         if (!array_key_exists("type_class", $array)) | ||||
| @@ -186,7 +202,7 @@ final class TypeExtension | ||||
|             } | ||||
|  | ||||
|             // - если значение является NULL | ||||
|             if ($value = "null") { | ||||
|             if (is_string($value) && $value == "null") { | ||||
|                 // -- присваиваю NULL | ||||
|                 try { | ||||
|                     $instance->$key = null; | ||||
| @@ -203,7 +219,7 @@ final class TypeExtension | ||||
|             // - если значение является классом | ||||
|             if (is_object($instance->$key) && is_array($array[$key]) && array_key_exists("type_class", $array[$key])) { | ||||
|                 // -- добавляю в массив через рекурсию | ||||
|                 $instance->$key = self::FromArray($array[$key]); | ||||
|                 $instance->$key = $onClass($array[$key]); | ||||
|  | ||||
|                 // -- следующий элемент | ||||
|                 continue; | ||||
|   | ||||
							
								
								
									
										56
									
								
								tests/data/D.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								tests/data/D.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| <?php | ||||
|  | ||||
| namespace goodboyalex\php_components_pack\tests\data; | ||||
|  | ||||
| use Closure; | ||||
| use goodboyalex\php_components_pack\extensions\TypeExtension; | ||||
|  | ||||
| class D | ||||
| { | ||||
|     public string $stringD; | ||||
|     public int $intD; | ||||
|     public bool $boolD; | ||||
|     public A $a; | ||||
|     public B $b; | ||||
|     public C $c; | ||||
|  | ||||
|     public function __construct (string $string = "", int $int = 0, bool $bool = false, ?A $a = null, ?B $b = null, | ||||
|         ?C $c = null) | ||||
|     { | ||||
|         $this->stringD = $string; | ||||
|         $this->intD = $int; | ||||
|         $this->boolD = $bool; | ||||
|  | ||||
|         $this->a = $a ?? new A(); | ||||
|         $this->b = $b ?? new B(); | ||||
|         $this->c = $c ?? new C(); | ||||
|     } | ||||
|  | ||||
|     public static function CLOSURE_FROM_ARRAY (): Closure | ||||
|     { | ||||
|         return fn (array $classArray): object | ||||
|             => $classArray['type_class'] == C::class | ||||
|             ? self::CLOSURE_FROM_FOR_C($classArray["type_value"]) : TypeExtension::FromArray($classArray); | ||||
|     } | ||||
|  | ||||
|     private static function CLOSURE_FROM_FOR_C (string $serializedC): object | ||||
|     { | ||||
|         $classC = new C(); | ||||
|         $classC->UnSerialize($serializedC); | ||||
|         return $classC; | ||||
|     } | ||||
|  | ||||
|     public static function CLOSURE_TO_ARRAY (): Closure | ||||
|     { | ||||
|         return fn (object $class): array => $class instanceof C ? self::CLOSURE_TO_FOR_C($class) | ||||
|             : TypeExtension::ToArray($class); | ||||
|     } | ||||
|  | ||||
|     private static function CLOSURE_TO_FOR_C (C $class): array | ||||
|     { | ||||
|         return [ | ||||
|             "type_class" => C::class, | ||||
|             "type_value" => $class->Serialize() | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -1,39 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace goodboyalex\php_components_pack\tests\data; | ||||
|  | ||||
| /** | ||||
|  * Модель, описывающая css классы, используемые в меню. | ||||
|  * | ||||
|  * @author Александр Бабаев | ||||
|  * @package freecms | ||||
|  * @version 0.1 | ||||
|  * @since 0.1 | ||||
|  */ | ||||
| final class MenuCssClassModel | ||||
| { | ||||
|     /** | ||||
|      * @var string $MenuClass Класс всего списка меню. | ||||
|      */ | ||||
|     public string $MenuClass = ""; | ||||
|  | ||||
|     /** | ||||
|      * @var string $ItemClass Класс элемента меню. | ||||
|      */ | ||||
|     public string $ItemClass = ""; | ||||
|  | ||||
|     /** | ||||
|      * @var string $ItemSubMenuClass Класс элемента, вызывающего вложенный список. | ||||
|      */ | ||||
|     public string $ItemSubMenuClass = ""; | ||||
|  | ||||
|     /** | ||||
|      * @var string $SubMenuClass Класс вложенного списка. | ||||
|      */ | ||||
|     public string $SubMenuClass = ""; | ||||
|  | ||||
|     /** | ||||
|      * @var string $SubItemClass Класс элемента вложенного списка. | ||||
|      */ | ||||
|     public string $SubItemClass = ""; | ||||
| } | ||||
| @@ -1,100 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace goodboyalex\php_components_pack\tests\data; | ||||
|  | ||||
| use Exception; | ||||
| use goodboyalex\php_components_pack\classes\ClassMapper; | ||||
| use goodboyalex\php_components_pack\extensions\GUIDExtension; | ||||
| use goodboyalex\php_components_pack\interfaces\IDuplicated; | ||||
|  | ||||
| /** | ||||
|  * Модель элемента меню. | ||||
|  * | ||||
|  * @author Александр Бабаев | ||||
|  * @package freecms | ||||
|  * @version 0.1 | ||||
|  * @since 0.1 | ||||
|  */ | ||||
| final class MenuItemModel implements IDuplicated | ||||
| { | ||||
|     /** | ||||
|      * @var string $Id Идентификатор элемента. | ||||
|      */ | ||||
|     public string $Id { | ||||
|         get { | ||||
|             return $this->Id ?? GUIDExtension::GUIDEmpty; | ||||
|         } | ||||
|         set { | ||||
|             // Проверка идентификатора | ||||
|             if (!GUIDExtension::Validate($value)) | ||||
|                 // - исключение | ||||
|                 throw new Exception("Неверный идентификатор (GUID)"); | ||||
|  | ||||
|             // Установка идентификатора | ||||
|             $this->Id = $value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @var string $Name Имя элемента. | ||||
|      */ | ||||
|     public string $Name; | ||||
|  | ||||
|     /** | ||||
|      * @var string|null $Description Описание элемента. | ||||
|      */ | ||||
|     public ?string $Description; | ||||
|  | ||||
|     /** | ||||
|      * @var string $URL Адрес URL элемента. | ||||
|      */ | ||||
|     public string $URL; | ||||
|  | ||||
|     /** | ||||
|      * @var bool $OpenInNewWindow Открывать ли в новом окне (добавлять к ссылке target="_blank"). | ||||
|      */ | ||||
|     public bool $OpenInNewWindow; | ||||
|  | ||||
|     /** | ||||
|      * @var string|null $IconClass Класс иконки. | ||||
|      */ | ||||
|     public ?string $IconClass; | ||||
|  | ||||
|     /** | ||||
|      * @var string $ParentId Идентификатор родительского элемента. | ||||
|      */ | ||||
|     public string $ParentId { | ||||
|         get { | ||||
|             return $this->ParentId ?? GUIDExtension::GUIDEmpty; | ||||
|         } | ||||
|         set { | ||||
|             // Проверка идентификатора | ||||
|             if (!GUIDExtension::Validate($value)) | ||||
|                 // - исключение | ||||
|                 throw new Exception("Неверный идентификатор (GUID)"); | ||||
|  | ||||
|             // Установка идентификатора | ||||
|             $this->ParentId = $value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @var int $Order Порядок. | ||||
|      */ | ||||
|     public int $Order; | ||||
|  | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     public function Duplicate (): MenuItemModel | ||||
|     { | ||||
|         // Создание дубликата модели | ||||
|         $model = new MenuItemModel(); | ||||
|  | ||||
|         // Копирование свойств | ||||
|         ClassMapper::MapClass($this, $model); | ||||
|  | ||||
|         // Возврат дубликата | ||||
|         return $model; | ||||
|     } | ||||
| } | ||||
| @@ -1,555 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace goodboyalex\php_components_pack\tests\data; | ||||
|  | ||||
| use goodboyalex\php_components_pack\classes\ActionState; | ||||
| use goodboyalex\php_components_pack\classes\ObjectArray; | ||||
| use goodboyalex\php_components_pack\extensions\GUIDExtension; | ||||
| use goodboyalex\php_components_pack\interfaces\IDuplicated; | ||||
| use goodboyalex\php_components_pack\interfaces\ISerializable; | ||||
| use IteratorAggregate; | ||||
| use Traversable; | ||||
|  | ||||
| /** | ||||
|  * Класс списка элементов меню. | ||||
|  * | ||||
|  * @author Александр Бабаев | ||||
|  * @package freecms | ||||
|  * @version 0.1 | ||||
|  * @since 0.1 | ||||
|  */ | ||||
| final class MenuItems implements ISerializable, IteratorAggregate, IDuplicated | ||||
| { | ||||
|     /** | ||||
|      * @var ObjectArray Переменная для хранения списка. | ||||
|      */ | ||||
|     private ObjectArray $_items; | ||||
|  | ||||
|     /** | ||||
|      * Конструктор. | ||||
|      * | ||||
|      * @param ObjectArray|array $items Список элементов меню. | ||||
|      */ | ||||
|     public function __construct (ObjectArray|array $items) | ||||
|     { | ||||
|         $this->_items = is_array($items) ? new ObjectArray($items) : $items; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Добавляет список элементов. | ||||
|      * | ||||
|      * @param ObjectArray|array $items Список элементов меню. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function AddItems (ObjectArray|array $items): void | ||||
|     { | ||||
|         // Получаю список | ||||
|         $itemsToAdd = is_array($items) ? new ObjectArray($items) : $items; | ||||
|  | ||||
|         /** | ||||
|          * @var MenuItemModel $item Элемент меню | ||||
|          */ | ||||
|         foreach ($itemsToAdd as $item) | ||||
|             // - добавляю элемент | ||||
|             $this->AddItem($item); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Добавляет элемент меню в список. | ||||
|      * | ||||
|      * @param MenuItemModel $item Элемент меню. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function AddItem (MenuItemModel $item): void | ||||
|     { | ||||
|         // Добавляю | ||||
|         $this->_items[] = $item; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Содержится ли элемент в списке. | ||||
|      * | ||||
|      * @param string $id Идентификатор элемента меню. | ||||
|      * | ||||
|      * @return bool Содержится ли элемент в списке. | ||||
|      */ | ||||
|     public function ContainsItem (string $id): bool | ||||
|     { | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($id)) | ||||
|             // - возвращаю отрицательный результат | ||||
|             return false; | ||||
|  | ||||
|         // Получаю, что элемент с таким идентификатором существует | ||||
|         return $this->_items->IsExist(fn (MenuItemModel $item) => $item->Id == $id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Вычисляет количество элементов меню. | ||||
|      * | ||||
|      * @param callable|null $predicate Выражение, уточняющее детали. | ||||
|      * | ||||
|      * @return int Количество элементов меню. | ||||
|      */ | ||||
|     public function Count (?callable $predicate = null): int | ||||
|     { | ||||
|         return $this->_items->Count($predicate); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Очищает список элементов. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function Clear (): void | ||||
|     { | ||||
|         // Очищаю | ||||
|         $this->_items->Clear(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Перемещает элемент меню вверх. | ||||
|      * | ||||
|      * @param string $id Идентификатор элемента меню. | ||||
|      * | ||||
|      * @return ActionState Результат выполнения. | ||||
|      */ | ||||
|     public function MoveUp (string $id): ActionState | ||||
|     { | ||||
|         // Создаю результат | ||||
|         $result = new ActionState(); | ||||
|  | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($id)) { | ||||
|             // - то выдаю ошибку | ||||
|             $result->AddError("Неверный идентификатор (GUID)"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Загружаю элемент | ||||
|         $item = $this->GetItem($id); | ||||
|  | ||||
|         // Если элемент не загружен | ||||
|         if ($item == null) { | ||||
|             // - то выдаю ошибку | ||||
|             $result->AddError("Пункт меню с идентификатором 0%s не найден!"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Получаю его текущий порядковый номер | ||||
|         $oldOrder = $item->Order; | ||||
|  | ||||
|         // Если порядковый номер не больше 1 | ||||
|         if ($oldOrder <= 1) { | ||||
|             // - то выдаю предупреждение | ||||
|             $result->AddWarning("Пункт меню находится на первом месте. Перемещение не требуется!"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Получаю новый порядковый номер | ||||
|         $newOrder = $oldOrder - 1; | ||||
|  | ||||
|         // Получаю элемент, находящийся на новом порядковом номере (или GuidExEmpty, если нет элемента) | ||||
|         $oldPlaceItem = $this->_items->GetRow(fn (MenuItemModel $item) => $item->Order == $newOrder); | ||||
|  | ||||
|         // Присваиваю новый порядковый номер изменяемого элемента | ||||
|         $item->Order = $newOrder; | ||||
|  | ||||
|         // Обновляю список элементов | ||||
|         $this->UpdateItem($item); | ||||
|  | ||||
|         // И если на новом порядковом номере существовал элемент | ||||
|         if ($oldPlaceItem !== false) { | ||||
|             // - то присваиваю ему старый порядковый номер | ||||
|             $oldPlaceItem->Order = $oldOrder; | ||||
|  | ||||
|             // - и обновляю список | ||||
|             $this->UpdateItem($oldPlaceItem); | ||||
|         } | ||||
|  | ||||
|         // Возвращаю результат | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Получает элемент меню. | ||||
|      * | ||||
|      * @param string $id Идентификатор элемента меню. | ||||
|      * | ||||
|      * @return MenuItemModel|null Элемент меню или null, если элемент не найден. | ||||
|      */ | ||||
|     public function GetItem (string $id): ?MenuItemModel | ||||
|     { | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($id)) | ||||
|             // - то возвращаю null | ||||
|             return null; | ||||
|  | ||||
|         // Получаю элемент | ||||
|         $item = $this->_items->GetRow(fn (MenuItemModel $item) => $item->Id == $id); | ||||
|  | ||||
|         // Если элемент не найден, то возвращаю null, иначе возвращаю найденный элемент. | ||||
|         return ($item === false) ? null : $item; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Обновляет элемент меню. | ||||
|      * | ||||
|      * @param MenuItemModel $item Обновлённый элемент меню. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function UpdateItem (MenuItemModel $item): void | ||||
|     { | ||||
|         // Если элемент существует | ||||
|         if ($this->_items->IsExist(fn (MenuItemModel $itemM) => $itemM->Id == $item->Id)) | ||||
|             // - то удаляю его | ||||
|             $this->RemoveItem($item->Id); | ||||
|  | ||||
|         // Добавляю новый | ||||
|         $this->AddItem($item); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Удаление элемента меню. | ||||
|      * | ||||
|      * @param string $id Идентификатор элемента меню. | ||||
|      * | ||||
|      * @return bool Статус удаления. | ||||
|      */ | ||||
|     public function RemoveItem (string $id): bool | ||||
|     { | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($id)) | ||||
|             // - возвращаю отрицательный результат | ||||
|             return false; | ||||
|  | ||||
|         return $this->_items->Delete(fn (MenuItemModel $item) => $item->Id == $id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Перемещает элемент меню вниз. | ||||
|      * | ||||
|      * @param string $id Идентификатор элемента меню. | ||||
|      * | ||||
|      * @return ActionState Результат выполнения. | ||||
|      */ | ||||
|     public function MoveDown (string $id): ActionState | ||||
|     { | ||||
|         // Создаю результат | ||||
|         $result = new ActionState(); | ||||
|  | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($id)) { | ||||
|             // - то выдаю ошибку | ||||
|             $result->AddError("Неверный идентификатор (GUID)"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Загружаю элемент | ||||
|         $item = $this->GetItem($id); | ||||
|  | ||||
|         // Если элемент не загружен | ||||
|         if ($item == null) { | ||||
|             // - то выдаю ошибку | ||||
|             $result->AddError("Пункт меню с идентификатором 0%s не найден!"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Получаю его текущий порядковый номер | ||||
|         $oldOrder = $item->Order; | ||||
|  | ||||
|         // Если порядковый номер не больше 1 | ||||
|         if ($oldOrder >= $this->GetLastItemOrder($item->ParentId)) { | ||||
|             // - то выдаю предупреждение | ||||
|             $result->AddWarning("Пункт меню находится на последнем месте. Перемещение не требуется!"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Получаю новый порядковый номер | ||||
|         $newOrder = $oldOrder + 1; | ||||
|  | ||||
|         // Получаю элемент, находящийся на новом порядковом номере (или GuidExEmpty, если нет элемента) | ||||
|         $oldPlaceItem = $this->_items->GetRow(fn (MenuItemModel $item) => $item->Order == $newOrder); | ||||
|  | ||||
|         // Присваиваю новый порядковый номер изменяемого элемента | ||||
|         $item->Order = $newOrder; | ||||
|  | ||||
|         // Обновляю список элементов | ||||
|         $this->UpdateItem($item); | ||||
|  | ||||
|         // И если на новом порядковом номере существовал элемент | ||||
|         if ($oldPlaceItem !== false) { | ||||
|             // - то присваиваю ему старый порядковый номер | ||||
|             $oldPlaceItem->Order = $oldOrder; | ||||
|  | ||||
|             // - и обновляю список | ||||
|             $this->UpdateItem($oldPlaceItem); | ||||
|         } | ||||
|  | ||||
|         // Возвращаю результат | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Получает порядковый номер последнего элемента в списке. | ||||
|      * | ||||
|      * @param string $parentItemId Идентификатор родителя. | ||||
|      * | ||||
|      * @return int Порядковый номер последнего элемента в списке. | ||||
|      */ | ||||
|     public function GetLastItemOrder (string $parentItemId): int | ||||
|     { | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($parentItemId) && $parentItemId !== GUIDExtension::GUIDEmpty) | ||||
|             // - возвращаю отрицательный результат | ||||
|             return 0; | ||||
|  | ||||
|         // Получаю последний элемент | ||||
|         $item = $this->GetSubItems($parentItemId)->Last(false); | ||||
|  | ||||
|         // Если элемент не найден | ||||
|         if ($item === false) | ||||
|             // - возвращаю нулевой результат | ||||
|             return 0; | ||||
|  | ||||
|         // Возвращаю порядок последнего элемента | ||||
|         return $item->Order; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Получает список вложенных элементов. | ||||
|      * | ||||
|      * @param string $parentItemId Идентификатор родителя. | ||||
|      * @param bool $sortByOrder Нужно ли сортировать по порядку элементов. | ||||
|      * | ||||
|      * @return ObjectArray Список вложенных элементов. | ||||
|      */ | ||||
|     public function GetSubItems (string $parentItemId, bool $sortByOrder = true): ObjectArray | ||||
|     { | ||||
|         // Проверяю, что идентификатор корректен | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($parentItemId) && $parentItemId !== GUIDExtension::GUIDEmpty) | ||||
|             // - возвращаю отрицательный результат | ||||
|             return new ObjectArray([]); | ||||
|  | ||||
|         // Получаю список элементов | ||||
|         $result = $this->_items->GetRows(fn (MenuItemModel $item) => $item->ParentId == $parentItemId); | ||||
|  | ||||
|         // Если нужно сортировать | ||||
|         if ($sortByOrder) | ||||
|             // - сортирую | ||||
|             $result->Sort("Order"); | ||||
|  | ||||
|         // Возвращаю результат | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Изменяет родителя у элемента. | ||||
|      * | ||||
|      * @param string $id Идентификатор элемента. | ||||
|      * @param string $newParentId Идентификатор нового родителя. | ||||
|      * | ||||
|      * @return ActionState Результат выполнения. | ||||
|      */ | ||||
|     public function ChangeParent (string $id, string $newParentId): ActionState | ||||
|     { | ||||
|         // Создаю результат | ||||
|         $result = new ActionState(); | ||||
|  | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($id) || GUIDExtension::IsNotValidOrEmpty($newParentId)) { | ||||
|             // - то выдаю ошибку | ||||
|             $result->AddError("Неверный идентификатор (GUID)"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Если имеет дочерние | ||||
|         if ($this->HasSubItems($id)) { | ||||
|             // - то выдаю ошибку | ||||
|             $result->AddError( | ||||
|                 "Пункт меню с идентификатором 0%s имеет дочерние пункты (подпункты). Пожалуйста, сперва переместите их, а потом будет возможно сменить родителя у данного элемента!"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Загружаю элемент | ||||
|         $item = $this->GetItem($id); | ||||
|  | ||||
|         // Если элемент не загружен | ||||
|         if ($item == null) { | ||||
|             // - то выдаю ошибку | ||||
|             $result->AddError("Пункт меню с идентификатором 0%s не найден!"); | ||||
|  | ||||
|             // - и прерываю | ||||
|             return $result; | ||||
|         } | ||||
|  | ||||
|         // Получаю список свободных порядковых номеров | ||||
|         $freeOrdersList = $this->GetFreeOrders($newParentId); | ||||
|  | ||||
|         // Получаю первый свободный порядковый номер | ||||
|         $newOrder = count($freeOrdersList) > 0 ? min($freeOrdersList) : $this->GetLastItemOrder($newParentId) + 1; | ||||
|  | ||||
|         // Присваиваю элементу идентификатор нового родителя | ||||
|         $item->ParentId = $newParentId; | ||||
|  | ||||
|         // Устанавливаю его порядок | ||||
|         $item->Order = $newOrder; | ||||
|  | ||||
|         // Обновляю список элементов | ||||
|         $this->UpdateItem($item); | ||||
|  | ||||
|         // Возвращаю результат | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Проверяет, имеет ли элемент дочерние. | ||||
|      * | ||||
|      * @param string $id Идентификатор элемента меню. | ||||
|      * | ||||
|      * @return bool Имеет ли элемент дочерние. | ||||
|      */ | ||||
|     public function HasSubItems (string $id): bool | ||||
|     { | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($id)) | ||||
|             // - возвращаю отрицательный результат | ||||
|             return false; | ||||
|  | ||||
|         // Проверяю, существует ли элемент, у которого идентификатор родителя совпадает | ||||
|         // с идентификатором указанного элемента | ||||
|         return $this->_items->IsExist(fn (MenuItemModel $item) => $item->ParentId == $id); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Получает порядковый номер отсутствующего элемента в списке между первым и последним. | ||||
|      * | ||||
|      * @param string $parentItemId Идентификатор родителя. | ||||
|      * | ||||
|      * @return array Список порядковых номеров, которые не используются. | ||||
|      */ | ||||
|     public function GetFreeOrders (string $parentItemId): array | ||||
|     { | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($parentItemId) && $parentItemId !== GUIDExtension::GUIDEmpty) | ||||
|             // - возвращаю отрицательный результат | ||||
|             return []; | ||||
|  | ||||
|         // Получаю список элементов | ||||
|         $items = $this->GetSubItems($parentItemId); | ||||
|  | ||||
|         // Создаю список-результат | ||||
|         $result = []; | ||||
|  | ||||
|         // Получаю минимальный порядок элементов | ||||
|         $min = $this->GetFirstItemOrder($parentItemId); | ||||
|  | ||||
|         // Получаю максимальный порядок элементов | ||||
|         $max = $this->GetLastItemOrder($parentItemId); | ||||
|  | ||||
|         // Если произошёл уникальный случай, когда нет элементов | ||||
|         if ($min == 0 || $max == 0) | ||||
|             return $result; | ||||
|  | ||||
|         // Заношу в список все до минимума | ||||
|         if ($min > 0) | ||||
|             for ($ind = 1; $ind < $min; $ind++) | ||||
|                 $result[] = $ind; | ||||
|  | ||||
|         // Прохожу "занятые" элементы | ||||
|         for ($ind = $min; $ind <= $max; $ind++) | ||||
|             // - и проверяю их на свободные | ||||
|             if (!$items->IsExist(fn (MenuItemModel $item) => $item->Order == $ind)) | ||||
|                 // -- заношу свободные в список | ||||
|                 $result[] = $ind; | ||||
|  | ||||
|         // Сортирую список | ||||
|         sort($result, SORT_NUMERIC); | ||||
|  | ||||
|         // Возвращаю результат | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Получает порядковый номер первого элемента в списке. | ||||
|      * | ||||
|      * @param string $parentItemId Идентификатор родителя. | ||||
|      * | ||||
|      * @return int Порядковый номер первого элемента в списке. | ||||
|      */ | ||||
|     public function GetFirstItemOrder (string $parentItemId): int | ||||
|     { | ||||
|         // Проверяю, что идентификатор не пустой | ||||
|         if (GUIDExtension::IsNotValidOrEmpty($parentItemId) && $parentItemId !== GUIDExtension::GUIDEmpty) | ||||
|             // - возвращаю отрицательный результат | ||||
|             return 0; | ||||
|  | ||||
|         // Получаю первый элемент | ||||
|         $item = $this->GetSubItems($parentItemId)->First(false); | ||||
|  | ||||
|         // Если элемент не найден | ||||
|         if ($item === false) | ||||
|             // - возвращаю нулевой результат | ||||
|             return 0; | ||||
|  | ||||
|         // Возвращаю порядок первого элемента | ||||
|         return $item->Order; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     public function getIterator (): Traversable | ||||
|     { | ||||
|         return $this->_items->getIterator(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     public function Duplicate (): MenuItems | ||||
|     { | ||||
|         return new MenuItems($this->_items); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     public | ||||
|     function Serialize (): string | ||||
|     { | ||||
|         return $this->_items->Serialize(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     public | ||||
|     function UnSerialize (string $serialized): void | ||||
|     { | ||||
|         // Создаю новый класс списка элементов | ||||
|         $this->_items = new ObjectArray([]); | ||||
|  | ||||
|         // Восстанавливаю данные | ||||
|         $this->_items->UnSerialize($serialized); | ||||
|     } | ||||
| } | ||||
| @@ -1,139 +0,0 @@ | ||||
| <?php | ||||
|  | ||||
| namespace goodboyalex\php_components_pack\tests\data; | ||||
|  | ||||
| use Exception; | ||||
| use goodboyalex\php_components_pack\classes\ClassMapper; | ||||
| use goodboyalex\php_components_pack\extensions\GUIDExtension; | ||||
| use goodboyalex\php_components_pack\interfaces\IDuplicated; | ||||
| use goodboyalex\php_components_pack\interfaces\IStoredAtSQL; | ||||
|  | ||||
| /** | ||||
|  * Модель меню. | ||||
|  * | ||||
|  * @author Александр Бабаев | ||||
|  * @package freecms | ||||
|  * @version 0.1 | ||||
|  * @since 0.1 | ||||
|  */ | ||||
| final class MenuModel implements IDuplicated, IStoredAtSQL | ||||
| { | ||||
|     /** | ||||
|      * @var string $Id Идентификатор меню. | ||||
|      */ | ||||
|     public string $Id { | ||||
|         get { | ||||
|             return $this->Id ?? GUIDExtension::GUIDEmpty; | ||||
|         } | ||||
|         set { | ||||
|             // Проверка идентификатора | ||||
|             if (!GUIDExtension::Validate($value)) | ||||
|                 // - исключение | ||||
|                 throw new Exception("Неверный идентификатор (GUID)"); | ||||
|  | ||||
|             // Установка идентификатора | ||||
|             $this->Id = $value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @var string $Name Имя меню. | ||||
|      */ | ||||
|     public string $Name = ""; | ||||
|  | ||||
|     /** | ||||
|      * @var string $Description Описание меню. | ||||
|      */ | ||||
|     public string $Description = ""; | ||||
|  | ||||
|     /** | ||||
|      * @var MenuItems Элементы меню. | ||||
|      */ | ||||
|     public MenuItems $Items; | ||||
|  | ||||
|     /** | ||||
|      * @var bool $AllowSubMenu Допустимо ли вложенное меню. | ||||
|      */ | ||||
|     public bool $AllowSubMenu = true; | ||||
|  | ||||
|     /** | ||||
|      * @var MenuCssClassModel $Css Модель стилей оформления меню. | ||||
|      */ | ||||
|     public MenuCssClassModel $Css; | ||||
|  | ||||
|     /** | ||||
|      * Конструктор. | ||||
|      */ | ||||
|     public function __construct () | ||||
|     { | ||||
|         $this->Items = new MenuItems([]); | ||||
|         $this->Css = new MenuCssClassModel(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     public function Duplicate (): MenuModel | ||||
|     { | ||||
|         // Создаю новый класс | ||||
|         $model = new MenuModel(); | ||||
|  | ||||
|         // Копирую свойства класса | ||||
|         ClassMapper::MapClass($this, $model); | ||||
|  | ||||
|         // Возвращаю класс | ||||
|         return $model; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritDoc | ||||
|      */ | ||||
|     public function ToSQL (bool $withId = true): array | ||||
|     { | ||||
|         // Создаем результат | ||||
|         $result = []; | ||||
|  | ||||
|         // Добавляем значения | ||||
|         $result['Name'] = $this->Name; | ||||
|         $result['Description'] = $this->Description; | ||||
|         $result['Items'] = $this->Items->Serialize(); | ||||
|         $result['AllowSubMenu'] = $this->AllowSubMenu ? 1 : 0; | ||||
|         $result['Css'] = serialize($this->Css); | ||||
|  | ||||
|         // Если требуется идентификатор | ||||
|         if ($withId) | ||||
|             // - то добавляем его | ||||
|             $result['Id'] = $this->Id; | ||||
|  | ||||
|         // Возвращаем результат | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @inheritDoc | ||||
|      */ | ||||
|     public function FromSQL (array $sqlData): MenuModel | ||||
|     { | ||||
|         // Создаем результат | ||||
|         $result = new MenuModel(); | ||||
|  | ||||
|         // Устанавливаем значения | ||||
|         if (isset($sqlData['Id'])) | ||||
|             $result->Id = $sqlData['Id']; | ||||
|         if (isset($sqlData['Name'])) | ||||
|             $result->Name = $sqlData['Name']; | ||||
|         if (isset($sqlData['Description'])) | ||||
|             $result->Description = $sqlData['Description']; | ||||
|         $this->Items = new MenuItems([]); | ||||
|         if (isset($sqlData['Items'])) | ||||
|             $result->Items->UnSerialize($sqlData['Items']); | ||||
|         if (isset($sqlData['AllowSubMenu'])) | ||||
|             $result->AllowSubMenu = $sqlData['AllowSubMenu'] == 1; | ||||
|         $this->Css = new MenuCssClassModel(); | ||||
|         if (isset($sqlData['Css'])) | ||||
|             $result->Css = unserialize($sqlData['Css']); | ||||
|  | ||||
|         // Возвращаем массив | ||||
|         return $result; | ||||
|     } | ||||
| } | ||||
| @@ -2,70 +2,90 @@ | ||||
|  | ||||
| namespace goodboyalex\php_components_pack\tests\extensions; | ||||
|  | ||||
| use goodboyalex\php_components_pack\extensions\GUIDExtension; | ||||
| use goodboyalex\php_components_pack\exceptions\TypeException; | ||||
| use goodboyalex\php_components_pack\extensions\TypeExtension; | ||||
| use goodboyalex\php_components_pack\tests\data\MenuCssClassModel; | ||||
| use goodboyalex\php_components_pack\tests\data\MenuItemModel; | ||||
| use goodboyalex\php_components_pack\tests\data\MenuItems; | ||||
| use goodboyalex\php_components_pack\tests\data\MenuModel; | ||||
| use goodboyalex\php_components_pack\tests\data\A; | ||||
| use goodboyalex\php_components_pack\tests\data\B; | ||||
| use goodboyalex\php_components_pack\tests\data\C; | ||||
| use goodboyalex\php_components_pack\tests\data\D; | ||||
| use PHPUnit\Framework\TestCase; | ||||
|  | ||||
| class TypeExtensionTest extends TestCase | ||||
| { | ||||
|     public function testFromArray () | ||||
|     { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public function testToArray () | ||||
|     { | ||||
|         // Подготавливаем данные к тестированию | ||||
|         $this->PrepareForTest(); | ||||
|  | ||||
|         $menu = new MenuModel(); | ||||
|         $menu->Id = GUIDExtension::Generate(); | ||||
|         $menu->Name = 'Menu'; | ||||
|         $menu->Description = 'Description'; | ||||
|         $menu->AllowSubMenu = true; | ||||
|         $menu->Css = new MenuCssClassModel(); | ||||
|         $menu->Css->MenuClass = "menuClass"; | ||||
|         $menu->Css->ItemClass = "itemClass"; | ||||
|         $menu->Css->SubItemClass = "subItemClass"; | ||||
|         $menu->Css->ItemSubMenuClass = "itemSubMenuClass"; | ||||
|         // Зададим имя файла | ||||
|         $fileName = __DIR__ . "/class.txt"; | ||||
|  | ||||
|         $menuItem1 = new MenuItemModel(); | ||||
|         $menuItem1->Id = GUIDExtension::Generate(); | ||||
|         $menuItem1->Name = 'MenuItem1'; | ||||
|         $menuItem1->Description = 'Description'; | ||||
|         $menuItem1->Order = 1; | ||||
|         $menuItem1->ParentId = GUIDExtension::GUIDEmpty; | ||||
|         $menuItem1->URL = 'https://www.google.com'; | ||||
|         $menu->Items->AddItem($menuItem1); | ||||
|         $menuItem2 = new MenuItemModel(); | ||||
|         $menuItem2->Id = GUIDExtension::Generate(); | ||||
|         $menuItem2->Name = 'MenuItem2'; | ||||
|         $menuItem2->Description = 'Description'; | ||||
|         $menuItem2->ParentId = $menuItem1->Id; | ||||
|         $menuItem2->Order = 1; | ||||
|         $menuItem2->URL = 'https://www.google.ru'; | ||||
|         $menu->Items->AddItem($menuItem2); | ||||
|         // Если файл не найден | ||||
|         if (!file_exists($fileName)) | ||||
|             // - то завершаем работу | ||||
|             die("The test data file could not be found or has not yet passed the ToArray test! / Файл с данными для тестирования не найден или не пройдено ещё тестирование ToArray!"); | ||||
|  | ||||
|         $closure = fn (object $class) => $class::class == MenuItems::class ? $class->Serialize() | ||||
|             : TypeExtension::ToArray($class, TypeExtension::DEFAULT_TO_ARRAY_ON_CLASS()); | ||||
|         // Считываем данные из файла и преобразуем в массив | ||||
|         $array = json_decode(file_get_contents($fileName), true); | ||||
|  | ||||
|         $array = TypeExtension::ToArray($menu, $closure); | ||||
|         // Преобразуем в объект | ||||
|         try { | ||||
|             $class = TypeExtension::FromArray($array, D::CLOSURE_FROM_ARRAY()); | ||||
|         } | ||||
|         catch (TypeException $e) { | ||||
|             // - если ошибка, то выводим её и завершаем работу | ||||
|             die($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         file_put_contents(__DIR__ . "/class.txt", json_encode($array, JSON_PRETTY_PRINT)); | ||||
|         var_dump($array); | ||||
|         die(); | ||||
|         // Проверяем | ||||
|         $this->assertEquals(D::class, $class::class); | ||||
|         $this->assertEquals(A::class, $class->a::class); | ||||
|         $this->assertEquals(B::class, $class->b::class); | ||||
|         $this->assertEquals(9876, $class->b->b); | ||||
|         $this->assertEquals(C::class, $class->c::class); | ||||
|         $this->assertEquals(54321, $class->c->intC); | ||||
|         $this->assertEquals('test_string', $class->stringD); | ||||
|     } | ||||
|  | ||||
|     private function PrepareForTest (): void | ||||
|     { | ||||
|         require_once __DIR__ . '/../../sources/exceptions/TypeException.php'; | ||||
|         require_once __DIR__ . '/../../sources/extensions/TypeExtension.php'; | ||||
|         require_once __DIR__ . '/../data/MenuItemModel.php'; | ||||
|         require_once __DIR__ . '/../data/MenuCssClassModel.php'; | ||||
|         require_once __DIR__ . '/../data/MenuItems.php'; | ||||
|         require_once __DIR__ . '/../data/MenuModel.php'; | ||||
|         require_once __DIR__ . '/../data/A.php'; | ||||
|         require_once __DIR__ . '/../data/B.php'; | ||||
|         require_once __DIR__ . '/../data/C.php'; | ||||
|         require_once __DIR__ . '/../data/D.php'; | ||||
|     } | ||||
|  | ||||
|     public function testToArray () | ||||
|     { | ||||
|         // Подготавливаем данные к тестированию | ||||
|         $this->PrepareForTest(); | ||||
|  | ||||
|         // Создаём тестовый класс | ||||
|         $class = new D ('test_string', 12345, true, new A("test_string_A", 6789, false), | ||||
|             new B("test_string_B", 9876, "false"), new C("test_string_C", 54321, true)); | ||||
|  | ||||
|         // Преобразуем в массив | ||||
|         try { | ||||
|             $array = TypeExtension::ToArray($class, D::CLOSURE_TO_ARRAY()); | ||||
|         } | ||||
|         catch (TypeException $e) { | ||||
|             // - если ошибка, то выводим её и завершаем работу | ||||
|             die($e->getMessage()); | ||||
|         } | ||||
|  | ||||
|         // Сохраняем массив в файл (для тестирования FromArray) | ||||
|         file_put_contents(__DIR__ . "/class.txt", json_encode($array, JSON_PRETTY_PRINT)); | ||||
|  | ||||
|         // Проверяем | ||||
|         $this->assertEquals(D::class, $array['type_class']); | ||||
|         $this->assertEquals(A::class, $array['a']['type_class']); | ||||
|         $this->assertEquals(B::class, $array['b']['type_class']); | ||||
|         $this->assertEquals(9876, $array['b']['b']); | ||||
|         $this->assertEquals(C::class, $array['c']['type_class']); | ||||
|         $this->assertEquals('test_string', $array['stringD']); | ||||
|         $this->assertEquals(12345, $array['intD']); | ||||
|         $this->assertTrue($array['boolD']); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user