はじめに

テンポラルモデルは思ったよりもずっと怖くはないでしょう。

通常はデータベースエンティティのテーブル内の行で表され、この行が更新されると、古い情報が上書きされます。テンポラルモデルはデータを時間で参照でき、それにより、ある時点でのエンティティの状態を問い合わせることができます。

たとえば、製品への変更を追跡するため、注文が出されたときに Orders テーブル内のデータを複製することなく商品の状態を知っていたいとします。あなたはテンポラルデータを利用せずに生じるのと同様に、製品をテンポラルにし、現在がどうかよりむしろその時点での注文された製品の状態を参照するために時間を使うことができます。

テンポラルモデルは Wiki ページのようなものへの監査の変更に利用できます。すべての変更は別々のログテーブルを使用せず自動的に記録されます。

"エンティティ" は、デザイン内の任意の項目で、例えば、製品、ユーザー、ギャラリーのフォルダ、あなたがモデルとして使用する物はなんであれエンティティと見なすことが出来ます。テンポラルモデルを使用すると、変更内容を保存することにより時間の経過とともにこれらのエンティティへの変更を追跡することを可能し、変化のそれぞれの新しいセットは "リビジョン" と呼ばれています。各リビジョンは時間 (テンポラル) データと共に保存されるので任意の時点でのエンティティの状態を取得することが可能となります。

テンポラルモデルの実装は、元のデータと同じテーブル内にエンティティのリビジョンを格納し、 "ログ" テーブルの重複を取り除くことが必要となります。それは複数のデータベース環境で機能しなければならないという事実と ORM の制限を考慮すると最も柔軟性があります。

なぜこれを ORM で行い、それを可能にするデータベースシステムを選択しないのですか?

ORM 内のテンポラル機能の実装はデーターベース階層から抜け出した抽象化の仕組みを可能にし、テンポラルモデル内の ORM サポートは任意のデータベースを利用することが可能で、より高い柔軟性を提供します。このモデルでは、データベースを利用する機能を置き換えるものではなく、テンポラル情報のサポートをするものですが、しかし、 "そのまま使える" 実装を提供します。

使用法

テーブルは、エンティティ ID の複合主キー、 (例えば) 単に標準の自動インクリメントする値、そして行の有効期間を表すタイムスタンプを使用して設定する必要があります。

class Model_MyTemporal extends Orm\Model_Temporal
{
	protected static $_primary_key = array('id', 'temporal_start', 'temporal_end');
	protected static $_temporal = array(
		'start_column' => 'temporal_start', //この行は有効部分の始まりの時刻が含まれているカラムの名前
		'end_column' => 'temporal_end', //この行は有効部分の終わりの時刻が含まれているカラムの名前
		'mysql_timestamp' => false, //true に設定すると MySQL のタイムスタンプ、 false に設定すると UNIX タイムスタンプを使用します
	);
}

主キーはエンティティ ID とテンポラルのタイムスタンプとの間で複合キーである必要がありますのでご注意ください。

テンポラルモデルを使用して定義されているすべてのリレーションは id とタイムスタンプの両方ではなく id にのみ関連する必要があります。

find()

Findメソッドは、正確に動作し、 where 句はエンティティの最新のリビジョンのみを選択するために追加されていること以外は通常のモデルの実装と同じです。

find_revision($id, $timestamp=null, $relations = array())

このメソッドは指定された時間にエンティティの状態を照会するために利用することができます。

静的 はい
パラメータ
パラメータ デフォルト 説明
$id 必須 検索対象のエンティティの ID 。
$timestamp
null
エンティティを参照するための時間。 null は最新のリビジョンを返します。
$relations
array
エンティティとともにロードするリレーションの名前。
戻り値 単一の Model_Temporal のサブクラス
Model_Product::find_revision($id, '2012-11-09 12:04:00', array('images', 'reviews'));

find_revision もしくは find_revisions を介して取得された任意のモデルは過去を変更出来ないように 読み取り専用 になっていることが考慮されるべきである!

find_revisions_between($id, $earliestTime = null, $latestTime = null)

このメソッドは、指定された時刻の間のエンティティの状態を返します。

静的 はい
パラメータ
パラメータ デフォルト 説明
$id 必須 検索対象のエンティティの ID 。
$earliestTime
null
検索する最古のリビジョン、もしくは、以前のリビジョンの数が無限の場合は null
$latestTime
null
,
string
or
integer
検索する最新のリビジョン、もしくは、リビジョンの数が無限 (最新まで) の場合は null
戻り値 Model_Temporal のサブクラスの配列
Model_Product::find_revisions_between($id, '2012-11-09 12:04:00', '2012-12-10 19:00:00');

それは find_revisions_between を使用し同時にリレーションを選択することは出来ません。

delete($cascade = null, $use_transaction = false)

このメソッドはデータベースからオブジェクトを削除します。これはデータベースから情報が削除されていないことを除き普通のモデルの delete 関数とまったく同じように機能します。これはあなたがまだある時点でオブジェクトを参照できることを意味しますが、差し当たりそれはもはや有効ではありません。

静的 いいえ
Model_Product::find(5)->delete();

カスケードされた削除は通常どおり削除されます。Soft または Temporal されていない任意のリレーションは通常どおり削除されます。

overwrite($cascade = null, $use_transaction = false)

新しいリビジョンを作成せずに情報を保存することができます。 Model::save() と同じように動作します。

静的 いいえ
$product = Model_Product::find(5);
$product->name = 'Super Awesome 12000';
//データベースへ新しい行を作成せず Product を更新
$product->overwrite();

restore()

エンティティが削除されている場合、このメソッドは現在の状態にエンティティを復元します。エンティティが削除されていない場合は何も起こりません。

静的 いいえ
Model_Product::find_revision(5, '2012-11-09 12:04:00')->restore();

purge()

データベースからこのエンティティのすべてのリビジョンを削除します。これは 永続的 です!これは元に戻すことはできません。

静的 いいえ
Model_Product::find(5)->purge();

これは元に戻すことはできず、データベースからエンティティのすべてのリビジョンが削除されます。もし、これを行う場合はデータが破棄されます!