私のプロジェクトのRailsアプリケーションがついに4.2系から5.0系に進化するときがやってまいりました。
その際、 squeel が結構幅を利かせており、直すのに結構四苦八苦してしまいました。
なのでこちらではその忘備録とともに、同じくsqueelにsqueal(悲鳴)をあげる方々の助けに少しでもなればという事で書いていきます。
squeelについて
- 公式
- 実は私はsqueelは使った事がないので、よくわからないのですが、要は短く綺麗にActiveRecordへの指示を出せるGemだったようです。
- さよならSqueelの記事にある通り、Rails5へのアップデートに伴い、ついにアップデートがされなくなってしまいました。
squeel修正対応表
- では早速本題に向かいます。
今回メインで使うモデルについて
class Corporation < ActiveRecord::Base
belongs_to :ceo
has_many :users
end
class User < ActiveRecord::Base
belongs_to :corporation
attr_accessor %i(name)
end
order
複数のプロパティでソートをしている場合
- squeelの公式には書いていなかったのですが、複数orderを指定する場合にこんな書き方ができたようです
User.joins(:corporation).order { [:name, :id] }
- これに関してはすでに簡単にAcriveRecordで書けるようになっているのでありがたかったです。
User.joins(:corporation).order(:name, :id)
特に問題はなし
複数のプロパティ(親要素含む)でソートをしている場合
User.joins(:corporation).order { [:name, corporation.name] }
- これはちょっと「ん?」となった。親要素ってどうやってソートするんだっけ?
- これが正解
User.joins(:corporation).order(:name, Corporation.arel_table[:name])
-
さらっと
arel_table
使ってますが、Arelでクエリを書くのはやめた方が良い5つの理由(Rails 5.0以前の場合)の中盤に書かれてある通り、5.1以降はarel_tableをガンガン使ってもいい方向性になるかもなので、使い方を覚えておいて損はないでしょう。 -
@jnchito さんからコメントいただきまして、arel_tableが今後ともまだプライベートAPIのままの可能性もあるということで、引き続き控えめにみておいた方がいいとのことでした。失礼いいたしました
-
ちなみに今回は
belongs_to
している対象が元のモデル名と同じだったからいいものの、
# user.rb
belongs_to :my_office, class_name: 'Corporation'
とかだった場合って迷いません?ですがこの場合はおとなしく
User.joins(:my_office).order(:name, Corporation.arel_table[:name])
が正解。
複数のプロパティ(親の親要素含む)でソートをしている場合
- あまり考えたくないけどもこんなバージョンが来た場合
User.joins(corporation: :ceo).order { [:name, corporation.ceo.name] }
何重すんねんって思ったけども、この場合はこう
User.joins(corporation: :ceo).order(:name, Ceo.arel_table[:name])
いやはやarel_tableの仕組み覚えななぁ‥‥
DESC,ASCが入っている場合
User.order { [name, age.desc] }
- 中かっこを使ってまとめてあげると見やすい
User.order(:name, { age: :desc })
joins(主にleft_outer_join)
- 私のプロジェクトでは、squeelでjoinsを使っている場合は大概が
LEFT OUTER JOIN
のためだった
一つのテーブルに対してLEFT OUTER JOINしている場合
User.joins { corporation.outer }
-
squeelのあった頃は
LEFT OUTER JOIN
するのが面倒くさかったらしいです -
これに対しては、Rails5ですでにメソッドが追加されているのでこう書きます
User.left_outer_joins(:corporation)
# もしくは
User.left_joins(:corporation)
どっちを使うかはお好みで。短いから普通は後者かな?
複数のテーブルに対してLEFT OUTER JOINしている場合
- 次はちょっとややこしい
User.joins { corporation.outer.ceo.outer }
- つまりはUserに対して
Corporation
でLEFT OUTER JOIN
した後に、またさらにCeo
をLEFT OUTER JOIN
している - これに対してはこう
User.left_joins(corporation: :ceo)
- これがもしも
User.joins { corporation.ceo.outer }
とかだった場合(Corporaton
がINNER JOIN
、Ceo
がLEFT OUTER JOIN
)
User.left_joins(corporation: :ceo).where.not(Corporation.arel_table[:user_id].eq nil)
こんな感じで無理やりそれっぽくこじつけるか、諦めて一つ前のようにまとめてLEFT OUTER JOIN
してしまうか、他の手立てを使うかを考えないといけない‥‥
まとめ
- order関係は楽だけども、join周りが場合によってはしんどいかも