Laravelの認証ライブラリであるJetStreamやBreezeをインストールする際にLivewireかInertiaを選択する箇所があります。Livewire、Inertiaどちらも認証機能のフロントエンド部分の認証画面やログイン画面を構成するファイル内で利用されています。

認証ライブラリのインストール時にLivewireとInertiaの違いがわからず悩まないように本文書ではLivewireについての基本的な動作を確認しながらLivewireがどういうものなのかの理解を深めていきます。動作確認はLaravel10を使っています。

Livewireとは

Livewireを利用することでJavaScriptのフレームワークのVue.jsやUIライブラリのReactと同様にページをリロードすることなくページ内容を更新することができるフロントエンドの技術です。通常はページをリロードすることなくページの更新を行うためにはJavaScriptでコードを記述する必要がありますが、LivewireではJavaScriptではなくPHPのみを利用してコードを記述することができます。バックエンドとの通信にはAjaxを利用していますがLivewireがバックエンドとの通信の処理をすべて行ってくれるのでVue.jsやReactのようにfetch関数、axiosライブラリ、GraphQLなどを組み込んだAPI用のコードを記述する必要がありません。

Livewireでもライフサイクルフックやバインドという言葉が出てくるためVue.jsやReactと比較しながら考えることもできるのでVue.jsやReactの学習者の方が理解度も早いように思います。しかしフロントエンドの技術の習得者であればInertia(Vue.jsまたはReactを利用)を選択する可能性が高いためLivewireを利用する機会は少ないかもしれません。Vue.jsやReactの構文と同様にLivewireにも構文が多数存在するため使いこなすためにはある程度の学習が必要となりますがLivewireではBladeの構文が利用できるためこれまでのLaravelの知識が役に立ちます。

PHPのみでフロントエンドとバックエンドの処理を記述することができるため非常に効率的にアプリケーションを構築することが可能です。そのためPHPのみしか使いこなせない開発者がインタラクティブなフロントエンド部分の画面を作成することができます。その代わりLaravelのみでしか利用できない技術のためLaravel以外でLivewireの技術を活用することができないという欠点もあります。

はじめてのLivewire

Livewireの動作確認を行うためにLaravelのインストールを行います。現在Laravelをインストールするとバージョンは10で、livewireはLaravelのインストール後にcomposerコマンドでインストールを行います。


 % laravel new laravel10_livewire
Laravel8からlaravelコマンドに–jetコマンドをつけてインストールを実行するとJetStreamをLaravelプロジェクトと同時にインストールすることができ、livewireかinertiaの選択を行う必要があります。livewireを選択してインストールした場合は下記のように個別にパッケージをインストールする必要はありません。
fukidashi

Laravelのインストールが完了したら、Livewireパッケージのインストールを行います。


 % composer require livewire/livewire
./composer.json has been updated
Running composer update livewire/livewire
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
  - Locking livewire/livewire (v3.3.5)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
  - Installing livewire/livewire (v3.3.5): Extracting archive
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi

   INFO  Discovering packages.  

  laravel/sail ................................ DONE
  laravel/sanctum ............................. DONE
  laravel/tinker .............................. DONE
  livewire/livewire ........................... DONE
  nesbot/carbon ............................... DONE
  nunomaduro/collision ........................ DONE
  nunomaduro/termwind ......................... DONE
  spatie/laravel-ignition ..................... DONE

84 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi --force

   INFO  No publishable resources for tag [laravel-assets].  

No security vulnerability advisories found.
Using version ^3.3 for livewire/livewire

Livewireインストールが完了したらphp aritsan serveコマンドを実行して、resources¥viewsディレクトリの下にあるwelcome.blade.phpファイルの中身を削除し、以下のように書き換えます。

headタグの最後に@livewireStylesとbodyタグの閉じタグの前に@livewireScriptsを追加しています。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Livewire</title>
    @livewireStyles
</head>
<body>
<h1>Hello Livewire</h1>

    @livewireScripts
</body>
</html>

@livewireStylesと@livewireScriptsは必須ではなく追加しないくてもlivewireコンポーネントを利用した時に自動で追加されます。明示的に追加しても問題はありません。


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Livewire</title>
</head>
<body>
<h1>Hello Livewire</h1>
</body>
</html>

php artisan serveコマンドで開発サーバが起動するので127.0.0.1:8000にブラウザでアクセスするとHello Livewireが表示されます。

動作確認前の初期画面
動作確認前の初期画面

ブラウザ上にHello Livewireが表示できることが確認できたら、Livewireのコンポーネントを作成します。Livewireを利用してカウンターを作成するので名前をcounterとします。Livewireの作成はphp artisan make:livewireコマンドで行います。


 % php artisan make:livewire counter
 COMPONENT CREATED  🤙

