20250711 1.1.1 Beta 3

This commit is contained in:
Александр Бабаев 2025-07-11 19:40:30 +03:00
parent 83a76dc8e2
commit 3fbb7dc81c
7 changed files with 1001 additions and 10 deletions

View File

@ -0,0 +1,36 @@
<?php
namespace goodboyalex\php_components_pack\exceptions;
use Exception;
/**
* Ошибка работы с расширением типов.
*
* @author Александр Бабаев
* @package php_components_pack
* @version 1.0
* @since 1.1.1
*/
final class TypeException extends Exception
{
/**
* @var string $TypeName Имя типа.
*/
public string $TypeName;
/**
* Конструктор.
*
* @param string $typeName Имя типа.
* @param string $message Сообщение об ошибке.
*/
public function __construct (string $typeName = "", string $message = "")
{
// Запускаем базовый конструктор
parent::__construct($message);
// Присваиваем тип
$this->TypeName = $typeName;
}
}

View File

@ -2,11 +2,13 @@
namespace goodboyalex\php_components_pack\extensions; namespace goodboyalex\php_components_pack\extensions;
use Closure;
use DateMalformedStringException; use DateMalformedStringException;
use DateTime; use DateTime;
use DateTimeImmutable; use DateTimeImmutable;
use DateTimeInterface; use DateTimeInterface;
use Exception; use Exception;
use goodboyalex\php_components_pack\exceptions\TypeException;
use UnitEnum; use UnitEnum;
/** /**
@ -19,16 +21,38 @@ use UnitEnum;
*/ */
final class TypeExtension final class TypeExtension
{ {
/**
* Дефолтный метод обработки классов для ToArray.
*
* @return Closure Метод обработки классов для ToArray.
*/
public static function DEFAULT_TO_ARRAY_ON_CLASS (): Closure
{
return fn (object $class) => self::ToArray($class, self::DEFAULT_TO_ARRAY_ON_CLASS());
}
/** /**
* Переводит объект в массив. * Переводит объект в массив.
* *
* @param object $class Объект.
* @param callable $onClass Метод обработки классов.
*
* @return array Массив свойств типа. * @return array Массив свойств типа.
* @throws TypeException Исключение, если объект не является классом.
*/ */
public function ToArray (object $class): array public static function ToArray (object $class, callable $onClass): array
{ {
// Создаю массив результата // Создаю массив результата
$result = []; $result = [];
// Сперва проверяю, что объект является классом
if (!is_object($class))
// - если нет, то выбрасываю исключение
throw new TypeException($class::class, "Object is not class / Объект не является классом.");
// Затем записываю имя класса
$result["type_class"] = $class::class;
// Получаю свойства класса // Получаю свойства класса
$properties = get_object_vars($class); $properties = get_object_vars($class);
@ -61,6 +85,15 @@ final class TypeExtension
continue; continue;
} }
// - если значение является классом
if (is_object($value)) {
// -- добавляю в массив через рекурсию
$result[$key] = $onClass($value);
// -- следующий элемент
continue;
}
// - присваиваю значение // - присваиваю значение
$result[$key] = $value; $result[$key] = $value;
} }
@ -73,21 +106,28 @@ final class TypeExtension
* Переводит массив в объект. * Переводит массив в объект.
* *
* @param array $array Массив свойств. * @param array $array Массив свойств.
* @param string $className Требуемый класс (внимание: с namespace!).
* *
* @return mixed Объект типа $className. * @return mixed Объект типа $className.
* @throws Exception Исключение, если объект не создан, или дата не корректного формата или пытается передать null * @throws TypeException Исключение, если массив не создан через ToArray, объект не создан, дата не корректного
* не nullable типу. * формата или пытается передать null не nullable типу.
*/ */
public function FromArray (array $array, string $className): object public function FromArray (array $array): object
{ {
// Проверяю, что массив является массивом, созданным через ToArray, то есть содержит ключ type_class
if (!array_key_exists("type_class", $array))
// - если нет, то выбрасываю исключение
throw new TypeException("array", "Array is not created by ToArray / Массив не создан через ToArray.");
// Получаю имя класса
$className = $array["type_class"];
// Создаем объект // Создаем объект
$instance = new $className(); $instance = new $className();
// Проверяем, что объект создан // Проверяем, что объект создан
if (!is_object($instance) && $instance instanceof $className) if (!is_object($instance) && $instance instanceof $className)
// - если нет, то выбрасываю исключение // - если нет, то выбрасываю исключение
throw new Exception("Object is not created / Объект не создан."); throw new TypeException($className, "Object is not created / Объект не создан.");
// Получаю свойства класса // Получаю свойства класса
$properties = get_object_vars($instance); $properties = get_object_vars($instance);
@ -109,7 +149,8 @@ final class TypeExtension
$instance->$key = new DateTime ('@' . $value); $instance->$key = new DateTime ('@' . $value);
} }
catch (DateMalformedStringException) { catch (DateMalformedStringException) {
throw new Exception("Date is not correct format / Дата не корректного формата."); throw new TypeException($className . "->{$key}::DateTime",
"Date is not correct format / Дата не корректного формата.");
} }
// -- следующий элемент // -- следующий элемент
@ -123,8 +164,8 @@ final class TypeExtension
$instance->$key = new DateTimeImmutable('@' . $value); $instance->$key = new DateTimeImmutable('@' . $value);
} }
catch (DateMalformedStringException) { catch (DateMalformedStringException) {
// -- создаю дату с нулевым временем, если ошибка throw new TypeException($className . "->{$key}::DateTimeImmutable",
$instance->$key = new DateTimeImmutable(); "Date is not correct format / Дата не корректного формата.");
} }
// -- следующий элемент // -- следующий элемент
@ -151,13 +192,23 @@ final class TypeExtension
$instance->$key = null; $instance->$key = null;
} }
catch (Exception $exception) { catch (Exception $exception) {
throw new Exception("Cannot assign null to non-nullable property. / Невозможно присвоить null не nullable типу: {$exception->getMessage()}."); throw new TypeException($className . "->$key",
"Cannot assign null to non-nullable property. / Невозможно присвоить null не nullable типу: {$exception->getMessage()}.");
} }
// -- следующий элемент // -- следующий элемент
continue; continue;
} }
// - если значение является классом
if (is_object($instance->$key) && is_array($array[$key]) && array_key_exists("type_class", $array[$key])) {
// -- добавляю в массив через рекурсию
$instance->$key = self::FromArray($array[$key]);
// -- следующий элемент
continue;
}
// - присваиваю значение // - присваиваю значение
$instance->$key = $value; $instance->$key = $value;
} }

View File

@ -0,0 +1,39 @@
<?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 = "";
}

View File

@ -0,0 +1,100 @@
<?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;
}
}

555
tests/data/MenuItems.php Normal file
View File

@ -0,0 +1,555 @@
<?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);
}
}

139
tests/data/MenuModel.php Normal file
View File

@ -0,0 +1,139 @@
<?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;
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace goodboyalex\php_components_pack\tests\extensions;
use goodboyalex\php_components_pack\extensions\GUIDExtension;
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 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";
$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);
$closure = fn (object $class) => $class::class == MenuItems::class ? $class->Serialize()
: TypeExtension::ToArray($class, TypeExtension::DEFAULT_TO_ARRAY_ON_CLASS());
$array = TypeExtension::ToArray($menu, $closure);
file_put_contents(__DIR__ . "/class.txt", json_encode($array, JSON_PRETTY_PRINT));
var_dump($array);
die();
}
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';
}
}