【個人アプリ開発メモ】プロフィール画像の保存・表示

## やりたいこと
プロフィール画像の登録ができるようにしたい。
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
```
とやると、

```ruby:20200820065846_add_image_to_users.rb
class AddImageToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :image, :string
  end
end
```
![スクリーンショット 2020-08-20 午後4.03.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547640/a79bfd70-cc22-9bdc-aeea-32b401dcd8c3.png)

と自動的に作ってくれる。

それであとは

```ターミナル
rails db:migrate
```
をやればOK。

これでusersテーブルにimageカラムが作れた。
![スクリーンショット 2020-08-20 午後4.03.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547640/bdc79ae1-c0e5-55c3-f6af-969027bcf215.png)

## ユーザー情報登録画面に画像登録用のフォームを作る

```new.html.haml
#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'
```
![スクリーンショット 2020-08-20 午後4.43.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547640/8fbbaf30-3e39-27ca-aecd-b9409887d2b8.png)
こんな感じで設置できた。

あとはここから画像を保存できるようにしておく。

現状このまま保存しようとするとどうなるか、一応やってみたら、エラーは出なかったが、usersテーブルのimageカラムがNULLになった。

## やることは2段階に分けられる
* 保存する段階
* 表示する段階

まず保存する段階から始めよう。
https://qiita.com/takasu97/items/646e356f5800d10aa548
こちらの記事を参考に進めていく。

### プロフィール画像を保存するための設定
まず画像を保存したいモデルImageUploaderの記述をする。

```ruby:/app/models/user.rb
mount_uploader :image, ImageUploader
```

次にストロングパラメーターにimageカラムを追加する。
投稿画像の保存の時も同じ実装をした。

```ruby:/app/controllers/users_controller.rb
private
  def user_params
    params.require(:user).permit(:id, :name, :email, :image)
  end
```
これでやってみたがうまくいかなかった。

`/app/controllers/users_controller.rb`のcreateアクションにbinding.pryを設定してもかからなかった。

ということは、動いているのはusers_controllerではない？

```ruby:ターミナル
Processing by Users::RegistrationsController#create_standard as HTML
```
ターミナルを見てみると、RegistrationsControllerのcreateアクションが動いてるとわかった。

自分のアプリの場合、ウィザード形式で、プロフィール情報の入力が2ページになってることが関わってると思う。

1ページ目で、ユーザー名、メールアドレス、パスワード、画像。
2ページ目で、目標カロリー、体重、PFCなど。
を登録するようになってる。

```ruby:/app/controller/users/registrations_controller.rb
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を設定した結果、

```ruby:ターミナル
[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

```ruby:/app/controller/users/registrations_controller.rb
def sign_up_params
  params.require(:user).permit(:name, :email, :password, :password_confirmation, :image)
end
```

```ruby:ターミナル
[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になってしまってること。

```ruby:ターミナル
[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} → ここが問題
```

```ruby:/app/controller/users/registrations_controller.rb
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　全ての属性
を取得

https://www.sejuku.net/blog/64345

### 現状を整理しよう
* sign_up_paramsにはimage情報は保存されている。
* `@user = User.new(sign_up_params)`の@userにはimage情報は保存されていない。
* @user.attributesにもimage情報は保存されていない。

やっぱり

```ruby：/app/controller/users/registrations_controller.rb
@user = User.new(sign_up_params)
```
これで右から左に情報が渡されてないことが問題だと思うんだよな🤔

```ruby:ターミナル
[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もデフォルトアイコンのままの人もいるし。

ということで、画像選択しなくてもデフォルト画像が表示されるようにして、
自分で画像変更したい人だけ変更できるような設定にしよう。