Lesson2


Lesson1の復習

使ったコマンド

$ rails new プロジェクト名
$ rails generate scaffold テーブル名 カラム:データ型
$ rake db:migrate
$ rails console #または rails c
$ rails server #または rails 
$ rails -h                      #railsコマンドのヘルプ
$ rails generate -h             #rails generateコマンドのヘルプ
$ rails generate scaffold -h    #rails generate scaffoldコマンドのヘルプ
$ rake -T                       #rakeで実行できるタスクの一覧
$ rake routes

今日はURLとリレーションのお話

例えば、リレーションがこんな風だとします。(同じキャラクターが他のアニメにも出てることは考えないでおきます。)

アニメ 1 : キャラクター

URLはこんな風にします。

http://example.com/animes/1/characters

animes/1の”1”の部分は、Animeテーブル内での通し番号 = idです。

(もし、URLのidの部分を”TIGER & BUNNY”とか名前にしたいという方には、こちらのgemをどうぞ。 つ gem friendly_id

これを実現するためには、リレーション(データベースに関する設定)とURLの設定が、それぞれ個々に必要です。

それをこれからお話します。


URLの設定

config/routes.rbを編集します。

例)http://example.com/animes/1/characters

resources :characters

resources :animes do    #編集
  resources :characters #追記
end                     #追記

animesの後ろにつながる部分をdo~endの中に書きます。元々あったresources :charactersは後で使うので残しておきます。

そうして、rake routesコマンドでURLの出来を確認してみてください。

$ rake routes
              Prefix Verb   URI Pattern                                     Controller#Action
          characters GET    /characters(.:format)                           characters#index
                     POST   /characters(.:format)                           characters#create
       new_character GET    /characters/new(.:format)                       characters#new
      edit_character GET    /characters/:id/edit(.:format)                  characters#edit
           character GET    /characters/:id(.:format)                       characters#show
                     PATCH  /characters/:id(.:format)                       characters#update
                     PUT    /characters/:id(.:format)                       characters#update
                     DELETE /characters/:id(.:format)                       characters#destroy
    anime_characters GET    /animes/:anime_id/characters(.:format)          characters#index
                     POST   /animes/:anime_id/characters(.:format)          characters#create
 new_anime_character GET    /animes/:anime_id/characters/new(.:format)      characters#new
edit_anime_character GET    /animes/:anime_id/characters/:id/edit(.:format) characters#edit
     anime_character GET    /animes/:anime_id/characters/:id(.:format)      characters#show
                     PATCH  /animes/:anime_id/characters/:id(.:format)      characters#update
                     PUT    /animes/:anime_id/characters/:id(.:format)      characters#update
                     DELETE /animes/:anime_id/characters/:id(.:format)      characters#destroy
              animes GET    /animes(.:format)                               animes#index
                     POST   /animes(.:format)                               animes#create
           new_anime GET    /animes/new(.:format)                           animes#new
          edit_anime GET    /animes/:id/edit(.:format)                      animes#edit
               anime GET    /animes/:id(.:format)                           animes#show
                     PATCH  /animes/:id(.:format)                           animes#update
                     PUT    /animes/:id(.:format)                           animes#update
                     DELETE /animes/:id(.:format)                           animes#destroy

どうですか?animesの後ろにcharactersが付いたURLがありますね。do~endで囲ったものが後ろに付いてくれます。また、do~endで囲ったほうのanimes単体のURLも作ってくれます。resources :charactersのように1行だけだと、そのテーブル単体のURLができます。

このように、URLをつなげるときは理論上do~endでどんどん中に囲んでいけばOKですが、あまり長いURLはお行儀がわるいのでテーブル名2つ分ぐらいまでにしておきましょう。


リレーションの設定

app/models以下のファイルを編集します。

例)アニメ 1 : キャラクター

anime.rb

class Anime < ActiveRecord::Base
  has_many :characters #追記
end

character.rb

class Character < ActiveRecord::Base
  belongs_to :anime #追記
end

英語のように読めばわかりやすいと思います。

DBにデータがない状態では特に確認する方法がないので、このまま次に進みます。


ページ上のリンクの変更

ここまでできたら、リレーションがあるリンクをページ上に作っていきましょう。

まずは、リンクの設定手順の確認です。

new_anime GET    /animes/new(.:format)   animes#new

“/animes/new”が、出来上がりのURL。”new_anime”が、html.erbに書くpath。

app/views/animes/index.html.erb

<%= link_to 'New Anime', new_anime_path %>

link_toの記述に”new_anime”を”_path”につなげて書くだけ。

ここまでいいですか?


では、例題です。

例)とあるアニメ詳細ページ /animes/:id に、キャラクター登録ページ /animes/:anime_id/characters/new へのリンクを貼る

rake routesで確認します。

new_anime_character GET    /animes/:anime_id/characters/new(.:format)   characters#new

また、URLとhtml.erbとの関係は以下のようになります。

