初学者によるubuntuでのRails + React で駅案内システム作り③～Scaffold機能を使わずCRUD～

# 前回までのあらすじ
API通信が出来るようになったよ！　やったね！

# 記事一覧
- [初学者によるubuntuでのRails + React で駅案内システム作り①～下準備編～](https://qiita.com/Akane-Toribe/items/3366b054e9a0f287f401)
- [初学者によるubuntuでのRails + React で駅案内システム作り②～Rails＋ReactでAPI通信編～](https://qiita.com/Akane-Toribe/items/604234a672ff3982b347)
- [初学者によるubuntuでのRails + React で駅案内システム作り③～Scaffold機能を使わずCRUD～](https://qiita.com/Akane-Toribe/items/fb345e5c59c7958b9e66)
  - 今回はこちら！
- [初学者によるubuntuでのRails + React で駅案内システム作り④～１：１、１：ｍで対応したモデルの登録更新、JSON出力～](https://qiita.com/Akane-Toribe/items/273f315f20d138b7d5e1)

# 新規テーブルのCRUDを実装
前回の記事で駅一覧のCRUDをScaffold機能でサクッと作成しました。確かに便利ではあったんですが、不要なjbuilderファイルが入っていたりとセキュリティの観点からひやひやしたので今回は地道にrailsのコマンドを叩いてModel、View、Controllerを作成していくことにしました。

## 今回作りたいテーブル
駅があればその駅は何かしらの路線に所属しているはずです。例えば根室駅は花咲線(正確には根室本線)に所属していますし、釧路駅は花咲線、根室本線、そして便宜上釧網本線に所属しているといっても良いでしょう。一方で花咲線には根室駅、釧路駅を始めとしていくつかの駅が所属していると考えることもできます。つまり、1：多やm：多ではなく、多：多の関係になっているということになります。
Railsでテーブル間を関連付ける(リレーションを貼る)時に、1：多やその逆だとその相互のテーブルでリレーションを貼ればいいのですが、多：多だと中間テーブルを設けてやる必要が出てきそうです。
また、その路線で何番目の駅かという情報も欲しいですよね？　例えば釧路駅は花咲線の1番目の駅で、根室駅は20番目位の駅、とのように順番づけて登録したいとなるとただの中間テーブルだとよくなさそう。ということで、``has_many through``のアソシエーションを構成するのが一番よさそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752048/fa2b6a08-1b40-2f4a-37a3-d904029352d9.png)

ということはひとまず路線テーブルは先にリレーションを考慮せず作っちゃっても問題なさそう。
## Model作成(路線テーブル)
ということで路線テーブルを作成しました。
```ターミナル:ターミナル
$ rails g model <model名> <カラム名>:<データ型>
```
ここでmodel名は単数形に(linesではなくline)とのこと。
```ターミナル:ターミナル
$ rails g model line name:string name_kana:
string name_english:string
      invoke  active_record
      create    db/migrate/20230121172132_create_lines.rb
      create    app/models/line.rb
      invoke    test_unit
      create      test/models/line_test.rb
      create      test/fixtures/lines.yml
```
今回初めてmodelをコマンドを叩いて作ったので、一応migrationファイルの中身も見ておきました。
```ruby:db/migrate/20230121172132_create_lines.rb
class CreateLines < ActiveRecord::Migration[7.0]
  def change
    create_table :lines do |t|
      t.string :name
      t.string :name_kana
      t.string :name_english

      t.timestamps
    end
  end
end
```
ということで中身も問題なさそうなので
```ターミナル:ターミナル
$ rails db:migrate
== 20230121172132 CreateLines: migrating ======================================
-- create_table(:lines)
   -> 0.0275s
== 20230121172132 CreateLines: migrated (0.0276s) =============================
```
## Controller作成(路線Controller)
お次は路線のCRUDを行うためのControllerを作成します。
```ターミナル:ターミナル
$ rails g controller lines
      create  app/controllers/lines_controller.rb
      invoke  erb
      create    app/views/lines
      invoke  test_unit
      create    test/controllers/lines_controller_test.rb
      invoke  helper
      create    app/helpers/lines_helper.rb
      invoke    test_unit
```
ルーティングはresourcesを使用して一括指定をします。
```ruby:config/routes.rb
Rails.application.routes.draw do
  resources :stations
  # 今回下の1行を追加
  resources :lines
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Defines the root path route ("/")
  # root "articles#index"
  namespace :api, { format: 'json' } do
    namespace :reactapi do #Reactアプリ用のAPI
      resources :stations
    end
  end
end
```

resourcesは非常に便利で、この1行を指定するだけで下のルーティングを一括で指定してくれます。(下のルーティング一覧は抜粋)
```ターミナル:ターミナル
    lines GET    /lines(.:format)                                                 
   lines#index
          POST   /lines(.:format)                                                 
   lines#create
 new_line GET    /lines/new(.:format)                                             
   lines#new
 edit_line GET    /lines/:id/edit(.:format)                                        
   lines#edit
     line GET    /lines/:id(.:format)                                             
   lines#show
          PATCH  /lines/:id(.:format)                                             
   lines#update
          PUT    /lines/:id(.:format)                                             
   lines#update
          DELETE /lines/:id(.:format)                                             
   lines#destroy
```
ということでルーティング指定が終わったところでControllerファイルを編集していきます。
```ruby:app/controllers/lines_controller.rb
class LinesController < ApplicationController
  before_action :set_line, only: %i[ show edit update destroy ]

  # GET /lines
  def index
    @lines = Line.all
  end

  # GET /lines/1
  def show
  end

  # GET /lines/new
  def new
    @line = Line.new
  end

  # GET /lines/1/edit
  def edit
  end

  # POST /lines
  def create
    @line = Line.new(line_params)

    respond_to do |format|
      if @line.save
        format.html { redirect_to line_url(@line), notice: "路線の登録に成功しました." }
      else
        format.html { render :new, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /lines/1
  def update
    respond_to do |format|
      if @line.update(line_params)
        format.html { redirect_to line_url(@line), notice: "路線の更新に成功しました." }
      else
        format.html { render :edit, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /lines/1
  def destroy
    @line.destroy

    respond_to do |format|
      format.html { redirect_to lines_url, notice: "路線の削除に成功しました." }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_line
      @line = Line.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def line_params
      params.require(:line).permit(:name, :name_kana, :name_english)
    end
end
```
## View作成(路線CRUDのView)
いよいよ残るはView、見てくれになります。こちらもフォームや一覧系で共通パーツも作っちゃいます。あとja.yml(Activerecord、View)の追加もお忘れなく。Viewファイルの各ファイルの中身は先にstationsで作ったものを踏襲しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752048/164d14d7-10d0-6c8c-4b83-be133986ffa4.png)
完了したら早速localhost:3000/linesへアクセスしてみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752048/5fa9320a-7b65-273b-8434-3e93681cfd5a.png)
このように無事表示されました！一連のCRUDを行っても特に問題はなさそうです。

# 中間テーブル作成～has_many through アソシエーション～
続いて間に挟むstation_line_relationsモデル周りの作成になります。作成方法はほかのモデルと変わらず、そしてControllerやViewもLineモデル周りの作成で行ったように作っていきました。

ここで作成していて他記事を見ていたのですが、どうやって中間テーブルの登録するんだ？？？となったのでまず試しにstation_line_relationsで一連のCRUDを作成しました。

## アソシエーション設定
さて、アソシエーションの設定周りです。どうも簡単らしい。最近ご無沙汰していたので試しに行ってみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752048/fa2b6a08-1b40-2f4a-37a3-d904029352d9.png)