CLASS: app/Livewire/Counter.php
VIEW:  resources/views/livewire/counter.blade.php

  _._
/ /o\ \   || ()                ()  __         
|_\ /_|   || || \\// /_\ \\ // || |~~ /_\   
 |`|`|    || ||  \/  \\_  \^/  || ||  \\_   


Congratulations, you've created your first Livewire component! 🎉🎉🎉

実行するとapp¥LivewireディレクトリにCounter.phpファイルとresouces¥views¥livewireディレクトリにビューファイルcounter.blade.phpファイルが作成されます。

Couter.phpファイルにはrender関数があり、view関数でCounter.phpファイルと一緒に作成されたビューファイルcounter.blade.phpファイルが指定されています。


namespace App\Livewire;

use Livewire\Component;

class Counter extends Component
{
    public function render()
    {
        return view('livewire.counter');
    }
}

counter.blade.phpファイルには中身のないdivタグだけが記述されているので以下のようにh1タグを追加します。


<div>
    <h1>初めてのLivewire</h1>
</div>

追加した内容を表示させるためにwelcome.blade.phpファイルにlivewireタグを以下のように追加します。


<h1>Hello Livewire</h1>
<livewire:counter>

ブラウザで確認し”初めてのLivewire”が表示されればLivewireは正常に設定されています。

初めてのLivewire
初めてのLivewire

変数を設定

今後はcounter.phpとcounter.blade.phpの2つのファイルのみ使って動作確認を進めていきます。

counter.blade.phpファイルに変数$countを設定します。


<div>
    <h2>{{ $count }}</h2>
</div>

Bladeファイル側で$counterを設定しただけでは定義をしていないのでエラーになります。$counterの定義はcounter.phpファイルで行います。


class Counter extends Component
{
    public $count = 10;

    public function render()
    {
        return view('livewire.counter');
    }
}

初期値を10に設定すると設定通りブラウザ上に10が表示されます。ここまでの設定では通常のLaravelでの表示とは何も変わりません。

countの初期値10
countの初期値10

カウンターの作成(Livewire Actions)

ボタンにクリックイベントを設定し、そのボタンをクリックするとcountの数字が増えるように設定を行います。ボタンのクリックなどブラウザ側のインタラクションによって実行されるメソッドをLivewire Actionsと呼びます。

Vue.jsではクリックイベントを@click(or v-on:click)、Reactではonclickと記述しますが、livewireではwire:clickと記述します。


<div>
    <h2>{{ $count }}</h2>
    <p><button wire:click="inc">+1</button></p>
</div>

clickイベントを設定後はcounter.phpファイルにclickイベントで指定したinc関数を追加します。inc関数はLaravelサーバ上で実行されるためブラウザ上からサーバ上のPHPの関数(inc actions)を実行することになります。


public $count = 10;

public function inc(){
    $this->count++;
}

ブラウザからボタンをクリックするとページのリロードすることなくcountの数字が増えることが確認できます。

カウンター数が増える
カウンター数が増える

デベロッパーツールを見るとボタンをクリックする度に/livewire/updateにPOSTリクエストが送信されていることがわかります。

Buttonをクリックする度に送信されるPOSTリクエスト
Buttonをクリックする度に送信されるPOSTリクエスト

counterの数字を増やすためにバックグランドではAjaxリクエストが送信されておりネットワーク上でのブラウザとLaravelとの通信が行われておりますがLivewireを使った場合はAjaxリクエストの処理を自分で記述する必要がなくLivewireが自動で行ってくれます。

php artisan route:listでルーティングを確認するとPOSTのリクエスト先のlivewire/messageを確認することができます。

ルーティングのリストの確認
ルーティングのリストの確認

POSTリクエストを送信後にバックエンドからはcount.blade.phpファイルに記述している内容がそのまま戻されていることが確認できます。effectsのhtmlを見ると現在表示されているカウントと同じ15が含まれています。

update後に戻されるデータの内容
update後に戻されるデータの内容

データバインディング

データバインディングについてはLivewireのバージョンによってデフォルトの動作が異なります。バージョン2ではwire:modelディレクティブを設定してinput要素に文字を入力すると文字を入力するためにサーバへのリクエストが送信されましたがバージョン3では動作が変わり、デフォルトではsubmitのボタンをクリックするまでリクエストは送信されません。

バージョン3の場合

form, input要素の追加

counter.blade.phpファイルにformタグとinputタグを追加します。formタグではwire:submitディレクティブを追加してsubmitボタンをクリックされるとsave関数が実行されます。input要素にはwire:modelディレクティブでmessageを指定します。


<div>
    <h2>{{ $count }}</h2>
    <p><button wire:click="inc">+1</button></p>
    <form wire:submit="save">
        <input type="text" wire:model="message" ><br />
        <button>Submit</button>
    </form>
</div>

