EXCEL VBAやPHP、Laravelに関するWINDOWS・WEBプログラミングの解説ブログです。

Laravel9 Livewireで本の情報を登録・表示・更新・削除・画像アップロード・プレビュー・検索・バリデーション基本

  • 2022年11月19日
  • 2022年12月11日
  • Laravel

Laravel9で、Livewireの登録・表示・更新・削除のミニWEBシステムを作成しました。画像のアップロードやその前のプレビューも可能です。また、検索も可能ですし、登録・更新時のバリデーションチェックも備えています。

Live Wireは、Laravelのブレードを使って、ダイナミックでリアクティブ且つモダンなインターフェースを作りましょうといったスタンスで、VUEを覚えるにはハードルが高いけども、Livewireであれば お気軽に実現できますよといったスタンスのようです。

小規模なWEBシステムであれば、大体の機能が最初から有り、素のPHPで一から作ることを考えると遥かに少ない時間で作成が可能と思います。

こちらのyoutubeの解説記事でもあります。

Laravelプロジェクトの作成

composer create-project “laravel/laravel=9.*” livewire2022_1

上のコマンドをターミナルで実行し、プロジェクト livewire2022_1  を作成し、c:\xampp\laravel>cd livewire2022_1 プロジェクトフォルダに移動します。

phpMyAdminで、DB livewire_20221 を作成して、.envファイルに、

DB_DATABASE=livewire_20221
とします。

c:\xampp\laravel\livewire2022_1>php artisan -v
Laravel Framework 9.39.0
Laravelのバージョンは、9.39.0がインストールされました。

JetStreamとLivewireのインストール

Laravel Jetstreamのマニュアルに従い、Jet StreamとLivewireを以下のようにターミナルで実行。
マニュアルURL https://jetstream.laravel.com/2.x/installation.html

composer require laravel/jetstream
php artisan jetstream:install livewire
npm install
npm run build
php artisan migrate

php artisan serveで簡易サーバを起動。http://127.0.0.1:8000/で、Laravelの初期画面が表示。

Registerリンクをクリックして、ユーザー登録。

ログインできました!

これだけで、認証画面まで準備出来てしまっているので、昔のようにちまちま認証画面を作成していたことを考えれば、楽なものです(笑)。

マニュアルに従い、こちらもターミナルで実行し、導入。
php artisan vendor:publish –tag=jetstream-views

※tagの前は -ハイフンが二つ です。

Bookモデルの作成

ターミナルでBookモデルを作成します。

php artisan make:model Book -m

app\Models\Book.php
database\migrations\2022_11_13_012203_create_sessions_table.php
が作成されます。

booksテーブルにカラムをマイグレーションファイルで追加

2022_11_13_012203_create_sessions_table.phpのスキーマを拡張。デフォルトである $table->id() と timestampの間に、
タイトル、画像ファイル名、価格、詳細のためのカラムを追加し、上書き保存します。
$table->string(‘title’);
$table->string(‘image’);
$table->integer(‘price’);
$table->text(‘description’);

ターミナルでマイグレーションファイルを実行します。
php artisan migrate:fresh
カラムが追加されたことをphpmyadmin等で確認できました。

app\Models\Book.phpの、use HasFactory;の下に
protected $fillable = [‘title’,’image’,’price’,’description’];
を追記し、Create Update できるカラムを指定します。

LivewireコンポーネントBookIndexの作成

php artisan make:livewire BookIndex
ターミナルでBookIndexコンポーネントを作成します。途中でYesかNoを聞かれますが、Noと応えます。

CLASS: app/Http/Livewire//BookIndex.php ← バックエンド側
VIEW: resources\views/livewire/book-index.blade.php ← フロント側
このようにバックエンド側ファイル BookIndex.php と フロント側ファイル book-index.blade.php が生成されます。
以下 BookIndex.php をバックエンド側、book-index.blade.php を フロント側と呼びます。

BookIndex.phpの生成されたときの状態

