
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、特にJavaScript
がそうだと思いますが、フロントエンドはホントに変化が早いので、トレンドについていくのがなかなか大変じゃないでしょうか。
しかも、新しいものがすべて生き残るわけではなく、芸能界と同じくスターになれるのは「ひとつまみ(にぎりより少ない)」ですよね。
そして、私のパターンで言うと「シンプル&強力」なVue.js
が好きなのですが、これもバージョンが3に上がったあたりから「シンプルさ」を失ってしまった感があり、以前よりVue
のニュースを聞くことが減っているような気もしています。(まだまだ人気はあると思いますが!)
そこで、いろいろと別のものを試してみるべきだろうと考えていたのですが、この度個人的な開発で livewire を使ってみることになったので、今回はその予習も兼ねて記事をお届けすることにしました。
livewire
は「フロントエンドでやってることをPHP側に取り戻す」みたいなコンセプトのテクノロジーです。
そこで
今回はLaravel + livewire
でシンプルなブログ記事のCRUD(追加・表示・編集・削除)機能をつくってみます。
ぜひ、何かの参考になりましたら嬉しいです
「なんと、Swift Mailer が
開発終了してました 」
開発環境: Laravel 8.x、livewire 2.8
目次 [非表示]
パッケージをインストールする
livewire
という名前ですが、これはcomposer
のパッケージです。
なので、まずは以下のコマンドを実行してパッケージのインストールをしておいてください。
composer require livewire/livewire
これで、livewire
開発の準備が整いました。
実際に開発をしていきましょう
DBまわりを用意する
今回DBにはarticles
というブログ記事を管理するテーブルを用意します。
そのため、まずは以下のコマンドを実行してください。
php artisan make:model Article -ms
すると、「モデル」「マイグレーション」「Seeder」の3ファイルが作成されるので、中身をそれぞれ以下のように変更してください。
app/Models/Article.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Article extends Model
{
use HasFactory;
protected $guarded = ['id'];
}
※ 通常Mass assignment
は使わない主義なので$guarded
や$fillable
はセットしないのですが、Copilot
がおすすめしてきたので、今回はこっちにしてみました。
database/migrations/****_**_**_******_create_articles_table.php
// 省略
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title')->comment('タイトル');
$table->text('content')->comment('本文');
$table->timestamps();
});
}
※ なお、今回はテストですのでuser_id
などのフィールドは省略しています。
database/seeders/ArticleSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Article;
use Illuminate\Database\Seeder;
class ArticleSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
for($i = 1; $i <= 50; $i++) {
Article::create([
'title' => 'タイトル ' . $i,
'content' => "本文\n本文\n本文\n本文\n本文 " . $i
]);
}
}
}
Seeder
ファイルはつくっただけでは実行できませんので、DatabaseSeeder.php
に登録しておきます。
database/seeders/DatabaseSeeder.php
// 省略
public function run()
{
$this->call(ArticleSeeder::class);
}
では、DBを再構築してみましょう。
以下のコマンドを実行してください。
php artisan migrate:fresh --seed
すると実際のテーブルはこうなりました。
livewire のコンポーネントをつくる
では、次にlivewire
のコンポーネントをつくります。
必要になるのは、次の2つです。
- 記事の一覧(表示&編集&削除)
- 送信フォーム(追加&編集)
では、ひとつずつ作っていきましょう
記事の一覧を表示するコンポーネントをつくる
まずは、記事を一覧表示するコンポーネントArticleList
をつくります。
以下のコマンドを実行してください。
php artisan make:livewire ArticleList
すると、「livewire 用の PHPクラス」と「View ファイル」の2つが作成されます。
※ ちなみに「Would you like to show some love by starring the repo?」と表示された場合はそのままno
で大丈夫です。毎回以下のアスキー文字を表示するかどうかを聞かれているようです。
では、それぞれの中身です。
app/Http/Livewire/ArticleList.php
<?php
namespace App\Http\Livewire;
use App\Models\Article;
use Livewire\Component;
use Livewire\WithPagination;
class ArticleList extends Component
{
use WithPagination;
protected $listeners = [
'refresh' => '$refresh', // 再読み込み
'destroy' => 'destroy'
];
public function render()
{
$articles = Article::paginate(10);
return view('livewire.article-list', [
'articles' => $articles,
]);
}
public function destroy(Article $article)
{
$article->delete();
}
}
この中でやっていることは次のとおりです。
(1)$listeners
これは「イベントリスナー」で、例えば以下のようにすることで該当するメソッド呼び出すことができるようになります。
- HTMLから呼び出す場合: wire:click=”$emit(‘******’)”
- JavaScript で呼び出す場合: Livewire.emit(‘******’);
- PHP で呼び出す: $this->emit(‘******’);
※ なお、コンポーネントが複数ある場合は、明示的に「どのコンポーネントのメソッドを呼び出すのか」が指定できるemitTo()
を使うことをおすすめします。
// emitTo のサンプル(PHPの場合)
$this->emitTo('article-list', 'refresh');
また、$refresh
はlivewire
が用意してくれているもので、コンポーネントの再読込みができます。
では、続いてビューです。
resources/views/livewire/article-list.blade.php
<div>
<table class="w-full text-sm mb-5">
<thead>
<tr>
<th class="border p-2">タイトル</th>
<th class="border p-2">本文</th>
<th class="border p-2">操作</th>
</tr>
</thead>
<tbody>
@foreach($articles as $article)
<tr>
<td class="border px-2 py-1">{{ $article->title }}</td>
<td class="border px-2 py-1">{{ $article->content }}</td>
<td class="border px-2 py-1 text-right">
<button
type="button"
class="bg-yellow-500 text-yellow-50 rounded p-2 text-xs"
wire:click="$emitTo('article-input', 'edit', {{ $article->id }})">
変更
</button>
<button
type="button"
class="bg-red-600 text-red-50 rounded p-2 text-xs"
onClick="onDelete({{ $article->id }})">
削除
</button>
</td>
</tr>
@endforeach
</tbody>
</table>
{{ $articles->links() }}
<script>
function onDelete(id) {
if(confirm('削除します。よろしいですか?')) {
Livewire.emitTo('article-list', 'destroy', id);
}
}
</script>
</div>
この中で特徴的なのは、wire:click
の部分ですが、ここが先ほどご紹介した「HTMLからメソッドを呼び出す」部分になります。
つまり、今回の例で言うとArticleInput
のedit()
を実行することができます。
また、onDelete()
の部分は確認ダイアログを通したいので、あえてJavaScript
内で使っていますが、この中では「JavaScriptからメソッドを呼び出す」方法を使っています。
// ArticleList の destroy() を ID をつけて実行
Livewire.emitTo('article-list', 'destroy', id);
なお、一点気をつけないといけないのは、ビューの一番外側のタグは1つしかセットできないということです。
例えば、以下の例はうまくいかなくなってしまいます。
<!--
これは間違った例です -->
<div>1つ目</div>
<div>2つ目</div>
送信フォームのコンポーネントをつくる
では、続いて「送信フォーム」のコンポーネントArticleInput
をつくります。
以下のコマンドを実行してください。
php artisan make:livewire ArticleInput
中身はそれぞれ以下のようになります。
app/Http/Livewire/ArticleInput.php
<?php
namespace App\Http\Livewire;
use App\Models\Article;
use Livewire\Component;
class ArticleInput extends Component
{
protected $listeners = [
'create' => 'create',
'edit' => 'edit'
];
protected $rules = [ // ここがないと wire:model に反映されない
'article.title' => ['required'],
'article.content' => ['required'],
];
public $article;
public function render()
{
return view('livewire.article-input');
}
public function mount()
{
$this->create();
}
public function create()
{
$this->article = new Article();
}
public function edit(Article $article)
{
$this->article = $article;
}
public function save()
{
$this->validate();
$this->article->save();
session()->flash('status', '保存が完了しました。');
$this->emitTo('article-list', 'refresh');
$this->create();
}
}
ここで重要なのが、$rules
の部分です。
(実は、なかなかハマってしまったのですが)ここがないとビュー内でwire:model
として使うことができません。
ということで、そのビューも見てみましょう。
<form wire:submit.prevent="save">
<div>
@if(session('status'))
<div class="text-green-700 p-3 bg-green-300 rounded mb-3">
{{ session('status') }}
</div>
@endif
</div>
<div class="mb-3">
<label>タイトル</label>
<br>
<input type="text" class="border w-full p-1" wire:model="article.title">
@error('article.title')
<span class="text-red-500">{{ $message }}</span>
@enderror
</div>
<div class="mb-4">
<label>本文</label>
<br>
<textarea rows="7" class="border w-full p-1" wire:model="article.content"></textarea>
@error('article.content')
<span class="text-red-500">{{ $message }}</span>
@enderror
</div>
@if($article->exists)
<button type="submit" class="bg-blue-700 text-blue-50 p-2 rounded">変更する</button>
@else
<button type="submit" class="bg-purple-700 text-purple-50 p-2 rounded">登録する</button>
@endif
</form>
これでArticleInput
の中で$article
が変化したら自動的にビューの方も書き換わる、というわけです。便利ですね
コンポーネントを呼び出すテンプレートをつくる
では、先ほどつくった2つのコンポーネントを使う部分をご紹介します。
resources/views/article/index.blade.php
<html>
<head>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
@livewireStyles
</head>
<body>
<div class="p-5">
<h1 class="text-3xl mb-4">
livewire のCRUDサンプル
</h1>
<div class="grid grid-cols-2 gap-7">
<div>
<livewire:article-list />
</div>
<div>
<button
type="button"
class="bg-gray-300 text-gray-700 rounded p-2 mb-5"
onClick="Livewire.emitTo('article-input', 'create')">+ 追加する</button>
<livewire:article-input />
</div>
</div>
</div>
@livewireScripts
</body>
</html>
まず、この中にある@livewireStyles
と@livewireScripts
ですが、これはlivewire
を使うために必要なCSS
とJavaScript
を書き出す部分になります。
そのため、livewire
を使うにはこの「おまじない」が必要ということになります。
また、実際にコンポーネントを使っている部分は<livewire:***** />
の部分です。
ルートをつくる
では、最後にルートをつくります。
routes/web.php
Route::get('/article', fn() => view('article.index'));
作業は以上です。
お疲れ様でした
livewireを使う上で気をつけたいこと
これまでのLaravel
開発にはなかった部分として以下2点を気をつけるとスムーズに使えると思います。
- $listerners の登録: これがないとメソッドを外から実行できません
- $rules の登録: これがないと wire:model が使えません
テストしてみる
では、実際にテストしてみましょう
まずはブラウザで「https://******/article」にアクセスしてみます。
すると、以下のように表示されました。(画面が横長なので2つにカットしています)
ではまず、以下のように入力して新規登録してみましょう。
登録ボタンを押すと・・・・・・
はい
完了メッセージが表示されました。
では、ちゃんと登録されているかページャーを移動して確認してみましょう。
うまく登録されています。
では、次に「変更」ボタンをクリックしてデータ変更できるかチェックしてみます。
クリックすると、フォームにデータが自動的に入力されるので、それぞれ以下のように変更します。
「変更する」ボタンをクリックすると・・・・・・??
はい
データを変更することができました。
では、続いて「削除」ボタンをクリックしてみます。
うまく削除することができました。
では、最後にバリデーションがうまくいっているかもチェックしてみましょう。
フォームを空にして送信してみると・・・・・・??
はい
日本語化していませんが、きちんとエラーが表示されました。
成功です
企業様へのご提案
livewire
は冒頭でもご紹介したとおり、「PHPをメインとした開発」をすることができます。
つまり、ある程度熟練したPHP開発者が1人いればフロントエンドはそれほど知識がなくても十分開発を進めることができると言っていいと考えています。
現在、ウェブ開発はあまりにも「すその」が広がりすぎて開発に多くのテクノロジーが必要になってしまいましたが、これは求人する側としてはなかなか由々しき問題ではないでしょうか。
そのため、もし「人員を減らした開発」や「学習コストが低いテクノロジー」をご希望でしたらlivewire
での開発もご検討になってみてはいかがでしょうか。そして、その際はぜひお問い合わせからご相談ください。
どうぞよろしくお願いいたします。m(_ _)m
おわりに
ということで、今回はlivewire
を使ってCRUD
を実装してみました。
前回はlivewire
がリリースされたばかりでまだまだ便利な機能がそろってないような印象でしたが、現在はver 2
になり、成熟してきた印象があります。
なお、今回livewire
を使ってみて「これはいい!」と感じたところを2点まとめてみました。
- ブラウザ側のデータをPHPで(擬似的に)リアルタイム保持してくれているので、保存が簡単: 通常だとデータを呼び出してから保存しないといけないのでとても楽です
- リロードしなくても変更を反映してくれる: これまではコードを変更したら一度リロードしてから確認してましたが、コンポーネント部分はこれが不要になります
なので、個人的にはlivewire
はもっと人気になってもいいのかな(2021.11.25現在でGitHub
のスターは13.7k
でした)と感じていますが、みなさんのお気持ちはどうでしょうか。
ただ、未だに一太郎が良かったって言う人もいますけど、ほぼMS Word
に置き換わってしまいましたし、電気自動車も日本は「うーん、、」ですが世界の流れに合わせざるを得ないのを見ると、好むと好まざるに限らずReactが
その他を一掃しそうな気もします。技術選定は難しいところですね…(Reactはせめて開発環境だけでもビルドせず実行できるようになればなって感じです)
今後も個人的に模索してみます。
ではでは〜
「マンホールの写真あつめ、
増えてきたぞ」