save関数とmessage変数はCounter.phpファイルで定義する必要があります。save関数ではinput要素に入力された値をデータベース等に保存する処理を行いますがここではログに出力させています。


namespace App\Livewire;

use Livewire\Component;

class Counter extends Component
{
    public $count = 10;

    public $message;

    public function render()
    {
        return view('livewire.counter');
    }

    public function save(){
        //保存処理
        logger()->info($this->message);
    }

    public function inc(){
        $this->count++;
    }
}

ブラウザに表示されるinput要素に文字列を入力して”Submit”ボタンをクリックします。

フォームからの入力
フォームからの入力

画面上には変化がありませんがLaravelのプロジェクトディレクトリの下にあるstorage/logsの下のlaravel.logファイルを確認すると”Hello Livewire”の文字列を確認することができます。

リアルタイムの更新

wire:modelはデータバインディングの設定なので入力した値をリアルタイムで更新しブラウザ上に表示させることができます。その場合はwire:modelの後のliveを設定します。入力した文字をブラウザ上に表示させるため$messageを表示させています。


<div>
    <h2>{{ $count }}</h2>
    <p><button wire:click="inc">+1</button></p>
    <form wire:submit="save">
        <input type="text" wire:model.live="message" >{{ $message }}<br />
        <button>Submit</button>
    </form>
</div>

文字を入力する度に$messageの値がブラウザ上に表示されます。

リアルタイムでブラウザの$messageの値が更新される
リアルタイムでブラウザの$messageの値が更新される

wire:modelにliveを設定すると文字を入力する度にネットワークリクエストが送信されます。ネットワークのリクエストが送信されても$messageの値が更新されるだけでsave関数が実行されるわけではありません。”Submit”ボタンをクリックした時のみsave関数が実行されます。

どちらも/livewire/updateに対してリクエストが送信されていますがRequestのPayloadに中にcallsプロパティがあり文字を入力しただけであれば空ですがsave関数を実行するとsaveという文字列を確認することができます。

バージョン2の場合

input要素の追加

バインドを利用することでinput要素に入力した内容をそのままブラウザ上に表示を行うことができます。バインディングはwire:modelで行うことができます。


<input type="text" wire:model="message" >

counte.phpファイルでmessageを定義する必要があります。


public $count = 10;

public $message;

画面に$messageの内容を表示できるように設定を行います。


<input type="text" wire:model="message" >{{ $message }}

input要素に文字列を入力するとそのまま入力した内容が表示されます。

input要素に入力を行う
input要素に入力を行う

debounceの設定

文字を入力するとバックグラウンドではajaxリクエストが行われています。リクエストの回数を下げるためにデフォルトでは入力を停止してから150ms後にajaxリクエストが行われます。設定値によってその時間を変更することができます。

設定を行うと入力後500ms後にajaxリクエストが行われます。


<input type="text" wire:model.debounce.500ms="message" >{{ $message }}
ajaxリクエストを確認したい場合はデベロッパーツールのnetworkタブを使って行ってください。
fukidashi

lazyの設定

デフォルトでは文字を入力後にajaxリクエストが行われていましたが、文字入力ではなくinputエリアから外れた時にajaxリクエストを行いたい場合はlazyを利用することができます。


<input type="text" wire:model.lazy="message" >{{ $message }}

deferの設定

debounceでは文字を入力してからajaxリクエストを送信するまでの時間、lazyではinput要素からカーソル外すとajaxリクエスト送信、deferでは入力後にボタンをクリックするとajaxリクエストを送信させることができます。


   <input type="text" wire:model.defer="message" >{{ $message }}<br/>
   <button wire:click="search">Search</button>

Searchボタンをクリックするとajaxリクエストが送信されますが、search関数を設定していないのでエラーが表示されます。search関数を設定すればエラーは表示されません。通常はボタンを押すとsearch関数を実行することになるので問題はありません。

if分の使い方

livewireでif文を利用する時はBladeの@ifをそのまま利用することができます。wire:ifといった構文はありません。livewireではvue.jsやReactとは異なり、bladeの構文を利用して制御できるものもあります。

if文を利用して文字を入力していない時としている時のメッセージを変えています。


<div>
    <h2>{{ $count }}</h2>
    <p><button wire:click="inc">+1</button></p>
    <input type="text" wire:model.live="message" ><br />
    @if(!$message)
        <p style="color:red;font-weight:bold">文字を入力してください。</p>
    @else
        <p>文字を入力しました。</p>
    @endif
</div>
バージョンが2の場合はwire:modelの後にliveは必要ありません。
fukidashi

文字を入力していない時は、赤字でメッセージが表示されます。

if文の分岐でメッセージ
if文の分岐でメッセージ

文字を入力するとページをリロードすることなくメッセージが変わります。

文字入力時のメッセージ
文字入力時のメッセージ

テーブルからのデータ取得

