Phalcon PHP Framework で Model を永続化させるメソッドには、謎のオプション引数が用意されています。
array $data
array $whiteList
こいつらはいったいなんぞやというお話です。
/*
* Inserts or updates a model instance. Returning true on success or false otherwise.
*
* @param array $data
* @param array $whiteList
* @return boolean
*/
public function save($data=null, $whiteList=null){ }
// - 以下同様 -
public function create($data=null, $whiteList=null){ }
public function update($data=null, $whiteList=null){ }
現在、これについてAPI リファレンスにはなにも記述がありません。
しかしほぼ書き終えてからドキュメントを見たら...一言記載がありましたorz
ドキュメント:Creating Updating/Records
調べた結果
いきなり結論です。
array $data
永続化クエリを実行する前に、Model に反映させるデータを連想配列で指定します。
クエリに用いるデータではなく、あくまでも Model に反映されるデータなので、
事前に Model のデータが変更されている場合、$data で渡したカラムの値以外も更新されます。
つまり、 $model->assign($data)
と同じ挙動ということです。
array $whiteList
第一引数である $data に対する、「Model にデータを反映するカラムを限定するホワイトリスト」です。
SQLには何ら影響を及ぼしません
$user = Users::findFirst($id); // 余談ですが findFirst には主キー値を渡すこともできます。
$user->first_name = "Foo";
$user->save(
array(
"first_name" => "Kenta",
"last_name" => "Suzuki",
"age" => "24"
),
array( "age" )
);
つまり上記のようにした場合、永続化実行前に $user に反映されるデータは age
だけとなります。
first_name
については、その前に設定した "Foo"
さんのまま、INSERT や UPDATE 文に反映されます。
調べた方法と内容
以降は、私が調査した内容ですので忙しい方には蛇足となります。
要するにエビデンスです。
実験してみた
Phalcon の O/R マッパは、常に全てのカラムを挿入・更新します。
追記: Dynamic Updateを使うことで変更されたカラムのみ更新することができるようになります。
もし SQL に使用するカラムを限定できるなら、BLOB などのサイズの大きいカラムを毎回更新せずに済み、パフォーマンス向上が期待できます!
save メソッドをオーバーライドして自分で SQL を発行したりすれば対応できる問題ですが、更新カラムを限定できるインターフェイス仕様くらあってもいいんじゃないかと常々思っていました。
そんな折に、array $whiteList
という引数がふと目に止まったので、これはもしやと思いさっそく実験してみました。
$user = Users::findFirst($id);
$user->first_name = "Kenta";
$user->last_name = "Suzuki";
$user->age = "24";
// ホワイトリストで age のみの更新にできるかな...?
$user->save(null, array( "age" ));
発行された SQL (ログより抜粋)
UPDATE `users` SET `username` = ?, `password` = ?, `email` = null, `first_name` = ?, `last_name` = ?, `age` = ?, `created_ts` = ?, `updated_ts` = ? WHERE `id` = ?
見事に全てのカラムが更新されるとともに、淡い期待は打ち砕かれました。
うっかりパスワードなどの秘密情報を一時削除したエンティティを save してしまったらアウトですね。
ソースを見てみた
C言語なのであまり見やすくはありません。
Phalcon2 からは Zephir という専用言語で開発されるということで、待ち遠しいですね。
model.c#3993 で in_array (phalcon_fast_in_array) を使って attribute の存在を確認してます。
さらに見つからなかったら continue でスキップしています。
スキップされたブロックを見ると、コメントに「We check if the field has a setter」とあります。
setter を探してみつかれば setter を使い、なければフィールドに値を格納しているようですね。
やはり、SQL は無関係でした。
ここまで書いて、ドキュメントに記載があることを見つけてしまいました...
API ばかり見ていて気づきませんでした。
ということで、以上です!