CMS Arch — Part 1
Как организовать структуру данных в современной CMS? Верноятно, каждый разработчик рано или поздно сталкивается с подобным вопросом (конечно, каждый уважающий себя чуви пробует своё тельце на поприще создания систем управления контентом, и абсолютно все наступают на одни и те же грабли). Итак, прежде чем отвечать на данный вопрос хотелось бы определиться с перечнем «необходимых фишек»:
- Древовидная структура данных (вложенность элементов друг в друга)
- ЧПУ
- Поддержка «крошек» для любой страницы
- Скорость работы при большом (больше 100 000 записей) сете данных
- Поддержка многоязычного контента
- Версионирование контента
- Поддержка свободнодефинируемого Workflow (начнём с банального состояния published / unpublished)
Конечно, перечень «запросов» может показаться вполне банальным, однако эффективно использовать СУБД для подобной структуры — задача далеко не из самых простых. Давайте поставим грабли в сарай, чтобы никто на них случайно не наступил.
Общее Положение
Nested Sets + Рекурсивный Метод + Отдельное поле level — это мой стандартный подход к хранению нестатичной иерархии (то бишь где структура может изменяться) которая требует «навигации по ней» с набором условий (пр. просчитать путь от ноды А до ноды Я). НО — Nested Sets штука довольно громоздкая, а потому и использовать повсеместно в системе этот подход было бы по-меньшей мере — глупо.
Если думать дальше, то сразу же становится ясно, что в почти любой системе есть концепты «Папок» и «Конечных Страниц». Структура папок меняется крайне редко (обычно закладывается один раз и остаётся таковой почти всё время существования проекта — идеальный кандидат для умного кеширования), когда конечные страницы могут добавляться довольно часто.
Соответственно, хранить какие-либо иерархичные данные для конечных страниц — пустая трата ресурсов, ибо нам изначально известно, что это конечная страница и потомков она иметь не может. Более того, куда более доступными будут выглядеть ЧПУ из серии /folder/subfolder/cool_page.html нежели часто встречающийся /folder/subfolder/cool_page/ ибо последний вариант наводит на подозрение о том, что это тоже папка.
Отсюда следует, что древо нужно только для папок, страницы можно хранить в линейной структуре. Итак, имеем уже как минимум две таблицы в СУБД:
CREATE TABLE folders(
folder_id SERIAL,
name VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
parent_folder_id INT,
lft INT,
rgt INT,
level INT,
PRIMARY KEY(folder_id),
FOREIGN KEY(parent_folder_id) REFERENCES folders
ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE (url, parent_folder_id));
CREATE INDEX folders_ns_lft ON folders (lft);
CREATE INDEX folders_ns_rgt ON folders (rgt);
CREATE TABLE pages(
page_id SERIAL,
folder_id INT,
name VARCHAR(255) NOT NULL,
url VARCHAR(255) NOT NULL,
PRIMARY KEY(page_id),
FOREIGN KEY(folder_id) REFERENCES folders
ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE (folder_id, url))
Это, дамы и господа, только начало, хотя, замечу, начало уже достаточно функциональное, чтобы работать быстро и эффективно с большими сетами данных.
О том как далее оптимизировать это «начало» и расширить его (отделить структуру от данных, сделать автоматическое кеширование ЧЗИ, создать для себя все удобства работы с данными и т.д.) дабы оно отвечало «семи запросам» я напишу чуть позже, так что, алас,
Продолжение Следует…
p.s. Структура, что я описал выше — заметно изменится, это только начало пути ;)
whatever
Извини, что пишу сюда. Может мой мозг не очень развит, так как я не понял как добавить сообщение в баг-трекер.
Interra - minor bug.
Проблема:
Поиск по сайту, поисковая фраза %%%
Причина:
Сам знаешь, SQL запрос → LIKE
Решение:
str_replace('%', '\%', $phrase);
str_replace('_', '\_', $phrase);
Спасибо не за что.
И ещё проблема: поля ввода, например «Ваше Имя», в Opera выглядят просто ужасно.
11.03.2006 // 20:34 [ ссылка ]
Ответ от Автора
принял
что сложного может быть вот в этом процессе?
http://npj.dull.ru/interra/trako/add/
12.03.2006 // 14:50 [ ссылка ]
gray
Позволю не согласиться. Приведённый выше feature set - это, так сказать, classic old-fashion CMS. К счастью, ситуация меняется - многие разработчики (забудем пока о заказчиках) приходят к "парадоксальному" выводу, что классическая древовидная организация контента - не единственная возможная концепция.
Можно ли применять среднестатистический блог как CMS? Как ни странно, иногда можно. Можно ли применять Wiki как CMS? Ещё бы!
Современные концепции CMS довольно далеко продвинулись от классического древовидного представления "чего-то". Хотя, с другой стороны, никто не представил широкой публике "универсального" решения, которое могло бы стать примером для подражания на ближайшие годы.
И, разумеется, ещё один нюанс: инструмент выбирается (или разрабатывается) в зависимости от задачи. Корпоративный сайт и, допустим, commynity site уместить в одну концепцию (не говоря уж о реализации) - дело весьма геморройное.
12.03.2006 // 14:47 [ ссылка ]
Ответ от Автора
это только начало — оставайтесь с нами 8) древо я вскоре несколько поменяю да и вообще ввезу несколько дополнительных связок/терминов, которые используют в современных ОС... писать сразу о том, к чему я пришёл за 7 лет разработок CMS — нес смысла, ибо нужно описать дорогу, каждый может остановиться там, где нужно
12.03.2006 // 14:49 [ ссылка ]
codex
Последнюю версию своей CMS я организовал именно так, но, как оказалось, подобная структура не совсем понятна/совсем не нравится клиентам. Обычно, клиент хочет, чтобы у него была страница и у этой страницы подстаница. Клиент не хочет понимать структуру сайта с её папками и страницами - ему подавай десять страниц в меню и по три подстраницы у каждой страницы. Именно поэтому пришлось перейти на хранение всего дерева в одной таблице, а для особо контентных проектов внедрять систему с папками.
Ах да, сначала пробовал сделать так - если в папке всего одна страница - она отображается сразу, но это усложняло структуру. Люди не хотят вместо одно действия, выполнять два - создавать папку, потом страницу.
14.03.2006 // 10:35 [ ссылка ]
Ответ от Автора
к типизации данных я ещё вернусь :-)
а с клиентов за обучение надо бабло просто брать
15.03.2006 // 13:35 [ ссылка ]
codex
Я сделал два модуля дерева - в одном страницы хранятся в дереве, в другом - в узлах типа "папка" и вынесены в отдельную таблицу. Это, собственно, подтвержает ошибочность мифа об универсальных CMS :)
15.03.2006 // 14:46 [ ссылка ]
Amix
Может, просто отделить иерархию данных от контента?
Я сделал пока NS-дерево категорий (~папок), к которым крепится контент. Единица контента крепится к любому числу категорий. Категории можно отображать как элементы навигации; контент -- нет.
Клиент со второй попытки уже без труда улавливает смысл и преимущества =)
И, да, в настройках конкретной категории ее владелец может проставить особый флажок, и тогда категория будет как бы оберткой для первого же ее контента (с учетом правил сортировки). Т.е., это уже не списковый контекст. Никакую структуру никак сие не усложняет.
Правда, в любом случае, я уже подумываю о другом подходе, но он как-то очень уж смутно пока вырисовывается... ибо заявленная цель -- универсальная cmf с универсальной базовой структурой данных -- довольно амбициозна. Постепенно приходит понимание этого "нюанса", угу. %)
18.03.2006 // 18:03 [ ссылка ]
Ответ от Автора
конечно-конечно ;) я до этого тоже дойду — как же ещё сделать по-вашему версионирование и мультиязычность? ;))
18.03.2006 // 18:10 [ ссылка ]
plandem
аналогично :) структура в nested, а "страницы" уже прикрепляются к разделам, могут к нескольким одновременно. ну и если к какому-то разделу прилеплено несколько "страниц", то в зависимости от настроек системы, выбирается та, которую показывать как содержимое раздела :)
20.03.2006 // 10:21 [ ссылка ]