LoginSignup
3
0

More than 5 years have passed since last update.

LaravelのfirstOrNewの変更差分について(v5.2.44使用者対象)

Last updated at Posted at 2016-12-30

ご挨拶

こんばんにゃ(≧∀≦*)これが間違いなく今年最後のQiitaになると思います。
今年も色々ありましたね、みなさんどうお過ごしでしたでしょうか?
私事で言うと
- 引越しした(7年近く住んだ池袋を離れた)
- 9/7付けで退職してフリーに戻った

ということで、フリーに戻ってCakePHP3(1ヶ月)とLaravel5.2を触ってます。
今回は主にというかLaravelのお話ですね。
とりあえず、簡単ではありますが、自分がF/Wを触る上で必要な3ヶ月を費やしてみての所感と、おっと!これはハマるぞ!!という出来事に関してです。

Laravelのいいところ

  • 結果セットが単一レコードであればモデルオブジェクト(entity)
  • 結果セットが複数レコードであればコレクション(entityの配列オブジェクト)
  • リクエストクラスがいい感じに処理されてくれてバリデーションが楽

Laravelの悪いところ

  • 上記を実現するにはPHPという言語の型問題があまりにもxxである。
    • 特に配列の扱いがxxだなと再認識した
  • せっかくのウリであるDBからの返却値がオブジェクトであることを生かせていない
    • モデルやコレクションの拡張をもっと主張して捗らせるべき
  • Model/Collection/QueryBuilderで同名、かつ、同じ動作を伴うメソッドが混乱を招く

本題です(正直、Ver問題に見舞われなければ特に害はありません)

  • 先日、チームメンバーからModel::firstOrNewの返り値がおかしいというタレコミを受けた。
  • 奇しくも自分が共通化したメソッドで使われているメソッドが利用していたものであった。
  • 再現性を認められず、本番リリース済みだった
  • 問題はORMが発行されるSQLの差異(期待した条件文の有無)ということ
    • これは(美味しそうだから)放ってはおけない!!

で、調べますよね、

  • タレコミ元の情報によると、開発者の環境ではうまくいく
  • だが、タレコミ元の環境では期待値を得られない
  • composer update してみたらなぜかうまくいった

いやいや!それってVersion問題ですかね?

  • ということで、ラッパーメソッドを作った自分としては放っておけないので調査開始
    • 自分の環境でも、条件文が正しくORMの生成するSQLに反映されていない
    • 対象のメソッドはfirstメソッド(LIMIT 1)が付加されるを利用していた
    • select .. limit 1 でテストしていた結果セットを利用するというずぼらをかましていた
    • これは!!絶対になにかある!!!!

ということで、調査開始

  • まずはタレコミ通りのSQLを発行してみる
    • 面白いことにタレコミ通りの結果となる(開発者の環境ではならない)
  • composer updateをかます
    • なんか、Laravelがアップデートされてるぞw
  • 手元のEmacsのバッファをコピペして、アップデートが終わるのを待つ
  • アップデートが終わるを待って、当該クラスを開き直す
    • SQLに差異があるぞ!!!!(元の実装がダメだったことを確認)
  • もはや、Versionの差異であることは間違いない。。

で、実際のソースを見てみる

v5.2.44

    public function firstOrNew(array $attributes)
     {
         $mutatedAttributes = $this->model->newInstance($attributes)->getAttributes();

         if (! is_null($instance = $this->where($mutatedAttributes)->first())) {
             return $instance;
         }

         return $this->model->newInstance($attributes);
     }

v5.2.45

public function firstOrNew(array $attributes)
     {
         if (! is_null($instance = $this->where($attributes)->first())) {
             return $instance;
         }

         return $this->model->newInstance($attributes);
     }

思いっきり、参照する変数を変えとりますやん!!

  • ということで、$mutatedAttributes というか$fillableや$guraredの調査は次に譲るとして、v5.2.44をお使いの方でfirstOrNewとお使いの方はご注意を

本番も v5.2.45 にするのが本来的にはよいと思いますが、いったんモデルの基底クラスで Model::fillable に存在しないフィールドが指定されていた場合は例外を吐かせるようにしました。
※ 年末だし、リリースまで1ヶ月はある機能で、ちゃんと調査する時間を設けたかったので

それでわヾ(。・ω・。)

3
0
0

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
3
0