FuelPHPをやってみる (8) - OrmModelを試す -

2012-11-10T00:00:00+00:00 FuelPHP PHP

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("/");
    }
}

FuelPHPをやってみる (9) - Controller_Templateを使ってみる - FuelPHPをやってみる (6) - モデルテストとモック -