質問: Rails4+Deviseでログインできるウェブサービスの練習をしています。 ``` rails g devise user rails g migration add_columns_to_users image ``` により、ユーザ画像のURLを持つimageカラムをuserモデルに追加し ``` rails g devise:views ``` によって生成されたusers#editのテンプレートファイルのform_forの中に ``` <%= f.hidden_field :image %> ``` を追加して、imageカラムの更新をusers#editからできるようにしようとしています。 このとき、input type='hidden'タグのvalueの中身は、 javascriptの他のプログラムによってcanvasのtoDataURL()の内容が入るようになっています。 toDataURL()によって出力されたDataURL文字列をそのままデータベースに保存しても動くとは思うのですが できれば、サーバ側で受け取ったDataURL文字列をファイルに変換出力して、 そのファイルのURLをimageカラムの情報として持たせたいと思っています。 (現在imageカラムは通常のstring型なのでdata too long for column 'image'...などのエラーもでます) さきほどまで、users#editから送信されたimageのデータを受け付けることもしてくれなかったのですが application_controller.rbに ``` devise_parameter_sanitizer.for(:account_update) << :image ``` を記述することにより、どうやらデータを受け付けることはできたようです。 しかし、このままだと受け取ったデータをそのままデータベースに受け渡してしまうだけで 受け取ったデータを書き換えることができていません。 DataURL文字列をファイル変換出力しURLを取得するメソッドは出来上がっているので その関数に、いくつかの情報を渡すだけでURLを取得できるのであと一歩なのですが ユーザ情報編集画面のフォームから受け取った値を書き換えて保存するにはどうすればよいのでしょうか。 --- 追記 現在、以下で教えて頂いた フォームの要素名を変更してuserモデルにgetterを作成して受け取る方法を試しています。 DataURL文字列をファイル変換出力するメソッドに関しては、その他のControllerなどで 利用することもあるので、Imageモデルのクラスメソッドとして定義しました。 これにより、userモデルのgetterメソッドが呼ばれた時にファイルを保存することが出来たのですが 保存する画像のファイル名にユーザIDを利用したくて、userモデルのgetterからcurrent_userにアクセスしてみたところcurrent_userの値にアクセスできないという問題に突き当たっています。 userモデルにgetterを作成する方法も発想として無かったのですごくためになっているのですが やはりcontrollerにコードを書いてuserモデルに渡してあげるべきなのではないかと悩んでいます。 users#editから送信されたデータがDeviseのControllerのどの部分に届くのか、さっぱりわからないのですが(そもそもControllerが存在しない?rails generateで作成するのかな?)Controllerベースでの実装方法がわかる方いらっしゃいましたら是非教えていただけないでしょうか。 (私の考え方が根本的に間違っている場合は、そこを指摘していただきたいです。)
回答: まず、カラムとフォームのフィールド名が一緒だと都合が悪いので、別のものに変更してください。ここではフィールド名を`:image_data`にしたとして説明します。 ``` class User << ActiveRecord::Base def image_data=(data) self.image = save_file(data) end def image_data #画像の読み込み open(self.image).read end private def save_file(data) #すでにあるというファイルを保存するメソッド return url end end ``` ただし、これだとimage_dataにデータを渡したタイミング(@users.image_data = dataしたタイミング)で保存が走ります。あまり頻繁に保存するのが都合が悪ければafter_saveあたりのコールバックで保存するようにしてください。 「画像をファイルに保存するメソッドをどこに定義すべきか」ですが、少なくともコントローラではありません。`User`の`id`を使用したいのであれば、 * Userモデルのインスタンスメソッドとして実装する。この場合、`self.id`で参照できます。 * 別クラスに実装し、Userモデルからそれを呼び出す時にパラメータとして`self.id`を渡す などという方法があります(他にもいろいろありますが) そのメソッドがどれだけの分量かわかりませんが、単純なメソッドならUserモデルのインスタンスメソッドとして定義してしまう(上記例のsave_fileメソッドにそのロジックを実装する)のが簡単です。 `current_user`は、「現在ログインしているユーザー」という文脈でのみ使用すべきです。たとえば、ファイル名にidを使用したいという場合に用いるidは「現在ログインしているユーザー」ではなく「その画像に紐付くユーザー」のはずです。