railsのまとめ5(ミニQ&Aサイトを作るその3)

その4から続きます。

21:リファクタリング

app/views/questions/new.html.erbapp/views/questions/edit.html.erb
の大部分が同じ記述なのでDry原則を破っている
ではどうするか?共通化します。
app/questionsに新規ファイル
_form.html.erbを作成してカットペースト↓
※共通化して呼び出すファイルには_をつけるのがルール

<%= form_with model: @question, local: true do |f| %>
  <div class="form-group">
    <label>Name</label>
<!--テキストフィールドをつける-->
     <%= f.text_field :name, class: "form-control" %>
  </div>
<!--テキストフィールドをつける-->
  <div class="form-group">
    <label>Title</label>
     <%= f.text_field :title, class: "form-control" %>
  </div>
<!--テキストフィールドをつける-->
  <div class="form-group">
    <label>Content</label>
     <%= f.text_area :content, class: "form-control" %>
  </div>
<!--saveボタンを付ける-->
  <div class="text-center">
    <%= f.submit "Save", class: "btn btn-primary" %>
  </div>
<% end %>

app/views/questions/new.html.erbを↓のように編集します。

<div>
  <div class="col-md-4 offset-md-4">
<!--中央に寄せる設定-->
    <h2 class="text-center">New question</h2>
   <%= render 'form' %>
  </div>
 </div>

app/views/questions/_form.html.erb以下の記述にします。

<form action="/questions" accept-charset="UTF-8" method="post"><input name="utf8" type="hidden" value="&#x2713;" /><input type="hidden" name="authenticity_token" value="aAhSPGiLYkFNeq6FyqkvGFyLFpLooxLsNyIdR4+/e+zq8FgqYyMlmNlWgruyu34AHg22g315u/gbC3GMDw63bw==" />
  <div class="form-group">
    <label>Name</label>
<!--テキストフィールドをつける-->
    <input class="form-control" type="text" name="question[name]" id="question_name" />
  </div>
<!--テキストフィールドをつける-->
  <div class="form-group">
    <label>Title</label>
     <input class="form-control" type="text" name="question[title]" id="question_title" />
  </div>
<!--テキストフィールドをつける-->
  <div class="form-group">
    <label>Content</label>
     <textarea class="form-control" name="question[content]" id="question_content">
     </textarea>
  </div>
<!--saveボタンを付ける-->
  <div class="text-center">
    <input type="submit" name="commit" value="Save" class="btn btn-primary" data-disable-with="Save" />
  </div>
</form>


同様にapp/views/questions/edit.html.erbも行います。

<div>
  <div class="col-md-4 offset-md-4">
<!--中央に寄せる設定-->
   <h2 class="text-center">Edit question</h2>
  <%= render 'form' %>
 </div>
</div>

22:質問削除機能の追加

rails routesで確認すると↓
DELETE /questions/:id(.:format) questions#destroy

app/controllers/questions_controller.rbにて↓のように記述します。

def destroy
  @question = Question.find(params[:id])
  @question.destroy
  redirect_to root_path, notice: 'Success!'
end

デストロイアクションが呼ばれるとき、クエスチョンのIDが渡されるので、そのIDを使ってデーターベースから削除したいデータを取得し@questionに代入、
destroyメソッドを呼ぶことで削除して
ルートURLにとばしSuccess!を表示

app/views/questions/index.html.erb
↓のように記述editの下の部分

[ <%= link_to 'Delete', question_path(question),
method: :delete, data:{ confirm: 'Are you sure?'} %> ]</td>

question_path(question)なのはルーティングから
(question)は引数を渡す(id)method: :delete,で削除
data:{ confirm: ‘Are you sure?’}で確認画面を作る

23:質問詳細画面へのリンク

app/views/questions/index.html.erbのtitle.idを↓のように記述します。

<%= link_to question.title, question_path(question) %>
#↓idを渡す必要があるため↑questionのインスタンスを渡している
question GET /questions/:id(.:format) questions#show

質問詳細画面へリンクしていればOKです。

24:回答投稿機能の概要

ユーザーが質問に対して、回答を投稿できる。
ユーザーが回答一覧を閲覧できる。
ユーザーが回答を編集できる。
ユーザーが回答を削除できる。

25:質問詳細画面の実装

app/controllers/questions_controller.rb↓を追加します。

def show
  @question = Question.find(params[:id])
end

app/views/questions/show.html.erbにて↓を記述します。

#rowはコンテナを水平に分割し上から下へと並べます。
<div class="row">
#rowの中にcolを定義しレイアウトを組みます
#画面幅Mediumのグリッド個数12という意味です。
  <div class="col-md-12">
    <h2><%= @question.title %></h2>
  </div>
    Content: <%= @question.content %>
   </div>
  <div>
    Name: <%= @question.name %>
  </div>
