Pull to refresh

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

Reading time 8 min
Views 6K
Система управления содержимым (CMS) обязана предоставить гибкие всеохватывающие функциональные возможности для управления содержимым сайта, облегчить работу администратора-конфигуратора и способствовать созданию удобного в использовании сайта. Содержимым сайта можно назвать новости, размещенные на нём, а также статьи, комментарии, фотографии. Содержимым также являются целые структуры информации: новостные ленты, каталоги, форумы, блоги. Обобщенно: содержимое – это данные, размещенные на сайте.

CMS может просто передавать данные по запросу клиентскому приложению, например сетевой программе, flash-клипу или AJAX-приложению. Но чаще всего, CMS предоставляет клиенту уже подготовленные для отображения данные в HTML формате. В этом случаи, для обеспечения доступности, легкости восприятия и удобства пользования содержимым, выполняется стилизация и объединение его с элементами оформления (темы, шаблоны), навигации (меню, ссылки) и управления (формы и ссылки тоже), и всем этим тоже нужно управлять.

Идея


Окружающий мир воспринимается нами объектным, мы мыслим «объектами», в наших умах выстраивается объектная модель мира. Поэтому нам не составит труда создать объектную модель содержимого сайта и управлять ею. Новости, товар в каталоге, сообщения на форуме и сами форумы, и все другое можно представить в виде объектов. Устанавливая связи между объектами, можно создавать структуры данных любой сложности, от добавления комментариев к статьям до создания социальных сетей и более.

Объекты, классы и связи данных – это информация, которую нужно уметь создавать, хранить, использовать, изменять и удалять. В нашем распоряжении реляционная база данных для хранения информации. Действия же совершаемые с информацией – часть логики функционирования CMS, которая в большей части будет реализована модулем данных Data. Речь идет о создаваемой CMS Boolive. Предыдущая статья про архитектуру CMS.

Фактически модуль данных будет осуществлять объектно-реляционную проекцию (ORM) – предоставлять объектно-ориентрованный доступ к базе данных. При этом сущности объектной модели (классы, связи, объекты) будут определяться в базе данных, а не программно, что позволит, не программируя, создавать новые классы данных – новые типы содержимого.
Чтобы не путаться с объектами и классами языка программирования, применяются термины «объект данных» и «класс данных»
Для хранения однотипных объектов в реляционной базе данных в принципе достаточно одной таблицы, поля которой будут соответствовать атрибутам объекта. Но объекты могут иметь связи на другие объекты и чаще только их и имеют. Вместо того, чтобы в таблице объектов создавать поля (внешние ключи) для связи с другими объектами, лучше создать отдельную таблицу для связей – это, кстати, позволит хранить дополнительные сведения о связях, в частности, именование и их тип. Связь образует свойство у объекта, владеющего связью. Свойством называется совокупность связи и объекта, на который установлена связь.

Атрибуты и свойства объектов определяются классом. Фактически же атрибуты определяются таблицей – её полями. Определение свойств у объектов можно реализовать наличием связей между соответствующими классами. Сами классы тоже нужно хранить, поэтому для них тоже нужно создать таблицу. По имени класса будет определяться таблица с его объектами.

Каждая сущность (класс, объект и связь) должна иметь идентификатор – уникальное целое число, уникальное среди всех сущностей. То есть не должно быть, например, класса и объекта с одинаковым идентификатором. Уникальность идентификатора среди разнотипных сущностей позволяет, в частности, хранить связи классов в таблице вместе со связями объектов. Термин «сущность» подразумевает и объект, и класс, и связь.

Одно из основных понятий объектной модели – наследование классов. Наследование тоже нужно представить в реляционном виде и это достаточно легко сделать, определив целочисленное поле в таблице классов для хранения идентификатора наследуемого класса (внешний ключ), и оперировать этой информацией для слияния соответствующих таблиц объектов при запросах. Если класс «B» наследует класс «A», то атрибуты объектов класса «B» будут храниться в таблице «А» и «Б».

Классы являются объектами! Пока без объяснений. Так как класс – это объект, а у любого объекта имеется класс, значит и у класса есть класс, определяющий атрибуты для него, в частности имя и идентификатор наследуемого класса. Связи тоже являются объектами, соответственно и они имеют свой класс. Таким образом, всё является объектами, но чтобы не путаться с назначениями разнотипных объектов, в целях обобщения будет применяться термин «сущность».