こちらに従ってリレーションを貼るならば、
```ruby:app/models/station.rb
class Station < ApplicationRecord
  has_many :station_line_relations
  has_many :lines, through: :station_line_relations
end
```

```ruby:app/models/line.rb
class Line < ApplicationRecord
  has_many :station_line_relations
  has_many :stations, through: :station_line_relations
end
```

```ruby:app/models/station_line_relation.rb
class StationLineRelation < ApplicationRecord
  belongs_to :station
  belongs_to :line
end
```

これで、試しに駅と路線関連付け一覧表示で試しにリレーションが表示できるかviewファイルを編集して確認してみます。

```erb:app/views/station_line_relations/index.html.erb
<p style="color: green"><%= notice %></p>

<h1>駅順番一覧</h1>
<%= link_to "新規駅順番登録", new_station_line_relation_path, {class: "btn btn-primary mx-1 col-2 ms-3"} %>
<div class="row ms-2 mt-2">
  <%= link_to "駅一覧", stations_path, {class: "btn btn-primary mx-1 col-2 ms-2"} %>
  <%= link_to "路線一覧", lines_path, {class: "btn btn-primary mx-1 col-2 ms-2"} %>
</div>
<div id="stations">
  <table class="table">
    <thead>
      <th>編集</th>
      <th>駅名</th>
      <th>路線名</th>
      <th>駅順番</th>
      <th>詳細</th>
      <th>削除</th>
    </thead>
    <tbody>
      <% @station_line_relations.each do |station_line_relation| %>
        <tr class="table-info">
          <td>
            <%= link_to "編集", edit_station_line_relation_path(station_line_relation), {class: "btn btn-success mx-1"} %>
          </td>
          <%= render station_line_relation %>
          <td>
            <%= link_to "詳細", station_line_relation, {class: "btn btn-info mx-1"} %>
          </td>
          <td>
            <%= button_to "削除", station_line_relation, {method: :delete, class: "btn btn-danger mx-1"} %>
          </td>
        </tr>
      <% end %>
    </tbody>
  </table>
</div>
```
```erb:_station_line_relation.html.erb
<td class="align-middle"><%= station_line_relation.station.name %></td>
<td class="align-middle"><%= station_line_relation.line.name %></td>
<td class="align-middle"><%= station_line_relation.order_num %></td>
```
この``station_line_relation.line.name``のように書くだけで値が拾ってこれるのは便利。これでいくつかデータを登録してみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752048/75c1b2a9-9869-fe22-e063-6f9fd4ac72a5.png)
良い感じに表示されてますね！
あとはわざわざthroughを設定したからには試してみないと。ということで試しに路線テーブルに編集を加えてみます。

```erb:app/view/lines/_line.html.erb
<td class="align-middle"><%= line.name %></td>
<td class="align-middle"><%= line.name_kana %></td>
<td class="align-middle"><%= line.name_english %></td>
<td class="align-middle"><%= line.name_russian %></td>
<td class="align-middle">
  <table>
    <tbody>
      <% line.stations.each do |station| %>
        <tr>
          <td><%= station.name %></td>
          <td><%= station.name_english %></td>
        </tr>
      <% end %>
    </tbody>
  </table>
</td>
```
あとは路線一覧画面で表示してみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2752048/b1f889d7-c33f-1e1d-4503-f48bacbce50e.png)
良い感じですね！こんな感じでリレーションも含めた一連のCRUDが出来てそうですね。
色々やりたいことはたくさんありますが、今回はこの簡単なCRUD実装とアソシエーション実装までで止めたいと思います。
次回はたぶんフロント側の話になると思います。それではまた！