2

我试图弄清楚如何为帖子和其他内容设置修订系统。我认为这意味着它需要使用基本的 belongs_to/has_one/has_many/has_many_though ORM(任何好的 ORM 都应该支持这一点)。

我在想我可以有一些表格,比如(有匹配的模型)

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

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

[[TEXT]]
id
body
user_id

我可以通过修订表加入其中以获取最新的 TEXT 正文。但我对这一切将如何运作有点模糊。有没有人设置这样的东西?

基本上,我需要能够加载一篇文章并请求最新的内容条目。

// 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;

能够及时回到以前的修订版并删除修订版也将是一个很大的帮助。

无论如何,这些只是我认为某种历史跟踪将如何工作的想法。我对任何形式的跟踪持开放态度,我只想知道最佳实践是什么。

:编辑:

看来,向前看,两张桌子似乎是最有意义的。由于我计划存储两份文本,这也将有助于节省空间。第一个表posts将存储当前版本的数据,以便快速读取,无需任何连接。帖子body将是匹配修订text字段的值 - 但通过 markdown/bbcode/tidy/etc 处理。这将允许我保留原始文本(用于下一次编辑),而不必在一个修订行中将该文本存储两次(或者每次显示时都必须重新解析它)。

所以获取将是 ORM 友好的。然后对于创建/更新,我将不得不单独处理修订,然后使用新的当前修订值更新 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

4 回答 4

2

正如您所展示的,您的表格模拟了和Revisions之间的多对多关系。这可能不是您想要的,除非给定的行可能会为. 这不是大多数 CMS 架构的工作方式。PostsTextTextPosts

你当然不需要三张桌子。我不知道你为什么认为 3NF 需要这个。3NF 的要点是一个属性不应该依赖于一个非关键属性,它并不是说你应该不必要地拆分成多个表。

因此,您可能只需要两个表之间的一对多关系:PostsRevisions. 也就是说,对于每个帖子,可以有多个修订,但给定的修订仅适用于一个帖子。其他人提出了两种查找当前帖子的替代方法:

  • 一个标志列,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,因此您只能拥有 1 行is_current和无限数量的 NULL 行。

这确实需要更新两行以使修订成为当前版本,但是通过将模型简化为单个表,您可以获得一些简单性。当您使用 ORM 时,这是一个很大的优势。

您可以创建一个视图来简化查询当前帖子的常见情况:

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

更新:关于您更新的问题:我同意适当的关系设计会鼓励两个表,以便您可以Post为所有帖子的修订创建一些不变量的属性。但是大多数 ORM 工具都假设实体存在于单个表中,而 ORM 在连接多个表中的行以构成给定实体方面很笨拙。所以我想说,如果优先使用 ORM,你应该将帖子和修订存储在一个表中。牺牲一点关系正确性来支持 ORM 范式的假设。

另一个建议是考虑维度建模。这是一所支持 OLAP 和数据仓库的数据库设计学院。它明智地使用非规范化,因此您通常可以在Star Schema中组织数据。主要实体(“事实表”)由单个表表示,因此这对于以 ORM 为中心的应用程序设计来说是一个胜利。

于 2009-12-31T23:45:42.913 回答
0

在这种情况下,您最好将 CurrentTextID 放在您的 Post 表上,以避免必须弄清楚哪个修订是最新的(替代方法是在 Revision 上设置一个标志,但我认为帖子上的 CurrentTextID 会让您更轻松查询)。

使用 Post 上的 CurrentTextID,您的 ORM 应该在您的 Post 类上放置一个属性(CurrentText),这将允许您使用您提供的语句访问当前文本。

您的 ORM 还应该为您提供一些基于帖子加载修订的方法;如果您想了解更多详细信息,那么您应该包括有关您正在使用的 ORM 以及您如何配置它的信息。

于 2009-12-28T16:17:35.437 回答
0

我认为这里有两张桌子就足够了。一个帖子表和它的修订。如果您不担心重复数据,单个表(非规范化)也可以工作。

于 2009-12-30T17:51:45.010 回答
0

对于任何感兴趣的人,这里是 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 ;
于 2010-01-19T02:08:45.960 回答