<hr>
  <div>
   <%= link_to '> home', root_path %>
  </div>
 </div>
</div>

質問詳細画面が表示されていればOKです。

26:Answersコントローラーを作成します。

rails g controller answers edit

保有するアクションはそのまま編集する画面で使用するedit

↓のようにファイルが自動生成される

create app/controllers/answers_controller.rb
route get 'answers/edit'
invoke erb
create app/views/answers
create app/views/answers/edit.html.erb
invoke test_unit
create test/controllers/answers_controller_test.rb
invoke helper
create app/helpers/answers_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/answers.coffee
invoke scss
create app/assets/stylesheets/answers.scss

重要なのはapp/controllers/answers_controller.rb
app/views/answers/edit.html.erbあとはルーティングなどです。

27:Answerモデルの作成

テーブルの構造 一対多の関係で作ります。

ひとつの質問に対して複数の回答を表示するため、テーブルの構造が一対多になります。

rails g model answer question:references name:string content:text

モデルの名前が単数であることに注意して、
question:references1対多の定義をしています。

生成されたもの↓

invoke active_record
create db/migrate/20210304022906_create_answers.rb
create app/models/answer.rb
invoke test_unit
create test/models/answer_test.rb
create test/fixtures/answers.yml

db/migrate/20210320022906_create_answers.rb

class CreateAnswers < ActiveRecord::Migration[5.2]

  def change
    create_table :answers do |t|
    t.references :question, foreign_key: true
    t.string :name
    t.text :content
    t.timestamps
    end
  end
end

answersテーブルを作成するための定義がRubyでされている

app/models/answer.rbには

class Answer < ApplicationRecord
  belongs_to :question
end
#Answerはquestion↑に紐づく(belong_to)という意味
#answer(答え)から見てquestion(質問)は一つあるという意味

app/models/question.rbに↓記述を追加します。

class Question < ApplicationRecord
  has_many :answers, dependent: :destroy #←追加

 ~~~~~~~~~~~~~以下略
end

questionは複数のanswersを持っているという意味です。
(Yahoo知恵袋のように質問に対して複数の人が質問に答えてくれるから)

dependent: :destroyは親である質問が削除されたら複数の回答も
みんな削除するよという意味です。

変更をデータベースに反映します。

rails db:migrate

エラーが発生しなければOK

28:回答機能関連のルーティング

app/config/routes.rbにて↓のように記述

root 'questions#index'
 resources :questions do
 resources :answers

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~以下略
end
rails routes
↓のようにanswers関連のルーティングが増えているのを確認します。
answers#index
POST /questions/:question_id/answers(.:format) answers#create
new_question_answer GET /questions/:question_id/answers/new(.:format) answers#new
edit_question_answer GET /questions/:question_id/answers/:id/edit(.:format) answers#edit
question_answer GET /questions/:question_id/answers/:id(.:format) answers#show
PATCH /questions/:question_id/answers/:id(.:format) answers#update
PUT /questions/:question_id/answers/:id(.:format) answers#update
DELETE /questions/:question_id/answers/:id(.:format) answers#destroy

29:回答投稿機能

app/controllers/questions_controller.rb↓のように記述します。

def show
  @question = Question.find(params[:id])
  @answer = Answer.new
end

空のインスタンスを作成してインスタンス変数@answerに代入してviewで使えるようにします。

30:回答投稿機能のviewを作成

app/views/answers/show.html.erbで記述します(<hr>タグから)

<hr>
<h3>Post new answer.</h3>
  <%= form_with model: [@question, @answer], local: true do |f| %>
    <%= f.hidden_field :question_id, { value: @question.id} %>
      <div class="form-group">
        <label>Name</label>
      <%=f.text_field :name, class: 'form-control' %>
      </div>
    <div class="form-group">
      <label>Content</label>
        <%= f.text_area :content, class: 'form-control' %>
    </div>
   <div class="text-center">
     <%= f.submit "Post", class: 'btn btn-primary' %>
   </div>
<% end %>

[@question, @answer]questionモデルに紐づくanswerモデルを
フォームで送信したい時は、
form_withのmodelに配列で@questionと@answerのインスタンスを渡します。

<%= f.hidden_field :question_id, { value: @question.id} %>
↑htmlには表示されているけど↑ページに描画されない値としてセットしています。

2つのインスタンスの値はcontrollerのshowの部分で渡しています。

def show
  @question = Question.find(params[:id])
  @answer = Answer.new
end

このようなビューが作成されてるのを確認したらOKです。

次にPostが押されたときの処理を実装します。

その6に続きます。

 

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です