Pull to refresh

Архитектура CMS. Модель данных. Часть 2

Reading time 9 min
Views 3.9K
Продолжаем тему объектной модели данных. В этой части речь пойдет о модуле Data, являющимся, по сути, ORM системой. Для наглядности работы модуля Data c его помощью будет создано содержимое простого сайта. Предыдущая статья: Архитектура CMS. Модель данных. Часть 1.

Модуль Data состоит из классов Data, Object, Multy, Query и набора классов Cond*. Сам модуль – это статический класс Data, остальные классы используются для представления структур данных, с которыми он работает. Для представления сущностей в программном коде используется класс Object. Не важно, какого типа сущность – класс данных, объект данных или связь между ними – для всех Object. Класс Multy используется для ассоциации с набором сущностей, в частности, для представления множественных свойств. Классы Query и Cond* необходимы для осуществления поиска по объектной модели (в базе данных) с учетом гибких условий.

Чтение сущности


Получить сущность из базы данных можно по её идентификатору следующим способом:

$obj = Object::Create(1); // сущность с идентификатором 1

Вместо оператора new применяется статический метод Create(). В нём проверяется существование экземпляра запрашиваемой сущности, возможно, сущность уже загружена из базы данных. Если загружена, метод Create() возвратить ссылку на её экземпляр, иначе экземпляр, ассоциируемый с запрашиваемой сущностью, будет сначала создан и потом уже возвращена ссылка на него.

После получения сущности, её можно полноценно использовать, например, обратиться к её классу и узнать системное имя.

echo $obj->getP('class')->getA('sys_name');

Обратите внимание, загруженная сущность – это класс, но работа выполняется с ним как с объектом. Сущность загружается из БД по требованию, то есть реальное обращение к базе данных происходит только при первом обращении к свойствам или атрибутам сущности, более того, загружаются только те свойства (сущности с которыми выполнена связь), которые используются.

Возможно, вы задались вопросом, нельзя ли укоротить конструкцию для доступа к свойствам и атрибутом? Магические методы, оптимизация, стилизация – это вопрос времени и решения некоторых тонкостей.

У объекта имеются свойства (Property) и атрибуты (Attribute). Атрибут – это скалярное значение. Свойство – это связь на другой объект, фактически – объект, с которым выполнена связь. Связь на класс и связь наследования оптимизированы – представлены в БД как атрибуты, но логикой модуля данных они превращаются в свойства.
Для обращения к свойствам используется метод getP($name), возвращающий объект, с которым установлена связь. Можно обратиться к самой связи с помощью метода getL($name) – возвратится объект связи.

В приведенном примере выполняется обращение к классу, но связь с классом не реализуется объектом (так как оптимизирована), поэтому обращение к связи на класс бессмысленно – объекта-связи нет. Метод getA($name) используется для чтения значения атрибута по его имени.

Загрузка сущности автоматически инициируется при первом обращении к любому из её атрибутов или свойству. Загрузка автоматически выполняется методом Load(). Для загрузки сущности определяется её класс, определяется иерархия наследования классов. По классам определяются таблицы, из которых загружаются атрибуты сущности. Для загрузки свойств достаточно напрямую обратиться к таблице связей и загрузить все связи, у которых атрибут «primary» равен идентификатору загружаемой сущности. Загружаются только связи. Объекты, на которые ссылаются связи, загружаются по общему принципу – если будут использоваться их атрибуты или свойства.

Создание сущности


Для примера, создадим объект класса «id». Объект будет иметь только идентификатор и связь на класс, которым определяется.

$new = Object::Create();
$new->setP('class', Object::Create(1));
$new->Save();
Или сокращенно:
$new = Object::Create(null, Object::Create(1));
$new->Save();

Всё очень просто: создается пустой экземпляр сущности, создается связь на класс с идентификатором 1 и выполняется сохранение. После этого в таблице «id» появляется новая запись. Атрибут-идентификатор сущности создаётся автоматически автоинкрементом ключевого поля таблицы «id».

Создание класса происходит точно также. Для примера создадим пробный класс с именем «my» и определением атрибута «value»:

$new = Object::Create();
$new->setP('class', Object::Create(2)); // Создаём класс, поэтому связь с #2
// В зависимости от класса создаваемой сущности, определяются значения
// атрибутов и свойств, (поэтому связь с классом определяется первой)
$new->setP('extend', Object::Create(1)); // Наследуем базовый класс
$new->setA('sys_name', 'my'); // Системное имя класса
$new->setA('final', 0);  // Можно наследовать
// Описание единственного атрибута
$new->setA_cl('value', array('type'=>'varchar', 'max'=>255)); // строковый атрибут
$new->Save();

