tixture55’s diary

主にプログラミング関係の日記です。

Railsでのモデルの書き方

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