book-index.blade.phpの生成されたときの状態は、<div></div>があるだけです。
<div>は、体裁をよくするために、<div class=”max-w-6xl mx-auto”>としておきます。

web.phpに追記します。
上に use App\Http\Livewire\BookIndex;
Route::get(‘/books’, BookIndex::class)->name(‘books.index’);

http://127.0.0.1:8000/booksでヘッダーだけの表示を確認!

手動でイチイチ http://127.0.0.1:8000/books とかURL書くのも手間ですので、ヘッダーにbooksリンクを作ります。
resources\views\navigation-menu.blade.php内のNavigation Linksに以下追加します。
<div class=”hidden space-x-8 sm:-my-px sm:ml-10 sm:flex”>
<x-jet-nav-link href=”{{ route(‘books.index’) }}” :active=”request()->routeIs(‘dashboard’)”>
{{ __(‘Books’) }}
</x-jet-nav-link>
</div>
下のようにBooksリンクを確認しました。

Tailwind CSS

livewireのCSSは、Tailwindcss が基本的なCSSテンプレートとなっているようですの、VSの拡張機能で Tailwind CSS Intellisense を入れておくと便利です。Tailwindcss のマニュアルは、https://tailwindcss.com/docs/width#fixed-widths こちらにあります。

登録(Create,Store)

モーダルウインドウ

バックエンド側 app\Http\Livewire\BookIndex.php に追加していきます。

上部に
use App\Models\Book;//追加

