やりたいこと
プロフィール画像の登録ができるようにしたい。
Twitterのように各投稿にユーザープロフィール画像が表示できるようにしたい。
まずはプロフィール画像を登録できるようにしよう。
usersテーブルに画像を保存するためのimageカラムを作ろう。
usersテーブルにimageカラムを作る
https://qiita.com/azusanakano/items/a2847e4e582b9a627e3a#%E3%82%AB%E3%83%A9%E3%83%A0%E8%BF%BD%E5%8A%A0
こちらの記事を参考にimageカラムを追加した。
ターミナルで
rails generate migration AddImageToUsers image:string
とやると、
class AddImageToUsers < ActiveRecord::Migration[5.2]
def change
add_column :users, :image, :string
end
end
と自動的に作ってくれる。
それであとは
rails db:migrate
をやればOK。
ユーザー情報登録画面に画像登録用のフォームを作る
#account-page.account-page
.account-page__inner.clearfix
.account-page__inner--left.account-page__header
%h2 Create Account
%h5 新規アカウントの作成
= render "devise/shared/links"
.account-page__inner--right.user-form
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= devise_error_messages!
.field
.field-label
= f.label :name
.field-input
= f.text_field :name, autofocus: true
.field
.field-label
= f.label :email
.field-input
= f.email_field :email, autocomplete: "email"
.field
.field-label
= f.label :password
%i (英数字6文字以上)
.field-input
= f.password_field :password, autocomplete: "off"
.field
.field-label
= f.label :password_confirmation
.field-input
= f.password_field :password_confirmation, autocomplete: "off"
#ここの部分を追加した
.field
.field-label
= f.label :プロフィール画像
.field-input
= f.file_field :image, class: 'userimage_upload'
.actions
= f.submit "Next", class: 'btn'
あとはここから画像を保存できるようにしておく。
現状このまま保存しようとするとどうなるか、一応やってみたら、エラーは出なかったが、usersテーブルのimageカラムがNULLになった。
やることは2段階に分けられる
- 保存する段階
- 表示する段階
まず保存する段階から始めよう。
https://qiita.com/takasu97/items/646e356f5800d10aa548
こちらの記事を参考に進めていく。
プロフィール画像を保存するための設定
まず画像を保存したいモデルImageUploaderの記述をする。
mount_uploader :image, ImageUploader
次にストロングパラメーターにimageカラムを追加する。
投稿画像の保存の時も同じ実装をした。
private
def user_params
params.require(:user).permit(:id, :name, :email, :image)
end
これでやってみたがうまくいかなかった。
/app/controllers/users_controller.rb
のcreateアクションにbinding.pryを設定してもかからなかった。
ということは、動いているのはusers_controllerではない?
Processing by Users::RegistrationsController#create_standard as HTML
ターミナルを見てみると、RegistrationsControllerのcreateアクションが動いてるとわかった。
自分のアプリの場合、ウィザード形式で、プロフィール情報の入力が2ページになってることが関わってると思う。
1ページ目で、ユーザー名、メールアドレス、パスワード、画像。
2ページ目で、目標カロリー、体重、PFCなど。
を登録するようになってる。
def create
binding.pry
# 1ページ目で送られてきたパラメータを@userに代入
@user = User.new(sign_up_params)
# 送られてきたパラメータがバリデーションに違反していないかチェック
unless @user.valid?
# エラーメッセージを出す
flash.now[:alert] = @user.errors.full_messages
# newアクションへrenderする
render :new and return
# 同じアクション内でrenderを2回記述する場合はand returnを使用する
end
# attributesで全ての属性を取得しsessionで保存
session["devise.regist_data"] = {user: @user.attributes}
# passwordを保存
session["devise.regist_data"][:user]["password"] = params[:user][:password]
standard = @user.standard
@standard = @user.build_standard
render :new_standard
binding.pry
end
このようにbinding.pryを設定した結果、
[1] pry(#<Users::RegistrationsController>)> sign_up_params
Unpermitted parameter: :image
=> {"email"=>"nakamura@text", "password"=>"nakamura", "password_confirmation"=>"nakamura", "name"=>"中村憲剛"}
sign_up_paramsというストロングパラメーターに1ページ目で入力した情報が保存されてることがわかった。
Unpermitted parameter: :image
というエラーが出ているが、これはまだsign_up_paramsにimageカラムを追加してないので当然だ。
ここでちょっと謎なのが、sign_up_paramsがプライベートメソッドで設定されてないのだ。
なぜこれで画像以外が保存できてるのかがわからない。
sign_up_paramsのカラム追加をどこで設定するのかがわからない。
registrations_controller.rbのsign_up_paramsにimageカラムを追加する
なんてことはなかった。
普通に/app/controller/users/registrations_controller.rbに以下の記述を付け足せばそれでいけた。
参考にした記事:https://qiita.com/nola1176/items/df75534c73320324601b
def sign_up_params
params.require(:user).permit(:name, :email, :password, :password_confirmation, :image)
end
[1] pry(#<Users::RegistrationsController>)> sign_up_params
=> <ActionController::Parameters {"name"=>"中村憲剛", "email"=>"nakamura@text", "password"=>"nakamura", "password_confirmation"=>"nakamura", "image"=>#<ActionDispatch::Http::UploadedFile:0x00007fc7f728d0e0 @tempfile=#<Tempfile:/var/folders/sy/26p55v5j47s2zjd8h7vr8f6h0000gn/T/RackMultipart20200821-38316-1jyviz2.png>, @original_filename="soccer_lifting.png", @content_type="image/png", @headers="Content-Disposition: form-data; name=\"user[image]\"; filename=\"soccer_lifting.png\"\r\nContent-Type: image/png\r\n">} permitted: true>
しかし、それでもMySQLに画像データ保存されない。
なぜだ?
考えられる原因は、
@user.attributes
のimageがnilになってしまってること。
[2] pry(#<Users::RegistrationsController>)> @user.attributes
=> {"id"=>nil,
"email"=>"nakamura@text",
"encrypted_password"=>"$2a$11$gLxNn5a08j6iez6lvrL1QuWxXe1WSJvPxsYQC4D7QKIOUjvjcd49S",
"reset_password_token"=>nil,
"reset_password_sent_at"=>nil,
"remember_created_at"=>nil,
"created_at"=>nil,
"updated_at"=>nil,
"name"=>"中村憲剛",
"image"=>nil} → ここが問題
def create
# 1ページ目で送られてきたパラメータを@userに代入
@user = User.new(sign_up_params)
# 省略
# ↓@user.attributesに保存されてないので当然sessionにも保存されてない
# attributesで全ての属性を取得しsessionで保存
session["devise.regist_data"] = {user: @user.attributes}
# passwordを保存
session["devise.regist_data"][:user]["password"] = params[:user][:password]
# 省略
end
次の課題は、どうやって@user.attributesにimage情報を保存するか。
attributesとは?
モデルに含まれる全ての属性のこと。
今回はuserモデルだから、name, email, password, image。
@user.attributesはどういう意味?
@user そのユーザーの
attributes 全ての属性
を取得
現状を整理しよう
- sign_up_paramsにはimage情報は保存されている。
-
@user = User.new(sign_up_params)
の@userにはimage情報は保存されていない。 - @user.attributesにもimage情報は保存されていない。
やっぱり
@user = User.new(sign_up_params)
これで右から左に情報が渡されてないことが問題だと思うんだよな🤔
[1] pry(#<Users::RegistrationsController>)> User.new(sign_up_params)
=> #<User id: nil, email: "nakamura@text", created_at: nil, updated_at: nil, name: "中村憲剛", image: nil>
調べてみたら、右辺のUser.new(sign_up_params)
の時点でimageがnilになっていた。
なぜだ?
ここでやり方を変えることにする
今までの過程では、ユーザー新規登録画面でユーザーに画像を設定してもらう実装で進めていたけど、
考えてみれば、画像を設定しない人もいるよな。
Twitterもデフォルトアイコンのままの人もいるし。
ということで、画像選択しなくてもデフォルト画像が表示されるようにして、
自分で画像変更したい人だけ変更できるような設定にしよう。