resourceを使ったプロフィール編集機能の実装
ルーティングの設定
今回、プロフィール詳細画面と編集画面へのurlは/profile, /profile/editと 現在ログインしているユーザーの詳細と編集画面だけ表示できればいいので、urlでユーザーのidを参照する必要がない。このような時に使うのが単数型のリソース routes.rbに
#routes.rb resource :profile, only: %i[show edit update]
を記載、bundle exec rails routes
でルーティングを確認すると
ターミナル edit_profile GET /profile/edit(.:format) profiles#edit profile GET /profile(.:format) profiles#show PATCH /profile(.:format) profiles#update PUT /profile(.:format) profiles#update
とidを使わないルーティングを生成できるし、urlヘルパーも使えるようになる ちなみに複数リソース(resources)と単数リソース(resource)もルーティング先のコントローラはデフォルトでリソース名の複数形名称である
コントローラー
bundle exec rails g controller profiles
を実行してprofilesコントローラーの作成
コードは以下のようになる
#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) end end
set_user
で@user = current_user
としてしまうとプロフィール名変更に失敗したときに、画面上で名前が変わってしまう。これは@userにはcurrent_userの保存されている場所の値(メモリ番地)が代入されており、これによりcurrent_userの値を参照しているので、@userをupdateしてしまうと@userを通してcurrent_userの値が影響を受けてしまうためである。なので、@userにはdbから取得したオブジェクトを利用している
アバターカラムの追加
今回は詳細ページにユーザーにアバター画像を追加するのでアバター画像のカラムを用意する
カラム名はavatarにするのでbundle exec rails g uploader Avatar
を実行してアバター画像の設定ファイルを生成したらbundle exec rails g migration add_avatar_to_users avatar:string
を実行して、bundle exec rails db:migrate
を実行してavatarカラムを追加
user.rbにmount_uploader :avatar, AvatarUploader
を記載して設定ファイルを適用させる
設定ファイルは以下の感じ
#avatar_uploader.rb class AvatarUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick # include CarrierWave::MiniMagick # Choose what kind of storage to use for this uploader: storage :file # storage :fog # Override the directory where uploaded files will be stored. # This is a sensible default for uploaders that are meant to be mounted: def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end # Provide a default URL as a default if there hasn't been a file uploaded: def default_url 'sample.jpg' end # Process files as they are uploaded: # process scale: [200, 300] # # def scale(width, height) # # do something # end # Create different versions of your uploaded files: # version :thumb do # process resize_to_fit: [50, 50] # end # Add a white list of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_whitelist %w[jpg jpeg gif png] end # Override the filename of the uploaded files: # Avoid using model.id or version_name here, see uploader/store.rb for details. # def filename # "something.jpg" if original_filename # end end
ビュー
ユーザー詳細ページと編集ページのビューは以下のように記載
#show.html.erb <% content_for :title do %> プロフィール <% end %> <div class="container"> <div class="row"> <div class="col-md-6 offset-md-1 col-lg-6 offset-lg-2"> <h1><%= t '.title' %></h1> </div> <div class="col-md-4 offset-md-1 col-lg-2 offset-lg-1"> <%= button_to t('.button'), {controller: 'profiles', action: 'edit' }, method: :get, class: "btn btn-success float-right" %> </div> <div class="col-md-11 offset-md-1 col-lg-9 offset-lg-2"> <table class="table"> <tr> <td> <strong><%= User.human_attribute_name(:email) %></strong> </td> <td><%= current_user.email %></td> </tr> <tr> <td> <strong><%= t('.name') %></strong> </td> <td><%= current_user.decorate.full_name %></td> </tr> <tr> <td> <strong><%= User.human_attribute_name(:avatar) %></strong> </td> <td><%= image_tag current_user.avatar.url,class: 'rounded-circle mr15',width: '40',height: '40' %></td> </tr> </table> </div> </div> </div> #edit.html.erb <% content_for :title do %> プロフィール編集 <% end %> <div class="container"> <div class="row"> <div class=" col-md-10 offset-md-1 col-lg-8 offset-lg-2"> <h1><%= t '.title' %></h1> <%= form_with model: current_user, url: profile_path, method: :patch, local: true do |form| %> <%= render 'shared/error_messages', object: form.object %> <div class="form-group"> <%= form.label :email, User.human_attribute_name(:email) %> <%= form.text_field :email, class: "form-control" %> </div> <div class="form-group"> <%= form.label :last_name, User.human_attribute_name(:last_name) %> <%= form.text_field :last_name,class: "form-control" %> </div> <div class="form-group"> <%= form.label :first_name, User.human_attribute_name(:first_name) %> <%= form.text_field :first_name,class: "form-control" %> </div> <div class="form-group"> <%= form.label :avatar, User.human_attribute_name(:avatar) %> <%= form.file_field :avatar,class: "form-control" %> </div> <div class="actions"> <%= form.submit t('defaults.update'),class: "btn btn-primary" %> </div> <% end %> </div> </div> </div>
レイアウトは
Bootstrapの使い方 導入方法と基本・レスポンシブデザインを徹底解説 - WEBST8のブログ
あたりを参考に作成
参考文献