やりたいこと formから、画像をアップロードしてそれを表示させる。
Laravelで画像ファイルなどをフォームから送って、それを表示させたい。
流れとしては画像をアップロードしてLaravelに保存→そのパスをDBに保存→保存したパスの情報を元に画像を表示という感じだ。
備忘録としてまとめます。
実際に作って確かめたい人は適当なLaravelプロジェクトを作成しておこう。1個くらいすぐに練習できる環境を作っておくと楽だ。コントローラーやビューファイルは記事を読んでいきながら適宜作ってみてください。
Bladeにformを用意する
まずは、画像をアップロードできるように、Bladeにformを用意する。
画像ファイルはDBに直で保存はしない。様々なデメリットの方が多いらしいので、DBには、画像ファイルのパスだけ保存しておくみたい。画像自体はLaravelプロジェクトの中に入っていく。(後述)
Bladeファイルを適当に用意して、以下のように記述していこう。
formタグにenctype="multipart/form-data"属性を用意する。ファイルアップロードをするためにはこれを書く必要がある。
<form action="/upload" enctype="multipart/form-data" method="post">
@csrf
<input type="file" name="imgpath">
<input type="submit" value="アップロードする">
</form>
web.phpでルーティングをして、上で作ったBladeファイルにアクセスしよう。ここまでよくわからないという人は、まずはLaravelのMVCモデルを理解しよう。そのうち記事でも作るかもしれないが、他にもたくさん優良の記事がネットに落ちているので調べてみよう。アクセスしたら以下のような表示になるはず。
コントローラーを作成して、送ったデータを確認
次に、送った画像ファイルをコントローラー側のstoreメソッドでとってくる。コントローラーを作成して、コードを記述したら、ファイルを送ってみよう。ddで確認するとUploadFileインスタンスに情報が保持されていることがわかる。ざっくり言うと、画像データを送ってそれがちゃんと取れてるか確認しただけだ。
//作成したコントローラーに記述
//$request->inputのname属性でつけた名前でデータがとれる。(この場合imgpath)
public function store(Request $request){
$img=$request->imgpath; //formで設置したname名
//$img=$request->file(imgpath); fileメソッドを使うとファイル?だけが取れるそうだが変わらなかったので使っても使わなくてもどっちでも良いと思う。
dd($img);
}
そして、UploadFileインスタンスはstoreメソッドとsotreAsメソッドを持っており、これを使ってLaravel側に保存する。
両者の違いは保存される画像ファイルの名前がどうなるかだ。
storeメソッドを使ってみよう
とりあえず、storeメソッドを追記して、画像を送ってみよう。store(' ')の「' '」を忘れずに!
public function store(Request $request){
$img=$reauest->imgpath->store(''); //storeメソッドを追加
}
送っただろうか?
では、アップロードした画像ファイルはLaravelプロジェクトのどこへ保存されるのか確認していこう。
Laravelでは、Laravelプロジェクトのstorage/appフォルダ直下に入るようになっている。
store(' ')
の( )に('好きな名前')をつけると、storage/app/好きな名前
直下にはいる。名前をつけフォルダが作成されるわけだ。
storeAsメソッドを使ってみよう
storeメソッドを使えば、Laravelプロジェクトの中にファイルを保存することができた。
ただ、storeメソッドだとファイル名が任意に決められず、勝手に名前が付けられてしまう。任意の名前をつけたい場合にはstoreAsメソッドを使おう。
名前の付け方はなんでもいいが、ファイルについていた元々の名前をそのまま付けたい場合はgetClientOriginName
メソッドを使いオリジナルの名前を取得する。
public function store(Request $request){
$titlename=$request->imgpath->getClientOriginalName()
//getClientOriginalNameでオリジナルの名前が取れる。
}
そして、storeAsの引数に突っ込む。
public function store(Request $request){
$filename=$request->imgpath->getClientOriginalName()
//getClientOriginalNameでオリジナルの名前が取れる。
$img=$request->imgpath->storeAs('',$filename);
//storeAsメソッドを追加して引数に上で取得したオリジナル名を入れる。
}
storeメソッドとstoreAsメソッドの使い方はこんな感じだ。なんとなくわかっただろうか。以下少し発展要素となるので余裕のある人は軽く目を通しておいたらいいかも。
発展
実は、storeメソッドは2つの引数、storeAsメソッドは3つの引数を持つ。
storeメソッドは1.フォルダ(詳しくはパス名だが)2.ディスク名
storeAsメソッドは1.フォルダ(詳しくはパス名だが)2.ファイルネーム 3.ディスク名
となっている。
この3番目のディスク名は正直初心者はよくわからなくてもよいと思う。ただ、このディスク名にpublic
を指定しておけば、1.フォルダ名
に名前をつけなくてもstorage/app/public/
に画像が保存される。
じゃあ「最初からやれよ!」と言われるかもしれないが、書きながら勉強していたので勘弁してください。。何事も基礎の基礎からわかった方がいいしね。。。
pathは空白にしておけばよいので、store(' ', public)
storeAs(' ',$filename,public)
だけで勝手にstorage/app/public
下に画像が保存される。しかも、DBにパスを保存するときはこっちの方が都合がよかったりもする。もっと詳しく知りたい人は公式ドキュメント飛んでってください。config/filesystem.php
でディスクの管理ができる。
画像ファイルのパス名をDBに保存する
さて、これでアップした画像がLaravelのstorage/app/public/
直下に保存された。(storage/app/
直下に入れている人はここでVScodeなどを使ってファイルを移動しておいて欲しい。後々Blade側で表示する時にこっちの方が都合が良いので。)
storeメソッドとstoreAsメソッドの返り値は保存したファイルのパス名なので、あとは、そのパス名をDBに保存してあげる。
下のコードでDBへ画像が保存される。
※なお、都合上storeAsメソッドを使用し、第一引数は空、第三引数はpublicを指定してある。こうすると、storage/app/public/
直下に画像が保存される。気になる人は↑の発展を見ておいてほしい。
class Practicecontroller extends controller
{
public function store(Request $request)
{
//画像のオリジナルネームを取得
$filename = $request->imgpath->getClientOriginalName();
//画像を保存して、そのパスを$imgに保存 第三引数に'public'を指定
$img = $request->imgpath->storeAs('',$filename,'public');
//ユーザークラスのインスタンス化
$user = new User();
//imgpathカラムに画像パスを挿入
$user->create(['imgpath'=> $img]);
return view('practice');
}
}
storeAsメソッドの返り値はあくまでパスだ。画像自体はstorage/app/public/
下に保存されており、DBに保存されてるわけではない。
今回Userモデルにimgpathを用意して、createメソッドで保存した。DBへの保存の方法はネットにたくさんやり方が落ちてるので確認してほしい。
シンボリックリンクを作成する
さあ、これでデータは保存された。後はBlarde側で表示させるだけだ。ここで、シンボリックリンクが登場する。
シンボリックリンクに関してはわからない方はを↓を参照!
Laravelの仕様上、ローカルではLaravelproject/publicになっているが、サーバーにアップした段階でpublicがルートフォルダ(一番上のフォルダ)になる。publicの中に画像を保存して置かないと、画像が表示されないのだ。画像はstorage/app/
以下に保存されるのにどうやって表示するのだろうか?
そこで、php artisanコマンドを使ってシンボリックリンクを作成する。以下のコマンドをLaravelprojectフォルダで打つ。(artisanファイルがあるところ)
ただし、このシンボリックリンクのコマンドを打つ場所はコンテナの中プロジェクト直下だ!docker-compose exec workspace bash
でworkspaceに入ったらLaravelprojectまで移動して以下のコマンドを打とう。(DockerとかLaradockでLaravel使っている人は!)
自分はこのコマンドを、コンテナの外で行ったことにより、画像が表示されず1日彷徨ってしまった、、
//適切な場所でコマンドを入力しよう。laradock使っている人はコンテナの中だ
php artisan storage:link
コマンドを打つとちょっと長いが、シンボリックリンクが作成される。シンボリックリンクが作成されたらpublicフォルダを覗いてみよう。
publicフォルダでls -l
コマンドを打つことでも確認できる。
Blade側で画像を表示する
上記の過程で、Laravelproject/public/storage
とLaravelproject/storage/app/public
がリンクされた。
storageという単語とpublicという単語がたくさん出てくるので混乱するかもしれないが、自分がどのフォルダとどのフォルダをリンクさせているかを意識して行うと理解しやすいかもしれない。
最後に、Blade側で画像を表示させれば完了だ。
asset()関数を使い以下のように表示する。最初は、DBからパスを読み込むのではなく直接ファイルパスを指定して表示させてみる。
※asset()関数は、URLを生成してくれる関数だ。
//二郎.jpgを直接記述。
<img src=" {{ asset('storage/二郎.jpg')}}">
一旦画像のサイズは置いといて、次にDBに保存してあるパスをとってきてそれを反映させてみる。該当のファイルパスをとってくるやり方は色々あると思うが下のは一例だ。
また、この下のコードでなぜDBから画像パスが取得できるのかわからない人は「データをDBに保存→DBから欲しいデータを引き出す→表示させる」といった練習をしてみると良いかも。
class Practicecontroller extends controller
{
public function store(Request $request)
{
//画像ファイルオリジナルの名前を取得。
$filename = $request->imgpath->getClientOriginalName();
//storeAs関数でstore/app/publicに画像を保存し、そのパスを$imgに入れる。
$img = $request->imgpath->storeAs('',$filename,'public');
//ユーザークラスのインスタンス化
$user = new User();
//Userテーブルのimgpathカラムに画像パスを挿入 $dataには画像パスを挿入したUserのレコードが取得されている。
$data = $user->create(['imgpath'=> $img]);
//dataをcompactを使ってpractice.bladeに送る。
return view('practice',compact('data'));
}
}
画像のサイズは一旦置いといて、これで画像を表示させることができた。DBからわざわざパスを持ってきて表示させる必要はないかと思うかもしれないが、実際に何かしらのアプリを作る時はDBに保存してその保存されたデータを引き出して表示させることが必要になってくる。特にLaravelで画像投稿付きのアプリを作ろうと思っている人は参考にしてもらえればと思います。
参考:画像のサイズを変更して保存する。
画像サイズを変更する場合はIntervention Imageという外部のライブラリを使った。DBにはファイル名を保存して、実際のLaravelの中にはリサイズされた画像を保存してある。参考にコードは以下のようなものだ。
外部のライブラリの使い方や、画像サイズ変更はまた別の記事でしっかりまとめていきます。
まとめ
ここまでを改めて整理する。
- bladeでformに
enctype="multipart/form-data"
とinput type file name="好きな名前
"を用意 - コントローラー側でそのファイルを受け取り、storeかstoreAsメソッドでLaravelproject側に保存
- storeかstoreAsメソッドの返り値(パス)をDBのカラムに保存する。
- DBから、そのパスを引き出してblade側で表示する。
流れとしては非常に単純だが、これを初心者が実際に行うのは大変だ。自分もものすごく時間がかかった、、。一応なんとか自分なりにまとめることはできたと思う。
コードはもっと綺麗に書けるのかもしれないが、そこらへんは多めに見てください、、!
画像ファイルを送るだけでこんなにややこしいとは。頑張るしかない、、!!