帳票をRailsで作る過程の試行錯誤

# はじめに
ユーザー ⇄ SIer ⇄ 開発者（←私）　といった立ち位置で、お客さんが業務で使ってるエクセルの情報なんかをうまいことアプリケーションにしましょうというよくありそうなプロジェクト。

```
環境
ruby '2.5.3'
rails '5.2.2'
```

# なにをしたかったのか

こういう帳票をWebアプリケーションで扱えるようにモデルを開発

<img width="1298" alt="スクリーンショット 2019-06-24 1.56.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/1d05c65a-b163-b61e-648e-2c03fcff8075.png">

・チームは大会に紐づいている
・チームに対して、戦士/魔法使い/盗賊のメンバーを追加でき、横一列で確認できる
・ユーザーはこの形式で慣れ親しんでおり、ここから大きく変更することができない
・チームのメンバーを戦士/魔法使い/盗賊で複数人追加することもできる、その場合、一行目は空白で二行目にだけ入力があるといったケースも存在する。
・一行目は空白で二行目にだけ入力があるといったケースのとき、表示は入力された通りにしたい。

## 設計その１

イメージ
<img width="1125" alt="スクリーンショット 2019-06-24 14.08.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/2c357da8-041a-41be-1888-076e768b9a21.png">

ざっくりとしたテーブル定義
<img width="596" alt="スクリーンショット 2019-06-24 14.31.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/712a7ec8-829f-38f0-ce09-cff972c91bab.png">

モデル定義

```ruby
class Tournament < ApplicationRecord
  has_many :teams, -> { order('team.order') }
  has_many :rows, through: :team
  has_many :warriors, through: :row
  has_many :wizards, through: :row
  has_many :thiefs, through: :row

  accepts_nested_attributes_for :teams
  accepts_nested_attributes_for :warriors
  accepts_nested_attributes_for :wizards
  accepts_nested_attributes_for :thiefs
end
```

```ruby
class Team < ApplicationRecord
  belongs_to :tournament
  has_many :rows, dependent: :destroy
end
```

```ruby
class Row < ApplicationRecord
  belongs_to :team
  has_one :warrior, dependent: :destroy
  has_one :wizard, dependent: :destroy
  has_one :thief, dependent: :destroy
end
```

```ruby
#wizard, fhief も同様
class warrior < ApplicationRecord
  belongs_to :row
end
```

このモデル定義で以下のように検索がかけられる

```ruby
tournament.warriors.max(:hit_point) #=> 大会に参加してる戦士の中でのmaxのHP
tournament.warriors.sum(:hit_point) #=> 大会に参加してる戦士の合計のHP
```

View側はfields_forでnestする

```ruby
= form_with model: @tournament, url: tournament_path(@tournament), html: { role: "form"} do |f|
  = f.fields_for :teams do |team|
    # team のインプット
  = f.fields_for :warriors do |warrior|
    # warrior のインプット
  = f.fields_for :wizards do |wizard|
    # wizard のインプット
  = f.fields_for :thiefs do |thief|
    # thief のインプット
```

Controller側では、モデルのaccepts_nested_attributes_forを利用してtournamentのupdate_attributesに引っ掛けて、関連するteam, warriors, wizards, fhiefsすべての値が更新される

```ruby
def update
  @tournament.update_attributes(params)
end
```

メリット
・viewとcontrollerがめちゃくちゃスッキリしている
・rowとwarrior, wizard, thiefを has_oneでリレーションしているので1行に複数データを追加できない
・大会のモデルから参加チームすべてのwarrior, wizard, thiefを横串で確認できる

## 設計その2
設計その１を提案したところ、以下のフィードバックを受けたので設計を修正した
・テーブルの一覧性が悪い
・戦士、魔法使い、盗賊はそれぞれに似ている項目があるので、ひとつのテーブルにまとめる

<img width="1118" alt="スクリーンショット 2019-06-24 14.54.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/485e07cf-25f4-a9bf-6941-4d19f757a2fe.png">

テーブル定義
<img width="418" alt="スクリーンショット 2019-06-24 14.57.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/bac6a43a-e1d6-5f92-f769-14f97a06169f.png">

魔法使いの専用カラムや盗賊用の専用カラムができてしまうが、単一テーブル継承で設計し直した

## 設計その3
設計その2を提案したところ、以下のフィードバッグを受けた
・INSERT文を書くときに、typeによって、どこのカラムを利用するのかを変えないといけなくて面倒なので、HP、攻撃力、防御力、MP、盗み成功率を、単位1,値1 ~ 単位4, 値4まで持たせて自由にかけるようにして欲しい（INSERT文...書く気なのか...）

<img width="378" alt="スクリーンショット 2019-06-24 15.06.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27192/d4cf48e8-7302-f52a-781b-f8a86a1492d5.png">

しかも、
魔法使いはkey_1にHP、key_2にMP、key_3に攻撃力、key_4に防御力
と固定化できず、
MPが入力されてない場合は、key_1にHP、key_2に攻撃力、key_3に防御力
その状態で保存し、さらにMPを追加した場合は、key_1にHP、key_2に攻撃力、key_3に防御力、key_4にMPが爆誕するという...

まだ試行錯誤中だが、DBの値からハッシュ構造を作り、対応させようかと考え中

```ruby
member.point
=> {
  member.key_1: member.val_1,
  member.key_2: member.val_2,
  member.key_3: member.val_3,
  member.key_4: member.val_4
}
```

```ruby
const HP = 1
member.point[HP]
```

```ruby
member.attribute_for_update
=> {
 member.key_1: ‘val_1’.to_sym,
 member.key_2: ’val_2‘.to_sym,
 member.key_3: ‘val_3’.to_sym,
 member.key_4: ‘val_4’.to_sym
}
member.update_attributes = { member.attribute_for_update[HP] => 200 }
```

最終的にどんな実装になるやら。。。