Реализация


Итак, исходя из всего изложенного, каждая сущность обладает идентификатором и классом, точнее связью на класс. Приступим к реализации и для всех сущностей создадим таблицу с названием «id». В таблице два поля – идентификатор сущности и идентификатор класса сущности. Мы создали (пока ещё не полностью) базовый класс «сущность» – все остальные классы обязаны будут наследовать класс «сущность» иначе смысла в их создании не будет.

CREATE TABLE `id` (  
  `id` int(10) unsigned NOT NULL auto_increment,  
  `class` int(10) unsigned NOT NULL,  
  PRIMARY KEY  (`id`),  
  KEY `index_class` (`class`)  
);


Создаем второй класс с названием «class». Пока создание класса сводится к созданию таблицы. Создаем таблицу с именем «class» и полями «id», «sys_name», «extend», «final». Поле «id» используется для связи «один к одному» с таблицей сущностей, поле «id» используется логикой системы для слияния записей при реализации наследования. «sys_name» – это системное имя класса, совпадающее с именем таблицы, в которой будут храниться объекты соответствующего класса. «extend» – идентификатор наследуемого класса. «final» – признак возможности наследования класса (если 0).

CREATE TABLE class (  
    `id` int(10) unsigned NOT NULL,  
    `sys_name` VARCHAR(255) NOT NULL,  
    `extend` int(10) unsigned NOT NULL default '1',  
    `final` tinyint(1) unsigned NOT NULL default '0',  
    PRIMARY KEY  (`id`),  
    UNIQUE `index_sys_name` (`sys_name`)  
);


Сейчас понять картину взаимосвязей будет непросто – это как случай с курицей и яйцом, типа, что было первым? В данном случае даже ошибочно рассказывать появление двух сущностей (классов) последовательно, так как они должны появиться одновременно и не могут существовать друг без друга.
Созданы две таблицы – как бы созданы два класса, но именно как бы, так таблицы пусты – сущностей нет. Первая сущность – это базовый класс «id» называемый «сущность». От того, что сущность эта является классом, ассоциируемая с ней запись должна быть как в таблице «id» так и в таблице «class». Идентификатор первой сущности равен 1. Обратите внимание на системное имя класса – оно совпадает с именем таблицы «id». Вторая сущность тоже является классом, поэтому запись тоже и в таблице «id» и в таблице «class». Системное имя второго класса «class» совпадает с именем таблицы созданной для классов. Второй класс наследует первый – это определено в поле «extend». Второй класс запрещено наследовать.

Таблицы «id» и «class»
Теперь обратим внимание на поле «class» в первой созданной таблице. Обе сущности определяются классом с идентификатором 2. Да, именно так! Первая сущность определяется классом 2, поэтому она является классом, у неё определены все атрибуты характерные классам. Вторая сущность, тоже является классом и наследует первый класс, поэтому объекты класса 2 имеют идентификатор и связь на класс. Проанализируйте взаимосвязи, посмотрите, как все лаконично само себя определяет – и таблицы и атрибуты и то, что обе сущности являются классами.

Создадим теперь класс для связей. Класс-связь определяет атрибуты специфичные для связей:
  • «sys_name» – системное имя связи;
  • «define» – идентификатор связи, определяющей данную связь;
  • «kind» – признак вида связи: «использовать» или «состоять»;
  • «size» – признак мощности связи: «много» или «один»;
  • «is_define» – признак, что связь определяет связь между объектами;
  • «is_mandat» – признак, что связь обязательно должна быть у объекта;
  • «primary» – идентификатор сущности владельца связи;
  • «secondary» – идентификатор сущности, с которым выполняется связь.


CREATE TABLE `link` (  
  `id` int(10) unsigned NOT NULL,  
  `define` int(10) unsigned NOT NULL default '0',  
  `sys_name` varchar(255) NOT NULL,  
  `kind` tinyint(1) unsigned NOT NULL default '0',   
  `size` tinyint(1) unsigned NOT NULL default '1',  
  `is_mandat` tinyint(1) unsigned NOT NULL default '1',  
  `is_define` tinyint(1) unsigned NOT NULL default '1',  
  `primary` int(10) unsigned NOT NULL,  
  `secondary` int(10) unsigned NOT NULL,  
  PRIMARY KEY  (`id`),    
  KEY `index_define` (`define`),    
  KEY `index_sys_name` (`sys_name`),  
  KEY `index_primary` (`primary`),  
  KEY `index_secondary` (`secondary`)  
);


