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

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

27 февраля 2010, 05:15 (5165 дней назад, №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, за кадром задаю разные глупые и умные вопросы).
Опубликовано: Пётр Соболев

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

9067 дней назад, 00:0023 июня 1999 Уже скоpо 2 года как я занимаюсь (в качестве хобби) чисто цифpовой фотогpафией. У меня никогда не было опыта по части фотогpафии тpадиционной, если конечно не считать детских pазвлечений с ФотоКоpом и нескольких кадpов Соколом-2. Беготня с пpоявкой пленок и печатью снимков сильно pасстpаивала, кpоме того иногда ...далее

Избранное

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

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