新規投稿機能

# はじめに
2024年2月からプログラミン学習を始めたばかりなので間違ったことを記載している可能性もあります。間違っている部分やアドバイスがありましたらご指摘していただけたら幸いです。

また、今回の記事は以下の記事の続きになっています。

https://qiita.com/sslevel5/items/74c5a92ae30e4d991778

## 前提
ruby
rails
gem device

上記の環境で作成します。

## 実装

今回は顧客側で投稿をする際に使用するジャンルの追加・編集ができるよう管理者側にジャンル投稿機能を実装したいと思います。

### テーブルの追加
Model名は大文字から始まる単数形になります
今回は投稿のジャンル名をtitileカラムとして作成をします。

```
rails g model Genre title:string
```
マイグレーションファイルを確認し少し編集します。
```〇〇〇〇〇〇〇〇_create_genres.rb
class CreateGenres < ActiveRecord::Migration[7.1]
  def change
    create_table :genres do |t|
      t.string :title, null: false #追記

      t.timestamps
    end
  end
end
```
このカラムには必ず値が入っていないといけないため、null: false 制約をつけて空の値を許可されないようにします。

### データベースに反映させる
データベースにテーブルを作成しカラムを追加する。
```
rails db:migrate
```

### コントローラーの追加
今回は、新規投稿機能と一覧機能を同じビュー内で完結させる予定なので以下のようにします。

```
rails g controller admin/genres index show create edit update
```
そうすると対応のビューとコントローラーが作成されます。

```app/controllers/admin/genres_controller.rb
class Admin::GenresController < ApplicationController
  def index
  end

  def show
  end

  def create
  end

  def edit
  end

  def update
  end
end

```

![スクリーンショット (398).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3794246/66001e8e-6873-5b19-6be2-7c5b5f08db13.png)

必要のないビューもありますが、最後に削除することにします。

### コントローラーの編集
```app/controllers/admin/genres_controller.rb
class Admin::GenresController < ApplicationController
  def index
    @genres = Genre.all
    @genre = Genre.new
  end

  def show
    @genres = Genre.all
    redirect_to edit_admin_genre_path(params[:id])
  end

  def create
    @genre = Genre.new(genre_params)
    @genres = Genre.all
    if @genre.save
      flash[:notice] = "ジャンルを追加しました。"
      redirect_to admin_genres_path
    else
      flash.now[:alert] = "ジャンルの追加に失敗しました。"
      render :index
    end
  end

  def edit
    @genres = Genre.all
    @genre = Genre.find(params[:id])
  end

  def update
    @genres = Genre.all
    @genre = Genre.find(params[:id])
    if @genre.update(genre_params)
      flash[:notice] = "ジャンルを変更しました。"
      redirect_to admin_genres_path
    else
      flash.now[:alert] = "ジャンルの変更に失敗しました。"
      render :edit
    end
  end

  private

  def genre_params
    params.require(:genre).permit(:title)
  end
end

```

<details>
<summary>コントローラーの詳細</summary>

管理者向けのジャンルに関するCRUD操作（作成、読み取り、更新、削除）を提供するものです。以下に各アクションとその役割について詳しく解説します。
※今回は削除機能を設けません。

### indexアクション

- すべてのジャンル（@genres）を取得します
- 新しいジャンルを作成するためのオブジェクト（@genre）を初期化します
- このアクションは通常、ジャンル一覧と新規作成フォームを表示するビューに対応します

### showアクション:

- すべてのジャンルを取得します
- 指定されたジャンルの編集ページにリダイレクトします（edit_admin_genre_path）
- showアクションは通常詳細表示を行いますが、このコードでは編集ページにリダイレクトしています

### createアクション

- 新しいジャンルオブジェクトを作成し、パラメーターから値を設定します（@genre）
- すべてのジャンルを取得します
- ジャンルが正常に保存された場合、成功メッセージを表示し、ジャンル一覧ページにリダイレクトします
- 保存に失敗した場合、エラーメッセージを表示し、indexビューを再表示します
### editアクション

- すべてのジャンルを取得します
- 指定されたIDのジャンルを取得します（@genre）
- このアクションは通常、ジャンルの編集フォームを表示するビューに対応します

### updateアクション

- すべてのジャンルを取得します
- 指定されたIDのジャンルを取得します
- ジャンルの更新が成功した場合、成功メッセージを表示し、ジャンル一覧ページにリダイレクトします
- 更新に失敗した場合、エラーメッセージを表示し、editビューを再表示します

