Laravelでの画像アップロード


皆さん、こんにちは!今回は私が、Laravelを勉強していて盛大にハマった箇所があるので今後のためにも、この記事を書くことにしました。そのハマった箇所というのが、画像をアップロードする方法です
あまり良い記事が無かったのでかなり時間を使ってしまいました。では早速いきましょう

開発環境

・Mac
・php 7.4.6
・Laravel 7.15.0
・MySQL

Laravel画像アップロード

今回は、Laravelの基礎知識があることを前提に話を進めて行きます。
今回私が作成したのは以下のようなものです。

このようなフォームを送信すると、

のようになるものです。

Model

今回は、membersテーブルにデータを挿入していくので、Memberモデルを作成します。

Member.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Member extends Model
{
    protected $guarded = ['id'];

    public static $rules = [
        'name' => 'required|max: 30',
        'year' => 'required',
        'shot' => 'required|max: 50',
        'comment' => 'required|max: 1000',
        'profile_img' => 'image|file',
    ];
}

Member.phpに書いているバリデーションは、後述するMembersController.phpで実行します。

view

create.blade.php
<form action="{{ route('admin.members.store') }}" method="POST" enctype="multipart/form-data">
  @csrf
  <input type="file" name="profile_img">
</form> 

このviewで大切なのは、formタグのenctype="multipart/form-dataです。
formで画像を扱う場合は、絶対必要になります。また、inputタグのtype属性は、fileになります。

Route

web.php
Route::get('members/first', 'MembersController@first')->name('first');
Route::resource('members', 'MembersController', ['only' => ['create', 'store', 'destroy']]);

Routeはこのようになっています。

controller

MembersController.php
public function store(Request $request)
    {
        $this->validate($request, Member::$rules);

        if ($file = $request->profile_img) {
            $fileName = time() . $file->getClientOriginalName();
            $target_path = public_path('uploads/');
            $file->move($target_path, $fileName);
        } else {
            $fileName = "";
        }

        $member = new Member;
        $member->name = $request->name;
        $member->year = $request->year;
        $member->shot = $request->shot;
        $member->comment = $request->comment;
        $member->profile_img = $fileName;
        $member->save();

        return redirect()->route('admin.members');
    }

    public function first()
    {
        $members = Member::orderBy('created_at', 'desc')->where('year', '1')->get();
        return view('admin.members.first', ['members' => $members]);
    }

コントローラが少しややこしいですよね(笑)
ここは、もっとスマートな書き方があると思いますが、、
1.

$this->validate($request, Member::$rules);

Member.phpで記述したバリデーションを実行しています。

2.

if ($file = $request->profile_img) {
            $fileName = time() . $file->getClientOriginalName();
            $target_path = public_path('uploads/');
            $file->move($target_path, $fileName);
        } else {
            $fileName = "";
        }

この部分では、$fileにformからくる画像の情報が入っています。
画像が場合は、$fileNameを空文字にしています。

getClientOriginalName() ---> 拡張子を含め、アップロードしたファイルのファイル名を取得することができる。
time() ---> タイムスタンプを取得する。
public_path() ---> publicディレクトリの完全パスを返します。ここでは、publicディレクトリ内にuploadsディレクトリを作成しています。
$file->move($target_path, $fileName) ---> 画像をpublic/uploads/に、$fileNameという名前で挿入しています。

$member = new Member;
        $member->name = $request->name;
        $member->year = $request->year;
        $member->shot = $request->shot;
        $member->comment = $request->comment;
        $member->profile_img = $fileName;
        $member->save();

ここでは、Memberモデルのインスタンスを作成して、それぞれのデータを挿入しています。
注目するべきは、$member->profile_img = $fileName;ですね。データベースには、画像の名前しか保存していません。

ここで、terminalで以下のコマンドを実行してください。

$ php artisan storage:link

これで、シンボリックリンクをはります。

最後にブラウザへ出力しましょう。

first.blade.php
@foreach ($members as $member)
 ~
<img src="../../uploads/{{ $member->profile_img }}" width="200px" height="200px">
 ~
@endforeach

今までの作業は、データベースには画像の名前だけを保存して、画像本体はpublicディレクトリ内のuploadsの中に保存したことになります。その上で、imgタグのsrc属性にデータベースから画像の名前を表示して、その名前に合う画像をpublicディレクトリ内のuploadsの中から取り出しているのです。ちなみにsrc="../../uploads/{{ $member->profile_img }}"../../の部分は、私のプロジェクトのファイル構造での話なので、ここは人により変わります。

参考にした記事、サイト

Laravelで画像アップロード

以上で終わります。間違っていたり分かりづらい箇所がありましたらコメントお願いします