Определение атрибута сводится к указанию параметров поля таблицы БД. После сохранения, автоматически будет создана таблица «my» с полями «id» и «value» и добавлена запись в таблице «id» и «class», ассоциируемая с сущностью-классом «my»

Описание атрибута выполняется методом setA_cl($name, $params). Ничего сложного, просто указывается имя атрибута и параметры, соответствующие параметрам поля таблицы БД.

Если нужно определить свойство, тогда методом setP_cl($name, $class) создается связь с именем $name на сущность-класс $class. При этом можно уточнить атрибуты связи, обратившись методом getL_cl($name) к автоматически созданной сущности-связи.

Теперь можно создать объект класса «my». Допустим, созданный класс «my» получил идентификатор 5.

$new_obj = Object::Create();<br>$new_obj->setP('class', Object::Create(5)); <br>$new_obj->setA('value', 'Тра ля ля'); // Установка значения атрибута<br>$new_obj->Save();<br>

Удаление сущности


Удалить сущность ещё проще:

$obj = Object::Create(6);
$obj->Delete();

Сначала создается экземпляр удаляемой сущности (сущность не загружается из БД), потом вызывается
метод Delete().

При удалении сущности, автоматически удаляются все её связи. Если связь является вида «состоять» (kind=0), то будет удалена и сущность, на которую ссылается связь. Удалить сущность, являющеюся составной частью другой сущности (вид связи = «состоять»), не получится. Также не получится удалить класс, если он наследуется другим классом или имеются сущности этого класса.

Текущая версия CMS Boolive в свободном доступе. Информация на сайте
Для тестирования приведенного в статье кода, вставляйте его в файл
/modules/page/templates/view/root.php

Модуль данных наделяет три базовые сущности необходимой логикой, обеспечивающей невероятно гибкие возможности для конструирования объектной модели любой сложности.

Моделирование содержимого


Приступим к моделированию содержимого сайта. Создадим несколько простых классов для представления строк и чисел: string, long_string, text, integer, double. Каждый класс определяет один атрибут «value» для значений. Класс «string» — для представления коротких строк размером не более 255 символов. «long_string» — для представления длинных срок, ограничения по длине не более 20486 символов (20Кбайт). Класс «text» — для очень длинных строк размером до 65535 символов включительно (64Кбайт). «integer» – для целых знаковых чисел разрядностью 32 бит (-2147483648… +2147483647). «double» — для действительных чисел (-1.7976931348623157e+308… 1.7976931348623157e+308). В представление значений других типов необходимости пока нет.

Все пять новых класса наследуют базовый класс «id». Определяются же они классом «class», от чего они и являются классами. На диаграмме (Рис. 1) у сущностей отображены не все атрибуты – идентификатор дублируется в заголовке прямоугольника сущности, там же указаны класс сущности его системным именем («class») и значение атрибута «sys_name» курсивом зеленым цветом.
PHP код для создания 5 классов представляющих строки и числа.

Основные классы для представления строк и чисел.
Рис 1. Основные классы для представления строк и чисел.

Теперь можно создавать объекты любого из новых классов. Но мы займемся другим. Добавим определение двух свойств у класса «class» и «link». Определяя два свойства в классе «class», мы тем самым добавляем соответствующие свойства всем классам: «id», «class», «link», «string» и остальным, так как они являются объектами класса «class». Также и для класса связей «link» – у всех объектов-связей появятся свойства вдобавок к существующим атрибутам.

