devise使わずユーザー編集・更新機能を実装する

#はじめに

今回はユーザーのマイページを実装してから、プロフィールを編集できるようにするための方法を解説します。

#前提条件

・deviseを導入済み
・deviseによるゆーざー新規登録/ログイン機能を実装済み

#開発環境

Ruby 2.6.5
Rails 6.0.0
MySQL 5.6.50

#手順

##1)ユーザー詳細(マイページの実装)

まずdeviseのものとは別に`usersコントローラー`を作成します。
ターミナルにて以下コマンドを実行します。

```ruby:ターミナル
$ rails g controller users show
```

コントローラーが作成できたら、以下のようにファイルを編集ます。

```ruby:controllers/users_controller
class UsersController < ApplicationController
  before_action :set_user, only: [:show]

  def show; end

  private

  def set_user
    @user = User.find(params[:id])
  end

end
```

次にルーティングも設定します

```ruby:config/routes.rb
# 既存の記述に追記する
resources :users, only: [:show] 
```

これでマイページへのパスとアクションの設定は終わりました。
次にビューの記述をしていきます。

`views/users/show.html.erb`にて記述していきます。
マイページの実装は以上です。

##2)ユーザー編集機能の実装

ここからは、あくまでも自分の場合なので、参考までにとどめてください。

まずDB設計(**マイグレーションファイル**)を以下のようにしました。

deviseのusesテーブルは`nickname`カラムのみ作成します。

```ruby:devise_create_users.rb

class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :nickname, null: false
      t.string :email,              null: false, default: ''
      t.string :encrypted_password, null: false, default: ''
      # 省略
end
```

続いてプロフィール用のテーブルの設計をしていきます。
ターミナルにて以下のコマンドを実行します
(僕の場合はプロフィール用に`intro`モデルを作成)

```ruby:ターミナル
$ rails g model intro
```

マイグレーションファイルを編集します。

``` ruby:create_intros.rb

class CreateIntros < ActiveRecord::Migration[6.0]
  def change
    create_table :intros do |t|
      t.string :image
      t.string :first_name
      t.string :last_name
      t.string :website
      t.text   :profile
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end
```

以上でテーブルの設計は終了です。

### 2-1)モデルのアソシエーションの設定

次にアソシエーションですが、以下のような設定にしました。

```ruby:models/user.rb
has_one :intro
```

```ruby:models/intro.rb
belongs_to :user
```

このように1対1の関係を結びました。

### 2-2)ルーティングとアクションの設定

```ruby:config/routes.rb
resources :users, only: [:show] 
resources :intros, only: [:new, :create, :edit, :update]
```

ルーティングは上記のようにし、**ネスト**はしませんでした。
理由は、ユーザー編集用のテーブルはイメージとしてはツイートなどの投稿用のテーブルと近いような気がしたからです。

アクションもそうですが、**イメージとしては投稿機能を、ユーザー編集に置き換える**とイメージを持つと実装しやすいかと思います。

次にアクションの定義を行います。

```ruby:controllers/intros_controller.rb
class IntrosController < ApplicationController
　# 未ログインユーザーの処理
  before_action :authenticate_user!, only: [:new, :edit]
  # リファクタリング
  before_action :set_intro, only: [:edit, :update]
 
  def new
    @intro = Intro.new
   # すでに登録済みのユーザーが新規登録ページに遷移しないようにする
　　if Intro.find_by(user_id: current_user.id)
      redirect_to root_path
    end
  end
  
  def create
    @intro = Intro.new(intro_params)
　　 # 保存できたら、ユーザー詳細ページに戻るため引数にintroモデルに紐づくuserのidをわたす
　   # intoro.valid?でないのはバリデーションをかけていないため
    if @intro.save
      redirect_to user_path(@intro.user.id)
    else
      render :new
    end
  end

  def edit
    # ログイン中のユーザーと編集するユーザーが一致しないとページに遷移できないようにする
    unless current_user.id == @intro.user.id
      redirect_to user_path(@intro.user.id)
    end
  end

  def update
   # createと同じように更新できたらユーザー詳細に戻るため引数をわたす
    if @intro.update(intro_params)
      redirect_to user_path(@intro.user.id)
    else
      render :edit
    end
  end

  private

  def set_intro
    @intro = Intro.find(params[:id])
  end

  def intro_params
    params.require(:intro).permit(:first_name, :last_name, :website, :profile, :image).merge(user_id: current_user.id)
  end
end
```

これでユーザー編集・更新機能の実装はほとんど完了です。
最後にビューを作成します。

また、この時**ユーザー編集ページへ遷移するリンクに条件を指定**します。


###2-3)views/users/show.html.erbの編集

```ruby:users/show.html.erb
<% if user_signed_in? %>
# introテーブルにUserの値が存在しなれば新規登録のリンクを表示
  <% unless @user.intro.present? %>
# ログインしているユーザーとマイページに表示されているユーザーが同じなら新規登録のリンクを表示
    <% if current_user.id == @user.id %>
       <div class="profile-btn">
         <%= link_to 'プロフィールを編集する', new_intro_path, class: "profile-edit-btn" %>
       </div>
    <% end %>
  <% end %>

# ログインしているユーザーとマイページに表示されているユーザーが同じで、かつintroテーブルに値が存在していれば編集ページへのリンクを表示する
  <% if current_user.id == @user.id && @user.intro.present? %>
    <div class="profile-btn">
      <%= link_to 'プロフィールを編集する', edit_intro_path(@user.intro.id), class: "profile-edit-btn" %>
    </div>
  <% end %>
<% end %>   
```

僕の場合はこのような記述しかできませんでしたが、もっと良い記述があればコメントくださると嬉しいです。

僕はここにたどり着くまでに1日半かかったので、この記事を見て少しでも作業が進んだという方がいらっしゃれば幸いです。

#参考文献

[【Rails】データが1件でもあるかどうかチェックするには`Model.exists?`](https://opiyotan.hatenablog.com/entry/tips-model-exists)