@popov654
Специалист в области веб-технологий

Как лучше организовать хранение и представление данных?

Доброго времени суток,

Я пишу систему вёрстки документов для публикации онлайн. Система нацелена на автоматический рендеринг текста и изображений, в первую очередь упор идёт на вывод текста колонками с выравниванием по левой строке, то есть стихов.

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

Каждый вложенный объект имеет размер, координаты, и радиус скругления углов.

При этом поддерживается упрощённая разметка (псевдо-HTML), чтобы пользователь мог быстро менять координаты и размеры прямо на месте (если не хочет делать это перетаскиванием мышкой), и видеть, что где находится. Разметка хранится сама по себе в виде текстового поля объекта-родителя, а дочерние элементы имеют вид <n pos="x, y" size="w,h" r="r">. При чём на этапе рендеринга эта разметка вставляется в генерируемый HTML почти как есть, после ряда обработок регулярными выражениями, и проставления нужных стилевых свойств.

Всё было довольно просто и понятно, пока очередь не дошла до реализации контейнеров.

Контейнеры - это элементы, обеспечивающие раскладку вложенных в них объектов. При этом сами они объектами не являются (по изначальному замыслу). Можно рассматривать их как некие менеджеры раскладки. Они, по замыслу, тоже должны превращаться в обычный HTML после обработки. Задумывалось два типа контейнеров - ряды и колонки (<row> и <col>). При этом ряд не может содержать вложенный ряд, а колонка не может содержать вложенную колонку. Вложение колонки внутрь ряда и наоборот - допускается.

Сейчас у меня есть функция, которая после редактирования дочернего объекта (контейнеры ещё не реализованы вообще) обновляет разметку родителя, отыскивая нужный фрагмент и заменяя его на новый. Кстати, важное замечание: разметка дочерних объектов, если есть, не включается в разметку родителя - то есть текстовая разметка "обслуживает" только прямых потомков (и контейнеры).

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

Я начал пытаться это реализовать. Написал функцию, которая обходит всю разметку и строит плоский список всех контейнеров (в случае, чтобы потом можно было легко реализовать функцию "поместить в контейнер" с произвольной целью назначения). При этом для каждого контейнера успешно сохраняется список его потомков, включая и вложенные в него контейнеры. Также сохраняются индексы начала и конца разметки данного контейнера в строке, содержащей разметку текущей страницы.

Далее у меня возник ряд вопросов, и я не знаю, в какую сторону лучше двигаться дальше.

1. При текущей модели каждая смена типа ячейки внутри контейнера с контейнера на не-контейнер потребует добавить или удалить элемент из массива дочерних объектов, а также заменить кусок разметки. При этом я хотел бы избегать ситуации, когда порядок следования элементов будет нарушен (например, такого
<row split="1,1,1"><col split="1,1"><1><2></col><3><6></row><4><5>
). Поэтому может потребоваться полная замена всех номеров в разметке и перестроение массива объектов, чтобы обеспечить правильный порядок следования.

2. С извлечением и добавлением возникает та же самая проблема нарушения нумерации и необходимости перестроения.

3. На данный момент индексы начала и конца у меня вообще сохраняются только для контейнеров (хотя нет никакой проблемы при обходе разметки сохранять их и для элементов).

4. Если не делать никаких перестроений, во всяком случае автоматически, это позволит пользователю перемещать элементы внутри разметки, сохраняя документ идентичным на выводе (кроме, возможно, порядка наложения слоёв, но для этого не проблема ввести поддержку z-order, и реализовать через банальные z-index). Но я сейчас не очень уверен в целесообразности наличия такой возможности. При том, что пользователь не видит массив объектов на странице в его "реальном" порядке следования, иметь разметку вида <4><1><2><3> - сомнительное преимущество, даже не знаю, зачем это может быть надо.

5. Можно сделать контейнеры объектами - просто в цикле рендеринга обрабатывать их особым образом в зависимости от их типа (то есть типов объектов будет уже не 2, а 4). Это позволит при смене типа не производить никаких манипуляций с массивом дочерних объектов - но проблема с нумерацией объектов при вставке в контейнер и извлечении из контейнера всё ещё остаётся.

