Orm

ORMはオブジェクト 関係マッピングの略です。 これは、2つのことを行います: オブジェクトにデータベースのテーブルの行をマップし、 それはあなたがそれらのオブジェクト間の関係を確立することができます。
それは Active Record パターンに従いますが、 他のシステムの影響も受けています。

オブザーバ:含まれるオブザーバ

付属のオブザーバは以下に記載されています:

Observer_Self

良い習慣ではありませんが、場合によっては、イベントメソッドだけを持つモデルはおそらく最も クリーンな方法です。 もしモデルが _event_ が前についたイベント名のメソッドを持っている場合は、 Observer_Self の出番です。たとえば after_save イベントの場合、 _event_after_save() という public メソッドをモデルに追加する必要があり、そうすることでそのモデル自身のオブザーバ呼び出しを 受け取ることができます。

// 単にオブザーバを追加
protected static $_observers = array('Orm\\Observer_Self');

// しかし、いくつかのイベントを観察するだけの場合にそれらのみを追加することによって最適化することが可能
protected static $_observers = array('Orm\\Observer_Self' => array(
	'events' => array('after_save', 'before_insert')
));

Observer_CreatedAt

このオブザーバは before_insert だけに作用します。 created_at プロパティを持っている モデルが初めて保存されるとき、 Unix タイムスタンプがセットされます。

// 単にオブザーバを追加
protected static $_observers = array('Orm\\Observer_CreatedAt');

// 以下の設定をつけて追加します:
// - before_insert 時のみ実行される
// - mysql のタイムスタンプを使います (デフォルトでは UNIX タイムスタンプを使用しています)
// - "created_at" の代わりに "created" を使用します
protected static $_observers = array(
	'Orm\\Observer_CreatedAt' => array(
		'events' => array('before_insert'),
		'mysql_timestamp' => true,
		'property' => 'created',
		'overwrite' => true,
	),
);

もしあなた自身のコード中で created_at のタイムスタンプを設定できるようにしたければ、 'overwrite' プロパティを false に設定してください。 これによって、オブザーバは手動で設定した値を保持するようになります。

Observer_UpdatedAt

このオブザーバは before_save または before_update だけに作用し、モデルが updated_at プロパティを持っていることを期待しています。 このプロパティは (初回も含め) 保存される際に Unix タイムスタンプが設定されます。

before_save を使うと、updatedAt カラムは insert と update の両方で更新されます。 before_update を使うと、 updateAt カラムは update のときだけ更新されます。

// 2 回実行されてしまうため、これは使用しないでください!
protected static $_observers = array('Orm\\Observer_UpdatedAt');

// 以下の設定をつけて追加します:
// - before_save 時のみで実行される
// - mysql のタイムスタンプを使います (デフォルトでは UNIX タイムスタンプを使用しています)
// - "updated_at" の代わりに、 "updated" を使用します
protected static $_observers = array(
    'Orm\\Observer_UpdatedAt' => array(
        'events' => array('before_save'),
        'mysql_timestamp' => true,
        'property' => 'updated',
        'relations' => array(
            'my_relation',
        ),
    ),
);

デフォルトでは、もし子オブジェクトが変更されたとしても、親のタイムスタンプは更新されません。この振る舞いは、 オプションの relations プロパティにリレーションの名前を追加することによって変更できます。

例えば Model_Blog が多くの Model_Post を持っていて、のろまなオブザーバが blog に割り当てられている場合に、 blog のリレーションを通して post が変更されたとしても blog の updated_at は更新されません。


$blog->post[0]->title = 'foobar';
$blog->save();
                

post のリレーションを relations プロパティに指定しておけば、 post が更新された際にオブザーバによって blog のタイムスタンプも更新されることになります。

もしリレーションが読み込まれていなければ、オブザーバは単にそれを無視します。

Observer_Validation

このオブザーバは before_insert および/または before_update に、または before_save に作用することができます。 ルールが 2 回呼ばれる原因となるので、 before_save と他の 2 つのどちらかを同時に指定しないで下さい。 これはバリデーションルールが失敗した場合に、モデルが間違って保存されることを防ぐためのものです。 Fieldset クラスを使用しますし、フォームを生成することもできます。

通常、主キーは編集できませんし、バリデーションにも、フォームにも追加されません。 そしてほとんどの場合自動インクリメントとなります。 独自に作成したりバリデーションが必要なときには手動でフィールドを追加する必要があります。

オブザーバは、以下のようにロードすることができます:

// オブザーバを追加し、必要なイベントを定義するだけです
protected static $_observers = array('Orm\\Observer_Validation' => array(
	'events' => array('before_save')
));

2 回呼び出される原因となるので、このオブザーバをイベントの指定なしに定義しないで下さい。

バリデーションルールは、モデルの $_properties に定義されなければなりません。 モデルの作成のページに例があります。 バリデーションオブザーバを登録した後では、モデルのデータを保存する際の事前のバリデーションで何か失敗が起きた場合は、 \Orm\ValidationFailed 例外が投げられることになります。 そのため、モデルの save メソッドを呼ぶ際には try/catch で括らないといけません。

より広範な機能例/scaffolding を以下に示します:

class Controller_Articles extends Controller
{
	public function action_create()
	{
		$view = View::forge('articles/create');
		if (Input::param() != array())
		{
			try
			{
				$article = Model_Article::forge();
				$article->name = Input::param('name');
				$article->url = Input::param('url');
				$article->save();
				Response::redirect('articles');
			}
			catch (Orm\ValidationFailed $e)
			{
				$view->set('errors', $e->getMessage(), false);
			}
		}
		return Response::forge($view);
	}

	public function action_edit($id = false)
	{
		if ( ! ($article = Model_Article::find($id))
		{
			throw new HttpNotFoundException();
		}

		$view = View::forge('articles/edit');
		if (Input::param() != array())
		{
			try
			{
				$article->name = Input::param('name');
				$article->url = Input::param('url');
				$article->save();
				Response::redirect('articles');
			}
			catch (Orm\ValidationFailed $e)
			{
				$view->set('errors', $e->getMessage(), false);
			}
		}
		return Response::forge($view);
	}

	public function action_delete($id = null)
	{
		if ( ! ($article = Model_Article::find($id))
		{
			throw new HttpNotFoundException();
		}
		else
		{
			$article->delete();
		}
		Response::redirect('articles');
	}

}

デフォルトでは、ビューに表示するための準備で ValidationFailed 例外オブジェクトへのメッセージとして HTML 表現は渡されます。 しかしながら、たとえば RESTful API の呼び出しの最中で JSON としてメッセージを返すようにしたいなど個々のエラーメッセージにアクセスしたいと思うケースがあるでしょう:

class Controller_Articles extends Controller_Rest
{
	public function action_create()
	{
		$view = View::forge('articles/create');
		if (Input::param() != array())
		{
			try
			{
				$article = Model_Article::forge();
				$article->name = Input::param('name');
				$article->url = Input::param('url');
				$article->save();
				Response::redirect('articles');
			}
			catch (Orm\ValidationFailed $e)
			{
				$errors = array();
				foreach ($e->get_fieldset()->validation()->error() as $error)
				{
					$errors[] = array(
						'field' => $error->field,					// エラーのフィールド
						'value' => $error->value,					// エラーの値
						'message' => trim($error->get_message(false, '\t', '\t')),	// エラーメッセージ
						'rule' => $error->rule,						// 失敗したルール
						'params' => $error->params,					// ルールへ渡されたパラメータ
					);
				}
				return $errors;
			}
		}

		return Response::forge($view);
	}
}

これは、Fieldset クラスを使用しているので、検証を実行し、 また、モデル用のフォームを作成することができます。次の例では、作成と編集フォームが一般的なビューで定義されています。 しかし、同じように簡単にモデルにそれを定義し、Fieldset::instance() を使用することで、 view でそれのインスタンスを取得することができます。

// フォームを作成する Model_Article のインスタンスを使用して、あなたはまた、クラス名を渡すことができます。
$fieldset = Fieldset::forge()->add_model($article);

// モデルのインスタンスからの値を使用してフォームを移入します。
// true を渡しても、セーブ失敗した後に再設定するために POST/PUT を使用します。
$fieldset->populate($article, true);

// フィールドセットは、文字列へのキャスト時に HTML としてビルドされます。
echo $fieldset;

Observer_Typing

これは 2 つのもののためのものです: 入力のための型強制とDBからの出力のための型キャストです。 つまり、 あなたがタイピングオブザーバを保存しているときにすることは予想される型に入力値をキャストしようとすることで、 それができないは例外をスローします。そして、あなたが DB のデータを取得しているとき、 通常、それはすべての文字列になります (偶数整数および浮動小数点数) が、タイピングオブザーバでそれらはそれらのスカラ型にキャストされます。

タイピングオブザーバは上記のほかに、シリアル&JSONフィールドのサポートを追加します。どちらも 文字列型のフィールドである必要があります ("テキスト"が望ましい) 。 しかし保存用に (serialize() もしくは json_encode() を使って) エンコードされたの値を持つことになり、そして DB から取得する際には (unserialize() もしくは json_decode() を使って) デコードをします。

Observer_Typing が検証に代わるものとして意図されていないため、検証としてそれを使用しようと しないでください。どちらもあなたのサイトの訪問者によって読まれることを意図してこのオブザーバによってスローされた例外はありません。 コードをデバッグするのに役立つことを意図しています。

// オブザーバを追加します。
protected static $_observers = array('Orm\\Observer_Typing');

// 特定のイベントだけのためのオブザーバを追加します。
protected static $_observers = array('Orm\\Observer_Typing' => array(
	'events' => array('before_save', 'after_save', 'after_load')
));

このオブザーバはあなたがそのモデルにセットした $_properties 静的変数を必要としますが、 もしセットされていない場合は DB::list_columns() (MySQL のみ) による検出を利用します。 もし自分で設定を行いたい場合、以下の設定が利用可能です。

パラメータ Valid input 説明
data_type varchar, int, integer, tinyint, smallint, mediumint, bigint, float, double, decimal[:d], text, tinytext, mediumtext, longtext, enum, set, bool, boolean, serialize, encrypt, json, time_unix, time_mysql フィールド上で使用されるタイピングオブザーバを持っているために必要な SQL データ型。
db_decimals int Number of decimals the value in the database has, if different from the specification in "data_type".
null bool null が値として許可されているかどうか、デフォルトは true
character_maximum_length int The maximum number of characters allowed for a string data type (varchar, text, serialized or encrypted result)
encryption_key string A custom encryption key to encrypt and decrypt this value. If given, it will replace the "crypto_key" set in the Crypt config file.
min int 整数の最小値
max int 整数の最大値
options array セットのための有効な文字列値の配列または列挙データ型
注: 現在のオプション自体はカンマを含めることはできません。

data_type が "decimal" の場合は、必要な小数点以下の桁数を持つタイプを後ろに付けることができます。 定義されている場合、戻り値は文字列で、小数点以下の桁の定義された数でフォーマットし、 現在定義されているロケールを考慮に入れます。

デフォルトで、タイピングオブザーバはロケールを考慮します。 つまり、現在のロケール設定でカンマが小数点の区切り記号として定義されていれば、 decimal または float として定義したフィールドに文字列が入力され、カンマが小数点として使われていた場合にうまく扱えるということです。 これを無効にするには、 config.php または orm.php という独立した設定ファイルに、 “orm.use_locale” を config キーとして定義して、 false をセットしてください。

Observer_Slug

このオブザーバは、モデルの url セーフな (デフォルトでは一意な) スラグを作成します。 それは before_insert でのみ動作し、 title (スラグを作成する) と slug (それを保存する) プロパティをモデルが持っていることを期待しています。

// オブザーバを追加します。
protected static $_observers = array('Orm\\Observer_Slug');

// 設定
protected static $_observers = array(
	'Orm\\Observer_Slug' => array(
		'events' => array('before_insert'),
		'source' => 'title',  // プロパティはスラグのベースとして使われ、またプロパティの配列でも構い
		'property' => 'slug', // 空の時にスラグに設定するプロパティ
		'separator' => '-',   // 区切り文字を設定するプロパティ
		'unique' => true,     // 一意性を必要とするか否かのプロパティ
	),
);
protected static $_observers = array('Orm\\Observer_Slug' => array('before_insert'));

オブザーバは Inflector::friendly_title() を使用して title から slug を作成し、スラグが既に存在する場合は、インデックスを追加します。

overwrite プロパティが false の場合には、手動でスラグを割り当てることができ、それはオブザーバによって上書きされることはありません。 before_update の際にスラグは、 (overwrite プロパティが false か否かに関わらず) title フィールドから生成されたものに上書きされますが、スラグがそれ自体の場合には変更されません。