Phalconモデルまとめ(1)データ取得の基本

「最速」PHPフレームワークPhalconのモデルについて、基本事項をまとめます(公式ドキュメントの翻訳+αです)。記事執筆時のPhalconのバージョンは1.3.1です。なお、サンプルコードを実行したい場合、環境構築を参考にしてください。

モデルの基本

Phlaconのモデルは、PhalconMvcModelを継承したクラスです。モデルクラスは以下の条件を満たす必要があります。

  • modelsディレクトリに配置する
  • モデルファイルは1つのクラスだけを含む
  • クラス名はキャメルケース

上記例が、Robotsモデルの実装例です。RobotsがPhalconMvcModelを継承している点に注目してください。PhalconMvcModelを継承することで、データベースにおいて基本的なCRUD処理から、データのバリデーション、複数のモデルの関係に基づいた検索など様々な機能を利用することができます。

デフォルトでは、「Robots」モデルは「robots」テーブルを参照します。参照するテーブルを手動で設定したい場合は、getSource()メソッドを使用します。

上記コード例では、Robotsは「the_robots」テーブルを参照します。

initialize()メソッドは、1リクエストに対して1回だけ呼ばれます。モデルの振る舞いのカスタマイズに適しています。

initialize()は1回のリクエストを通して1回だけ呼ばれ、モデルの全てのインスタンスの初期化に使うことを目的としています。インスタンスが作られる度に呼ばれるメソッドが欲しい場合は、onConstruct()メソッドを使います。

publicプロパティとgetter/setter

モデルのプロパティは、publicに実装し、どこからでも読み取り・変更できるようにすることができます。(Phalcon DevToolsのデフォルトではpublicプロパティでモデルを自動生成します)

getterとsetterを使うことで、プロパティの操作を自由に行えるようにしつつ、モデルにセットされるデータの整形やバリデーションを行うことができます。(Phalcon DevToolsのモデル生成コマンドに–get-setオプションを付けると、getter/setterでモデルを自動生成します)

publicなプロパティは、利用する際にシンプルなコードになるという利点があります。一方、getter/setterは、テストのしやすさや拡張性・メンテナンス性を向上させてくれます。自分の作ろうとしているアプリケーションに適した実装を選んでください。PhalconのORマッパーはいずれの実装にも対応しています。

モデルの名前空間

クラス名の衝突を避けるため、名前空間を使えます。参照するテーブルはクラス名に基づくため、以下の例では「robots」テーブルが参照されます。

レコードからオブジェクトへの変換

モデルの全てのインスタンスは、テーブルの1行を表します。オブジェクトのプロパティを取得することで、DBのレコードにアクセスすることができます。一例として、以下のrobotsテーブルを考えます。

以下のコードで、プライマリーキーによる検索を行い、その結果を表示できます。

レコードを一度取得すれば、そのデータを変更して保存することもできます。

Phalconのモデルを使う場合、生のSQLを書く必要はありません。PhalconMvcModelはデータベースの抽象化を行ってくれます。

レコードの検索

PhalconMvcModelはデータの検索のためにいくつかのメソッドを提供しています。以下はfind()メソッドの使用例です。

findFirst()メソッドを使うことで、判定基準に合った最初のレコードを取得することができます。

find()/findFirst()には、検索条件となる連想配列を渡すことができます。

利用可能なクエリオプションは以下です。

パラメータ 説明
conditions 検索条件。PhalconMvcModelは第1引数を検索条件とみなす。 “conditions” => “name LIKE ‘steve%’”
columns 指定したカラムだけを返すようにする。 “columns” => “id, name”
bind プレースホルダーをbindする値で置き換える。 “bind” => array(“status” => “A”, “type” => “some-time”)
bindTypes バインド時にパラメーターを指定した型に変換する。 “bindTypes” => array(Column::BIND_TYPE_STR, Column::BIND_TYPE_INT)
order 検索結果をソートする。 “order” => “name DESC, status”
limit 検索結果の件数を制限する。 “limit” => 10 / “limit” => array(“number” => 10, “offset” => 5)
group 複数のレコードからデータを集め、結果を1つ以上のグループにまとめる。 “group” => “name, status”
for_update 検索対象データに排他ロックをかける。 “for_update” => true
shared_lock 検索対象データに共用ロックをかける。 “shared_lock” => true
cache 結果をキャッシュし、DBアクセスを減らす “cache” => array(“lifetime” => 3600, “key” => “my-find-key”)
hydration 結果セットの型を指定する(オブジェクト/連想配列)。 “hydration” => Resultset::HYDRATE_OBJECTS

