Обратите внимание, что новости можно получать по RSS.
X
-

События, Информационные технологии, LiveJournal cr_it - архив

27 февраля 2010, 05:15 (5169 дней назад, №8784)Об универсальной CMS "Engine"
Взаимосвязи объектов CMS "Engine"Примерно десять лет назад мной была начата разработка веб-движка, как сейчас бы сказали - CMS. Изначально, он предназначался всего лишь для автоматической вёрстки HTML (картинки + текст) в несколько "газетных" полос, одинаковой высоты. Поскольку я тогда время от времени делал на заказ различные сайты, постепенно этот проект стал развиваться. Задачи возникали весьма разнообразные, поэтому архитектура движка (под рабочим названием "Engine") была задумана универсальной,  позволяющей:
1) реализовать любой сайт, не переписывая движок 2) добавлять на ходу новые возможности, которые изначально не предполагались. Ценой, на которую я осмысленно пошёл, стало увеличение нагрузки на сервер, что на практике ни разу не стало существенной проблемой.

Движок реализован на PHP/MySQL.. Главным понятием в системе являются т.н. "объекты". Это название не связано с ООП (до определенного момента весь код вообще был чисто процедурным). Объектами является всё, что может хранится в базе и меняться. Это, к примеру, страницы сайта, разделы/темы/сообщения форума, личные сообщения, файлы, категории, опросы, задачи, пользователи, ..., и (особо подчеркну): связи между объектами, также являющиеся объектами.


С точки зрения структуры базы, центральной является таблица objects. В ней регистрируются все объекты (функцией RegisterObject),  каждому присваивается уникальный uid. При регистрации, и в ходе работы, каждый объект имеет ряд характеристик: тип, класс, даты создания/изменения/чтения, уровни доступа для различных операций, имя шаблона для вывода, пользователь, который создал и последним изменял (что позволяет в любой момент узнать, что и как происходило в системе, пусть даже год назад) и т.д.
Тип объекта определяет, в какой таблице хранится его содержательная часть - данные.

Форум на сайте CC8 (в привилегированном режиме)Возьмём конкретный пример. Наиболее распространённым типом объекта является "text". У этого типа существует ряд "подтипов", называемых "класс". Это, к примеру, news (новость в ленте новостей), doc (страница) , forum_message (сообщение в форум), forum_topic (тема форума), forum_section (раздел форума), privmsg (личное сообщение). Логика здесь простая - все перечисленные сущности очень близки по смыслу, а следовательно методы работы с ними - похожи.

Допустим, мы хотим представить тему форума и в ней пару сообщений.
В таблице objects для этого у нас будет как минимум три объекта - один text:forum_topic и два text:forum_message
Кроме того, в таблице texts будет тоже три объекта, с теми же uid. Там будут храниться заголовки и текст.
Соответственно, SELECT * FROM objects, texts WHERE objects.uid = texts.uid AND objects.uid = $obj_uid даст нам полную информацию об объекте.

СВЯЗИ

Но, нам нужно не просто хранить эти три объекта. Два из них (сообщения) должны быть связаны с третьим (темой). Для этого существуют связи (устанавливаются функцией LinkObjects). Любые связи хранятся в таблице links и являются такими же объектами. Помимо собственного uid, связь содержит информацию о том, какие объекты она связывает (uid_1, uid_2) и её тип. Так, forum_message связаны с forum_topic связью типа LINK_MESSAGE.
Отметки о прочитанных темах и сообщениях фиксируются путём установления связей LINK_USER_READ между пользователем и темой/сообщением.

Понятно, что поскольку связи хранятся отдельно, это часто приводит к лишнему SELECT'у из базы. Впрочем, в жизни посещаемый сайт с миллионами записей в базе (десятками тысяч сообщений в форуме) работал вполне прилично. Кроме того, такой подход (помимо универсальности и постоянства структуры базы) даёт ряд возможностей:

1. Контекст.

Мы можем отображать или редактировать объект не сам по себе, а в контексте другого объекта. Для этого у связи есть поля n, comment, tpl и некоторые другие. К примеру, возьмём распространённую ситуацию - галерею с несколькими разделами. В ней есть фотографии. Причем, некоторые встречаются одновременно в двух разделах.

Необходимо было:
а) Расставлять их в нужном для каждого раздела порядке
б) Показывать различные подписи к одному и тому же снимку, опубликованному в разных разделах.

Это как раз те случаи, когда информация хранится в связи (links.n, links.comment соответственно).
Еще одна ситуация - допустим, на сайт загружена картинка, которая опубликована и в ленте новостей и в галерее. Понятно, что её оформление (рамка, фон, отступы и прочее) в этих двух ситуациях должны быть разными. Здесь и используется links.tpl - в нём хранится имя темплейта для вывода объекта взависимости от того, к чему он относится. Если в links.tpl ничего не указано, используется темплейт из objects.tpl. Если и там ничего нет, используется дефолтный (для данного типа/класса объекта).

