Help us understand the problem. What is going on with this article?

Before Rails Tutorial3章 Scaffold を用いない開発

本投稿は講義資料であり、Rubyの基礎は理解しているが、rails tutorialで躓く読者を対象としています。
1章:環境構築
2章:Scaffold を用いた高速なアプリケーション構築 及び MVCの理解
3章:Scaffold を用いない開発方法 及び 応用
4章:ログイン機能

前章では、Scaffoldを用いて高速でアプリケーションを構築しました。
自動化された部分も多く、現段階では、大まかな理解であるとおもいます。

今回は、route Model View Controller それぞれ自分で作っていきます。
とはいっても、継承まで含めてすべて書くには理解することが膨大で大変ですので、
前回も使用した generate は使用していきます。

まずは、Railsの新しいアプリケーションを作成します。
1章:環境構築の内容を参考に作成してみましょう。

terminal
$ cd ~/rails_app
$ mkdir bukukore
$ cd bukukore
$ bundle init
   (Gemfileにgem "rails","~> 5.2.3"を追加し保存してください)
$ bundle install --path vendor/bundle
$ bin/rails s

http://localhost:3000/ 
にアクセスして表示されていればokです。

$ rails s -p 3100 などとポートを変更することでサーバーを複数起動可能です。
この場合は http://localhost:3100/ にアクセスします。

MVC 復習

前章では、アプリ制作を通して、MVCの流れを理解してきました。

Modelは、データベースとのやりとりを担う
Viewは、表示関連全般を担う
Controllerは、橋渡しの役割を担う

前回、制作したながれを図を見ながら復習していきます。
それぞれの番号が、コードではどうなっていたか思い出しながら見てください。

MVC-02.png

1, ブラウザから /users へのリクエストを Rails sever に送信する
2, router によって、/users は Users controller の index アクションを呼び出す
3, index アクションから、User Model が呼び出される
4, User Model は DataBase からデータを取り出す
5, 取り出したデータを、Controller に返す
6, 受け取ったデータを @users に保存し、 View (indexアクションなので index.html.erb) に渡す。
7, ERBを実行し、@users のデータを含んだ HTMLを生成し、Controller へ返す
8, Controller は、受け取ったHTMLをブラウザにわたす

今回も Model View Controller を構築していくので、適宜図を見返してください。

Controller と View の作成

作成は基本的に generate コマンドを用います。
(gは generateコマンドの短縮形です)
g の後は、controller に続けて、コントローラーの名前(複数形)を書きます。
rails g controller Books index show new
そのうしろ(引数といいます)に書いたものは、同名のviewとアクションが自動生成されます。

tarminal
$ bin/rails g controller Books index show new
     create    app/controllers/books_controller.rb
     route     get 'books/index'
               get 'books/show'
               get 'books/new'
     create    app/views/books
     create    app/views/books/index.html.erb
     create    app/views/books/show.html.erb
     create    app/views/books/new.html.erb

ルーティングが生成されているので、確認してみましょう。

terminal
$ bin/rails routes
  Verb  URI             Pattern
  GET   /books/index    books#index
  GET   /books/show     books#show
  GET   /books/new      books#new

これは具体的には、 http://localhost:3000/books/index
というURLに対するリクエストがあれば、 books_controller の index アクション を呼び出すというものでした。
今回アクションの中身が空なので、Model は呼び出されず、単に対応するView (index.html.erb) が実行されるだけです。

ルーティングがうまくいってみるか確認するために、試しにアクセスしてみましょう。
http://localhost:3000/books/index

Books#index
Find me in app/views/books/index.html.erb

と表示されているとおもいます。

Viewの変更

erbの変更は前回行ったとおりですね。とてもシンプルなものでした。
Books#index の部分を、全蔵書一覧などに変更しておきましょう。

app/views/books/index.html.erb
<h1>全蔵書一覧</h1>

 erb の中身がシンプルすぎて不思議な方もいらっしゃるかもしれません。
 【気になって次にすすめない人向けの解説】
  app/views/layouts/application.html.erb を開いてください。
 コード内の、<%= yield %> の部分に、 index.html.erb が移植されて HTML を生成しています。 
 いまはこの程度の理解で問題ありません。 https://railstutorial.jp/ でより深く理解できます。

Controllerの変更

コントローラーの中にアクションが本当に作られているかも確認してみましょう。
app/controllers/books_controller.rb を開いてみてください。

app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
  end

  def show
  end

  def new
  end
end

データベース内のデータを取り出すには、Controllerのアクションから
Model を呼び出せば良いのでした。

