New-Village

月間ブログ。だいたい1カ月に1回は更新しているようです。

作って知ったMVCの住み分けかた

「モデルの役割がビミョーだよね」

なんて思っていたのですが、以下のスライドシェアのスライドを読んで、自分が未熟なことことが良くわかりました。ちゃちゃっとSQLを書く手軽さに比べると、O/Rマッパーを使ったコーディングは、データそのものが見えずらいので使いにくい…そう思っていましたが、餅は餅屋、プログラミングにおいてはO/Rマッパーを使う意味というのが良くわかりました。

で、これを読んでいると、モデルの中に"named_scope"とやら(メソッド?)を仕込んで、コントローラーで必要なネームドスコープを使うことで、データベースの値を動的に捉えるといった考え方が正しいらしい。(後記:スコープはスコープでメソッドとは違うみたいだ…そりゃそうだよね名前違うんだし。)

たしかに、ネームドスコープを使って基本的なデータセットをモデルに定義しておいて、コントローラーで必要なネームドスコープを呼んで、ビューが表示するという関係だとMVCっぽい気がする。

後は読む価値は無いです。僕の反省文です。

 

■ 良くないMVCプログラミング

データベース: db/migrate/[timestamp]_create_mains.rb

class CreateMains < ActiveRecord::Migration
  def change
    create_table :mains do |t|
      t.integer :user_id
      t.integer :feed_id
      t.boolean :read_flg

      t.timestamps
    end
  end
end

モデル: app/models/main.rb

class Main < ActiveRecord::Base
  belongs_to :user
  default_scope -> { order('created_at DESC') }
end

コントローラー: app/controllers/mains_controller.rb

class MainsController < ApplicationController
...
def index
  @main = Main.where(:user_id => current_user.id) # フィード取得用
end
...

ビュー: app/views/shared/_navmenu.html.erb

<ul class="nav nav-pills nav-stacked">
  <li><%= link_to "未読一覧", "#", :style => "display:inline-block" %>
    <span class="badge"><%= @main.where(:read_flg=>"f").count %></span></li>
</ul>

ビュー: app/views/mains/index.html.erb

<div class="row">
  ...
  <div class="panel panel-default">
    <%= render @main.where(:read_flg=>"f") %>
  </div>
  ...
</div>

コントローラーで、Mainモデルから特定のユーザのレコードを引っ張ってきて、インスタンス変数"@main"にセットした後に、さらに、ビュー上でインスタンス変数"@main"の中からfalseのレコードだけを抽出しています。

肝心のモデルは、データに関してソートするだけで、コントローラーとビューが、頑張って働いてくれています。モデルさんは声がかけ辛かったので(パラメーターの渡し方が分からなかったので)、コントローラーさんとビューさんに代わりに仕事をしてもらったのですが、MVCとしての在り方としては良くないですよね、きっと。

 

■ 良いMVCプログラミング

つまらないけど、嵌った点を2点、先に書いておきます。
① rails4では、named_scopeは存在しない。scopeでいいらしい。
② "scope :own, :condition ..." て書き方はNG。lambda もしくは "->"を使う必要がある。
参考:Rails4でscopeにlambdaを付け忘るとチェインが外れる - Qiita

モデル: app/models/main.rb

class Main < ActiveRecord::Base
  belongs_to :user
  default_scope -> { order('created_at DESC') }
  ...
  # ログインユーザのレコードを抽出
  scope :own, lambda { |uid| where("user_id = ?", "#{uid}") }
  # 未読のレコードを抽出
  scope :unread, lambda { where(read_flg: false) }
end

コントローラー: app/controllers/mains_controller.rb

class MainsController < ApplicationController
...
def index
  @main = Main.own(current_user.id).unread # 未読フィード取得
  @cnt  = @main.count  # 未読フィード件数取得 
end
...

ビュー: app/views/shared/_navmenu.html.erb

<ul class="nav nav-pills nav-stacked">
  <li><%= link_to "未読一覧", "#", :style => "display:inline-block" %>
    <span class="badge"><%= @cnt %></span></li>
</ul>

ビュー: app/views/mains/index.html.erb

<div class="row">
  ...
  <div class="panel panel-default">
    <%= render @main %>
  </div>
  ...
</div>

おおお。ビューさんとコントローラーさんが凄くスッキリしましたし、メソッドを呼んでる感があるので、MVCプログラミング的には、前より良くなったのではないでしょうか?

正解かどうかはわかりませんが、少なくとも前の「良くないMVCプログラミング」よりは断然可読性が上がったと思います。

 

■ 残課題

  • lambdaって何者?発音も含めて。