laravelの学習として作っている掲示板で、プロフィール画像をアップロード&変更する、という機能を実装しています。
こんな感じで、初期値はnoimageという画像が表示される設定で、マイページからプロフィール画像を簡単に変更できる仕様です。
今回の記事では
- プロフィール画像の初期設定
- ユーザーがプロフィール画像を登録するためのフォーム設置
- DBに保存 & ブラウザで表示
について、参考になったサイトや実際書いたコードについてまとめています。
全てのコードは載せておりませんので、もしまだlaravelを触ったことのない方でしたら、こんな感じで書けばこうなるんだなーくらいで見ていただければ幸いです。
参考サイト様
今回の実装をするにあたり参考させていただいたサイト様を先にご紹介します。
- 画像のライブプレビュー機能・・・リンク先が無くなりました
- laravelでの画像アップロード・・・Laravelで画像をアップロードする方法
- laravelで画像のパスをDBに保存・・・Laravelで画像ファイルアップロードをする簡単なサンプル
主に2つめのむちょこさんの記事から、アップロードの流れや細かなコードをお借りしています。
そして今回はデータベースを使いたかったので、3つめのサイト様からデータベース保存のコードをお借りしました。
laravel(PHP)で画像保存とは
先に、私自身が画像を保存することについてちゃんと理解していなかったので、簡単にまとめてみます。
ユーザーが画面から登録ボタンを押したら、その画像は
データベース(DB)へ格納される・・・
のではなく、
プロジェクトフォルダのディレクトリ内に画像を保存し、
画像のパスをDBに保存します。
もちろん画像自体をデータベースに保存することもできますが、データサイズが大きい画像をデータベースにあげることは良いことではないようです。
laravelで画像をアップロードする流れ
では画像アップロードの流れを追ってゆきましょう〜
初期画像の設定
デフォルトではこのようにNo Imageと書かれた画像が表示されるように設定します。
- デフォルト画像を用意する(今回はnoimage.png)
- public配下の任意のフォルダに画像を入れておく
- ユーザー登録時、自動でデフォルト画像のパスが保存されるようにする
この流れを実現させるためには、以下のように設定してゆきます。
①テーブルに画像パス保存用のカラムを追加
usersテーブルにパスを保存するためのカラムを作成します。
今回はすでにusersテーブルを作ってしまっていたので、
php artisan make:migration add_my_pic_to_users_table
というコマンドで(ファイル名は任意)カラム追加用のマイグレーションファイルを作成しました。
upの部分はこんな感じ
public function up() { Schema::table('users', function (Blueprint $table) { $table->string('my_pic')->default('noimage.png'); }); }
これで、パスを保存する my_pic というカラムと、デフォルトで noimage.png というデータを与える設定ができました。
public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('my_pic') ; }); }
モデルにも追記します。
app/User.php ファイルの$fillableのところに
protected $fillable = [ 'name', 'email', 'password', 'my_pic', ];
‘my_pic’,を追加。
コントローラーも修正。
app/Http/Controllers/Auth/RegisterController.php
にユーザー登録に関わるコントローラーの内容が記載されているので、
その中のcreate(データベース登録)の部分に追記。
protected function create(array $data) { return User::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), 'my_pic' => 'noimage.png', ]); }
最後の行で、’noimage.png’が入るよう指定しています。
画像をドラッグ&ドロップでライブプレビュー
画像のライブプレビュー、HTML構造?的な部分だけをまとめておきます。
最初何がどうなっているのか理解できなかったので・・・
こんな感じでformタグに囲まれたタグ達がレイヤーになっていて(position:absolute;でレイヤーにする)、それぞれのタグで役割が別れています。
ポイントは画像ファイルを受け取るインプットタグ。
一番下に配置してしまうとドラッグアンドドロップができなくなってしまうので、z-indexでちゃんと一番上にくるようにします。
オレンジ色のプレビュー画面を表示するタグですが、このsrc=””にはjsでライブプレビュー用の一時的なパスを挿入する仕様です。
ディレクトリに保存&DB登録
ブラウザから画像を受け取るための、inputタグを改めて見てゆきます。
<input type="file" name="myPic" class="input-file">
- type=”file” とすると画像などのファイルを受け取れるようになります
- name属性はこのあとコントローラーで処理するために必要になります
- classはcssのために必要
上記インプットタグから受け取った画像データは以下の流れで処理されます。
まずはルート。
routes/web.php 内
Route::post('/mypage', 'Auth\TimelineController@storeMyImg');
こちらは、
第二引数の Auth\TimelineController の storeMyImg というコントローラーの処理を通る、という意味です。
その storeMyImg というコントローラーの記述はこんな感じ。
app/Http/Controllers/Auth/TimelineController.php 内
public function storeMyImg(ProfileRequest $request) { //画像ファイルに名前をつけて指定ディレクトリに保存、変数に代入 //postで受け取ったデータ($request)の中にある myPic(ネーム属性)を、第二引数”ユーザーid+日付.jpg”の名前で第一引数のディレクトリに保存 $filepath = $request->myPic->storeAs('public/profile_images', Auth::id() . date("YmdHis"). '.jpg'); //ユーザーIDからユーザー情報を取得、変数に代入 $user = User::find(auth()->id()); //ユーザー情報からmy_picカラムのデータをピックアップし、 //$filepathのファイル名の部分のみをmy_picカラムに代入 $user->my_pic = basename($filepath); //保存 $user->save(); //ルート名 showMypage へ移動。フラッシュメッセージのデータも一緒に。 return redirect()->route('showMypage')->with('success', '新しいプロフィールを登録しました'); }
$filepath に入るデータをダンプしてみると(dd(変数名); でダンプできます)
"public/profile_images/520191228112710.jpg"
このように、ディレクトリ+ファイル名の文字列が入っています。
そのあとに続く処理に出てくる basename($filepath); で、最後のファイル名の部分のみを取得し、
my_picカラムに代入。
続いて
$user->my_pic = basename($filepath);
とすることで、
DBへの保存に関しては、こちらの記事を参考にさせていただきました。
Laravelで画像ファイルアップロードをする簡単なサンプル
バリデーション
画像をポストするときにバリデーションを行う場合、
コントローラーにバリデーションを記述してもいいですが、以下のように
カスタムリクエストクラスにファイルを作成して、そちらにバリデーション内容を書くほうが後々良いそうです。
1行目の()内にある ProfileRequest がリクエストのファイル名です!
リクエストクラスのファイルは、コマンドに
$ php artisan make:request ProfileRequest
と入力すると作成できます。
app/Http/Requests/ProfileRequest.php 内一部抜粋
public function rules() { return [ 'myPic' => 'required|file|image|mimes:jpeg,png,jpg,gif|max:2048' ]; }
作成したファイルのrulesの中に、ネーム属性( この場合inputタグに書いたネーム属性 )に対してバリデーションを指定しています。
エラー表示
バリデーションでひっかかったらエラー表示をしてほしいてますよね。
こちらはむちょこさんのコードをそのままお借りしています。
HTMLコードを貼るだけでちゃんとエラー表示してくれるなんて、larvalすごい。
ビューで画像を表示
保存した画像をビューテンプレートで使うには
<img src="/storage/profile_images/{{ $mypic }}">
表示したい場所に、このように
imgタグのsrcにディレクトリとDBのファイル名を挿入ればOKです。
これで晴れて、
画像を保存&表示できるようになりました・・・!
ドキュメント読もう
色々なところからコードをお借りしてなんとか作成した状態なので、リファクタできる要素が十分にあるのではないかと思います。
一番気になる点としては、
DBのデータ(画像ファイル名)は新しく保存するたびに上書きされて消えるけど、
フォルダに保存した画像ファイルはどんどん溜まってしまうこと。
最新のだけ残してあとは自動で削除されるようにしたい。
ちゃんとドキュメント読んで理解してゆかないと、です。
ドキュメントはなんだか読みにくくてあまり好きではないのですが、そうも言ってられない…