Laravel livewire を 使用して画像プレビュー、リアルタイムバリデーションを実装する


こんにちは

こんにちは。T.tsubasaと申します。

東南アジア発のスタートアップスタジオ「GAOGAO」に所属させていただいています。

普段はPHP(フレームワークはLaravel)を用いて開発案件に携わっております。
また、プログラミング研修事業「GAOGAOゲート」のメンターもしておりますので、もしご興味あれば是非ご連絡ください。

では本題に移ります。

Livewire

Laravel8からメジャーになった、Livewireを活用し画像プレビュー・リアルタイムバリデーションを実装していきます。

突然ですが、このキャラクターをご存知でしょうか・
https://laravel-livewire.com/
ドキュメントを見ていただくと、左上に可愛い、そして若干上下に動く物体をご確認いただけます。
可愛すぎる....

このマスコットキャラは「タコ」でしょうか。「クラゲ」でしょうか。
ご存知の方がいらっしゃいましたら、ご教授願います。

そんな冗談はさておき、Laravel8でメジャーになったLivewireを触ってみたので、便利だった機能をメモがてらアウトプット致します。

Livewireとはなんぞ

Livewire is a full-stack framework for Laravel that makes building dynamic interfaces simple, without leaving the comfort of Laravel.
https://laravel-livewire.com/

公式ドキュメントには、Livewireとはなんぞやが英語で記載されています。
Livewireを熟知する人物、まだLivewireを知らない人物らから繰り出される、短い掛け合いをご覧いただけます。

その掛け合いかなり意訳ですが、PHPを用いるだけで、VueやReactがなしえることを代替できるよ!と書いてあると私は認識しました!
また、実際にJSを書くことなくリアルタイムな実装がLivewireにて可能となったと感じました。

Livewireを用いることで、PHPのみでインタラクティブなフロントエンドの画面を作成することができます。
今回はLivewireを用いてアプリを開発していく中で、便利だった機能についてご紹介します。

  • Real-time Validation
  • Temporary Preview

今回は上記に2つについて、一例のコードを示しつつLivewireでの実装の容易さを感じていただきたいと思います。

Real-time Validation

Livewireではバリデーションをリアルタイムで行うことができます。
updatedフックを使用します。
下記は一例です。

app/http/livewire/students.php

//省略

class Students extends Component
{
    public $firstname;
    public $lastname;
    public $email;
    public $phone;
    public $image;
    public $photoStatus = true;
   
    // validationのルールを定義
    protected $rules = [
        'firstname' => 'required|min:3',
        'lastname'  => 'required|min:3',
        'email'     => 'required|email',
        'phone'     => 'required',
        'image'     => 'image|max:1024|mimes:jpeg,png,jpg,gif',
    ];

    // inputタグが更新された際に、validateを行う。validateOnlyメソッドはinputタグ一つ一つに対してvalidateを行うことができます。
    public function updated($propertyName)
    {
        $this->validateOnly($propertyName);
    }

    // 以下には、保存の処理等を記入しているが今回は省略。
}

Bladeは、普段のformと少し変わっています。

// create.blade.php

<form>
    <input type="text" wire:model="firstname">
    @error('firstname') <span class="error">{{ $message }}</span> @enderror

    <input type="text" wire:model="lastname">
    @error('lastname') <span class="error">{{ $message }}</span> @enderror

    <input type="text" wire:model="email">
    @error('email') <span class="error">{{ $message }}</span> @enderror

    <input type="text" wire:model="phone">
    @error('phone') <span class="error">{{ $message }}</span> @enderror

    <button type="submit">Save Contact</button>
</form>

Temporary Preview

画像をアップロードした際に、アップロードした画像をPreviewで表示したい時に非常に便利です。
Livewireを使用しない場合には、JSでコードを書く必要がありますがLivewireを使用することで簡単に画像のプレビューを実装することができます。

// create.blade.phpのform内に追記。

<input type="file" wire:model="image">
    @error('image') <span class="error">{{ $message }}</span> @enderror
            @if ($image)
                @php
                    try {
                        $url = $image->temporaryUrl();
                        $photoStatus = true;
                    }catch (RuntimeException $e){
                        $this->photoStatus = false;
                    }
                @endphp
                @if($photoStatus) <img src="{{ $url }}" class="w-auto h-64"> @endif
            @endif

PreviewはtemporalyUrl()メソッドを用いることで簡単に表示することが可能となります。

@phpを使用し、かなり複雑(読みにくい)コードが繰り出されていますが、こちらは故意です。
@if($image)以下は、<img src="{{ $url }}" class="w-auto h-64">
だけでも綺麗に動くように見えます....(見えるだけ)

しかし、その実装にした場合にはプレビュー画像を表示できない為、.heicなどの画像をアップロードすると、エラー画面が表示されてしまいました。

validationも反応しつつ、画像のプレビューも表示しようとしてしまうのです...
実際に試していただくと、バグが起きていることをご理解いただけると思います。
このあたりは非常に使いにくいので、アップデートを期待ですね。

今回のこの条件分岐は
https://github.com/livewire/livewire/issues/1106
こちらを参考に実装しました。先人感謝。

Livewireを使用してみて

Laravelでの開発を主としている方にとっては非常に便利だと感じました。
しかし、いざLaravelではないフレームワークを使用するとなった際にはLivewireを使用することはできません。

私が少しアプリを作成する中で感じた感想と致しましては、Laravelを使っていてPHPだけで簡易的なリアルタイムなバリデーションを行いたい方にはとてもオススメできるものであるなと感じます。

一方で、Vue.jsなどを学習している方に関しての、Livewireを活用するメリットとしては
簡単な実装であれば、Livewireを活用することですぐに実装できる点であると思います。

いずれは、PHPだけでフロントエンドまでリッチに実装できるのかな.....

私もLaravel8に関してはまだまだ勉強せねばならぬと感じつつ、本記事を終了にさせていただきます。

コメント等ございましたら。お気軽にご連絡ください!