クラス BookIndex 内に、以下追加します。class BookIndex extends Component{の下に書きます。
    public $liveModal = true;//モーダルウインドウ
    public $title;//タイトル
    public $newImage;//画像
    public $price;//価格
    public $description;//詳細
モーダルウインドウは、登録・編集のときのためですので、初期値はfalseにするべきですが、livewireのモーダルを理解するために敢えてtrueにしています。後ほど、falseにします。タイトルから詳細までは、変数を定義するのみです。

フロント側 resources\views\livewire\book-index.blade.php に以下追加します。

ブラウザ http://127.0.0.1:8000/books で確認しますと以下のようにモーダルが表示されています!public$liveModal = false;にします

フロント側に以下追加
<div class=”text-right m-2 p-2″>
<x-jet-button class=”bg-blue-600″ wire:click=”showBookModal”>登録</x-jet-button>
</div>

バックエンド側に以下追加
public function showBookModal(){
 $this->reset();
 $this->liveModal = true;
}

wire:clickで、showBookModalメソッドが実行され、モーダルが開きます。

「登録」ボタンでモーダルが開くことを確認しました!

登録フォーム フロント側

フロント側<x-slot name=”content”>内に以下追加します。

<x-jet-button wire:click=”bookPost”>登録実行</x-jet-button>をフッター部分に追加。
wire:model.lazy=”***”の***箇所がフォームの部品名に該当します。@errorの箇所は、後ほど記述するバリデーション処理のエラーメッセージを表示する記述です。ブラウザでの表示は下のようになります。

登録 バックエンド側

バックエンド側に以下追加します。

use Livewire\WithFileUploads;//ファイルのアップロードに必要 上部に追加
use WithFileUploads;//ファイルのアップロードに必要 class BookIndex extends Component{の下に追加
bookPostメソッドを追加します。

本の情報を登録する中で、誤った情報や未記入の項目は以下のようにエラーが表示されます。エラーの日本語化はしていませんが、たくさん情報があるはずですので簡単かと思います。

以下のように、正しく入力された情報が「登録実行」ボタンをクリックすると、テーブルbooksに登録されます。画像も、public\storage\booksに保存されたことを確認しました!

アップロード画像のプレビュー

画像のプレビューをするためには、フロント側に以下を追記します。
@if ($newImage)
Photo Preview:
<img src=”{{ $newImage->temporaryUrl() }}” class=”w-48″>
@endif
下のように画像のプレビューが確認できました!

一覧表示

登録したデータを一覧表示してみましょう。booksテーブルの全レコードを一覧表示します。

一覧表示 バックエンド側

use Livewire\WithPagination;//別途追加 上部に追加
use WithPagination;//別途追加 BookIndexクラスに追加
renderメソッド内を書き換えます。
return view(‘livewire.book-index’, [
‘books’ => Book::select(‘id’,’title’,’price’,’image’,’description’)
->orderBy(‘id’,’DESC’)->paginate(3),
]);
フロントが側にbooksテーブルの情報を並び順、ページングの結果を含めて渡していることになります。

一覧表示 フロント側

フロント側に以下追加しまし。foreachで出力、$books->links()でページネーションのためのリンクを設置しています。

ページングを含めたブラウザでの表示

登録した内容の一覧表示がブラウザで確認できました!Laravel9 Livewire凄く楽ですね!ページネーションまで実装できています!

編集(Edit、Update)

まずは、編集ボタンをクリックしたら、モーダルウインドウが開いて、該当する本の情報をDBから取り出し、ウインドウに表示するまでを作成します。

編集のためのモーダルウインドウの起動

フロント側に追加

<x-jet-button class=”bg-green-600″ wire:click=”showEditBookModal({{ $book->id }})”>編集</x-jet-button>

バックエンド側のメソッドshowEditBookModalに引数idを渡します。

バックエンド側

BookIndex内に 該当レコードのid、画像情報を入れる変数、編集作業中かどうかを識別するための変数を定義
public $Id;
public $oldImage;
public
$editWork = false
;
メソッド showEditBookModal を追加
編集ボタンをクリックすると、下のようにモーダルウインドウ内の各部品にbooksテーブルの該当するレコードの情報が表示されていることを確認しました!

登録されている画像の表示をするために、以下のようにフロント側を書き換えます。

ブラウザで確認します。登録されている画像も表示されました!

タイトルの表示とフッターの表示も変更するために、
フロント側を書き換えます。

モーダルウインドウのタイトル部分

@if ($editWork)
<x-slot name=”title”><h2 class=”text-green-600″>編集</h2></x-slot>
@else
<x-slot name=”title”><h2 class=”text-blue-600″>登録</h2></x-slot>
@endif

モーダルウインドウのボタン部分

@if ($editWork)
<x-jet-button wire:click=”updateBook({{ $Id }})”>編集実行</x-jet-button>
@else
<x-jet-button wire:click=”bookPost”>登録実行</x-jet-button>
@endif

タイトルとフッター部分の表示が変わったことを確認しました!

編集の実行

フロント側に追加します。更新した結果のメッセージの表示のための記述です。

formの上あたりに追加@if (session()->has(‘message’))
<h3 class=”p-2 text-2xl text-green-600″>{{ session(‘message’) }}</h3>
@endif

バックエンド側にメソッドupdateBookを追加します。引数Idを受け取り、画像の入れ替えがあるかどうかで処理を分けています。updateが実行されたら、セッションmessageにメッセージを入れます。

ブラウザで編集の実行を確認しました!

削除(Delete)

本の情報の削除です。

フロント側

<x-jet-button class=”bg-red-400″ wire:click=”deleteBook({{ $book->id }})”>削除</x-jet-button>

バックエンド側

上部に追記 use Illuminate\Support\Facades\Storage;
メソッド deleteBook を追加
ブラウザで削除できることを確認しました。いきなり削除されてしまうので、削除実行の前のJavascriptを追加するとか、一旦 削除の確認用モーダルウインドウを設置するなどの措置が実務的には必要になりますが。

検索

検索機能も設置します。Titleから検索できるようにします。

フロント側

登録ボタンの上に追記
<input type=“text” wire:model=“search” id=“search” class=“border-gray-300 rounded-md” placeholder=“キーワード” />

バックエンド側

変数search追加
public
$search=”;
renderの箇所を以下に書き換えます。変数searchが空でない場合は、titleからあいまい検索するようにしています。
ブラウザで確認します。検索機能が有効であることを確認しました!