今回はユーザーのプロフィール画面の設定及び編集できる機能を追加していく。
まずユーザーのアイコンを設定・編集したいのでavatarカラムをstring型でUserのmigrationファイルに追加する。
rails g migration AddAvatarToUsers avatar:string
Usersテーブルにavatarカラムを追加できた。
次にUsersSettingsコントローラーを作成していく。
rails g controller UserSettings edit update show
プロフィール画面は詳細・編集画面があれば良いので今回はshow・edit・updateアクションを定義。
中身はこんな感じ
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)
resource :UserSetting, only: %i[show edit update]
単一系なのは
・プロフィールはユーザーに対するプロフィールは1つしか存在しないため、なのでidが不要なため。こうすることでURLはidを参照しなくなる。
・また他のユーザーのプロフィールは編集することはないのでresourcesと複数形にするメリットがない。
- 今回は一覧(index:/profiles)を表示する必要がなく、詳細ページでidを表示させると自分が何番目に作成されたユーザーか分かってしまう。詳細ページ(show:/profiles/:id)に他ユーザーのidを入力されてしまうこともあるので、単数形リソースを使うことが望ましい。これはかなりのデメリット。
ユーザーのアイコンを設定したいのでavatar_uploaderを作成する。
rails g uploader avatar
userモデルに
mount_uploader :avatar, AvatarUploader
を追記。こうすることでuserモデルでAvatarUploaderを読み込める。
avatar_uploader.rb内では
def default_url
'c0974050b840004ab1afea36d7bbb890_1.jpg'
end
def extension_allowlist
%w(jpg jpeg gif png)
end
を追記することでアイコンが設定してない時のデフォルトのアイコンを表示できる。また設定できる画像の拡張子を決定できる。
usersetting/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: 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
<%= 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>