【Rails】[コントローラー名]_controller.rbについて

# 記事概要
Ruby on Railsのコントローラーについて、まとめる

# 前提
- Ruby on Railsでアプリケーションを作成している

# 基本情報
## ファイルパス
app/controllers/[コントローラー名]_controller.rb

## 記載方法
ルーティングで設定したアクション名のインスタンスメソッドを定義する
```[コントローラー名]_controller.rb
class PostsController < ApplicationController
  def アクション名
    # 処理を記述
  end
end
```

## インスタンス変数
[インスタンス変数](https://qiita.com/likapantrip/items/28dc379c57aec6177a74#インスタンス変数)は、ビューファイルで使用できる


## モデルの処理
データ取得のみなど、簡単なモデルの処理はコントローラーに記述できる

## パラメーター
リクエストに含まれて、サーバーの外部から渡されるデータのこと

### params
送られてきたパラメーターをハッシュのような構造で格納したもの
`params[:キー名]`で値を取り出せる

[![Image from Gyazo](https://i.gyazo.com/14fe94c7e9b73724530b8da54d381c35.png)](https://gyazo.com/14fe94c7e9b73724530b8da54d381c35)

### ストロングパラメーター
意図しないデータベースの読み書きを防ぐために使用する
[プライベートメソッド](https://qiita.com/likapantrip/items/28dc379c57aec6177a74#プライベートメソッド)として、`require`メソッドと`permit`メソッドを組み合わせることで、定義できる

[![Image from Gyazo](https://i.gyazo.com/f438000c8053def83ae50ceda5c3e0cd.png)](https://gyazo.com/f438000c8053def83ae50ceda5c3e0cd)

# まとめ(データ編集)
## newメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#newメソッド)を参照

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

`save`メソッドと合わせて使用することで、データの新規作成とテーブル保存ができる

### 親子関係のデータ保存
has_manyのアソシエーションを組んでいれば、`親モデルのインスタンス.子モデルの複数形(小文字).new`という記述方法で、親モデルのインスタンスに紐づく子モデルのインスタンスを生成できる

[ミニアプリ](https://qiita.com/likapantrip/items/66b03d482f57b1a6aa4f)を参照

```ruby
# paramsに含まれているorganization_idをもとに、Organizationのレコードを取得
@organization = Organization.find(params[:organization_id])
# 上記Organizationに紐づくMemberモデルのインスタンスを生成し、属性値を指定
@member = @organization.members.new(member_params)
# インスタンスを保存
@member.save
```

## saveメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#saveメソッド)を参照

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

```ruby
# postsテーブル(Postモデル)のcontentカラムに、パラメーターから受け取ったcontentに格納された情報を格納する
# 上記を変数postに格納する
post = Post.new(content: params[:content])

# 変数postを保存する
post.save
```

```ruby
# 保存の成功・失敗による条件分岐を行う
def create
  @room = Room.new(room_params)
  if @room.save
    # 保存処理が成功した場合、トップ画面に遷移
    redirect_to root_path
  else
    # 保存処理が失敗した場合、new画面に遷移
    render :new, status: :unprocessable_entity
  end
end
```

## createメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#createメソッド)を参照

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

受け取ったパラメータの情報を保存する

```ruby
# postsテーブル(Postモデル)のcontentカラムに、パラメーターから受け取ったcontentに格納された情報を保存する
Post.create(content: params[:content])
```

## updateメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#updateメソッド)を参照

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

```ruby
def update
  # findメソッドで編集対象を指定
  item = Item.find(params[:id])

  # item_paramsの情報で、レコードを編集
  item.update(item_params)

  # ルートパスへ移動
  redirect_to root_path
end

private
def item_params
  params.require(:item).permit(:name, :price)
end
```

## destroyメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#destroyメソッド)を参照

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

削除対象を指定する場合は`params[:id]`を使用し、削除するレコードを取得する
[![Image from Gyazo](https://i.gyazo.com/eaeef18de38516a54cb8408bcb2ca979.png)](https://gyazo.com/eaeef18de38516a54cb8408bcb2ca979)

```ruby
def destroy
  # findメソッドで削除対象を指定
  tweet = Tweet.find(params[:id])

  # レコードを削除
  tweet.destroy

  # ルートパスへ移動
  redirect_to root_path
end
```

# まとめ(データ取得)
## findメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#findメソッド)を参照

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

## allメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#allメソッド)を参照

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

## includesメソッド
引数に指定された関連モデルを1度のアクセスでまとめて取得するメソッド

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

```ruby
# includesメソッドを使用するとすべてのレコードを取得するため、allメソッドは省略可能
モデル名.includes(:紐付くモデル名)
```

```ruby
@tweets = Tweet.includes(:user)
```

[![Image from Gyazo](https://i.gyazo.com/c40d07ab0f8b771c3e1e49ead990e2d8.png)](https://gyazo.com/c40d07ab0f8b771c3e1e49ead990e2d8)
[![Image from Gyazo](https://i.gyazo.com/068b1d20979ffbe3de45934c0bc84719.png)](https://gyazo.com/068b1d20979ffbe3de45934c0bc84719)

## orderメソッド
テーブルから取得してきた複数のレコード情報を持つインスタンスの、並び順を変更するメソッド

事前定義されているため、開発時にモデルファイルで定義しなくてもコントローラーファイルに記述することが可能

```ruby
インスタンス = モデル名.order("並び替えの基準となるカラム名 並び順")
```

```ruby
# tweetsテーブルのレコードを、作成日が新しいものから順に取得する
@tweets = Tweet.includes(:user).order("created_at DESC")
```

|並び順|内容|
|-|-|
|ASC(昇順)|小さいものから大きいものになる。古いものから新しいのものになる|
|DESC(降順)|大きいものから小さいものになる。新しいものから古いものになる|

## アソシエーション
レコードに紐づいている別モデルのデータを取得

```ruby
# Tweetモデルのデータを取得し、変数tweetに格納
1] pry(main)> tweet = Tweet.find(3)
  Tweet Load (0.4ms)  SELECT `tweets`.* FROM `tweets` WHERE `tweets`.`id` = 3 LIMIT 1
=> #<Tweet:0x0000000109137e00
 id: 3,
 text: "葉桜になってる",
 image: "hasakura.jpeg",
 created_at: Wed, 30 Apr 2025 16:38:13.788717000 UTC +00:00,
 updated_at: Wed, 30 Apr 2025 16:38:13.788717000 UTC +00:00,
 user_id: 1>

# 変数tweetに格納されているuser_idを元に、Userモデルのレコード情報を取得
[2] pry(main)> tweet.user
  User Load (1.2ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #<User id: 1, email: "taro@email.com", created_at: "2025-04-30 16:37:03.919419000 +0000", updated_at: "2025-04-30 16:37:03.919419000 +0000", nickname: "太郎">

# tweet.userのレコード情報のうち、nicknameカラムの値のみを取得
[3] pry(main)> tweet.user.nickname
=> "太郎"
```

## whereメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#whereメソッド)を参照

```ruby
def search
  # モデルで設定したsearchメソッドの引数に、params[:keyword]と記述
  @tweets = Tweet.search(params[:keyword])
end
```

## session
セッションを使用する際に使うオブジェクト
使い方はよくわかってないが、存在することを理解すれば一旦OK

```ruby
# sessionデータはviewsとcontrollerでのみ利用可能
session[:sample] = 1
```

### CookieStore
Ruby on Railsでセッションを用いる際の、デフォルトの保存先。一般的に、セッションストレージと呼ぶ
セッションはCookieStoreにハッシュ形式で保存される

セッションは悪意のあるユーザーが利用すると非常に厄介な項目であるため、情報を暗号化したり、定期的にリセットしたりすることで対策している

## find_by_sqlメソッド
SQL文を使用し、データを検索し取得する際に使用するメソッド

```ruby
# tweetsテーブルからすべてのレコードを取得するSQL文
query = "SELECT * FROM tweets"

# SQL文を使用して、データを取得
@tweets = Tweet.find_by_sql(query)
#=> Tweet.allと同義
```

# まとめ(パラメーター)
## requireメソッド
送信されたパラメーターの情報を持つ`params`が使用できるメソッド
パラメーターから、どの情報を取得するかを選択する

`params[:モデルのファイル名]`と記述しても同じ情報を取得できるが、`require`メソッドを使うことで、意図しないパラメーターであった場合にエラーとして返すことができ、原因特定やユーザーにエラーを示すなどの対応ができる

[![Image from Gyazo](https://i.gyazo.com/07c80d1e891744d82dc8bfa751a62ad6.png)](https://gyazo.com/07c80d1e891744d82dc8bfa751a62ad6)
[![Image from Gyazo](https://i.gyazo.com/b5c8dadedc6a6308d469f59a7137214e.png)](https://gyazo.com/b5c8dadedc6a6308d469f59a7137214e)

```ruby
# 取得したい情報を指定する
params.require(:モデルのファイル名)
```

## permitメソッド
送信されたパラメーターの情報を持つ`params`が使用できるメソッド
パラメーターから取得したいキーを指定し、指定したキーと値のセットのみを取得する

キーを指定すると、それ以外のキーがあっても値を受け付けない

[![Image from Gyazo](https://i.gyazo.com/02038a5da1eff78336ded0a589fa962a.png)](https://gyazo.com/02038a5da1eff78336ded0a589fa962a)

```ruby
# 取得したいキーを指定する
params.require(:モデルのファイル名).permit(:キー名, :キー名)

# 配列に対して保存を許可したい場合は、キーに対し[]を値として記述
params.require(:モデルのファイル名).permit(:キー名, キー名:[])
```

## mergeメソッド
詳細は、[こちら](https://qiita.com/likapantrip/items/655e2c8fc2d4c02eb12e#mergeメソッド)を参照

```ruby
class TweetsController < ApplicationController
  def new
    @tweet = Tweet.new
  end

  def create
    Tweet.create(tweet_params)
    redirect_to '/'
  end

  private
  def tweet_params
    # paramsとcurrent_userのIDを結合
    params.require(:tweet).permit(:text, :image).merge(user_id: current_user.id)
  end
end
```

[![Image from Gyazo](https://i.gyazo.com/5f479aa63db612401782b16543b11189.png)](https://gyazo.com/5f479aa63db612401782b16543b11189)

## ルーティングのネスト
詳細は、[こちら](https://qiita.com/likapantrip/items/f50811b766d6fa15234c#ルーティングのネスト)を参照

```ruby
def comment_params
  params.require(:comment).permit(:text).merge(user_id: current_user.id, tweet_id: params[:tweet_id])
end
```

[![Image from Gyazo](https://i.gyazo.com/89458556eee5431729e9759059182337.png)](https://gyazo.com/89458556eee5431729e9759059182337)

# まとめ(その他)
## redirect_toメソッド
Railsでリダイレクト処理を行う際に使用するメソッド
処理終了後、アクションに対応するビューファイルを参照せずに、別ページへリダイレクトする

### URLを直接指定する

```ruby
redirect_to "URIパターン"
```

### アクションを指定する

```ruby
redirect_to action: :アクション名
```

## renderメソッド
呼び出すビューファイルを指定するメソッド
データの保存や更新などに失敗した場合に使用ことが多い
※ビューで使用するrenderメソッドとは、別物

[![Image from Gyazo](https://i.gyazo.com/75868626e481a8edb39c63d107b90248.png)](https://gyazo.com/75868626e481a8edb39c63d107b90248)

```ruby
# 「:unprocessable_entity」が発生したエラーの種類を示すもので、「何らかの処理に失敗した」という汎用的な指定方法

# エラーが発生した場合に、newのビューファイルを呼び出す
render :new, status: :unprocessable_entity
```

```ruby
# 使用例(create)
def create
  @prototype = Prototype.new(params_proto)
  if @prototype.save
    redirect_to root_path
  else
    render :new, status: :unprocessable_entity
  end
end

# 使用例(update)
def update
  @prototype = Prototype.find(params[:id])
  if @prototype.update(params_proto)
    redirect_to prototype_path(@prototype.id)
  else
    render :edit, status: :unprocessable_entity
  end
end

# 使用例(別コントローラーへ移動)
@prototype = Prototype.find(params[:id])
render 'prototypes/show', status: :unprocessable_entity
```

### jsonオプション
レスポンスで返却されるデータフォーマットをJSON形式に指定する

```ruby
render json:{ [キー]: [JavaScriptに送信するデータが格納されている変数] }
```

```ruby
# 変数memoにJavaScriptで表示するデータを格納
memo = Post.create(content: params[:content])

# キーpostと変数memoをJavaScriptに連携し、データをブラウザ画面に表示
render json:{ post: memo }
```

## before_actionメソッド
コントローラで定義されたアクションが実行される前に、共通の処理を行うことができる

```ruby
class コントローラー名 < ApplicationController
  before_action :共通処理のメソッド名

  # 省略
  
  private
  def 共通処理のメソッド名
    # 共通処理
  end
end
```

### onlyオプション
指定したアクションの実行前にのみ、共通処理を行う

```ruby
class コントローラー名 < ApplicationController
  before_action :共通処理のメソッド名, only: [:アクション名, :アクション名]

  # 省略
  
  private
  def 共通処理のメソッド名
    # 共通処理
  end
end
```

### ifオプション
値にメソッド名を指定し、戻り値が`true`の場合のみ、共通処理行う

```ruby
class コントローラー名 < ApplicationController
  before_action :共通処理のメソッド名, if: :メソッド名?

  # 省略
  
  private
  def 共通処理のメソッド名
    # 共通処理
  end
end
```

### exceptオプション
指定したアクションに対しては、共通処理を行わない

```ruby
class コントローラー名 < ApplicationController
  before_action :共通処理のメソッド名, except: [:アクション名, :アクション名]

  # 省略
  
  private
  def 共通処理のメソッド名
    # 共通処理
  end
end
```

## authenticate_user!メソッド
ログイン状態によって表示するページを切り替えるdeviseのメソッド

`authenticate_user!`メソッドが呼ばれた段階で、ユーザーが未ログインだとログイン画面に遷移させる

```ruby
# 基本的には、beforeアクションと併せて利用する
before_action :authenticate_user!
```

# Ruby on Railsまとめ
https://qiita.com/likapantrip/items/46b43c9d1309b6633f0a

# コントローラー
https://qiita.com/likapantrip/items/66a30e887ffe20ad08a4