FuelPHPのモデルとデータベース操作方法の選択

FuelPHPはデータベース操作方法を複数提供しており、初心者の方はどれを使ったらいいのかがわかりにくいです。

そこで、どのようなデータベース操作方法を使ったらいいかを判断できるような情報を提供してみたいと思います。

FuelPHPのモデルとは?

モデルはデータの管理を担当します。いわゆるビジネスロジックはモデルに配置します。データベースを操作するコード(SQL文を発行するもの)もモデルに配置することになります。

FuelPHPのModelクラスは空のクラスです。メソッドやプロパティを一切持ちません。つまり、FuelPHPはモデルについて制約を設けていません。

自由放任ということです。

とはいえ、それだけでは不親切なのでデータベース操作についての複数の方法をFuelPHPは提供しています。

FuelPHPでのデータベース操作方法

FuelPHPでデータベースを操作する方法は以下の5つが考えられます。

  1. DB::query()メソッド(DBクラス)
  2. クエリビルダー(DBクラス)
  3. Model_Crudクラス
  4. ORMパッケージ
  5. サードパーティライブラリ

1.~4.までがFuelPHPが提供するものです。これら4つは、イメージとしては下に行くほど高機能でオブジェクト指向的になり、また、オーバーヘッドも高くなります。

それでは、それぞれの方法について見ていきましょう。

以下のコードは、『はじめてのフレームワークとしてのFuelPHP 第2版(3)実践編』にあるformsテーブルを操作するサンプルです。

1. DB::query()メソッド(DBクラス)

DB::query()メソッドは、引数にSQL文を記述するものです。どんなSQL文も自由に記述できます。

クエリのバインディング機能(プレースホルダ)がありますので、パラメータを渡す場合はそれを使いましょう。適切にSQL文を組み立てないとSQLインジェクションの危険があります。

SQL文をそのまま書きたい場合に利用します。オーバーヘッドは最も少ないです。

結果を連想配列で受け取る

結果は連想配列で返ります。

コード:

$result = DB::query('SELECT * FROM cf_forms')->execute()->as_array();

テーブル名はデータベースで定義されているものをそのまま指定する必要があります。

結果:

array (size=3)
  0 => 
    array (size=8)
      'id' => string '1' (length=1)
      'name' => string '小野小町' (length=12)
      'email' => string 'komachi@example.jp' (length=18)
      'comment' => string '花の色は 移りにけりな いたづらに

我が身世にふる ながめせし間に' (length=92)
      'ip_address' => string '::1' (length=3)
      'user_agent' => string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:30.0) Gecko/20100101 Firefox/30.0' (length=81)
      'created_at' => string '1403159141' (length=10)
      'updated_at' => string '1403159141' (length=10)
...

結果をオブジェクトで受け取る

結果をオブジェクトで取得することも可能です。

コード:

$result = DB::query('SELECT * FROM cf_forms')->as_object()->execute()->as_array();

as_object()メソッドにクラス名を指定することができます。

結果:

array (size=3)
  0 => 
    object(stdClass)[28]
      public 'id' => string '1' (length=1)
      public 'name' => string '小野小町' (length=12)
      public 'email' => string 'komachi@example.jp' (length=18)
      public 'comment' => string '花の色は 移りにけりな いたづらに

我が身世にふる ながめせし間に' (length=92)
      public 'ip_address' => string '::1' (length=3)
      public 'user_agent' => string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:30.0) Gecko/20100101 Firefox/30.0' (length=81)
      public 'created_at' => string '1403159141' (length=10)
      public 'updated_at' => string '1403159141' (length=10)
...

クラス名を指定しなかったのでstdClassが返っています。

2. クエリビルダー(DBクラス)

クエリビルダーはSQL文をメソッドで組み立てるものです。

DB::query()メソッドのように完全に自由にSQL文を作成することはできませんが、いろいろなメソッドが用意されており、たいていのSQL文を組み立てられます。

引数のエスケープ処理は自動的にされるので、SQLインジェクションを心配する必要はありません(クエリビルダーのコードにバグがない限り)。

結果を連想配列で受け取る

コード:

$result = DB::select()->from('forms')->execute()->as_array();

テーブル名はデータベース設定ファイルで指定したプリフィックスなしで指定します。

結果:

array (size=3)
  0 => 
    array (size=8)
      'id' => string '1' (length=1)
      'name' => string '小野小町' (length=12)
      'email' => string 'komachi@example.jp' (length=18)
      'comment' => string '花の色は 移りにけりな いたづらに

我が身世にふる ながめせし間に' (length=92)
      'ip_address' => string '::1' (length=3)
      'user_agent' => string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:30.0) Gecko/20100101 Firefox/30.0' (length=81)
      'created_at' => string '1403159141' (length=10)
      'updated_at' => string '1403159141' (length=10)
...

結果をオブジェクトで受け取る

DB::query()メソッドと同じく、結果をオブジェクトで受け取ることもできます。

コード:

$result = DB::select()->from('forms')->as_object()->execute()->as_array();

結果:

array (size=3)
  0 => 
    object(stdClass)[28]
      public 'id' => string '1' (length=1)
      public 'name' => string '小野小町' (length=12)
      public 'email' => string 'komachi@example.jp' (length=18)
      public 'comment' => string '花の色は 移りにけりな いたづらに

我が身世にふる ながめせし間に' (length=92)
      public 'ip_address' => string '::1' (length=3)
      public 'user_agent' => string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:30.0) Gecko/20100101 Firefox/30.0' (length=81)
      public 'created_at' => string '1403159141' (length=10)
      public 'updated_at' => string '1403159141' (length=10)
