Railsを久しぶりに触ったので整理してみた

## 概要
開発時に使用するメモを整理しました。

## Rail APIモードでの始め方
1. `bundle init`
2. `gemfileを修正`
3. `bundle install`
4. `rails new . --api -d postgresql`
5. `gibo dump Rails macOS >> .gitignore`
6. `rails db:create`

5番のgiboは.gitignoreを簡単に充実してくれるパッケージ。
`vendor/bundle`や`.DS_store`などをよしなに設定してくれる
`brew`経由でinstallした気がする 興味があれば調べてみてください



## Model
DB関係の処理を担う

## 新しいモデル・テーブルを作成
```rb
# 引数を与えてmigrationの中身を埋める
rails g model User[任意のモデル名] name:string age:integer

# Modelを作成
rails g model user(任意のモデル名)

# 間違えたら下記で削除
rails d model user(任意のモデル名)
```

## バリデーション
```rb
# validates :カラム名, ルール

# not null
validates :content, {presence: true}

# booleanが空ではない
validates :bool, inclusion: { in: [true, false] }

# チェックボックス
validates :gender, {acceptance: true}

# 一意性(ユニーク)
validates :user_name, uniqueness: true

# 2つのカラムが一致していることを確認
validates :email, confirmation: true
```

```rb:長さ
# 最大140文字(140文字以下)
validates :content, {length : {maximum:140} }

# 50文字以上
validates :content, {length : {minimum:50} }

# 1文字以上75文字以下
validates :content, {length: {in: 1..75} }

# 5文字のみ
validates :content, {length: {is: 5} }
```

```rb:含まれる or 含まれない
# 含まれるか チェック
validates :kind, inclusion: { in: %w(draft publish private) }

# 含まれないか チェック
validates :subdomain, exclusion: { in: %w(www us ca jp) }
```

```rb:メールアドレス
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, uniqueness: true, format: { with: VALID_EMAIL_REGEX }
```

## 関連性
Modelを互いに関連づける
ModelはDB設計と密に連携する。 DB側でも外部キーの設定などをする必要有
```rb
# UserとTweetの関連づけを想定

# User (複数の投稿を保持)
has_many :tweets, dependent: :destroy

# Tweet (userに依存する)
belongs_to :user
```
```rb:Controlelr側で下記が使えるようになる
# ツイート作成
@tweet = Tweet.create(tweet_params)

# ツイート一覧
@user = User.find(params[:id])
@tweets = @user.tweets

# ツイートしたユーザー
@tweet.user
```

## Controller
ModelとViewに値を渡す

```rb:ViewとControllerを作成
# 作成 (複数形にする)
rails g controller users index show

# 削除 (生成されたroutingは残る)
rails destroy controller users
```

```rb:controller file
# index
@user = User.all

# show
@user = User.find(params[:id])

# new (formに渡す用)
@user = User.new

# create
@user = User.new(params[:xx])
@user.save
User.create(params[:xx]) # 1行でまとめられる

# edit (編集対象を見つけてviewに返却)
@user = User.find(params[:id])

# update
@user = User.find(params[:id])
@user.update(params[:id])

# destroy
@user = User.find(params[:id])
@user.destroy
```

## before_action
アクションが実行される前に何らかの処理を行う
```shell
# 適応したいControllerで定義
# application Controllerで定義すると全体に適応可能
```
```rb
# 重複した処理などはprivateで最下部でメソッド化すると良い
```

```rb
# Actionが走る前にメソッドが走る
before_action :メソッド名

# 特定のActionが走る前
before_action :メソッド名, only: [:index, :update]

# 除外したAction以外が入る前
before_action :メソッド名, except: :index
```

## strong Parameter
```rb
# 受け付けるparameterを定義する (ハック対策)
@user = User.new(user_params)
private
  def user_params
    params.require(:モデル名).permit(:カラム名, :カラム名)
  end
```

## フラッシュメッセージ
```rb
# 次のアクションまで表示
flash[:notice] = "表示したい文字列"

## 失敗した時
flash.now[:notice] = "表示したい文字列"
```

## リダイレクト
controllerの経由回数が違うのでflashの使い方に気をつける

```rb:redirect_to
# レダイレクトする 引数はrouting
redirect_to("/posts/index")

# 省略した書き方 記述したControllerのnewアクションへ
redirect_to 'new'
```

```rb:render
# 他のactionを経由せず、現在のactionの@変数を使える
# render("フォルダ名/ファイル名")
render("posts/edit")
```
```rb:redirect_back(直前の画面へ)
# redirect_back fallback_location: 直前のページに戻れなかった際のパス
redirect_back fallback_location: root_path
```

## migration
DBのテーブルを作成するファイル
`migrationFile → DBの設計図`
`model → データの説明` `app/models/<モデル名.rb>`
```rb:2通りの作成方法
# db/migrate配下にmigrationファイルを作成
rails g migration [任意のmigration名]

# Modelとmigrationファイルを同時に生成
rails g model <Model名># 単数系
```

```rb:migration file
# 特に制約をつけない基本の指定
def change
  create_table :Products do |t|
   #t.型の名前 :カラムの名前
    t.string  :name
    t.text    :description
    t.integer :price
    t.timestamps
  end
end
```

```rb:not null
def change
  create_table :Products do |t|
   #t.型の名前 :カラムの名前, 制約: 許可、非許可
    t.string  :name, null: false
    t.text    :description, null: false
    t.integer :price, null: false
    t.timestamps
  end
end
```

```rb:外部キーを付与する
# imagesテーブルがあることを想定
def change
  create_table :Products do |t|
    t.string  :name
    t.text    :description
    t.integer :price

   #t.reference型 :紐付けたいテーブル名, 外部キー制約の設定
    t.references :image, foreign_key: true
    t.timestamps
  end
end
```

```rb:カラムを追加
def change
#add_column :カラムを追加するテーブル名, :新しく追加するカラム名, :型
  add_column :products, :size, :string
end
```

```rb:カラムを削除
# 削除の際は型を指定するのがポイント
def change
#remove_column :テーブル名, :削除するカラム名, :型
  remove_column :products, :description, :text
end
```

```rb:カラム名を変更
# 間違えて作成した「prise」カラムを「price」に変更する想定
def change
#rename_column :テーブル名, :変更前のカラム名, :変更後のカラム名
  rename_column :products, :prise, :price
end
```

```rb:カラムのデータ型を変更
# 「string」から「integer」に変更する想定
def up
 #change_column :テーブル名, :型を変更したいカラム名, :変更後のデータ型
  change_column :products, :price, :integer
end

def down
 #change_column :テーブル名, :型を戻すカラム名, :変更前のデータ型
  change_column :products, :price, :string
end
```

```rb:後からnot null制約
def change
#change_column_null :テーブル名, :制約を設定するカラム名, :null許可、非許可
  change_column_null :posts, :name, false
end
```

```rb:一意性(uniq)
# indexを付与することによって全件検索を避ける
# 辞書でいう所の索引を用意してパフォーマンスを上げる
def change
  add_index :users, :email, unique: true
end
```

## データ型
```rb
integer 整数
float 少数
string 文字列
text 長い文字列
boolean 真偽値
```

## 参考

[アソシエーション\[関連性\]](https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1)
[バリデーション](https://qiita.com/h1kita/items/772b81a1cc066e67930e)
[関連づけ Railsガイド](https://railsguides.jp/association_basics.html)
[before_action](https://pikawaka.com/rails/before_action)
[【Rails】基本的なマイグレーションファイルの書き方 - ユウマのブログ](https://yumanoblog.com/rails-migrationfile/)