Вообще говоря, хранение данных внутри связи - не вполне красивое решение и продиктовано отчасти эффективностью, отчасти простотой. Следующим логическим шагом были..

2. Связи между связями и объектами.

Пример - реализация опросов/голосований. Схема такая:

Имеется корневой объект poll (это конкретный опрос, содержащий сам вопрос) и ряд объектов dec (это возможные варианты ответов). Объекты poll и dec связаны связями типа LINK_DEC.
Когда пользователь выбирает один из вариантов ответа, это фиксируется не счётчиками, а установлением связи (типа LINK_LINK) между _связью_  poll--LINK_DEC--dec и пользователем user.
Это даёт а) возможность отзыва голоса/переголосования б) возможность посмотреть, кто как голосовал в) возможность для пользователя прокомментировать свой выбор (комментарий будет помещен в эту самую связь типа LINK_LINK).
Стоит упомянуть, что для опроса poll вариантами ответов могут быть необязательно dec, но и любые объёкты (к примеру, страничка сайта, картинка, новость и т.п.). Таким образом была реализована возможность оценки - т.е. выставления пользователем баллов объекту и даже комментирование его (число баллов записывается в links.power, возможный комментарий в links.comment).
Эта схема позволяет реализовать самые сложные варианты голосований, оценок и вообще фиксации мнения пользователя о чём-либо.

И ЕЩЕ О ГИБКОСТИ СИСТЕМЫ

Когда понадобилось не просто публиковать ленту новостей, а еще и комментировать новости, это было сделано безболезненно - введением связи между новостью (news) и темой форума (forum_topic). Тема создавалась для каждой новости и заодно привязывалась к отдельному разделу форума (forum_section).
Таким образом, при желании можно было посмотреть все комментарии к новостям и работать с ними как с любыми другими темами и сообщениями форума (подписаться на новые комментарии, к примеру).
Аналогичная ситуация была, когда темы форумы привязывались к задачам (tasks) и статьям бюджета (things) на сайте Chaos Constructions.
Иными словами, над любым объектом можно потенциально производить любую работу. Оценивать, комментировать, помечать прочитанным, прицеплять к нему другие объекты - нет никаких технических ограничений - любая такая доработка сводится главным образом к тому, как это должно быть отображено.
Можно создавать сложные многоуровневые каталоги при помощи объектов cat (категории) и связей LINK_CAT к категоризуемым (причем, любого типа) объёктам.
Структуры и взаимосвязи между объектами могут быть любыми.
Так, если для файла изображения недостаточно иконки одного размера, можно сделать 10 размеров, последовательно соединяя между собой объекты file связями LINK_FILE.
А связь типа LINK_REDIR позволяет при клике на объект показывать другой объект (использовалась для перехода по URL при клике на картинку).

Замечу, что поскольку всё от форума до страниц и новостей хранится и регистрируется по одним принципам, поиск по сайту позволяет искать сразу везде, при необходимости с ограничением по типу объектов, периоду времени и пользователям их создавшим или редактировавшим.

О ДРУГИХ ХАРАКТЕРИСТИКАХ ОБЪЕКТОВ

Поскольку добавлять для каждого объекта новые поля из-за каждой новой характеристики не хотелось (тем более, выборка по ним как правило была не нужна), появилось поле options (TINY TEXT), в котором записывались, в текстовом формате, различные опции. К примеру:

sort: dt_crt
sortdir: desc


для объекта cat (категория) означали, что все объекты с ней связанные, при показе надо отсортировать по дате их создания, в сторону убывания.

Объекты text могли быть "статическими" и "динамическими". В первом случае их содержимое (текст странички, к примеру) хранились в базе данных, в таблице texts. Во втором, в таблице texts указывался путь или URL по которому нужно было взять эти данные в момент показа.

Любые объекты помимо их uid могли иметь также alias'ы. Т.е. во всех функциях можно было указывать не только uid объектов, но и их произвольное имя в виде текстовой строки (к примеру, ...&uid=mainpage... вместо ...&uid=37826... в query string).

Техническая информация о юните на странице где он показываетсяЧто касается прав доступа, то они реализованы довольно примитивно. Для каждого объекта в таблице objects были поля, где хранился минимально необходимый уровень доступа на разные операции (any access, create, read, write, delete). Почти с самого начала (судя по черновикам - с 2001 года) я планировал реализовать так называемую capability based схему - с выдачей пользователю ключей на определенные операции и их проверкой, но серьезной практической необходимости так и не возникло.

Одним из усовершенствований стала многоязычность. Каждый объект, для которого существует перевод, имеет копии на всех необходимых языках. Один из объектов является основным. К нему происходит обращение в любом случае. В options у него прописаны uid/alias всех переведенных объёктов:

alterlang:deu=cat_wallpapers_deu
alterlang:fra=cat_wallpapers_fra


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

