Day 2 ~ 8:Scaffold なしでの掲示板作成

post on
post cover image

from Qiita:

使用環境

  • 仮想環境OS: Ubuntu 18.04
  • Ruby:2.51
    • Rails:5.2.2

rails db:migrate

  • Railsドキュメントより(http://railsdoc.com/references/rake%20db:migrate)
    • rails db:migrateを実行
    • schema_migrationsテーブルを調べ、存在しなければ作成
    • db/migrateディレクトリ内のすべてのマイグレーションファイルを調べる
    • データベースの現在のバージョンと異なるバージョンがあった場合、データベースに適応
    • schema_migrationsテーブルの更新

3日目

scaffoldを利用せずにApp作成をし、Scaffoldの有難みを知る

前準備

  1. rails s new qiita_routes -d mysql
  2. Gemfileのminiracerコメントインして、bundle install
  3. config/database.ymlのpassword情報編集
  4. rails db:create

前提:知識

ページ作成に必要なもの

  • view(@ /app/views/コントローラ名/
    • 今日はしなかったので、今投稿には未記載
    • viewの中身がブラウザに表示される内容
  • controller(コントローラ
    • ページ表示の際、controllerを経由して、viewをブラウザに返す。
    • controllerで設定したactionは、controllerと同じ名前のviewフォルダの中から、actionと同じ名前のhtmlファイルを探してブラウザに返します(まだ理解しきれていない気がする。
  • routing(ルーティング
    • ブラウザとcontrollerを繋ぐ。

ページ表示の流れ

Routing => Controller => Model => View
modelはデータベース情報が必要な時だけ使用.今回は必要ではないので、とばす。

本段階

controllerを作成

#rails generate controller コントローラ名 (+アクション名)
rails generate controller Users

routingの設定:ブラウザとコントローラをつなぐ

# config/routes.rb
Rails.application.routes.draw do
  get 'users', action: :index, controller: 'users'
end
rails routes

# Prefix Verb URI Pattern
# Controller#Action
# users GET  /users(.:format)   #追加された行
# users#index            #追加された行

controller:modelとviewをつなぐ

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    render plain: 'Hello'
  end
end

無事に、ブラウザ上でHelloが表示された。

renderメソッド

上controller編集時に用いた、renderメソッドは実際に画面に表示される内容を生成する。今回のrenderのplainオプションを指定すると、文字列を直接表示できる。
Railsのcontrollerでrenderを省略すると、代わりにapp/views/コントローラ名/アクション名.html.erbを用いる
=> ということはcontroller作成コマンドは rails g controller コントローラ名 アクション名

参照: Ruby on Rails でページを作成する仕組み by @np_misaki氏

  • config : アプリケーションの設定情報を格納する
  • /routes.rb : ルーティングの設定を行う
  • /locales : 辞書ファイル(グローバル対応等)
  • /app : アプリケーション開発中にメインで使用するディレクトリ
  • /controllers..Controller クラスを格納する
  • /models : Modelクラスを格納する
  • /views :View クラスを格納する

4日目

モデルを作成

  • modelとは
    • データベースを操作する。
    • app/models下に配置される
    • データベースに含まれるテーブル毎に用意され、データの登録・取得・更新・削除などを行う

model作成コマンド

# rails generate model モデル名 カラム名:データ型 カラム名:データ型 ...
rails generate model User name:string email:string sex:integer age:integer address:integer attendance:integer opinion:text
# string型は文字型、integer型は整数型

DBの操作

テーブルの作成をする。

rails db:migrate

出来たテーブルをMySQL側で確認してみる。

-- mysql
USE scanashi0307_development;
-- Reading table information for completion of table and column names
-- You can turn off this feature to get a quicker startup with -A

-- Database changed
-- mysql> SHOW TABLES;
-- +------------------------------------+
-- | Tables_in_scanashi0307_development |
-- +------------------------------------+
-- | ar_internal_metadata               |
-- | schema_migrations                  |
-- | users                              |
-- +------------------------------------+
-- 3 rows in set (0.00 sec)

-- usersテーブルが作成されたことが分かる。
SHOW CREATE TABLE users;

-- | users | CREATE TABLE `users` (
--   `id` bigint(20) NOT NULL AUTO_INCREMENT,
--   `name` varchar(255) DEFAULT NULL,
--   `email` varchar(255) DEFAULT NULL,
--   `sex` int(11) DEFAULT NULL,
--   `age` int(11) DEFAULT NULL,
--   `address` int(11) DEFAULT NULL,
--   `attendance` int(11) DEFAULT NULL,
--   `opinion` text,
--   `created_at` datetime NOT NULL,
--   `updated_at` datetime NOT NULL,
--   PRIMARY KEY (`id`)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 |

rails g modelsで設定したカラム名が作成されているのが分かる。

データベースにfooさんのレコードを追加してみる

INSERT INTO `users` (`name`, `email`, `sex`, `age`, `address`, `attendance`, `opinion`, `created_at`, `updated_at`) VALUES ('foo', 'foo@gmail.com', 1, 23, 2, 0, 'foooo', '2017-04-04 04:44:44', '2018-04-04 04:44:44');
-- Query OK, 1 row affected (0.00 sec)

MySQLの中から、追加されているレコードを確認してみる。

SELECT * FROM users;
-- +----+------+---------------+------+------+---------+------------+---------+---------------------+---------------------+
-- | id | name | email         | sex  | age  | address | attendance | opinion | created_at          | updated_at          |
-- +----+------+---------------+------+------+---------+------------+---------+---------------------+---------------------+
-- |  1 | foo  | foo@gmail.com |    1 |   23 |       2 |          0 | foooo   | 2017-04-04 04:44:44 | 2018-04-04 04:44:44 |
-- +----+------+---------------+------+------+---------+------------+---------+---------------------+---------------------+

rails console から新たにレコードを追加する。

user = User.create(name: "taro", email: "val@gmail.com", sex: 0, address: 1, attendance: 1, opinion: 'nothing special')

#user.saveでDBに保存する
user.save
# => true

Rails Console上でレコード取得

User.all # get all users from record
User.all.last # get last added user from record

first_user = User.all.first # get all users and then get the first user
first_user.destroy # delete the first user

user = User.all.first
user.name = "ichitaro" # overwrite the name with 'ichitaro
user.save # save the data

User.all.count # count the number of all users
User.find_by(id:2) # get a user with id:2

controller

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
   end
end

view

# app/view/index.html.erb
 <body>
    <h1>Users</h1>
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Email</th>
          <th>Sex</th>
          <th>Age</th>
          <th>Address</th>
          <th>Attendance</th>
          <th>Opinion</th>
          <th colspan="3"></th>
        </tr>
      </thead>

      <tbody>
        <% @users.each do |user| %>
          <tr>
            <td><%= user.name %></td>
            <td><%= user.email %></td>
            <td><%= user.sex %></td>
            <td><%= user.age %></td>
            <td><%= user.address %></td>
            <td><%= user.attendance %></td>
            <td><%= user.opinion %></td>
          </tr>
        <% end %>
      </tbody>
    </table>
  </body>
# app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    # 割愛
  </head>
  <body>
    <%= yield %>
  </body>
</html>

ルーティングを変更

# app/config/routes.erb
resources :users

rails routes実行

# =>
#  Prefix Verb   URI Pattern Controller#Action

#     users GET    /users(.:format)            users#index
#           POST   /users(.:format)            users#create
#   new_user GET    /users/new(.:format)        users#new
# edit_user GET    /users/:id/edit(.:format)   users#edit
#       user GET    /users/:id(.:format)        users#show
#           PATCH  /users/:id(.:format)        users#update
#           PUT    /users/:id(.:format)        users#update
#           DELETE /users/:id(.:format)        users#destroy
# ...

次に上にある、「users GET /users(.:format) users#index 」を実装
UserControllerの中にshowアクションを作成

# app/controllers/users_controller.rb
class UsersController < ApplicationController
    def index
        @users = User.all
    end
# 下が追加したshowアクション
    def show
    end
end
# app/views/users/index.html.erb
# user.nameのラインを下の様に変更
<td><%= link_to user.name, user_path(user.id) %></td>
# <% link_to ("A"."/B") %>
# 上はhtml上で <a href="/B">A</a>に変化する。

この時点でrails sで立ち上げると

scaffold first

showアクション

  • users_pathはusers#indexへのリンク
  • new_user_pathはusers#newへのリンク
  • edit_user_pathはusers#editへのリンク
  • user_pathはusers#showへのリンク
# app/views/users/show.html.erb
<p id="notice"><%= notice %></p>
<p>
  <strong>Name:</strong>
  <%= @user.name %>
</p>
<p>
  <strong>Email:</strong>
  <%= @user.email %>
</p>
# 割愛

<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>

show, edit アクションの定義

# app/controllers/users_controller.rb
def show
  @user = User.find params[:id]
end

def edit
  @user = User.find(params[:id])
end
# app/views/users/edit.html.erb
<%= form_with(model: @user, local: true) do |form| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this @user from being saved:</h2>
      <ul>
        <% @user.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>
  <div class="field">
    <%= form.label :email %>
    <%= form.text_field :email %>
  </div>
  <div class="field">
    <%= form.label :sex %>
    <%= form.number_field :sex %>
  </div>
  # 割愛
<% end %>

追加:2日目を参考にし、表示を触ってみる。

性別の値 0 or 1 を男性or女性で表示させる。

# app/models/user.rb
class User < ApplicationRecord
    enum sex: { male: 0 ,female: 1}
end

男性、女性で表示されるようになった。だが、editページは、テキスト入力のままだ。

male / female enum

ラジオボタンに変更

# app/views/users/edit.html.erb
<div class"field">
    <%= form.label :sex %>
    <%= form.radio_button :sex, 'male' %>男性
    <%= form.radio_button :sex, 'female' %>女性
</div>

同様に、年齢、住所、参加不参加もラジオボタンにしておく。

change to radio button
user = User.find_by(name:"foo") # get a user named 'foo'
user.age = 0 # rewrite the user's data about age with 0
user.save
User.find_by(name: "foo") # confirm
# =>
# <User id: 1, name: "foo", email: "foo@gmail.com", sex: "女性", age: 0, address: "日本以外", attendance: "参加", opinion: "foooo", created_at: "2017-04-04 04:44:44", updated_at: "2019-03-08 00:07:47"

年齢のラジオボタンを追加

changed to radiobutton

8日目

users_controller

# app/controllers/uupdate.rb
def update
    @user = User.find params[:id]
    if @user.update(params.require(:user).permit(:name, :email, :sex, :age, :address, :attendance, :opinion))
      respond_to do |format|
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
      end
    else
      respond_to do |format|
        format.html { render :edit }
      end
    end
end

def destroy
  @user = User.find params[:id]
  @user.destroy
  respond_to do |format|
    format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
  end
end

def new
  @user = User.new
end

indexページからのdestroyへのリンク作成

# app/views/users/index.html.erb
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>

newページ編集

# app/views/users/new.html.erb
<h1>New User</h1>
<%= form_with(model: @user, local: true) do |form| %>
  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:</h2>
      <ul>
      <% @user.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>
  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>
  <div class="field">
    <%= form.label :email %>
    <%= form.text_field :email %>
  </div>
  # 割愛
  
<% end %>
<%= link_to 'Back', users_path %>

indexからnewへのリンク作成

# app/views/users/index.html.erb
<%= link_to 'New User', new_user_path %>

createアクション定義

# app/controllers/users_controller.rb
def create
    @user = User.new(params.require(:user).permit(:name, :email, :sex, :age, :address, :attendance, :opinion))
    if @user.save
      respond_to do |format|
        format.html { redirect_to @user, notice: 'User was successfully updated.' }
      end
    else
    respond_to do |format|
      format.html { render :edit }
    end
  end
end

リファクタリング

wikipedia

リファクタリング (refactoring) とは、コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。

createアクションとupdateアクションの共通化

2アクションに下の共通箇所がある

# app/controllers/users_controller.rb
@user = User.new(params.require(:user).permit(:name, :email, :sex, :age, :address, :attendance, :opinion))

リファクタリング後

# app/controllers/users_controller.rb
def create
  @user = User.new(user_params)
  if @user.save
    respond_to do |format|
      format.html { redirect_to @user, notice: 'User was successfully updated.' }
    end
  else
    respond_to do |format|
      format.html { render :edit }
    end
  end
end

def update
  @user = User.find params[:id]
  if @user.update(user_params)
    respond_to do |format|
      format.html { redirect_to @user, notice: 'User was successfully updated.' }
    end
  else
    respond_to do |format|
      format.html { render :edit }
    end
  end
end

private

def user_params
    params.require(:user).permit(:name, :email, :sex, :age, :address, :attendance, :opinion)
  end
end

show. edit. updata, destroyの共通化

# app/controllers/users_controller.rb
# 共通している部分
@user = User.find params[:id]

# 共通化した結果
# set_user追加
def set_user
  @user = User.find params[:id]
end

# before_action追加
# show, edit, update, destroyアクションの前に、必ず実行の意
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

アクションのリファクタリング後(全体)

# app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

def index
    @users = User.all
end
def show; end
def edit; end
def update
  if @user.update(user_params)
    respond_to do |format|
    format.html { redirect_to @user, notice: 'User was successfully updated.' }
  end
  else
    respond_to do |format|
      format.html { render :edit }
    end
  end
end
def destroy
  @user.destroy
  respond_to do |format|
    format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
  end
end
def new
  @user = User.new
end
def create
  @user = User.new(user_params)
  if @user.save
    respond_to do |format|
      format.html { redirect_to @user, notice: 'User was successfully updated.' }
    end
  else
    respond_to do |format|
      format.html { render :edit }
    end
  end
end

private
  def set_user
    @user = User.find params[:id]
  end
  def user_params
    params.require(:user).permit(:name, :email, :sex, :age, :address, :attendance, :opinion)
  end
end