【问题标题】:How do you setup Post Revisions/History Tracking with ORM?你如何使用 ORM 设置 Post Revisions/History Tracking?
【发布时间】:2009-12-19 19:35:46
【问题描述】:

我正在尝试弄清楚如何为帖子和其他内容设置修订系统。我认为这意味着它需要使用基本的 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;

能够及时回到以前的修订版并删除修订版也会有很大帮助。

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

:编辑:

似乎向前看,两张桌子似乎最有意义。由于我计划存储两份 text,这也将有助于节省空间。第一个表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 ;

【问题讨论】:

  • 要弄清楚你到底想知道什么并不容易:)

标签: orm revision


【解决方案1】:

您的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 为中心的应用程序设计来说是一个胜利。

【讨论】:

  • 我想我宁愿坚持两张桌子。我认为处理这个问题的最好方法是跳过is_current 字段,只在文本表中的每个条目中包含一个日期,这样我就可以按修订日期排序。或者等等,这行不通,因为我可能想要保留修订并恢复(这需要is_current 密钥)。也许一个表格会更好,因为作者、标题、文本和日期都可能在每次修订时都发生变化。再说一次,诸如 cmets/pings 是否打开之类的事情可能需要保持不变。
  • 对,is_current的属性独立于date。在关系数据库设计中,将每个逻辑属性存储在单独的列中是值得的。不要试图让列做双重任务。
  • 如果您只使用一个表,您将如何使用主键?我想,与其依靠表格来增加行 ID - 你将不得不获取最新帖子行的最后一个帖子 ID...
  • 好点。两个选项:您可以将postid 设为序列列,revision 将从每个 postid 的 1 开始,您可以手动递增它。或者您可以将revisionid 串行和postid 设置为GUID 以避免竞争条件。
  • 看看我上面更新的解决方案。我认为这将是 ORM 友好的,同时仍然允许两张表可以提供的整洁的东西。
【解决方案2】:

在这种情况下,您最好将 CurrentTextID 放在您的 Post 表上,以避免必须确定哪个修订是当前的(替代方法是在 Revision 上设置一个标志,但我认为帖子上的 CurrentTextID 会给你更容易的查询)。

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

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

【讨论】:

  • 嗯,我不确定这是否有助于加快速度,因为在通过修订表获取帖子文本时,我也可以只使用ORDER BY date DESC, LIMIT 1
  • 如果您一次抓取多个帖子(例如,如果您想显示最近的 10 个帖子),使用 LIMIT 就不会那么容易了。无论如何,如果这不是您想要的,那又是什么呢?
  • 我说的是使用 $post->text->body 并让它自动创建 SQL 以使用 ORDER BY date 子句获取最新版本。无论如何,我并不在乎它是如何完成的,我只想让已经设置了修订系统的人告诉我他们是如何做到的(即使用的表、模型关系等)。
【解决方案3】:

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

【讨论】:

  • 好吧,我正在尝试通过使用直通表来遵循正确的 3NF+ 练习。 en.wikipedia.org/wiki/Database_normalization 但是,您有权使用两个表更容易 - 就我而言,我不确定“修订”表是否有好处。
  • 那你还要在表之间移动数据吗?
【解决方案4】:

对于任何感兴趣的人,这里是 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 ;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-06
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    相关资源
    最近更新 更多