Livewireって一体何?LaravelでLivewireの使い方の基礎を学ぶ


Livewireがどんな技術か知ってますか?Laravelの認証ライブラリであるJetStreamやBreezeをインストールする際にLivewire、Inertia、Bladeのいずれを選択する必要があります。Livewire、Inertiaどちらも認証機能(サインイン、ログイン)のフロントエンド部分を構成するファイル内で利用されておりLaravelでアプリケーションを構築する上で必要な知識になっています。
本記事は認証ライブラリのインストール時にLivewireの知識がないためにLivewireとInertiaの違いがわからず悩まないことを目的にしています。本記事を読み終えれば、Livewireがどのようなものかの理解が進みテーブルデータの表示など基本的な利用方法を理解することができます。
目次
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(執筆時の最新版が10であったため)で、LivewireはLaravelのインストール後にcomposerコマンドでインストールを行います。
% laravel new laravel10_livewire

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は正常に設定されています。

変数を設定
今後は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での表示とは何も変わりません。

カウンターの作成(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リクエストが送信されていることがわかります。

counterの数字を増やすためにバックグランドではAjaxリクエストが送信されておりネットワーク上でのブラウザとLaravelとの通信が行われておりますがLivewireを使った場合はAjaxリクエストの処理を自分で記述する必要がなくLivewireが自動で行ってくれます。
php artisan route:listでルーティングを確認するとPOSTのリクエスト先のlivewire/messageを確認することができます。

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

データバインディング
データバインディングについては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の値がブラウザ上に表示されます。

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" >
counter.phpファイルでmessageを定義する必要があります。
public $count = 10;
public $message;
画面に$messageの内容を表示できるように設定を行います。
<input type="text" wire:model="message" >{{ $message }}
input要素に文字列を入力するとそのまま入力した内容が表示されます。

debounceの設定
文字を入力するとバックグラウンドではajaxリクエストが行われています。リクエストの回数を下げるためにデフォルトでは入力を停止してから150ms後にajaxリクエストが行われます。設定値によってその時間を変更することができます。
設定を行うと入力後500ms後にajaxリクエストが行われます。
<input type="text" wire:model.debounce.500ms="message" >{{ $message }}

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>

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

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

テーブルからのデータ取得
ここまでの処理では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がどのようなものか理解は進んだのではないでしょうか。