この記事はRuby on Railsを始めた初心者向けの記事です。
##この記事を書いた理由
初めて使うフレームワークがRuby on Railsという方なら、モデルの使い方について悩まれる方が多いのではないでしょうか?
僕自身がそうでした。
controllerにメソッドを書いて、find(id)とかでデータベースからユーザー情報を引っ張ってきて、viewにHTMLを書いてOK。。。って、モデル必要なくね?
僕は独学でRailsを勉強したので、モデルの必要性がいまいち分かりませんでした。
モデルに書くのはせいぜいassociation(has_manyとかbelongs_toとか)やvalidationで、「空欄でのフォーム送信を防止する時にvalidationってやつを書くためにモデルがあるんだな」みたいに最初は考えてました。
例えばUserモデルには以下のようなコードしか書いていませんでした。
has_many :article, dependent: :destroy #Articleモデルとの関係定義
#空欄防止
validates :username, presence: true
validates :email, presence: true
もちろんモデルを使わなくても小さいサービスであれば全然問題ないんですが、
少し大きなサービスになってくるとすぐにコントローラーのコード量が増えて(ファットコントローラー)、その時は理解できても、数ヶ月後とかにコードを見返すと何のコードなのかわからなくなってしまう可能性があります。
でも、「モデルにメソッド書いても、それをどうやってコントローラーで使うの?」
僕はずっとそうを思っていました。
ネット上でいろいろ探してみましたが、初心者には理解が難しく、結局いつもコントローラーにメソッドをたくさん書いて対応していました。
この記事ではなるべくインスタンスメソッドをどうやってコントローラーで使用するのかを
説明したいと思います。(クラスメソッドはまた別の記事にでも書こうかと思います)
あるところにデータベースの中にこんなユーザーが保存されていました。
id:1
名前(username):山田太郎
メールアドレス(email):hogehoge@example.com
年齢(age):28歳
そして、以下のようなuserのコントローラーがあったとします。
def index
@user = User.find(1)
@my_age = @user.too_young #これ何?
end
userにはusername, email, ageの3つしかパラメータとして登録していません。
じゃあtoo_youngって何?
このtoo_youngがインスタンスメソッドというもので、モデルで書いたメソッドを
コントローラーで呼び出しています。
Userモデルを見てみましょう。
今、Userモデルにはvalidationの他に、何やらtoo_youngというメソッドが書かれているようです。
class User < ApplicationRecord
validates :username, presence: true
validates :email, presence: true
#ここがインスタンスメソッド
def too_young
if self.age >= 20 #20歳以上の場合
return "大人です!"
else #20歳以下の場合
return "子供です!"
end
end
end
さて、インスタンスメソッドとは何でしょうか?
##インスタンスメソッド
まず、モデルでメソッドを使う場合、インスタンスメソッドについて理解するのが重要です。
インスタンスメソッドとは
メソッドは『メソッド』で見たとおり実行する一連の処理をまとめたものです。ただし通常のメソッドがプログラム中からいつでも呼び出せるのに対してクラス内に記述されたメソッドはクラスから作成されたオブジェクトしか呼び出すことが出来ません。このようなメソッドをインスタンスメソッドと呼びます。
引用: Let'sプログラミング(インスタンスメソッド) https://www.javadrive.jp/ruby/class/index3.html
いや、分からんww
まず、「インスタンス」とは何でしょうか?
インスタンスとはクラス(設計図)から作られるものになります。
よく例に挙げられるたい焼きに例えて説明すると
クラスとは一言で言うならば「設計図」です。
たい焼きで言うとたい焼きを作る「型」になります。
たい焼きは生地を型(Taiyakiクラス)に流し込んで、中身にあんこやクリームを入れて作ります。
出来上がったたい焼きそのものがインスタンスになります。
#newはインスタンスを作るメソッドです。(元からrubyの言語内で定義されている)
@taiyaki_anko = Taiyaki.new(nakami: anko)
@taiyaki_cream = Taiyaki.new(nakami: cream)
#ここで作られる「@taiyaki_anko」「@taiyaki_cream」がインスタンスです。
あんこ味のたい焼きもクリーム味のたい焼きも中身が違うだけで、型は一緒です。
つまり、クラスとは設計図であり、そのクラスからできたもの(ここではたい焼き)がインスタンスになります。
Userモデルの話に戻すと、以下のusers_controllerの@userがインスタンスになります。
@user = User.find(params[:id])
#findはインスタンスを探すメソッド
#Userクラスから調べたいidに該当するユーザー情報をデータベースから取得しています。
@my_age = @user.too_young #@userというインスタンスにtoo_youngというメソッドを使っている。
ここで、@userというインスタンスに対してtoo_youngというメソッドが使われています。
@userというインスタンスに使うメソッド....これがインスタンスメソッドとなります。
もう一度user.rb内に記載されているtoo_youngメソッドを見てみましょう。
class User < ApplicationRecord
validates :username, presence: true
validates :email, presence: true
#ここがインスタンスメソッド
def too_young
if self.age >= 20 #20歳以上の場合
return "大人です!"
else #20歳以下の場合
return "子供です!"
end
end
end
too_youngメソッド内に「self」というものがあります。
この「self」は何かというとusers_controller.rbで定義された@userのことです。
@userというインスタンスに対して「.(ドット)」で繋げてインスタンスメソッドを呼び出す時、インスタンスメソッド内では「self」が使え、self=呼び出し元のインスタンス(@user)となります。
今@userの中身は
id: 1, username: "山田太郎", email: "hogehoge@example.com", age: 28
なので、self.ageでageカラムの値が呼びだされ、「28」という数字が返ってきます。
今、if self.age >= 20 で条件分岐しようとしているため、この場合self.ageは28、
つまりtrueとなり、「大人です!」という文字列が返されることになる。
if self.age >= 20 #山田太郎は28歳だからtrue
return "大人です!"
ちなみにrubyではメソッド内最後の式を評価した値が戻り値として自動的に返されるため、
returnは省略しても問題ありません。
(僕はreturnを付けた方が分かりやすいので、いつもreturnを付けてます)
つまり、以下のコード内の@my_ageには"大人です!"という文字列が入る。
def index
@user = User.find(params[:id])
@my_age = @user.too_young #"大人です"
end
このようにしてモデルにインスタンスメソッドを定義して、コントローラーでメソッドを呼び出します。
(もう一つ重要なものとしてクラスメソッドがありますが、クラスメソッドの説明はまた別の記事で書きます。)
ちなみに実はインスタントメソッド内のselfは省略可能です。
以下のようにselfを省略して記述しても、問題なく動きます。
class User < ApplicationRecord
validates :username, presence: true
validates :email, presence: true
#ここがインスタンスメソッド
def too_young
if age >= 20 #selfを省略!
return "大人です!"
else
return "子供です!"
end
end
end
##モデルにインスタンスメソッドを定義する理由
別にtoo_youngというメソッドはUser.rbではなく、users_controllerにそのまま書いても問題なく動きます。
def index
@user = User.find(params[:id])
#########ここが今までuser.rbに書いていたtoo_youngメソッドの中身###############
if @user.age >= 20
@my_age = "大人です!"
else
@my_age = "子供です!"
end
#######################################################################
end
上記のようにコードをコントローラーに書いても動きますが、コントローラー内がこのようなif文等がたくさん記述されるとごちゃごちゃして読みにくいです。
何よりも他の人がこのコードを読む場合、理解するのにもっと苦労します。
ここらへんのコードの管理については、オブジェクト指向について勉強するにつれて、よく分かってくると思います。
##まとめ
何か計算したり、条件分岐したりする際はできるだけモデルに記述して、
コントローラー内をきれいに保ちましょう。
そうすれば後から「コードを読み返した時に理解できない!!」みたいなことは少なくなってくるかと思います。
逆にモデルに書きすぎてファットモデルになる場合もありますが....
これが僕にとってQiita初投稿です。
自分の勉強のためにも、今後もちょこちょこ投稿していきたいと思っています。