プロフィール編集機能の実装

# プロフィール編集機能の実装

## 手順
①profileコントローラーの作成
②ルーティングの設定
③アバター画像のアップロード機能
④viewの表示

## 1.profileコントローラーの作成
rails g controller profilesでプロフィールコントローラーを作成し、ユーザーのプロフィール画面では詳細、編集、更新ができれば良いので、show、edit、updateアクションのみを定義。

```app/controllers/profiles_controller.rb
class ProfilesController < ApplicationController
  before_action :set_user, only: %i[edit update]
  
  def edit; end

  def update
    if @user.update(user_params)
      redirect_to profile_path, success: t('defaults.message.updated', item: User.model_name.human)
    else
      flash.now['danger'] = t('defaults.message.not_updated', item: User.model_name.human)
      render :edit
    end
  end

  def show; end

  private

  def set_user
    @user = User.find(current_user.id)
  end

  def user_params
    params.require(:user).permit(:email, :last_name, :first_name, :avatar, :avatar_cache)
  end
end
```

set_userメソッドで現在ログインしているユーザーのidを@userに代入している。
そして、プロフィールの編集や更新する時にはこのidが必要なのでedit、updateにbefore_actionを使って読み込ませている。

[before_action](https://pikawaka.com/rails/before_action)

プロフィール編集対象のユーザー情報（@user）をcurrent_userのインスタンスではなく、DBから取得したオブジェクトを利用する。

```
# BAD
def set_user
  @user = current_user
end

# GOOD
def set_user
  @user = User.find(current_user.id)
end
```
BAD例で実装すると、プロフィール名の変更に失敗した際に、画面上でプロフィールとして表示される名前が変わってしまう。
これはプロフィール編集の失敗時にcurrent_userがupdateの影響を受けてしまうため。
一度@userに格納しているのに、どうしてcurrent_userの中身が変わってしまうのかと疑問に思ったが、それはrubyでの変数の受け渡しが値渡しで、ここでは参照の値渡しであることが原因だそう。

[参照の値渡し](https://magazine.rubyist.net/articles/0032/0032-CallByValueAndCallByReference.html)

## ２.ルーティングの設定
ユーザープロフィール用のURLはidを参照しないことが望ましいため、単数系リソースを使う。

複数作成できる掲示板(Board)の仕様と異なり、ユーザーに対するプロフィールは1つしか存在せず、
また他ユーザーのプロフィールを編集することはないので、idを表示させるメリットがない。

今回は一覧(index：/profiles)を表示する必要がなく、詳細ページでidを表示させると自分が何番目に作成されたユーザーか分かってしまう。
詳細ページ(show：/profiles/:id)に他ユーザーのidを入力されてしまうこともあるので、単数形リソースを使うことが望ましい。

```routes.rb
resource :profile, only: %i[show edit update]
```

```
edit_profile GET    /profile/edit(.:format)             profiles#edit
     profile GET    /profile(.:format)                  profiles#show
             PATCH  /profile(.:format)                  profiles#update
             PUT    /profile(.:format)                  profiles#update
```

## 3.アバター画像のアップロード機能
[この記事](https://pikawaka.com/rails/carrierwave)を参考にアバター画像のアップロード機能を実装する。

## 4.viewの表示

```app/views/profiles/show.html.erb
<% content_for(:title, t('.title')) %>
<div class="container pt-3">
  <div class="row">
    <div class="col-md-10 offset-md-1">
      <h1 class="float-left mb-5"><%= t('.title') %></h1>
      <%= link_to t('defaults.edit'), edit_profile_path, class: 'btn btn-success float-right' %>
      <table class="table">
        <tr>
          <th scope="row"><%= t(User.human_attribute_name(:email)) %></th>
          <td><%= current_user.email %></td>
        </tr>
        <tr>
          <th scope="row"><%= t(User.human_attribute_name(:full_name)) %></th>
          <td><%= current_user.decorate.full_name %></td>
        </tr>
        <tr>
          <th scope="row"><%= t(User.human_attribute_name(:avatar)) %></th>
          <td><%= image_tag current_user.avatar.url, class: 'rounded-circle', size: '50x50' %></td>
        </tr>
      </table>
    </div>
  </div>
</div>
```

[この記事](https://pikawaka.com/rails/carrierwave)をもとにアバター画像を表示させる。

```app/views/profiles/edit.html.erb
<% content_for(:title, t('.title')) %>
<div class="container">
  <div class="row">
    <div class="col-md-10 offset-md-1">
      <h1><%= t('.title') %></h1>
      <%= form_with model: @user, url: profile_path, local: true do |f| %>
        <%= render 'shared/error_messages', object: f.object %>
        <%= f.label :email %>
        <%= f.email_field :email, class: 'form-control mb-3' %>

        <%= f.label :last_name %>
        <%= f.text_field :last_name, class: 'form-control mb-3' %>

        <%= f.label :first_name %>
        <%= f.text_field :first_name, class: 'form-control mb-3' %>

        <%= f.label :avatar %>
        <%= f.file_field :avatar, onchange: 'previewFileWithId(preview)', class: 'form-control mb-3', accept: 'image/*' %>
        <%= f.hidden_field :avatar_cache %>

        <div class='mt-3 mb-3'>
          <%= image_tag @user.avatar.url,
                        class: 'rounded-circle',
                        id: 'preview',
                        size: '100x100' %>
        </div>
        <%= f.submit class: 'btn btn-primary' %>
      <% end %>
    </div>
  </div>
</div>
```

form_withのmodelを指定しただけではprofilesコントローラーのeditアクションが実行されないので、urlでパスを指定する必要がある。

[この記事](https://pikawaka.com/rails/carrierwave)をもとにアバター画像を選択するパーツを表示する。

```app/views/shared/_header.html.erb
</li>
        <li class="nav-item dropdown dropdown-slide">
          <a href="#" class="nav-link" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" id="header-profile">
            <%= image_tag current_user.avatar_url, size: '40x40', class: 'rounded-circle mr15'%>
          </a>
          <div class="dropdown-menu dropdown-menu-right">
            <div class="dropdown-item"><%= current_user.decorate.full_name %></div>
            <div class="dropdown-divider"></div>
            <%= link_to (t 'profiles.show.title'), profile_path, class: 'dropdown-item' %>
            <%= link_to (t 'defaults.logout'), logout_path, class: 'dropdown-item', method: :delete %>
          </div>
        </li>
```


```app/views/comments/_comment.html.erb
<tr id="comment-<%= comment.id %>">
  <td style="width: 60px">
    <%= image_tag comment.user.avatar_url, class: 'rounded-circle', size: '50x50' %>
  </td>
  <td>
    <h3 class="small"><%= comment.user.decorate.full_name %></h3>
```