お好みであれば、パラメータではなくオブジェクト指向の構文でクエリを組み立てることもできます。

静的メソッドのquery()がPhalconMvcModelCriteriaオブジェクトを返します。

全てのクエリは内部的にPHQLとして扱われます。PHQLは、Phalcon独自のクエリ言語です。

最後に、findFirstBy<プロパティ名>メソッドがあります。このメソッドは、先に紹介したfindFirst()の拡張です。

一例として、以下のようなモデルがあるとします。

ここで、nameを元に検索したい場合、findByName()メソッドを使って以下のように検索できます。

モデルの結果セット

findFirst()は、検索結果のモデルのインスタンスを直接返します。

一方、find()はPhalconMvcModelResultsetSimpleオブジェクトを返します。このオブジェクトは、データの順次取得や特定のデータの探索、レコード数のカウント等の機能をカプセル化します。

これらのオブジェクトは、通常の配列よりも機能が豊富です。PhalconMvcModelResultsetで最も素晴らしい機能の1つは、メモリ内に存在するレコードはどんな時でも1件だけである、という点です。このため、大量のデータを扱うときでもメモリの消費は最小限に抑えられます。

Phalconの結果セットはDBMSのカーソルをエミュレートしているため、場所を指定してデータを取得することができます。DBMSによってはカーソルに対応していないものもあるため、注意してください(非対応のDBMSの場合、特定の位置のレコードを取得しようとする度にDBに問い合わせが行われます)。

巨大な問い合わせ結果をメモリに持っておくと、リソースを大きく消費します。そのため、結果セットは32行の固まりとしてDBから取得し、問い合わせ回数の減少とメモリの節約を行っています。

結果セットをキャッシュしておくことができます(PhaclonCacheなどが利用できます)。しかし、データのシリアライズを行うと全てのデータを取得して配列にするため、キャッシュの作成処理を行っている間はメモリ消費が多くなります。

結果セットのフィルタング

最も効率的なデータのフィルタリング方法は、検索条件を指定することです。この場合、データベースは(利用可能であれば)インデックスを使用するため、高速にデータを取得できます。加えて、PHPでデータのフィルタリングを行うこともできます。

パラメータのバインド

PhaclonMvcModelはパラメータのバインド機構をサポートしています。バインド機構を使うことによるパフォーマンスへの影響はわずかですが、この方法を使うことでSQLインジェクション攻撃を受ける可能性を減少させることができます。文字列と数字のプレースホルダーがサポートされています。

数字のプレースホルダーを使う場合、1, 2といったint型の値として定義する必要があります。’1′, ‘2’といった定義は文字列とみなされるため、数字のプレースホルダーとしては機能しません。

文字列のプレースホルダーは、PDOによって自動的にエスケープされます。この機能は文字コードを考慮するため、データベース設定で適切な文字コードを指定することを推奨します。

パラメーターの型を指定することもできます。

bindTypesのデフォルトはPhalconDbColumn::BIND_PARAM_STR(文字列)です。文字列型のパラメータの型を指定する必要はありません。

バインド機構はfind()/findFirst()/findFirstBy<プロパティ名>のいずれでも利用できます。一方、count()、sum()、average()といったメソッドには利用できません。

取得したレコードの初期化

DBからデータを取得した後に、何かしらの初期化処理が必要な場合があります。このようなメソッドは、「afterFetch」メソッドとして定義できます。このイベントはインスタンスの作成とデータの代入時に実行されます。

もしgetter/setterを使っていれば、アクセス時に初期化することもできます。

今回はここまで

以上で、データ取得の基本は終わりです(公式ドキュメントのInitializing/Preparing fetched recordsまで)。次回は、Relationships between Modelsから先、複数テーブルの扱いをみていきます。

コメントを残す