Итак, добавляем определение, для этого создаем связь между классом «class» и классом «string» (Рис 2. #9). У связи устанавливаем значения атрибутов: вид связи («kind»=0), мощность («size»=1), признак обязательного существования определяемого свойства у объектов («is_mandat»=1). Остальные атрибуты инициализируются автоматически. Создаются две связи, одна на класс «string» другая на класс «long_string».
PHP код для определения свойств в классе «class»

После у каждого класса будет свойство «name» и «descript». Первое свойство для именования класса на русском языке, второе для описания назначения класса. Фактически, мы наделили объектную модели возможностью самой себя документировать. Для примера на сайте будет ссылка «создать Новость» при этом само слово «Новость», да ещё и описание, что такое новость или, что реально будет создано, будет осмысленно взято из БД, а не прописано в шаблоне оформления или подгружено откуда-то со стороны.

Теперь добавим определение двух свойств в классе «link». Эти свойства будут необязательны для связей (атрибут «is_mandat» = 0). Первое свойство с системным именем «label» будет использоваться для именования связи на естественном языке, второе свойство для пояснения связи (свойства объекта, владеющего связью). Рис 2. #11, #12.
PHP код для определения свойств в классе «links»

Созданные определения свойств для связей имеют такой же смысл, что свойства для классов. Благодаря ним объектная модель, словно сама будет говорить: «новость имеет заголовок». Объектная модель теперь способна сама себя описывать и способствовать созданию удобного пользовательского интерфейса.

Теперь необходимо у классов и связей создать свойства, наличие которых только что было определено. Начнем со связей. Так как свойства для связей определены необязательными, ограничимся лишь именованием связей у классов «class» и «link». Рис 2. #13 – #20
РHP код для добавления свойств «label» у связей, определяющих свойства классов и связей

Теперь разберемся с классами. Всего уже 8 классов. Так как свойства для классов определены обязательными, значит, их необходимо создать у каждого класса. Фактически добавляется нормальное название и описание каждому классу. Рис 2.
РHP код для добавления свойств «name» и «descript» у всех классов

Основные классы данных и их описание
Рис 2. Основные классы данных и их описание.

Как видно по диаграмме (Рис 2), у каждого класса есть название и описание на русском языке. Все связи между сущностями являются объектами, что тоже видно по диаграмме. Объекты строк схематично упрощены, текст в нутрии прямоугольника – это значение единственного их атрибута «value».

Теперь ближе к теме содержимого сайта. Создадим для примера четыре класса. Первый из них – класс «content» (Материал) со свойствами «head» (Заголовок) и «text» (Текст). Это будет самый простой тип материала для сайта, который подойдет для новостей или статей. Класс «content» наследуется от базового класса «id», а в качестве свойств используются классы «string» и «text». Рис 3. #52 – #63
PHP код для создания класса Материал

Продемонстрируем возможности наследования. Создадим класс комментария «comment» наследуя его от класса «content», от чего он уже будут иметь определение свойства заголовок и текст. Для комментария посчитаем их достаточными и ничего нового в нем определять не будем. Рис 3. #64 – #68.
PHP код для создания класса Комментраий

Для категоризации материала сайта создадим класс категории «category». Класс «category» наследует базовый класс «id», поэтому у него нет ещё определений свойств. Для категории необходимо название и его пояснение – всё те же свойства с подобным назначением. Особенностью категорий является их иерархичная структура. Чтобы можно было создавать иерархии из категорий, у каждой категории должна быть связь на родительскую категорию. Эту возможность обеспечивает свойство «parent», в определении оно связывает класс «category» с самим собой, что указывает: родитель для категории является категорией. Рис 3. #69 – #82.
PHP код для создания класса Категория

И последний класс, который будет нами создан – класс новостей «news». Этот класс наследует класс «content» и добавляет новые определения свойства «comments» (Комментарии) и «category» (Категория). Свойство «comments» множественное, потому что у новости может быть много комментариев. Рис 3. #83 – #90.
PHP код для создания класса Новость

Классы матариала и его категоризации
Рис 3. Классы матариала и его категоризации

Все необходимые классы созданы. В рабочей CMS конечно будет множество других классов и не только для представления содержимого. Сейчас цель – продемонстрировать работу модуля Data и возможностей, предоставляемых им в управлении данными.

Создадим, наконец, несколько объектов содержимого. Три категории, две новости и два комментария, принадлежащих к одной новости. Вторая новость без комментариев. На диаграмме хорошо видны созданные объекты и их составные свойства и свойства вида «использовать». Рис 4.
PHP код для создания трех категорий, двух новостей и двух комментариев для первой новости

Три категории, две новости и два комментария
Рис 4. Три категории, две новости и два комментария.

Это только пример объектной модели содержимого. В виде объектов данных можно представить любой элемент сайта: модули, блоки, пользователей, группы, действия и права на них – возможности фантастические.

Теперь возникает вопрос, как с этим всем работать? Как, например, получить список новостей? А как насчет условий, например новости определенной категории? Все эти функциональные возможности уже реализованы. В начале статьи были упомянуты классы Query и Cond*. С помощью них осуществляется поиск сущностей. Об этом будет рассказано в продолжение статьи – в третьей части.

Продолжение: Модель данных. Часть 3
Сайт проекта: boolive.ru
Tags:
Hubs:
+23
Comments 53
Comments Comments 53

Articles