КАКИЕ ЕЩЕ БЫЛИ ОБЪЕКТЫ

Задачи (tasks) представленные в виде диаграммы Ганта для сайта Chaos Constructionsseqs (последовательности) - позволяют задать закономерность для последовательного показа нескольких объектов (например, баннеров или объявлений). Т.е. при показе объекта seq в этом месте последовательно демонстрировались объекты, по закону, указанному в этом seq (случайному, по-порядку, в указанном порядке).

notices (уведомления) - любого пользователя можно подписать на уведомление об изменении состояния объекта (создан новый объект интересующего типа, изменён или удалён определенный объект и т.п.)

В одном из проектов, для сайта поддержки игры, потребовалось реализовать каталог юнитов (танки, самолёты, орудия и т.п.) Причем, у каждого юнита могло быть установлено вооружение, в свою очередь снаряженное боеприпасами. Для этого были добавлены типы объектов units, weapons, shells. Взаимосвязи, как обычно, устанавливались через links.
Данные каталога использовались одновременно для просмотра посетителями сайта и для экспорта в саму игру. Так как для русскоязычной и иностранной аудитории были сделаны два разных (по содержимому и структуре) сайта, а каталог нужен и там и там, данные именно по units/weapons/shells периодически синхронизировались, что оказалось непростой задачей.

Для сайта Chaos Constructions, а также для багтрэкера было добавлено еще несколько типов объектов:
tasks (задачи), things (статьи бюджета), items (конкурсные работы), contacts (контакты).

С ТОЧКИ ЗРЕНИЯ РЕДАКТОРА САЙТА

Поскольку система никогда не была "коробочной", её установка и обслуживание требовало квалификации и знания внутренней, весьма нетривиальной структуры. Тем не менее, некоторые идеи и по сей день кажутся мне правильными.
Так например, во многих CMS редактирование содержимого сайта осуществляется в отдельном разделе, а результат смотрится уже на самом сайте. В нашем случае это было реализовано иначе. После логина привилегированного пользователя, на страницах сайта появлялись кнопки типа "редактировать", "удалить" и т.п., так что можно было просто редактировать конкретную часть той страницы, на которой находимся, даже не зная структуры сайта.
Интерфейс редактора так и не был доведён до user-friendly состояния - сначала были более важные задачи, а затем всё это стало неактуальным.

ЭПИЛОГ

Стас и Frog обсуждают движокДля меня эта система - один из лучших примеров работы "двойного назначения", когда один и тот же проект, с одной стороны, успешно использовался для ряда коммерческих сайтов (часть из которых эксплуатируются до сих пор) и в качестве bugtracker'a при их разработке. С другой - применялся для некоммерческих проектов - таких, как сайты Chaos Constructions 2004-2008 (кроме 2009), включая систему регистрации / подготовки показа конкурсных работ и project management'a.

В 2007 году Стас (фамилию и ник он попросил не упоминать) перевёл значительную часть системы из процедурной в ОО, а также придумал и реализовал ряд упомянутых выше возможностей (в ходе жарких споров со мной :).
Я сам не слишком люблю ООП, но когда система становится сложной - это неизбежно. Тот период был кульминацией развития движка - мы оба и сейчас считаем, что с точки зрения архитектуры и потенциальных возможностей разработка уникальна (по сравнению с известными существующими).
В завершение, предлагаю заинтересовавшимся посмотреть видео  (90 минут), где Стас рассказывает как раз об ОО части и архитектуре одной из последних версий системы. Рассказ был записан мной и предназначался для нового сотрудника (Дима, за кадром) и меня (я в тот период уже выполнял функции PM'a, за кадром задаю разные глупые и умные вопросы).
Опубликовано: Пётр Соболев

Случайная заметка

9191 день назад, 00:0023 февраля 1999 (Михаил Лихачев, 23 февраля 1999) Эта история не претендует на абсолютную историческую точность, так как была воспроизведена по памяти относительно событий пятилетней давности. Кроме того, так она выглядела с моей точки зрения и, вероятно, я мог упустить из виду какие-то важные моменты ее развития, ...далее

Избранное

2545 дней назад, 01:575 мая 2017 Часть 1: От четырёх до восьми Я люблю читать воспоминания людей, заставших первые шаги вычислительной техники в их стране. В них всегда есть какая-то романтика, причём какого она рода — сильно зависит от того, с каких компьютеров люди начали. Обычно это определяется обстоятельствами — местом работы, учёбы, а иногда и вовсе — ...далее

2057 дней назад, 20:305 сентября 2018 "Finally, we come to the instruction we've all been waiting for – SEX!" / из статьи про микропроцессор CDP1802 / В начале 1970-х в США были весьма популярны простые электронные игры типа Pong (в СССР их аналоги появились в продаже через 5-10 лет). Как правило, такие игры не имели микропроцессора и памяти в современном понимании этих слов, а строились на жёсткой ...далее