LoginSignup
18
16

More than 5 years have passed since last update.

Phalcon Model::save メソッドのオプション引数の正体

Last updated at Posted at 2013-12-03

Phalcon PHP Framework で Model を永続化させるメソッドには、謎のオプション引数が用意されています。

  • array $data
  • array $whiteList

こいつらはいったいなんぞやというお話です。

Phalcon.Mvc.Modelより抜粋
/*
 * 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 ばかり見ていて気づきませんでした。

ということで、以上です!

18
16
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
16