Как настроить отслеживание публикаций / истории с помощью ORM? - PullRequest
2 голосов
/ 19 декабря 2009

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

Я думал, что у меня могут быть такие таблицы, как (с соответствующими моделями)

[[POST]] (has_many (text) through (revisions)
id
title

[[Revisions]] (belongs_to posts/text)
id
post_id
text_id
date

[[TEXT]]
id
body
user_id

Где я могу присоединиться ЧЕРЕЗ таблицу ревизий, чтобы получить самое последнее тело ТЕКСТА. Но я как-то туманно отношусь к тому, как все это будет работать. Кто-нибудь настраивал что-то подобное?

По сути, мне нужно иметь возможность загрузить статью и запросить последнюю запись содержимого.

// Get the post row
$post = new Model_Post($id);
// Get the latest revision (JOIN through revisions to TEXT) and print that body.
$post->text->body;

Наличие возможности перетаскивания назад во времени к предыдущим ревизиям и удаления ревизий также будет большой помощью.

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

: EDIT:

Кажется, что движение вперед за двумя столами, кажется, имеет смысл. Так как я планирую хранить две копии текста , это также поможет сэкономить место. Первая таблица posts будет хранить данные текущей ревизии для быстрого чтения без каких-либо объединений. Записи body будут значением поля text соответствующей ревизии, но обработаны через markdown / bbcode / tidy / etc. Это позволит мне сохранить исходный текст (для следующего редактирования), не сохраняя этот текст дважды в одной строке редакции (или не анализируя его при каждом его отображении).

Так что выборка будет удобной для ORM. Затем для create / updates мне придется обрабатывать ревизии отдельно, а затем просто обновить объект post новыми значениями текущей ревизии.

  CREATE TABLE IF NOT EXISTS `posts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `published` tinyint(1) unsigned DEFAULT NULL,
  `allow_comments` tinyint(1) unsigned DEFAULT NULL,
  `user_id` int(11) NOT NULL,
  `title` varchar(100) NOT NULL,
  `body` text NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`),
  KEY `published` (`published`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

CREATE TABLE IF NOT EXISTS `postsrevisions` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `post_id` int(10) unsigned NOT NULL,
  `user_id` int(10) unsigned NOT NULL,
  `is_current` tinyint(1) unsigned DEFAULT NULL,
  `date` datetime NOT NULL,
  `title` varchar(100) NOT NULL,
  `text` text NOT NULL,
  `image` varchar(200) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `post_id` (`post_id`),
  KEY `user_id` (`user_id`),
  KEY `is_current` (`is_current`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

Ответы [ 4 ]

2 голосов
/ 01 января 2010

Ваша таблица Revisions, как вы показали, моделирует отношение «многие ко многим» между Posts и Text. Это, вероятно, , а не , что вы хотите, если только данная строка в Text не может предоставить содержимое для нескольких строк в Posts. Это не то, как работает большинство архитектур CMS.

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

Таким образом, вам может понадобиться только отношение «один ко многим» между двумя таблицами: Posts и Revisions. То есть для каждого поста может быть несколько ревизий, но данная ревизия применяется только к одному посту. Другие предложили две альтернативы для поиска текущей записи:

  • Флаговый столбец в Revisions, чтобы отметить текущую версию. Изменить текущую ревизию так же просто, как поменять флаг на true в желаемой ревизии и на false для ранее текущей ревизии.

  • Внешний ключ в Posts для ревизии, которая является актуальной для данного сообщения. Это еще проще, потому что вы можете изменить текущую редакцию в одном обновлении вместо двух. Но циклические ссылки на внешние ключи могут вызвать проблемы с резервным копированием и восстановлением, каскадными обновлениями и т. Д.

Вы даже можете внедрить систему ревизий, используя одну таблицу :

CREATE TABLE PostRevisions (
  post_revision_id SERIAL PRIMARY KEY,
  post_id INT NOT NULL,
  is_current TINYINT NULL,
  date DATE,
  title VARCHAR(80) NOT NULL,
  text TEXT NOT NULL,
  UNIQUE KEY (post_id, is_current)
);

Я не уверен, что дублирование для хранения title с каждой ревизией дублируется, потому что название может быть пересмотрено так же, как и текст, не так ли?

Столбец is_current должен быть либо 1, либо NULL. Уникальное ограничение не считает NULL, поэтому вы можете иметь только одну строку, где is_current равно 1, и неограниченное количество строк, где оно равно NULL.

Это требует обновления двух строк, чтобы сделать ревизию текущей, но вы получаете некоторую простоту, сводя модель к одной таблице. Это большое преимущество при использовании ORM.

Вы можете создать представление, чтобы упростить общий случай запроса текущих сообщений:

CREATE VIEW Posts AS SELECT * FROM PostRevisions WHERE is_current = 1;

обновление: Относительно вашего обновленного вопроса: я согласен, что правильный реляционный дизайн будет поощрять две таблицы, чтобы вы могли сделать несколько атрибутов Post инварианта для всех ревизий этого поста. Но большинство инструментов ORM предполагают, что сущность существует в одной таблице, а ORM неуклюжи при соединении строк из нескольких таблиц, чтобы образовать данную сущность. Поэтому я бы сказал, что если использование ORM является приоритетом, вы должны хранить сообщения и ревизии в одной таблице. Пожертвуйте немного правильности отношений, чтобы поддержать предположения парадигмы ORM.

Еще одно предложение - рассмотреть Измерение размеров . Это школа проектирования баз данных для поддержки OLAP и хранилищ данных. Он разумно использует денормализацию, поэтому вы обычно можете организовать данные в Star Schema . Основная сущность («Таблица фактов») представлена ​​одной таблицей, поэтому это было бы выигрышно для ORM-ориентированного дизайна приложения.

0 голосов
/ 19 января 2010

Для всех, кто интересуется, вот как WordPress обрабатывает редакции, используя одну таблицу сообщений MySQL.

CREATE TABLE IF NOT EXISTS `wp_posts` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `post_author` bigint(20) unsigned NOT NULL DEFAULT '0',
  `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_date_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_content` longtext NOT NULL,
  `post_title` text NOT NULL,
  `post_excerpt` text NOT NULL,
  `post_status` varchar(20) NOT NULL DEFAULT 'publish',
  `comment_status` varchar(20) NOT NULL DEFAULT 'open',
  `ping_status` varchar(20) NOT NULL DEFAULT 'open',
  `post_password` varchar(20) NOT NULL DEFAULT '',
  `post_name` varchar(200) NOT NULL DEFAULT '',
  `to_ping` text NOT NULL,
  `pinged` text NOT NULL,
  `post_modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_modified_gmt` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `post_content_filtered` text NOT NULL,
  `post_parent` bigint(20) unsigned NOT NULL DEFAULT '0',
  `guid` varchar(255) NOT NULL DEFAULT '',
  `menu_order` int(11) NOT NULL DEFAULT '0',
  `post_type` varchar(20) NOT NULL DEFAULT 'post',
  `post_mime_type` varchar(100) NOT NULL DEFAULT '',
  `comment_count` bigint(20) NOT NULL DEFAULT '0',
  PRIMARY KEY (`ID`),
  KEY `post_name` (`post_name`),
  KEY `type_status_date` (`post_type`,`post_status`,`post_date`,`ID`),
  KEY `post_parent` (`post_parent`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 ;
0 голосов
/ 30 декабря 2009

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

0 голосов
/ 28 декабря 2009

Возможно, в этом случае было бы лучше поместить CurrentTextID в вашу таблицу Post, чтобы не приходилось выяснять, какая ревизия является текущей (альтернативой может быть флаг на Revision, но я думаю, что CurrentTextID на записи будет более простые запросы).

При наличии идентификатора CurrentTextID в сообщении ваш ORM должен поместить в свой класс Post одно свойство (CurrentText), которое позволит вам получить доступ к текущему тексту по существу с указанным вами утверждением.

Ваш ORM также должен дать вам возможность загружать Ревизии на основе Почты; Если вы хотите получить более подробную информацию об этом, вам следует включить информацию о том, какой ORM вы используете и как вы его настроили.

...