app/controllers/books_controller.rb
class BooksController < ApplicationController
  def index
  end

  def show
   @book = Book.find(1)
  end

  def new
  end
end

このままではエラーがでます。(Modelも、本の情報も作っていないので)
generate コマンドを使用して、Model を作成していきます。

Model の作成

booksテーブルには、前章と同じカラムを持たせていきます。

terminal
$ bin/rails g model Book country:text title:text author:text year:integer user_id:integer comment:text
      create    db/migrate/20200202020202_create_books.rb
      create    app/models/book.rb

g の後は、model に続けて、Model名(単数形)を書きます。
データベースにテーブルを作成するには migrate を実行するのでしたね。

terminla
$ bin/rails db:migrate
== 20200202020202 CreateBooks: migrating ======================================
-- create_table(:books)
   -> 0.0008s
== 20200202020202 CreateBooks: migrated (0.0009s) =============================

補足. migrate はマイグレーションファイルを実行しています。
db/migrate/(作成日時)_create_books.rb というファイルです。
ひらいてみると、下記のようなコードがあると思います。

class CreateBooks < ActiveRecord
 def change
   create_table :books do |t|
    t.text :country
    t.text :title
    t.text :author
    t.integer :year
    t.integer :user_id
    t.text :comment
    t.timestamps
   end
 end
end

上記は text型であるcountryというカラム(他6つ)を持つ books テーブルを作成
せよという意味です。 change 意外に up down などもあります。詳細は下記サイトなどを参照ください
https://www.sejuku.net/blog/14229

これでbooksテーブルが作成されました。

テーブルは作成したけども、本の情報はまだなにもいれてませんね。
初期データをいれるには db/seeds.rb ファイルを使用します。
( seed = 種 という意味です )
大量のデータを入力するのは手間なのでseed.rbを作成していきます。
(そもそもブラウザからデータをいれる機能もまだ作っていません)

下記の seed ファイルを実行すると、bookテーブルに本の情報が3件保存されます

db/seeds.rb
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save

@book = Book.new
@book.country = "jp"
@book.title = "人間失格"
@book.author = "太宰治"
@book.year = 1948
@book.user_id = 2
@book.comment = "すばらしかった"
@book.save

@book = Book.new
@book.country = "gb"
@book.title = "Alice's Adventures in Wonderland"
@book.author = "Lewis Carroll"
@book.year = 1865
@book.user_id = 1
@book.comment = "Amazing"
@book.save

seed ファイルは、下記のコマンドで実行できます。

terminal
$ bundle exec rake db:seed

データが保存されたかの確認は。rails c でコンソールを起動すると確認できます。

railsConsole
@book = Book.find(1)

ブラウザ(show)で表示してみましょう。

app/view/books/show.html.erb
<p>本のタイトル: <%= @book.title %></p>
<p>著者: <%= @book.author %></p>

本のタイトル: 吾輩は猫である

著者: 夏目漱石

と表示されていればOKです。

課題1,

@book はどこで定義したか、再確認してください。

URLからパラメーターの取得

本のデータを取り出して表示するところまでうまくいきました!
すごい進歩です!

ただ、まだ不満点があります。
それは、 id:1 の本のデータしか表示できていない点です。
理由は、Contoroller の show アクションをみると、
@book = Book.find(1)
となっているためです(id:1 の本の情報しか見つけてきていない)

ここをidパラメーターが1以外も入力できるように変更するにはどうすればよいでしょうか。
@book = Book.find(params[:id])
とすることで、1以外のパラメーターを見つけてこれます。

app/controllers/books_controller.rb
 def show
    @book = Book.find(params[:id])
 end

パラメーター(数値)は、どこで指定すればよいでしょうか。
URL にパラメーターを含めて渡すテクニックがあるので覚えておきましょう。
例)ID:2の本の情報を取得したい場合 http://localhost:3000/books/show/2
  このままではルーティングエラーが表示されます。
  books/show へのリクエストに関しては記述していますが、
  books/show/(数字) へのリクエストについてはなにも書いていない為です。

config/routes.rb を開いてください。
URLに含まれる数字をパラメーターとして取得するには、下記のように記述します。

config/routes.rb
#   get 'books/show'(# でコメントアウトしてます)
get 'books/show/:id', to:'books#show'

こうすることで /books/show/2 の 2の部分が、:id パラメーターとして利用可能になります。
( 利用するときは、params[:id] とかきます)

to: 'books#show' 
books Cコントローラーの show アクションを呼び出す指示になります。

これで、任意のidの本情報を取得できるようになりました。

