0
1

More than 1 year has passed since last update.

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

Posted at

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

手順

①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

プロフィール編集対象のユーザー情報(@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での変数の受け渡しが値渡しで、ここでは参照の値渡しであることが原因だそう。

参照の値渡し

2.ルーティングの設定

ユーザープロフィール用の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.アバター画像のアップロード機能

この記事を参考にアバター画像のアップロード機能を実装する。

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>

この記事をもとにアバター画像を表示させる。

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でパスを指定する必要がある。

この記事をもとにアバター画像を選択するパーツを表示する。

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>
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1