6. Можно избавиться от нумерации вообще, представляя все объекты как <obj pos="x,y" size="w,h" r="r">. Это сразу снимет кучу проблем при разработке и отладке, а пользователю позволит путём cut-paste фрагментов разметки менять содержимое блоков местами без всяких кнопок. Довольно оригинально, хотя не особо наглядно и привычно, как по мне.

7. Наконец, можно вообще не хранить разметку: параметры, хранящиеся сейчас в виде атрибутов, хранить прямо в JSON (а чтобы это не выглядело "грязно", можно вынести их во вложенный объект layout, например, то есть сгруппировать). Что же касается разметки (чтобы пользователь видел, сколько у него элементов и какие они имеют координаты) - можно генерировать её вообще в рантайме при загрузке в редактор текущей страницы.

Как бы вы решили данную задачу? Я не имею большого опыта в построении таких серьёзных систем, поэтому хотел бы попросить совета.

UPD: в случае отсутствия контейнеров в системе поле разметки вообще было бы не нужно: можно было бы выводить объекты списком (традиционным или плитками в специальном поле по горизонтали в ряд), либо просто сделать кнопку "показать все объекты на странице", чтобы они выделились рамками. Но вот в случае контейнеров, да ещё и при том, что они могут вкладываться друг в друга (на мой взгляд, это удобнее, чем создавать новый контейнер в обычном объекте с размерами 100% на 100%) - хорошо бы дать пользователю понимание, кто в кого вложен. Хотя, опять же, можно решить это иначе - например, через динамически создаваемую строку пути (как путь в файловой системе ОС, только для контейнеров и объектов), или даже небольшое дерево в специальном поле, по элементам которого можно будет кликать.

UPD2: На самом деле, вся сложность заключается в том, что я хочу сделать контейнеры элементами абстрактного дерева (они могут быть вложены друг в друга, и элементы должны быть вложены в них), но при этом я хочу, чтобы объекты в своей странице, которой они принадлежат, хранились плоским списком - и даже если делать контейнеры объектами для удобства написания кода, я не хочу вкладывать в них обычные объекты, изымая их из общего списка объектов страницы. Именно эту проблему может решить поле с разметкой: объекты хранятся плоским массивом (кроме тех, которые имеют потомков уже внутри себя, но эти потомки и в текущем массиве не представлены), а контейнеры вообще хранятся только в текстовой разметке, и в JSON их нет. Если же помещать их в JSON - тут уже становится сложнее. Потомков можно "положить" в контейнеры через указание их индексов в плоском списке, допустим. А как вложить контейнеры друг в друга, опять использовать индексы в рамках плоского списка всех контейнеров? Как отличать эти два типа индексов, если они будут формировать единый список? Путём прибавки некоторого большого числа?

UPD3: Сама суть этого визуального редактора в том, что я должен мыслить немного как пользователь-гуманитарий (чтобы понимать нужды и потребности, и знать, что для человека наглядно и просто, а что нет), а реальный пользователь должен мыслить немного как программист (чтобы делать всё быстро и эффективно, и не путаться в иерархии объектов, а ещё чтобы выбирать оптимальный путь реализации при наличия нескольких вариантов - ведь можно одну раскладку подчас создать 3-4 разными способами с разным уровнем вложенности контейнеров и элементов). Поэтому с одной стороны, я не хотел бы применять подход, который применяют в традиционных программах такого плана, вроде Word, PowerPoint и аналогов, когда вся реализация полностью скрыта, а пользователь только косвенно влияет на результат с помощью разных команд и инструментов - а с другой нельзя делать модель слишком сложной, иначе она просто взорвёт людям мозг и никто не сможет пользоваться системой.
  • Вопрос задан
  • 137 просмотров
Пригласить эксперта
Ответы на вопрос 1
@popov654 Автор вопроса
Специалист в области веб-технологий
Отвечаю сам себе: действительно, самым простым способом оказалось хранить списки целых чисел в отдельном поле для каждого контейнера. При удалении и добавлении нужно просто будет обходить все контейнеры и корректировать числа, большие искомого значения на единицу вверх или вниз.

К тому же, в JS удаление элемента из массива делается одним вызовом splice(), так что с "перестроением массива" я тут конечно загнул.

От разметки отказался, данные по координатам теперь хранятся во вложенном объекте layout.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы