Railsでのモデルの書き方の学習の記録です。
今回はアソシエーションとか複雑なクエリを何回も書かなくて済むスコープの書き方です。
railsのhas_manyの書き方
class Post < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belong_to :post
end
上が一番分かりやすい例だと思います。Postの下にCommentがぶら下がっている1対多の定義です。
dependentの定義
下記例だと、Episodeの下にcommentsとtaggingsとtagsがぶら下がっているイメージである。
class Episode < ActiveRecord::Base
has_many :comments, :dependent => :destroy
has_many :taggings, :dependent => :destroy
has_many :tags, :through => :taggings
end
この場合、親に当たる投稿が削除されたら、コメントの扱いをどうするか、それを定義するのがdependentである。投稿が削除されたら、紐づくコメントも削除する場合は下記のように書く。
1. has_many :comments, :dependent => :destroy
2. has_many :comments, :dependent => :delete_all
1の場合はComment::destroy()が毎回よばれるのに対し、2はdestroy()をスルーして1の場合はComment::destroy()が毎回よばれるのに対し、2はdestroy()をスルーして 削除するSQLを走らせる。
通常はdestroy()を使う
Railsのスコープ機能
モデルのスコープ機能とは、共通的に使うクエリをモデルのメソッドのように定義できる機能モデルのスコープ機能とは、共通的に使うクエリをモデルのメソッドのように定義できる機能 何度も複雑なSQLを何度も書かなくてよくなり、保守性が高まる。
#スコープメソッドで定義
class Post < ActiveRecord::Base
scope :published, -> { where(published: true) }
end
上の定義をしたうえで
Post.published
とすると、publishedカラムがtrueのPostのみを取得できる。
スコープ同士のマージ
class User < ActiveRecord::Base
scope :inactive, -> { where state: 'inactive' }
scope :finished, -> { where state: 'finished' }
end
# 使用方法
User.inactive.finished
# => select users from users where state = "inactive" and state = "finished"
scopeを組み合わせれば複雑なSQLロジックを分離して考えることができる。
他のモデルのスコープを利用する方法
class Category < ActiveRecord::Base
has_many :posts
# スコープでjoinsを行い、Postクラスのrecentスコープを利用する方法
scope :with_posts, -> { joins(:posts).merge(Post.recent) }
end
class Post < ActiveRecord::Base
belongs_to :category
scope :recent, -> { where(created_at: Time.zone.now..3.days.ago) }
end
# 使い方
Category.with_posts
これで内部結合postsテーブルと内部結合させるSQLが作れる。ロジックが分割されているので、仮にmodelクラスがもっと増えても、scopeの組み合わせでいろんなバリエーションのSQLを作ることができる。
参考サイト
qiita.com
ruby-rails.hatenadiary.com