Laravel9とlivewireでファイルアップロードを行う。

modelを作成。

php artisan make:model Sample

「/Models/Sample.php」を開き、「$fillable」に登録する項目を設定する。項目はデータベースのカラムメイと合わせる。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Sample extends Model
{
    use HasFactory;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'text1',
        'select1',
        'file_path',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [];
}

「livewire」を作成する。

php artisan make:livewire samples

「/app/Http/Livewire/Sample.php」を開く。ここでは入力項目の登録と、ファイルのアップロードを行う。
アップロードを行うにあたり、「Livewire\WithFileUploads」を呼び出す。
また、先程作成した「Samples」モデルも呼び出す。

<?php

namespace App\Http\Livewire;

use GuzzleHttp\Psr7\Request;
use Livewire\Component;
use Livewire\WithFileUploads;
use App\Models\Samples;

「Samples」クラスでは、「WithFileUploads」を呼び出す。

class Samples extends Component
{
    use WithFileUploads;

次に「Samples」クラス内にて、ファイルの受け取りと、テキストボックス、セレクトボックスの受け取りを行う変数を作成する。

    public $files = [];
    public string $text1 = '';
    public int $select1 = 0;

「render」メソッドにて、セレクトボックス内のデータを作成する。この時、同一のカラム名をつけるとエラーとなるため、注意が必要。

    public function render()
    {
        $selSelect1 = [];
        for($i = 1;$i <= 10;$i++){
            $selSelect1[] = $i;
        }

        return view('livewire.samples', ['selSelect1' => $selSelect1]);
    }

「save」メソッドにて、ファイルのアップロードサイズや拡張子を設定する。
このとき、単一のファイルと複数ファイルとでは書き方が異なるため、例として2つ記述する。
状況に合わせ、どちらかを設定する。
なお、拡張子を書かない場合は全てのイメージをアップする、という設定となる。
容量制限を行う場合は、max値を設定する。

単一のファイルアップロード

$this->validate([
  'files' => 'required|image|mimes:jpg,jpeg,png,svg,gif|max:1024', // 1Mに制限
]);

複数ファイルのアップロード

$this->validate([
  'files.*' => 'required|image|mimes:jpg,jpeg,png,svg,gif|max:1024',
]);

複数ファイルを保存する場合、「$this->files」に画像が渡されているため、foreachにてstoreで保存する。保存場所は「storage/app/public/files/」内に保存を行うこととした。
「$aryFile」ファイルは保存後の保存場所を入れる箱として用意したもの。

        $aryFile = [];
        foreach ($this->files as $file) {
            $aryFile[] = $file->store('files', 'public');
        }

データの保存では、先程作成したsampleモデルを元にcreateで保存する。
そのほか「file_path」へ先程取得したファイル名をjson形式で保存している。



        return Sample::create([
            'text1' => $this->text1,
            'select1' => $this->select1,
            'file_path' => json_encode($aryFile),
        ]);
    }
}

「/resources/views/livewire/samples.blade.php」を開く。
formにはlivewire用の修飾子「wire:submit.prevent=”save”」を設定する。このとき「save」は先ほどのメソッド名となる。
そのほか修飾子とともに、ファイル、テキストボックスと、先程作成したセレクトボックスの値を元にセレクトボックスを作成する。

<form wire:submit.prevent="save">
<input name="files" type="file" wire:model="files" multiple>

<input type="text" name="text1" wire:model="text1">

<select name="select1" wire:model="select1">
@foreach ($selSelect1 as $v)
<option value="{{ $v }}">{{ $v }}</option>
@endforeach
</select>

<button type="submit">送信</button>
</form>

「resources/views/sample.blade.php」を開き、先ほど作成したlivewireを設定する。

@extends('layouts.app')

@section('content')

<h1>フォーム</h1>
<hr>
<livewire:samples />

@endsection

「livewire.js」を読み込む。設定箇所は「@endsection」の前とする。

@livewireScripts

この時、読み込めずに404エラーとなった場合、laravelの環境がサブディレクトリの可能性がある。
サブディレクトリでの構成は考えられていないため、404エラーを起こしている。

例:「https://example.com/」直下なら動作する。
  「https://example.com/laravel/」にインストールしていた場合、404エラーとなる。

理由として、自動的に設定されるパスが「/vendor/livewire/livewire.js」となるため。
サブディレクトリの場合、「/laravel/vendor/livewire/livewire.js」とする必要がある。

そこで「config/livewire.php」にて次の箇所を修正し、URIを考慮した作るに微調整を行う。

// 修正前
'asset_url' => null,

// 修正後
'asset_url' => url('/'),

ただし、この設定を行うと「config/{ファイル}.php」にurl()かasset()が存在すると、
「php artisan migrate、php artisan make:xxx」などのコマンドが下記のようなエラーが出力され、実行できなくなる。そのため実行する際は先ほどの設定を戻し、実行させるといった無駄な作業が発生してしまうのが難点。

Illuminate\Routing\UrlGenerator::__construct(): Argument #2 ($request) must be of type Illuminate\Http\Request, null given, called in /Users/xxx/xxx/vendor/laravel/framework/src/Illuminate/Routing/RoutingServiceProvider.php on line 67

livewireでは非同期通信にて画像等を仮保存するため、ファイルを選択した時点にて添付ファイルの取得が可能。一時ファイルは次の方法で取得できます。

@if ($files)
  @foreach ($files as $v)
    <img src="{{ $v->temporaryUrl() }}">
  @endforeach
@endif

livewireのviewの全体は以下の通り。

<form wire:submit.prevent="save">
<input name="files" type="file" wire:model="files" multiple>

@if ($files)
  @foreach ($files as $v)
    <img src="{{ $v->temporaryUrl() }}">
  @endforeach
@endif

<input type="text" name="text1" wire:model="text1">

<select name="select1" wire:model="select1">
@foreach ($selSelect1 as $v)
<option value="{{ $v }}">{{ $v }}</option>
@endforeach
</select>

<button type="submit">送信</button>
</form>

フォームのview全体は以下の通り。

@extends('layouts.app')

@section('content')

<h1>フォーム</h1>
<hr>
<livewire:samples />

@livewireScripts
@endsection