http://localhost:3000/books/show/1
http://localhost:3000/books/show/2

Model 検索

いま本の情報の取り出しに id を利用していますが、
id 以外のカラムを用いて取り出す方法も覚えておくと便利です。

コンソールを起動してください。 ( rails c )
下記を参考に色々と試してみてください。

railsConsole
>> Book.all
   #bookテーブルのすべてのデータを取得します。
>> Book.find_by(title: "人間失格")
   #条件に合ったデータを、1件だけ取得します。
>> Book.where(country: "jp")
   #条件に合ったデータを、すべて取得します。
>> Book.where(country: "jp").where(year: 1905)
   #AND検索。 和書で 1905年出版の本を取得しています。
>> Book.where("(country = ?) OR (year = ?)","jp",1905)
   #OR検索。 和書 または 1905年出版の本を取得しています。
>> Book.order(create_at: :asc)
   #並び順を指定します。 作成日時の昇順で取得しています。
>> Book.where(user_id: 1).order(id: :desc)
   #user_id 1さんの蔵書を、idの降順で取得しています。 

where の利用

検索条件を学んだのでこれ使って、蔵書管理アプリケーションらしく
個人の蔵書を一覧表示する機能を構築してみましょう。

app/controllers/books_controller.rb
def show
#   @book = Book.find(params[:id]) コメントアウトしました。
    @book = Book.where(user_id: params[:id])
end

user_id が パラメーター番号の人の蔵書データをすべて取得して@bookに格納しています。
View も編集していきましょう。 @book のなかに複数のデータがある場合(連想配列)
each を用いて、順に表示させていくのでしたね。(2章の復習です)

app/views/books/show.html.erb
<% @book.each do |book| %>
  <p>本のタイトル: <%= book.title %></p>
  <p>著者: <%= book.author %></p>
<% end %>

http://localhost:3000/books/show/1
にアクセスしてみると、 user_id:1 になっている本のデータがすべて表示されました。

 本のタイトル: 吾輩は猫である
 著者: 夏目漱石

 本のタイトル: Alice's Adventures in Wonderland
 著者: Lewis Carroll

【補足】「あれ? has_many とかつけなくてよいんだっけ?」
と思った方は、前章の内容を覚えてくれていますね。
ただ少し勘違いがあります。 has_many,belongs_to は異なるテーブルの紐付けに使います。
今回は、 Book Modelを経由し、Bookテーブルだけにアクセスしているので不要です。
User Model から、Bookテーブルのデータを取り出すならば必要になってきます。

データ登録フォームの作成

さて、データベースから情報を取り出したり加工したりする方法は覚えました。
しかしこのままではユーザーは、本を登録することができません。

本を登録するための機能を作成する方法を学んでいきましょう。

登録形式は、フォーム。
URL は http://localhost:3000/books/new
とします。

form_for

データベースに新しいデータを保存したいので、 Model を呼び出しましょう。
場所はどこに書きましょう。
new.html.erb に登録フォームを置くので、 new アクションの中ですね。
新しいデータを登録するときは、 Model名.new とします。下記のコードを追記してください。

app/controllers/books_controller.rb
  def new
    @book = Book.new
  end

new.html.erb に 入力フォームを作成していきましょう。
rails では、フォームを簡単につくれる form_forヘルパーというものがあります。

app/views/books/new.html.erb
<%= form_for(@book) do |f| %>
  <%= f.label :country %>
  <%= f.text_field :country %>

  <%= f.label :title %>
  <%= f.text_field :title %>

  <%= f.label :author %>
  <%= f.text_field :author %>

  <%= f.label :year %>
  <%= f.number_field :year %>

  <%= f.label :user_id %>
  <%= f.number_field :user_id %>

  <%= f.label :comment %>
  <%= f.text_area :comment %>

  <%= f.submit "送信" %>
<% end %>

form_for に関する詳しい使い方は、こちらのサイトで学習してください。
form_forの使い方を徹底解説!

ここでは重要な部分だけ、簡単に解説します。
上の form_forヘルパーにより、下記のような HTML が生成されます。(少しシンプル化してます)

new.html
<form class="new_book" id="new_book" action="/books" method="post">
   <label for="book_country">Country</label>
   <input type="text" name="book[country]" id="book_country" />

   <label for="book_title">Title</label> 
   <input type="text" name="book[title]" id="book_title" /> 

   <label for="book_author">Author</label> 
   <input type="text" name="book[author]" id="book_author" /> 

   <label for="book_year">Year</label> 
   <input type="number" name="book[year]" id="book_year" /> 

   <label for="book_user_id">User</label> 
   <input type="number" name="book[user_id]" id="book_user_id" /> 

   <label for="book_comment">Comment</label> 
   <textarea name="book[comment]" id="book_comment">  </textarea> 

   <input type="submit" name="commit" value="送信" /> 
 </form> 

