2

投稿やその他のコンテンツのリビジョン システムをセットアップする方法を理解しようとしています。これは、基本的な belongs_to/has_one/has_many/has_many_through 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;

以前のリビジョンにさかのぼり、リビジョンを削除する機能も大きな助けになるでしょう。

いずれにせよ、これらは、ある種の履歴追跡がどのように機能すると私が考えるかについてのアイデアにすぎません。私は、ベストプラクティスが何であるかを知りたいだけです。

:編集:

今後は、2 つのテーブルが最も理にかなっているようです。テキストのコピーを 2 つ保存する予定なので、スペースの節約にもなります。最初のテーブルpostsには、結合なしで高速に読み取るための現在のリビジョンのデータが格納されます。投稿bodyは、一致するリビジョンのtextフィールドの値になりますが、markdown/bbcode/tidy/etc によって処理されます。これにより、元のテキストを 1 つのリビジョン行に 2 回保存する (または表示するたびに再解析する) ことなく、(次の編集のために) 元のテキストを保持できます。

したがって、フェッチは 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 ;
4

4 に答える 4

2

あなたRevisionsが示したテーブルは、 と の間の多対多の関係をモデル化していPostsますText。の特定の行が の複数の行のコンテンツを提供する可能性がある場合を除き、これはおそらくあなたが望むものではありません。これは、ほとんどの CMS アーキテクチャの仕組みとは異なります。TextPosts

確かに 3 つのテーブルは必要ありません。これが 3NF に必要だと思う理由がわかりません。3NF のポイントは、属性が非キー属性に依存してはならないということです。不必要に複数のテーブルに分割する必要があるとは言いません。

したがって、2 つのテーブル間の 1 対多の関係のみが必要な場合があります:PostsRevisions. つまり、投稿ごとに複数のリビジョンが存在する可能性がありますが、特定のリビジョンは 1 つの投稿にのみ適用されます。他の人は、現在の投稿を見つけるための 2 つの代替案を提案しています。

  • Revisions現在のリビジョンを示すフラグ列。現在のリビジョンを変更するには、フラグを目的のリビジョンで true に変更し、以前の現在のリビジョンで false に変更するだけです。

  • Posts特定の投稿の最新のリビジョンへの外部キー。現在のリビジョンを 2 回ではなく 1 回の更新で変更できるため、これはさらに簡単です。ただし、循環外部キー参照は、バックアップと復元、カスケード更新などに対して問題を引き起こす可能性があります。

単一のテーブルを使用してリビジョン システムを実装することもできます。

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は 1 つだけで、NULL である行は無制限に使用できます。

これには、リビジョンを最新にするために 2 つの行を更新する必要がありますが、モデルを 1 つのテーブルに減らすことで、いくらか単純化できます。これは、ORM を使用している場合の大きな利点です。

ビューを作成して、現在の投稿を照会する一般的なケースを簡素化できます。

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

更新:更新された質問について:適切なリレーショナル設計では2つのテーブルが推奨されるPostため、その投稿のすべてのリビジョンに対して不変のいくつかの属性を作成できることに同意します。しかし、ほとんどの ORM ツールは、エンティティが 1 つのテーブルに存在することを想定しており、ORM は、複数のテーブルの行を結合して特定のエンティティを構成することに不器用です。したがって、ORM の使用が優先される場合は、投稿とリビジョンを 1 つのテーブルに格納する必要があります。ORM パラダイムの仮定をサポートするために、リレーショナルの正しさを少し犠牲にします。

もう 1 つの提案は、次元モデリングを検討することです。これは、OLAP とデータ ウェアハウジングをサポートするためのデータベース設計の学校です。非正規化を慎重に使用するため、通常はスター スキーマでデータを整理できます。メイン エンティティ (「ファクト テーブル」) は 1 つのテーブルで表されるため、これは ORM 中心のアプリケーション設計に適しています。

于 2009-12-31T23:45:42.913 に答える
0

この場合、Post テーブルに CurrentTextID を配置して、最新のリビジョンを特定する必要がないようにすることをお勧めします (別の方法としては、Revision のフラグを使用することもできますが、Post に CurrentTextID を設定すると、より簡単になります)。クエリ)。

Post の CurrentTextID を使用して、ORM は単一のプロパティ (CurrentText) を Post クラスに配置する必要があります。これにより、本質的に提供したステートメントで現在のテキストにアクセスできるようになります。

ORM は、投稿に基づいてリビジョンをロードする方法も提供する必要があります。詳細が必要な場合は、使用している ORM とその構成方法に関する情報を含める必要があります。

于 2009-12-28T16:17:35.437 に答える
0

ここは2テーブルで十分だと思います。投稿テーブルとそのリビジョン。データの複製について心配していない場合は、単一のテーブル (非正規化) でも機能する可能性があります。

于 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 に答える