ここまでの処理ではcounter.phpファイルで定義した変数の内容を表示したり、数字を増やしたりするだけでした。次はLaravelのテーブルに保存されているデータの取得をlivewire内で行いテーブル内のユーザ情報の削除の方法まで確認します。

データベース、テーブルの作成

データベースもテーブルも作成していないため、テーブルを作成する必要があります。本文書では、簡易的に作成ができるsqliteを利用してデータベースを作成します。

.envファイルを使ってデフォルトのMySQLからsqliteへ接続するデータベースを変更します。接頭語にDB_がついているものでDB_CONNECTIONのみを残してsqliteを設定します。


DB_CONNECTION=sqlite

php artisan migrateコマンドをSQLiteのデータベースファイルを作成するか聞かれるので”Yes”を選択します。実行すると4つのテーブルが作成されます。


% php artisan migrate

   WARN  The SQLite database does not exist: database/database.sqlite.  

 ┌ Would you like to create it? ────────────────────────────────┐
 │ Yes                                                          │
 └──────────────────────────────────────────────────────────────┘

   INFO  Preparing database.  

  Creating migration table ................................ 8ms DONE

   INFO  Running migrations.  

  2014_10_12_000000_create_users_table ..................... 4ms DONE
  2014_10_12_100000_create_password_reset_tokens_table ..... 1ms DONE
  2019_08_19_000000_create_failed_jobs_table ............... 3ms DONE
  2019_12_14_000001_create_personal_access_tokens_table .... 3ms DONE

Seedingを利用してにユーザテーブルにダミーデータを登録します。app¥database¥seedersを開いてコメントアウトされている行のコメントを外します。


namespace Database\Seeders;

// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     */
    public function run(): void
    {
        \App\Models\User::factory(10)->create();

        // \App\Models\User::factory()->create([
        //     'name' => 'Test User',
        //     'email' => 'test@example.com',
        // ]);
    }
}

php artisanコマンドを実行してダミーデータを作成します。


 % php artisan db:seed

   INFO  Seeding database.

ユーザ一覧の表示

couter.blade.phpファイルを引き続き利用するため、下記のように更新します。bladeの@foreachを利用して$usersを展開します。このファイルだけ見るとLivewireの設定はありません。


<div>
    <h2>ユーザ一覧</h2>
    <ul>
    @foreach($users as $user)
    <li>{{ $user->name }}
    @endforeach
    </ul>
</div>

counter.phpファイルの中で$usersを取得する必要があります。取得はライフサイクルフックのmountを利用します。livewireのコンポーネントが初期化した直後に一度だけ実行され、render関数の前に実行が行われます。


namespace App\Livewire;

use Livewire\Component;

use App\Models\User;

class Counter extends Component
{
    public $users;

    public function mount(){
        $this->users = User::all();
    }

    public function render()
    {
        return view('livewire.counter');
    }
}

ブラウザで確認するとユーザの一覧が表示されます。

ユーザ一覧を表示
ユーザ一覧を表示

ユーザの削除

ユーザ一覧の表示であれば通常のBladeファイルを利用したユーザ一覧の表示方法との違いがわかりません。@clickイベントを設定してユーザを削除する方法を確認します。

まずclickイベントを持つ削除ボタンの追加します。


<div>
    <h2>ユーザ一覧</h2>
    <ul>
    @foreach($users as $user)
    <li>{{ $user->name }} <button wire:click="delUser({{ $user->id }})">削除</button>
    @endforeach
    </ul>
</div>

ユーザ名の横に削除ボタンが表示されます。

削除ボタンを追加
削除ボタンを追加

clickイベントに設定したdelUser関数をCounter.phpファイルで設定します。filterを利用して削除ボタンを押した$idを持たない要素のみ取り出します。


public function delUser($id){
    $this->users = $this->users->filter(function($value, $key) use($id){
        return $value['id'] != $id;
    });
}

ブラウザ上で削除ボタンを押すとページのリロードなしでユーザが削除できることを確認できます。削除ボタンを押すごとにユーザが削除されます。

ユーザ情報の削除
ユーザ情報の削除

しかし、この状態では再度ページにアクセスすると削除したユーザは再度表示されます。テーブル内のデータは実際に削除されていないためです。

Counter.phpファイル内でテーブル内のユーザを削除します。通常Laravelで利用するdeleteメソッドを使用します。


public function delUser($id){
    $this->users = $this->users->filter(function($value, $key) use($id){
        return $value['id'] != $id;
    });

    $user = User::find($id);

    $user->delete();
    
}

deleteメソッド追加後、削除ボタンでユーザを削除するとページに再度アクセスすると削除したユーザの情報は表示されません。

非常にシンプルなコードを利用して動作確認を行っただけですがLivewireがどのようなものか理解は進んだのではないでしょうか。