ここで重要な属性は、
< form class="new_book" id="new_book" action="/books" method="post" >
の中の、 action="/books"method="post" の2つです。

この2つは送信ボタンが押されたら、
/books に対して、 POST リクエストを送信する
という指示をしています。

なので、 /books 対して、 POST リクエストがあった場合の処理を書いていきましょう。
送信したあとは、books Controller の create アクションに飛ばしたいです。
(のちほどデータベースに保存する処理を create アクションに書いていきます)

post 'books', to: 'books#create'
コードはこのようになるので、 routes.rb に追記しましょう。
(ルーティング関係は、 routes.rb でしたね)

config/routes.rb
Rails.application.routes.draw do
  get 'books/index'
  get 'books/show/:id', to: 'books#show'
  get 'books/new'
  post 'books', to: 'books#create'
end

index と new は to: が書かれていませんね。
前章でも説明したとおり、Railsは、アクションが呼び出されると、
自動的に アクション名.html.erbが呼び出されるので、
下記と同じ意味になります。
get 'books/index' to: 'books#index'
get 'books/new', to: 'books#new'

1,登録ボタンが押される
2,フォームに入力されたデータを、create アクションに飛ばす

という流れで、データが動いているので最後に、create アクションの中で
情報をデータベースに保存する処理を書きます。Modelを呼び出します。

書き込みの仕方は、seed.rb でもやりましたね。

seed.rb
@book = Book.new
@book.country = "jp"
@book.title = "吾輩は猫である"
@book.author = "夏目漱石"
@book.year = 1905
@book.user_id = 1
@book.comment = "おもしろかった"
@book.save

 【備考】
 ちなみに、このままなにも受け取り処理を書かずに情報を送信してみるとどうなるでしょうか。
 試しに、本の情報をフォームに入力し送信してみると、なにも動きません。

 送信しているので、フォームから パラメーターは飛んでいます。
 このパラメーターですが、実は見ることが出来るんです。

 rails s を起動している、 terminalをみてください。
 いつの間にか、いろんな文字が出力されていますね。
 サーバーが受け取ったデータなどの情報が表示されています。
 この先、困ったときにここをみると解決できることがあると思います。

 "どういったデータがどういう形式で動いているかは、サーバーのログをみる"
 自分でサービスをつくるのに、重要なテクニックなので覚えておいてください

 

 
サーバーログの一番下をみてみましょう。(下図は、ややシンプル化しています)

terminal
Started POST "/books"
Processing by BooksController#create as HTML
  Parameters: {"book"=>{"country"=>"jp", "title"=>"吾輩は猫である", "author"=>"夏目漱石",
               "year"=>"1905", "user_id"=>"1", "comment"=>"おもしろかった"}}

パラメーターをみると、二重の連想配列になっているのがわかると思います。
( parameters{} の中に book{} が入っている )

通常の連想配列の場合は、params[:title] として取得できていました。
二重の連想配列の場合は、params[:book][:title] とすることで取得できます。
bookキー の中のtitleキー のデータを取得しています )
 連想配列について詳しくはこちらを参照してください Array(配列)とHash(連想配列)入門

さて、ここまで理解できていれば create アクションの中身も想像できると思います。
( seed.rb の、本の情報が書かれていた部分をパラメーターの中身に書き換えるだけです )

app/controllers/books_controller.rb
  def create
    @book = Book.new
    @book.country = params[:book][:country]
    @book.title = params[:book][:title]
    @book.author = params[:book][:author]
    @book.year = params[:book][:year]
    @book.user_id = params[:book][:user_id]
    @book.comment = params[:book][:comment]
    @book.save
    redirect_to '/books/show/1'
  end

最後の、redirect_to '/books/show/1' の部分ですが、
登録ボタンを押したあとに、画面遷移してほしいので、リダイレクト先を指定しました。

http://localhost:3000/books/new ここで本の情報を登録
http://localhost:3000/books/show/1 ここで登録内容を確認
(Userの欄を、1以外にした人は、該当ユーザーのページを開いてください)

課題

1, show ページに、タイトルと著者以外の情報も表示してください。
2, リダイレクト先を、 http://localhost:3000/books/index に変更してください。
3, index ページに、全蔵書のタイトルを表示させてださい。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした