FuelPHPをやってみる (8) - OrmModelを試す -
OrmパッケージなModelを使ってみる。この記事から検証バージョンが1.4
Ormパッケージを使用できるように設定
fuel/app/config/config.phpをpackagesにormを指定する
<?php
return array(
"language" => "ja",
"always_load" => array(
"packages" => array(
"orm" // 通常はコメントアウトされているはず
)
)
);
fuel/app/classes/model/items.php
<?php
class Model_Items extends Orm\Model {
protected static $_table_name = "items";
protected static $_has_many = array(
"sales" => array(
"model_to" => "Model_Sales",
"key_to" => "item_id",
"conditions" => array(
// salesリレーションを取得時にORDER BY
"order_by" => array("id" => "DESC")
)
)
);
protected static $_observers = array(
// 指定したイベント時に_event_イベント名なメソッドコールされるオブサーバー
"Orm\Observer_Self" => array(
"events" => array("before_insert")
),
// 取得時等に適切な型にキャストされるオブサーバー
"Orm\Observer_Typing" => array(
"events" => array("after_load")
),
// INSERT時にcreated_atカラムに適切な値を自動設定されるオブサーバー
"Orm\Observer_CreatedAt" => array(
"events" => array("before_insert"),
"mysql_timestamp" => true
),
// sourceで指定したカラムをInflector::friendly_titleをやった値をぶち込まれる(例. "Galaxy Nexus" -> "galaxy-nexus")
"Orm\Observer_Slug" => array(
"events" => array("before_insert"),
"source" => "item_name",
"property" => "slug"
)
);
private $validation = null;
public function _event_before_insert() {
Log::info("Observer_Self: before_insert: " . $this->price);
$this->price = $this->price * 2;
}
public static function find_all() {
$entries = array();
try {
// salesリレーションをロードしつつリストを取得する
$entries = static::find(
"all",
array("related" => "sales")
);
} catch (Database_Exception $e) {
Log::warning($e->getMessage());
}
return $entries;
}
public static function findById($id) {
$entry = null;
try {
$entry = static::find(
"first",
array(
"where" => array("id" => $id),
"related" => "sales"
)
);
} catch (Database_Exception $e) {
Log::warning($e->getMessage());
}
return $entry;
}
public function validation() {
if (is_null($this->validation)) {
$this->validation = Validation::forge();
$this->validation->add_field(
"item_name",
"アイテム名",
"required|max_length[50]"
);
$this->validation->add_field(
"price",
"値段",
"required|valid_string[numeric]"
);
}
return $this->validation;
}
public function validate() {
$v = $this->validation();
return $v->run();
}
}
Orm\Modelを使用する事でテーブル間のリレーションを参照する事が出来る模様。でオブサーバーは現在サポートされているのは http://fuelphp.com/docs/packages/orm/observers/included.html を参照
fuel/app/classses/model/sales.php
<?php
class Model_Sales extends Orm\Model {
protected static $_table_name = "sales";
protected static $_belongs_to = array(
"item" => array(
"key_from" => "item_id",
"model_to" => "Model_Items",
"key_to" => "id",
"cascade_save" => true
)
);
}
belongs_toを使う事で、Items<->Sales間の参照をオブジェクトをぶち込んでINSERTしたり出来る模様。例えば
$sale = new Model_Sales();
$sale->sales = $item // Model_Itemsの参照をぶち込む。上記の定義上で、item_idカラムにModel_Itemsのidを参照としてぶち込む
$sale->save();
というような感じでぶち込める模様。これでOrm\Modelはこんな所
fuel/app/classes/controller/home.php
<?php
class Controller_Home extends Controller {
public function get_index() {
$data["entries"] = Model_Items::find_all();
$data["errors"] = Session::get_flash("validation_errors", array());
return View::forge("home/index", $data);
}
public function action_incr($id) {
$item = Model_Items::findById($id);
if (!is_null($item)) {
$sale = new Model_Sales();
$sale->item = $item;
$sale->save();
}
return Response::redirect("/");
}
public function action_register() {
if (Input::method() === "POST") {
$item = Model_Items::forge(
array(
"item_name" => Input::post("item_name"),
"price" => Input::post("price")
)
);
if ($item->validate()) {
try {
$item->save();
} catch (Database_Exception $e) {
Log::warning($e->getMessage());
}
} else {
$save_errors = array();
$errors = $item->validation()->error();
foreach ($errors as $err) {
array_push($save_errors, $err->get_message());
}
Session::set_flash("validation_errors", $save_errors);
}
}
return Response::redirect("/");
}
}
Model_Crudとは違いリレーションやオブサーバー等を利用する事が出来るが、若干パフォーマンスは落ちる模様
余談1 Orm\Observer_Validationを使ってOrmModelにバリデーションを定義する
<?php
class Model_Items extends Orm\Model {
protected static $_table_name = "items";
protected static $_properties = array(
"id",
"item_name" => array(
"data_type" => "varchar",
"null" => false,
"label" => "アイテム名",
"validation" => array(
"required",
array("max_length" => 50)
)
),
"price" => array(
"data_type" => "int",
"null" => false,
"label" => "値段",
"validation" => array(
"required",
array("valid_string" => "numeric")
)
),
"created_at"
);
protected static $_has_many = array(
"sales" => array(
"model_to" => "Model_Sales",
"key_to" => "item_id",
"conditions" => array(
"order_by" => array("id" => "DESC")
)
)
);
protected static $_observers = array(
"Orm\Observer_CreatedAt" => array(
"events" => array("before_insert"),
"mysql_timestamp" => true
),
"Orm\Observer_Validation" => array(
"events" => array("before_save")
)
);
}
っていうようにOrm\Observer_Validationなオブサーバーを定義しておいて、$_propertiesでカラム定義と共にバリデーションルールを定義する。で前とは違い、バリデーションに関わるようなメソッドは定義しない。(この場合だと)saveする際等にOrm\Observer_Validationが作用してsaveしようとしているオブジェクトに対してバリデーションが作用するようになる模様
でコントローラーでバリデーションエラー時にはOrmValidationFailedな例外が送出される。以下な感じで取得してエラーをビューに渡したりする
<?php
class Controller_Home extends Controller {
public function get_index() {
$data["entries"] = Model_Items::find_all();
$data["errors"] = Session::get_flash("validation_errors", array());
return View::forge("home/index", $data);
}
public function action_register() {
if (Input::method() === "POST") {
$item = Model_Items::forge(
array(
"item_name" => Input::post("item_name"),
"price" => Input::post("price")
)
);
try {
$item->save();
} catch (Database_Exception $e) {
Log::warning($e->getMessage());
} catch (OrmValidationFailed $e) {
$save_errors = array();
// バリデーションエラーを取得する
$errors = $e->get_fieldset()->validation()->error();
foreach ($errors as $err) {
array_push($save_errors, $err->get_message());
}
Session::set_flash("validation_errors", $save_errors);
}
}
return Response::redirect("/");
}
}