...

3. Model_Crudクラス

Model_Crudクラスは1つのテーブルのCRUD操作を提供します。

バリデーション機能も用意されています。

モデルクラス:

<?php

class Model_Db_Crud extends Model_Crud
{
    protected static $_table_name = 'forms';
    protected static $_created_at = 'created_at';
    protected static $_updated_at = 'updated_at';
}

コントローラ:

$result = Model_Db_Crud::find_all();

結果:

array (size=3)
  0 => 
    object(Model_Db_Crud)[29]
      protected '_data' => 
        array (size=8)
          'id' => string '1' (length=1)
          'name' => string '小野小町' (length=12)
          'email' => string 'komachi@example.jp' (length=18)
          'comment' => string '花の色は 移りにけりな いたづらに

我が身世にふる ながめせし間に' (length=92)
          'ip_address' => string '::1' (length=3)
          'user_agent' => string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:30.0) Gecko/20100101 Firefox/30.0' (length=81)
          'created_at' => string '1403159141' (length=10)
          'updated_at' => string '1403159141' (length=10)
      protected '_is_new' => boolean false
      protected '_is_frozen' => boolean false
      protected '_sanitization_enabled' => boolean false
      protected '_validation' => null
...

結果はモデルクラスのオブジェクトとして返ります。

4. ORMパッケージ

ORMパッケージはテーブル間の関係を処理できるORMです。

HasOne、HasMany、ManyManyという関係を処理できる他、バリデーション、論理削除、テンポラルモデル、ネスティドセット、EAVコンテナの機能もあります。

オブザーバが用意されており、特定のイベントに動作を追加することもできます。

Model_Crudより高機能ですが、その分、オーバーヘッドも大きくなります。

モデルクラス:

<?php

class Model_Db_Orm extends \Orm\Model
{
    protected static $_table_name = 'forms';

    protected static $_properties = array(
        'id',
        'name',
        'email',
        'comment',
        'ip_address',
        'user_agent',
        'created_at',
        'updated_at',
    );

    protected static $_observers = array(
        'Orm\Observer_CreatedAt' => array(
            'events' => array('before_insert'),
            'mysql_timestamp' => false,
        ),
        'Orm\Observer_UpdatedAt' => array(
            'events' => array('before_save'),
            'mysql_timestamp' => false,
        ),
    );
}

コントローラ:

$result = Model_Db_Orm::find('all');

結果:

array (size=3)
  1 => 
    object(Model_Db_Orm)[28]
      protected '_is_new' => boolean false
      protected '_frozen' => boolean false
      protected '_sanitization_enabled' => boolean false
      protected '_data' => 
        array (size=8)
          'id' => string '1' (length=1)
          'name' => string '小野小町' (length=12)
          'email' => string 'komachi@example.jp' (length=18)
          'comment' => string '花の色は 移りにけりな いたづらに

我が身世にふる ながめせし間に' (length=92)
          'ip_address' => string '::1' (length=3)
          'user_agent' => string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:30.0) Gecko/20100101 Firefox/30.0' (length=81)
          'created_at' => string '1403159141' (length=10)
          'updated_at' => string '1403159141' (length=10)
      protected '_custom_data' => 
        array (size=0)
          empty
      protected '_original' => 
        array (size=8)
          'id' => string '1' (length=1)
          'name' => string '小野小町' (length=12)
          'email' => string 'komachi@example.jp' (length=18)
          'comment' => string '花の色は 移りにけりな いたづらに

我が身世にふる ながめせし間に' (length=92)
          'ip_address' => string '::1' (length=3)
          'user_agent' => string 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:30.0) Gecko/20100101 Firefox/30.0' (length=81)
          'created_at' => string '1403159141' (length=10)
          'updated_at' => string '1403159141' (length=10)
      protected '_data_relations' => 
        array (size=0)
          empty
      protected '_original_relations' => 
        array (size=0)
          empty
      protected '_reset_relations' => 
        array (size=0)
          empty
      protected '_disabled_events' => 
        array (size=0)
          empty
      protected '_view' => null
      protected '_iterable' => 
        array (size=0)
          empty
...

結果はモデルクラスのオブジェクトとして返ります。

5. サードパーティライブラリ

ORMパッケージはActive Recordパターンなので、それが要件に合わない場合は、別のライブラリを探す必要があるでしょう。

有名なものにDoctrineがあります。

どう選択するか?

以上のようにそれぞれ機能が違うので、要求や要件によって自ずからどれをメインで使うかは決まってくると思います。

それから、FuelPHPが提供する4つの方法はどれか1つのみを選択しないといけないというものではなく、1つのアプリ内で複数を使うことも可能です。

Model_CrudやORMパッケージはDBクラスを使って実装されていますので、ORMパッケージを使いつつ一部ではDB::query()メソッドやクエリビルダーを使うという選択もあります。

意味もなくきまぐれに別の方法を使うのはよくないと思いますが、ORMパッケージで処理できない、あるいは、あまりにも複雑になるクエリはDB::query()メソッドで書くというのは現実的な対応だと思います。

ベンチマーク

一応、ベンチマークしてみました。

データベースから全3件のデータを取得してvar_dump()するというだけです。

$ ab -n 50 http://localhost/fuelphp/db/query
Requests per second: relative(%):
DB::query() 12.55 100.0%
クエリビルダー 12.26 97.7%
Model_Crud 11.86 94.5%
ORMパッケージ 10.61 84.5%

この程度の処理だとそれほど違いはないですが、予想通りの結果になってます。

参考

Date: 2014/07/09

Tags: fuelphp, database, validation