Railsでプロフィール画面設定

今回はユーザーのプロフィール画面の設定及び編集できる機能を追加していく。

まずユーザーのアイコンを設定・編集したいのでavatarカラムをstring型でUserのmigrationファイルに追加する。

```ruby
rails g migration AddAvatarToUsers avatar:string
```

Usersテーブルにavatarカラムを追加できた。

次にUsersSettingsコントローラーを作成していく。

```ruby
rails g controller UserSettings edit update show
```

プロフィール画面は詳細・編集画面があれば良いので今回はshow・edit・updateアクションを定義。

中身はこんな感じ

```ruby
class UserSettingsController < 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
```

privateでset_userメソッド、user_paramsメソッドを定義。user_params内ではストロングパラメーターで編集する際に受けっとった値を格納している。

そしてupdateアクションで@user.update(user_params)で渡された値に更新されている。ちなみに@userはprivateのset_userメソッドで定義されているのをbefore_actionでアクションが実行される直前に実行されている。なので@userにはcurrent_userのidが入っている。(ログインしているユーザーのこと)

routing定義(profile)

```ruby
resource :UserSetting, only: %i[show edit update]
```

単一系なのは

・プロフィールはユーザーに対するプロフィールは1つしか存在しないため、なのでidが不要なため。こうすることでURLはidを参照しなくなる。

・また他のユーザーのプロフィールは編集することはないのでresourcesと複数形にするメリットがない。

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

ユーザーのアイコンを設定したいのでavatar_uploaderを作成する。

```ruby
rails g uploader avatar
```

userモデルに

```ruby
mount_uploader :avatar, AvatarUploader
```

を追記。こうすることでuserモデルでAvatarUploaderを読み込める。

avatar_uploader.rb内では

```ruby
def default_url
  'c0974050b840004ab1afea36d7bbb890_1.jpg'
end

def extension_allowlist
  %w(jpg jpeg gif png)
end
```

を追記することでアイコンが設定してない時のデフォルトのアイコンを表示できる。また設定できる画像の拡張子を決定できる。

usersetting/edit.html.erb

```ruby
<% 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: usersetting_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>
```

usersettings/show.html.erb
```ruby
<%= 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>
```