20250712 1.1.1 Beta 4
This commit is contained in:
parent
3fbb7dc81c
commit
117deab9d8
1
.gitignore
vendored
1
.gitignore
vendored
@ -79,3 +79,4 @@ fabric.properties
|
|||||||
|
|
||||||
.idea/
|
.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
|
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 object $class Объект.
|
||||||
* @param callable $onClass Метод обработки классов.
|
* @param callable|null $onClass Метод обработки классов. Если передаётся null, то используется
|
||||||
|
* <code>DEFAULT_TO_ARRAY_ON_CLASS</code>. По умолчанию, null.
|
||||||
*
|
*
|
||||||
* @return array Массив свойств типа.
|
* @return array Массив свойств типа.
|
||||||
* @throws TypeException Исключение, если объект не является классом.
|
* @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 = [];
|
$result = [];
|
||||||
|
|
||||||
@ -102,16 +106,28 @@ final class TypeExtension
|
|||||||
return $result;
|
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 array $array Массив свойств.
|
||||||
|
* @param callable|null $onClass Метод обработки классов. Если передаётся null, то используется
|
||||||
|
* <code>DEFAULT_FROM_ARRAY_ON_CLASS</code>. По умолчанию, null.
|
||||||
*
|
*
|
||||||
* @return mixed Объект типа $className.
|
* @return mixed Восстановленный объект.
|
||||||
* @throws TypeException Исключение, если массив не создан через ToArray, объект не создан, дата не корректного
|
* @throws TypeException Исключение, если массив не создан через ToArray, объект не создан, дата не корректного
|
||||||
* формата или пытается передать null не nullable типу.
|
* формата или пытается передать null не nullable типу.
|
||||||
*/
|
*/
|
||||||
public function FromArray (array $array): object
|
public static function FromArray (array $array, ?callable $onClass = null): object
|
||||||
{
|
{
|
||||||
// Проверяю, что массив является массивом, созданным через ToArray, то есть содержит ключ type_class
|
// Проверяю, что массив является массивом, созданным через ToArray, то есть содержит ключ type_class
|
||||||
if (!array_key_exists("type_class", $array))
|
if (!array_key_exists("type_class", $array))
|
||||||
@ -186,7 +202,7 @@ final class TypeExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - если значение является NULL
|
// - если значение является NULL
|
||||||
if ($value = "null") {
|
if (is_string($value) && $value == "null") {
|
||||||
// -- присваиваю NULL
|
// -- присваиваю NULL
|
||||||
try {
|
try {
|
||||||
$instance->$key = null;
|
$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])) {
|
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;
|
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;
|
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\extensions\TypeExtension;
|
||||||
use goodboyalex\php_components_pack\tests\data\MenuCssClassModel;
|
use goodboyalex\php_components_pack\tests\data\A;
|
||||||
use goodboyalex\php_components_pack\tests\data\MenuItemModel;
|
use goodboyalex\php_components_pack\tests\data\B;
|
||||||
use goodboyalex\php_components_pack\tests\data\MenuItems;
|
use goodboyalex\php_components_pack\tests\data\C;
|
||||||
use goodboyalex\php_components_pack\tests\data\MenuModel;
|
use goodboyalex\php_components_pack\tests\data\D;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class TypeExtensionTest extends TestCase
|
class TypeExtensionTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testFromArray ()
|
public function testFromArray ()
|
||||||
{
|
{
|
||||||
|
// Подготавливаем данные к тестированию
|
||||||
}
|
|
||||||
|
|
||||||
public function testToArray ()
|
|
||||||
{
|
|
||||||
$this->PrepareForTest();
|
$this->PrepareForTest();
|
||||||
|
|
||||||
$menu = new MenuModel();
|
// Зададим имя файла
|
||||||
$menu->Id = GUIDExtension::Generate();
|
$fileName = __DIR__ . "/class.txt";
|
||||||
$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";
|
|
||||||
|
|
||||||
$menuItem1 = new MenuItemModel();
|
// Если файл не найден
|
||||||
$menuItem1->Id = GUIDExtension::Generate();
|
if (!file_exists($fileName))
|
||||||
$menuItem1->Name = 'MenuItem1';
|
// - то завершаем работу
|
||||||
$menuItem1->Description = 'Description';
|
die("The test data file could not be found or has not yet passed the ToArray test! / Файл с данными для тестирования не найден или не пройдено ещё тестирование ToArray!");
|
||||||
$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);
|
|
||||||
|
|
||||||
$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);
|
$this->assertEquals(D::class, $class::class);
|
||||||
die();
|
$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
|
private function PrepareForTest (): void
|
||||||
{
|
{
|
||||||
require_once __DIR__ . '/../../sources/exceptions/TypeException.php';
|
require_once __DIR__ . '/../../sources/exceptions/TypeException.php';
|
||||||
require_once __DIR__ . '/../../sources/extensions/TypeExtension.php';
|
require_once __DIR__ . '/../../sources/extensions/TypeExtension.php';
|
||||||
require_once __DIR__ . '/../data/MenuItemModel.php';
|
require_once __DIR__ . '/../data/A.php';
|
||||||
require_once __DIR__ . '/../data/MenuCssClassModel.php';
|
require_once __DIR__ . '/../data/B.php';
|
||||||
require_once __DIR__ . '/../data/MenuItems.php';
|
require_once __DIR__ . '/../data/C.php';
|
||||||
require_once __DIR__ . '/../data/MenuModel.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']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user