2009-12-19 69 views
2

我想弄清楚如何为帖子和其他内容设置修订系统。我认为这意味着它需要使用基本的belongs_to/has_one/has_many/has_many_though ORM(任何好的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 

在那里我可以加入通过修改表以获取最新的文本正文。但是我对它的工作方式有点模糊。有没有人设置这样的东西?

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

// 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友好的。然后,为了创建/更新,我将不得不分别处理修订版,然后使用新的当前修订版值更新后期对象。

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 ; 
+0

这并不容易弄清楚你想知道:) – 2009-12-22 13:22:04

回答

2

您的Revisions表,如您所示,它建模了PostsText之间的多对多关系。这可能是而不是你想要什么,除非Text中的给定行可以提供Posts中多行的内容。这不是大多数CMS架构的工作原理。

你肯定不需要三张表。我不知道为什么你认为这是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,因此您可以只有一行,其中is_current为1,而无限数量的行为NULL。

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

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

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

更新:重新更新的问题:我同意适当的关系设计会鼓励两个表,这样您可以对Post的某些属性进行修改,使其不变。但是大多数ORM工具假定一个实体存在于一个表中,而ORM在连接多个表中的行以构成一个给定的实体时很笨拙。所以我会说,如果使用ORM是一个优先事项,您应该将帖子和修订版存储在一张表中。牺牲一点关系正确性来支持ORM范式的假设。

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

+0

我想我宁愿坚持两张桌子。我认为处理这个问题的最好方法是跳过'is_current'字段,并在文本表中包含每个条目的日期,以便我可以按修订日期排序。或者等一下,那是行不通的,因为我可能会想要保留一个修订并恢复(需要一个'is_current'键)。也许一张表会更好,因为作者,标题,文本和日期可能都会更改每个修订版。再次,诸如评论/评论是否公开可能需要保持不变。 – Xeoncross 2010-01-01 18:00:48

+0

对,'is_current'的属性与'date'无关。在关系数据库设计中,值得将每个逻辑属性存储在单独的列中。不要试图让列做双重任务。 – 2010-01-01 18:08:05

+0

如果只使用一张表,你将如何使用主键?我想这不是依靠表格来增加行ID - 你将不得不取得最后一行的最后一个ID ... – Xeoncross 2010-01-19 02:00:49

0

你可能会是在这种情况下,最好穿上后您的表CurrentTextID以避免找出哪个版本是最新的(替代将是对修订版的标志,但我认为一个CurrentTextID在帖子上会让你更容易查询)。

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

你的ORM还应该给你一些方法来加载基于帖子的修订;如果你想了解更多的细节,那么你应该包含你正在使用哪个ORM以及你如何配置的信息。

+0

那么到底是什么,我不知道,这将有助于速度的东西了很多,因为我也可以只'ORDER BY日期DESC,LIMIT 1 '通过修订表获取发布文本时。 – Xeoncross 2009-12-28 16:38:05

+0

如果您一次抓取多个帖子(例如,如果您想显示最近的10个帖子),则使用LIMIT并不容易。无论如何,如果这不是你正在寻找的东西,那又怎么样? – 2009-12-28 20:00:49

+0

我正在谈论使用'$ post-> text-> body'并让它自动创建SQL以使用'ORDER BY date'子句获取最新版本。无论如何,我并不在乎它是如何完成的,我只想要一个已经设置了修订系统的人来告诉我他们是如何做到的(即使用的表格,模型关系等)。 – Xeoncross 2009-12-30 16:56:35

0

我认为两张表就足够了。帖子表和它的修订版。如果您不担心重复数据,则可以使用单个表(非标准化)。

+0

嗯,我试图通过使用直通表遵循适当的3NF +练习。 http://en.wikipedia.org/wiki/Database_normalization 但是,你的权利,只是使用两个表更容易 - 在我的情况下,我不知道会有“修订”表的好处无论如何。 – Xeoncross 2009-12-30 20:37:51

+0

那么你必须在表格之间移动数据? – AnApprentice 2009-12-30 23:08:09

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 ; 
+0

哇 - 为什么在世界上他们会使用VARCHAR(255)作为guid? – 2010-01-19 02:18:59