### プライベートメソッド(genre_paramsメソッド)

- ストロングパラメーターとして、許可されたジャンルの属性（title）を定義します
- 外部からの不正な入力を防ぐために、パラメーターを制限します
</details>


### ルーティングの編集

```config/routes.rb
  namespace :admin do
    resources :genres, only: [:index, :show, :create, :edit, :update] #追加
    get 'genres/index' #削除
    get 'genres/show' #削除
    get 'genres/create' #削除
    get 'genres/edit' #削除
    get 'genres/update' #削除
    resources :posts
  end
```

念のため、'rails routes'で確認

![スクリーンショット (399).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3794246/8351e418-ee9a-69c6-7ef3-b0d4cf4f1f28.png)

### ビューの編集

#### 新規投稿・一覧画面(/admin/genres)

```app/views/admin/genres/index.html.erb
<h3>ジャンル一覧・追加</h3>
<%= form_with url: admin_genres_path, model: @genre, class: "mb-4" do |g| %>
    <div>
        <%= g.text_field :title, placeholder: 'ジャンル' %>
    </div>
    <%= g.submit '新規登録' %>
<% end %>
<div>
    <% @genres.each do |ge| %>
        <%= link_to edit_admin_genre_path(ge) do %>
            <%= ge.title %>
        <% end %>
    <% end %>
</div>
```

<details>
<summary>詳細</summary>

- form_with: ジャンルの新規登録フォームを作成するために使用されるヘルパーです。urlはフォームの送信先のURL (admin_genres_path) を指定し、modelはフォームに関連付けるモデル (@genre) を指定します。
- g.text_field :title, placeholder: 'ジャンル'：ジャンルの名前を入力するためのテキストフィールドです。プレースホルダーに「ジャンル」と表示されます。
- g.submit '新規登録': フォームを送信するためのボタンです。「新規登録」と表示されます。
- @genres.each do |ge|: @genres配列の各ジャンル (ge) について繰り返し処理を行います。
- link_to edit_admin_genre_path(ge) do: 各ジャンルタイトルにリンクを設定します。このリンクをクリックすると、ジャンルの編集ページ (edit_admin_genre_path(ge)) に移動します。
- <%= ge.title %>: ジャンルのタイトルを表示します。

</details>

#### 編集画面(/admin/genres/〇/edit)

```app/views/admin/genres/edit.html.erb
<div>
  <div>
    <div>
      <h3>ジャンル編集</h3>
      <%= form_with url: admin_genre_path, method: :patch, model: @genre do |g| %>
      <div>
      <%= g.text_field :title, placeholder: 'ジャンル' %>
      </div>
      <%= g.submit '保存' %>
      <% end %>
    </div>
  </div>
</div>
```

<details>
<summary>詳細</summary>

- form_with: フォームを作成するためのヘルパーメソッドです。
- url: admin_genre_path: フォームの送信先URLを指定します。admin_genre_path は編集アクションに対応するURLです。
- method: :patch: HTTPメソッドをPATCHに設定します。PATCHはリソースの部分的な更新に使用されます。
- model: @genre: フォームに関連付けるモデル (@genre) を指定します。これにより、フォームフィールドがモデルの属性にバインドされます。
- <%= g.text_field :title, placeholder: 'ジャンル' %>: ジャンルの名前を入力するためのテキストフィールドです。プレースホルダーには「ジャンル」と表示されます。
- g.submit '保存': フォームの送信ボタンです。「保存」というラベルが表示されます。

</details>

ビューの編集が完了しました。
最後にバリデーションの定義します。

### モデルの編集

```app/models/genre.rb
class Genre < ApplicationRecord
  validates :title, presence: true, uniqueness: true
end
```

<details>
<summary>詳細</summary>

- validates :title: title属性に対してバリデーションを設定します。
- presence: true: titleが空でないことを検証します。空の値（nilまたは空文字列）を保存しようとするとバリデーションエラーになります。
- uniqueness: true: titleが一意であることを検証します。同じタイトルが既にデータベースに存在する場合、バリデーションエラーになります。
</details>

## 最後に
今回は、管理者側で投稿機能を作成しました。
次回は、顧客側で今回管理者側の投稿機能で作成したデータを使用した投稿機能を実装します。



## 参考資料

https://qiita.com/seikin123/items/51caa5dfde851f7352f9

https://railsguides.jp/form_helpers.html#%E4%B8%80%E8%88%AC%E7%9A%84%E3%81%AA%E6%A4%9C%E7%B4%A2%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0