Связи имеют двойное назначение, используемое логикой модуля Data. Первое назначение – непосредственная связь между объектами для реализации свойств, второе назначение – определение этих свойств классом. Класс, установкой связи с другим классом, определяет свойства для своих объектов. Но если рассматривать класс как объект, то он тоже может иметь свойства. Значит простым наличием связи между классами нельзя определить, что эта связь описывает свойство у объектов. Поэтому у связи имеются атрибуты «define» и «is_define». По атрибуту «define» можно найти связь между классами, определяющею данную связь. Атрибут «is_define» является признаком, принимающим значение 0, если это обычная связь между объектами и 1, если эта связь описывает свойство и применяется классом.

Связи бывают двух видов: «использовать» (агрегация, ассоциация) или «состоять» (композиция, быть частью). Вид определяется атрибутом «kind». Объекты, связанные с другим объектом связью вида «состоять» и являющиеся подчиненными, будут зависеть от объекта-владельца связи. Зависимость сводится к тому, что удаляя главный объект, будут автоматически удаляться все подчиненные объекты связанные связью вида «состоять». Есть другие особенности, например объект не может «быть частью» более чем для одного объекта.
Связи второго вида – вида «использовать» сохраняют независимость связанным объектам. Без ограничений и последствий может быть независимо удален как объект владелец свойства, так и объект являющийся свойством. Объект может «использоваться» любым количеством объектов.

Если бы не были бы оптимизированы связи объекта с классом и связь наследования у классов, то в общем счете было бы четыре вида связи. Вдобавок к описанным были бы связи вида «являться» и «наследует».

Следующий важный атрибут связи – «size» (мощность), принимающий значение «много» или «один». Например, у статьи может быть несколько комментариев, что будет определяться множественным свойством. Этот атрибут используется связью класса, описывающей свойства для объектов. На самом деле статья будет иметь ровно столько связей, сколько комментариев должно быть с ней связано, но эти связи будут иметь одинаковые значения атрибутов, отличие только в идентификаторе и объекте (комментарии), с которым выполняется связывание. Ниже в таблице приведены примеры свойств объекта статьи (в скобках указана мощность связей). Атрибуты, кроме идентификатора, у статьи отсутствуют.

Статья:
состоит использует
заголовок (1);
­дата создания (1);
­текст (1).
категория (1);
­автор (1);
­комментарий (Много);
­файл (Много);
­фотография (Много);

Атрибут «is_mandat» тоже используется связью класса, определяющей свойства у объектов, и является признаком, что свойство обязательно должно существовать у объекта, если значение атрибута не равно 0 (логическое значение).

Атрибуты «primary» и «secondary» осуществляют уже непосредственную связь между сущностями по их идентификаторам – являются внешними ключами для связывания таблиц объектов. Само связывание выполняется логикой модуля Data.

Таблицы «id», «class» и «link»
В итоге у нас имеется три основных класса – три сущности, на основе которых будет полностью построена модель содержимого сайта. На диаграмме отображены три созданные сущности. Пришлось даже сочинять способ их отображения одновременно в виде классов и объектов. Простой объект от класса на диаграмме будет отличаться отсутствием прямоугольника с определением атрибутов.

Диаграмма базовых классов
Логика модуля данных Data различает только эти три типа сущностей и предоставляет базовые действия: создание новых сущностей, их загрузку из БД, а также изменение и удаление. Каждый объект может иметь атрибуты и свойства в зависимости от своего класса. Классы наделяются возможностью определять свойства и атрибуты для объектов и возможностью наследоваться. Связи реализуют непосредственные ссылочные связи между объектами в программном смысле.

Что дальше


Впереди ещё как минимум две части на тему «модель данных». В следующей статье речь пойдет о самом модуле данных Data и будет продемонстрирован процесс создания содержимого сайта с помощью него.

Продолжение: Модель данных. Часть 2
Сайт проекта: boolive.ru Исходники в свободном доступе.
Tags:
Hubs:
+35
Comments 102
Comments Comments 102

Articles