URI Pattern                               Controller#Action        views
/characters(.:format)                     characters#index         app/views/characters/index.html.erb
/characters(.:format)                     characters#create        -
/characters/new(.:format)                 characters#new           app/views/characters/new.html.erb, _form.html.erb
/characters/:id/edit(.:format)            characters#edit          app/views/characters/edit.html.erb, _form.html.erb
/characters/:id(.:format)                 characters#show          app/views/characters/show.html.erb
/characters/:id(.:format)                 characters#update        -
/characters/:id(.:format)                 characters#update        -
/characters/:id(.:format)                 characters#destroy       -

アニメ詳細ページに該当するhtml.erbは、app/views/animes/show.html.erbです。ここに以下のリンクを追加します。

<%= link_to 'New Character', new_anime_character_path(@anime) %>

“_path”の後ろに、URLの:anime_idのネタとなる@animeを渡してください。

この@animeは、アニメ詳細ページを呼び出すapp/controllers/animes_controller.rbから来ています。rake routesコマンドで表示されたController#Actionの部分で、どこがどう呼ばれるのか確認してみてください。

animes_controller.rb

class AnimesController < ApplicationController
  before_action :set_anime, only: [:show, :edit, :update, :destroy]
  ・・・
  # GET /animes/1
  # GET /animes/1.json
  def show
  end
  ・・・
  private
    # Use callbacks to share common setup or constraints between actions.
    def set_anime
      @anime = Anime.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def anime_params
      params.require(:anime).permit(:name)
    end
end

Rubyぽくなってきましたが、焦らなくても大丈夫です。すべては設定だと思ってください。

def~endで囲まれたところがそれぞれ、controllerのactionと呼ばれる部分です。

今回該当するdef show~endの中には何もありません。でも、2行目にbecore_action(というコールバック)で:set_anime(メソッド)を実行します。いつ実行するのか、only: [:show …]のactionのときです。

そうすると、privateの下にあるdef set_anime~endの中には@animeというのがあって、def show~endのとき、この@animeが呼ばれるというわけです。

@animeには、:idを持つanimeの情報がすべて入っています。なので、リンクの”_path”にそのまま渡せばRailsがよしなにしてくれるという訳です。


データ登録時のリレーション情報の受け渡し

データを登録するとき、リレーション情報も一緒に登録してしまいましょう。

方法その1:URLからリレーションのidをもらう

例)とあるアニメ詳細ページ /animes/:anime_id からキャラクター登録ページ /animes/:anime_id/characters/new へ移動して登録する

rake routesで/animes/:anime_id/charactors/newがあることを確認しましょう。

そして、以下の編集をします。

app/controllers/characters_controller.rb

def new
  @anime     = Anime.find(params[:anime_id]) #追記
  @character = @anime.characters.new         #編集
  ・・・
end

def create
  @anime     = Anime.find(params[:anime_id])             #追記
  @character = @anime.characters.new(character_params)   #編集
  ・・・
end

controller内でparams[:anime_id]と書くと、URLの/:anime_idを勝手に参照してくれます。また、@characterに@animeからつないだカタチで値を入れると、@animeとcharacterのリレーション情報を勝手に引き継いでくれます。

便利!

こうすると、キャラクター登録ページでanime情報を表示できるようになり、登録するときにcharacterとの関係も保存されます。

app/views/characters/_form.html.erb

<%= form_for([@anime, @character]) do |f| %> <!--編集(create用URLの作成)-->
  ・・・
  <div class="field">
    <%= f.label :anime_id %><br />
    <%= @anime.name %>                       <!--編集(anime情報の表示)-->
  </div>
  ・・・
<% end %>

方法その2:登録フォームのセレクトボックスでリレーションのidをもらう

例)キャラクター登録ページ /characters/new でガンガン登録する

まず、viewでセレクトボックスを用意して、controllerに情報が渡せるようにします。

app/views/characters/_form.html.erb

<%= form_for(@character) do |f| %>
  ・・・
  <div class="field">
    <%= f.label :anime_id %><br />
    <%= f.select :anime_id, @animes.map{|a| [a.name, a.id]} %> <!--編集(セレクトボックスに変更)-->
  </div>
  ・・・
<% end %>

ちょっとRubyとhtmlなお話。

次に、rake routesで/charactors/newがあることを確認しましょう。そして、以下の編集をします。

app/controllers/characters_controller.rb

def new
  @character = Character.new
  @animes   = Anime.all #追記
  ・・・
end

def create
  @character          = Character.new(character_params)
  @character.anime_id = params[:character][:anime_id] #追記
  ・・・
end

params[:character][:anime_id]と書くと、viewから送られて来た配列をうまい具合に拾ってくれて、データが登録されるというわけです。

便利!


ここまでできること

これだけは駆使してできると思います。がんばってください!


おまけ

$ rails generate model テーブル名 カラム:データ型
$ rake db:migrate
$ rails generate controller コントローラー名s #コントローラー名は複数形で書くこと!

Lesson2はここまでです。おつかれさまでした

This work is licensed under a Creative Commons Attribution-Share Alike 3.0 License

produced by Minami.rb