Open10

laravel12 livewire book draft

モッモッ

はじめに

かつてbootcamp.laravel.comというサイトでchiperというtwitter的に一言だけ残せるwebサービスを作るチュートリアルが掲示されていたのだが、laravel12の内容と適合しなくなったのか消滅してしまった。ここではその内容をwebarchiveより復活させ、さらにはlaravel12 starter kitsをvscodeで編集するというアレンジも加えた内容で再編集した。

モッモッ

gitリポジトリのclone

ある程度laravel12の環境でlivewireを使えるように仕込んでおいたリポジトリを用意したので

https://github.com/catatsumuri/laravel12-livewire-init.git

これをcloneする。

接続までの準備

cloneしたら .env.example.env にリネームする

さらにcomposer installする。

composer install

これでようやく接続できるようになるので

ここから接続する

ブラウザーからの確認

成功すると、このようにキーのエラーになる。ここでターミナルから

php artisan key:generate

する

リロードするとエラーになる

これはsessionをDBに保存しにいっているのだが、当該テーブルが無い

php artisan migrate

で解決する

あるいは、 .envSESSION_DRIVERfileにしちゃうのも手っちゃ手だ。開発はこっちの方が楽かも。

ただ、どのみちログインしないといけないのでmigrateは必須

さらにログインしようと思うと

このように

Vite manifest not found at: /var/www/html/public/build/manifest.json

になる。これはターミナルでnpm installして

npm run devしないといけない

そうすればログイン画面が見えるはずで、ここで

  • test@example.com
  • password

でログインするのだが、seedを入れてなかったので

php artisan migrate:fresh --seed

を再実行すればログインできる

ここまで出来ないと次に進めないので、これは何とかして環境構築して欲しい

モッモッ

Chirpの作成

モデルとDBの設定

ターミナルよりartisanを起動する

php artisan make:model -mc Chirp

-mc オプションによりマイクレーションとコントローラも作成される。viewを作成してないあたりがポイントにもなる

migrationとモデルの更新

以下のように作成されたマイグレーションを変更する


ctrl+pを使うとはやいかも

database/migrations/xxxx_create_chirps_table.php
@@ -13,6 +13,8 @@ public function up(): void
     {
         Schema::create('chirps', function (Blueprint $table) {
             $table->id();
+            $table->foreignId('user_id')->constrained()->cascadeOnDelete();
+            $table->string('message');
             $table->timestamps();
         });
     }

さらにリレーションも変更

app/Models/User.php
@@ -7,6 +7,7 @@
 use Illuminate\Foundation\Auth\User as Authenticatable;
 use Illuminate\Notifications\Notifiable;
 use Illuminate\Support\Str;
+use Illuminate\Database\Eloquent\Relations\HasMany;

 class User extends Authenticatable
 {
@@ -57,4 +58,9 @@ public function initials(): string
             ->map(fn (string $name) => Str::of($name)->substr(0, 1))
             ->implode('');
     }
+
+    public function chirps(): HasMany
+    {
+        return $this->hasMany(Chirp::class);
+    }
 }
app/Models/Chirp.php
@@ -3,8 +3,12 @@
 namespace App\Models;

 use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;

 class Chirp extends Model
 {
-    //
+    public function user(): BelongsTo
+    {
+        return $this->belongsTo(User::class);
+    }
 }

これでUser<->ChirpにおいてbelongsTohasManyそれぞれの関係が確立された

massアサイメントの対応

Chirpにはmessageを保存するのでこれをfillableプロパティにセットする

app/Models/Chirp.php
@@ -7,6 +7,11 @@

 class Chirp extends Model
 {
+    protected $fillable = [
+        'user_id',
+        'message',
+    ];
+
     public function user(): BelongsTo
     {
         return $this->belongsTo(User::class);

この状態で一度migrateし直す

php artisan migrate:fresh --seed

livewireコンポーネントの挿入

index.blade.phpの中に、livewireコンポーネントを利用してつぶやきのフォームを挿入する。createに移動するタイプではなくindexからそのまま送信するタイプである。

resources/views/chirps/index.blade.php
@@ -1,5 +1,5 @@
 <x-layouts.app :title="__('Dashboard')">
     <div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
-        <h1>Chirps Index</h1>
+        <livewire:chirps.create />
     </div>
 </x-layouts.app>

このように変更すると即座にエラーとなる

chirps.createコンポーネントを作成する

これは make:voltを利用する

php artisan make:volt chirps/create --class

この時点でエラーが解消されるが、謎の「 // 」が現われる


謎の「 // 」

このコンポーネント自体は resources/views/livewire/chirps/create.blade.php に配置される

resources/view/livewire
<?php

use Livewire\Volt\Component;

new class extends Component {
    //
}; ?>

<div>
    //
</div>

こんな状態になっているので // が表示されているというわけだ。これを以下のように修正していく

モッモッ

フロントエンドの開発

ここまででバックエンド側は大体整ったので、いよいよ保存できるようにしていく。今のdashboardは以下のように見えているかと思う

Chirpへの動線

dashboardを取り壊して書けるようにしてもいいのだけど、ここではChirpへ動けるように新たにメニュー作成し、そこで出来るようにする。これは resources/views/components/layouts/app/sidebar.blade.php で定義されているので、これを弄る必要がある。


ここでは折り返されているが、このあたりにメニューの記述がある

ここで以下のようにする

<flux:navlist.group :heading="__('Platform')" class="grid">
    <flux:navlist.item icon="home" :href="route('dashboard')" :current="request()->routeIs('dashboard')" wire:navigate>{{ __('Dashboard') }}</flux:navlist.item>
    <flux:navlist.item icon="home" :href="route('chirps')" :current="request()->routeIs('chirps')" wire:navigate>{{ __('Chirp') }}</flux:navlist.item>
</flux:navlist.group>

するとchirpsというrouteがないので即座にエラーとなる

routeの追加

ここでroutes/web.phpに以下を追加する

routes/web.php
@@ -1,5 +1,6 @@
 <?php

+use App\Http\Controllers\ChirpController;
 use Illuminate\Support\Facades\Route;
 use Livewire\Volt\Volt;

@@ -19,4 +20,8 @@
     Volt::route('settings/appearance', 'settings.appearance')->name('settings.appearance');
 });

+Route::get('chirps', [ChirpController::class, 'index'])
+    ->middleware(['auth', 'verified'])
+    ->name('chirps');
+
 require __DIR__.'/auth.php';

これで一応は表示されるがアイコンがhomeだ

ここでは https://lucide.dev/icons/message-circle-more これを利用する

まずartisanで

php artisan flux:icon message-circle-more

とかしておいて、

<flux:navlist.item icon="message-circle-more" ...

message-circle-moreに変更すればok


アイコンが変わった

indexの作成

この状態でChirpメニューを押すとやはりエラーとなる

というわけでindexを作成していく

app/Http/Controllers/ChirpController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\View\View;

class ChirpController extends Controller
{
    public function index(Request $request): View
    {
        return view('chirps.index', []);
    }
}

このように書いたらchirps.indexが存在しないので views/chirps/index.blade.php を作成する。ディレクトリが無いのでそれは作ること

views/chirps/index.blade.php
<x-layouts.app :title="__('Dashboard')">
    <div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
        <h1>Chirps Index</h1>
    </div>
</x-layouts.app>

すると、以下のように修正されてくるはずだ

フォームの描画とメッセージの保存

resources/views/livewire/chirps/create.blade.php
@@ -3,9 +3,39 @@
 use Livewire\Volt\Component;

 new class extends Component {
-    //
+    public string $message = '';
+
+    public function store(): void
+    {
+        dd($this->message);
+    }
 }; ?>

-<div>
-    //
-</div>
+<div class="flex flex-col gap-6">
+
+    <x-auth-session-status class="text-center" :status="session('status')" />
+
+    <form wire:submit="store" class="flex flex-col gap-6">
+        <flux:textarea
+            wire:model="message"
+            name="message"
+            placeholder="{{ __('What\'s on your mind?') }}"
+            {{-- required --}}
+            class="w-full"
+        />
+
+        <div class="flex items-center justify-end">
+            <flux:button variant="primary" type="submit">
+                {{ __('Chirp') }}
+            </flux:button>
+        </div>
+    </form>
+</div>

とするとフォームが描画される

テスト、と入力するとdd()により出力がdumpされる

このように、livewireの場合createのコンポーネントの中でstoreしてしまうという事を普通に行うようだ。

validationと保存

今、あえてrequiredをコメントアウトしているが、これでstoreからdd()を削除し、以下のようにしてみる

resources/views/livewire/chirps/create.blade.php
@@ -7,8 +7,13 @@

     public function store(): void
     {
-        dd($this->message);
-   }
+        $this->validate([
+            'message' => ['required', 'string', 'max:255'],
+        ]);
+
+        $this->message = '';
+        session()->flash('status', __('Chirp created successfully!'));
+    }
 }; ?>

 <div class="flex flex-col gap-6">

これはfluxの仕様でlabelが無いとどうやらエラーメッセージを出さないようなのでこれを利用する場合は何とかlabelを逐一書くようにしよう

<flux:textarea
  wire:model="message"
  name="message"
  placeholder="{{ __('What\'s on your mind?') }}"
  :label="__('Chirp')"
  {{-- required --}}
  class="w-full"
/>


エラーメッセージが表示された

実際に保存する

あとは保存するだけ

    public function store(): void
    {
        $validated = $this->validate([
            'message' => ['required', 'string', 'max:255'],
        ]);
        // 保存
        auth()->user()->chirps()->create($validated);

        $this->message = '';
        session()->flash('status', __('Chirp created successfully!'));
    }

これで保存できる


保存されたようだ

保存を確認する

コンソールでまず

composer dump-autoload

としたのち

php artisan tinker

するとtinkerのシェルに入れるので

Chirp::all()

とすると以下のように保存が確認できる

アトリビュートベースバリデーションへ

一応ほらphpの新しい構文推しみたいなんで、

resources/views/livewire/chirps/create.blade.php
@@ -1,15 +1,15 @@
 <?php

 use Livewire\Volt\Component;
+use Livewire\Attributes\Validate;

 new class extends Component {
+    #[Validate('required|string|max:255')]
     public string $message = '';

     public function store(): void
     {
-        $validated = $this->validate([
-            'message' => ['required', 'string', 'max:255'],
-        ]);
+        $validated = $this->validate();

         auth()->user()->chirps()->create($validated);

個人的には超絶可読性が悪くなるようにおもえてならないが、ここではbootcampのクローンを目指すので紹介しないわけにはいかない。一応validationが効いている事を確認したらformのrequiredのコメントアウトは取り払っておくこと(後でちゃんとテストも書く)

@@ -28,7 +28,7 @@ public function store(): void
             name="message"
             placeholder="{{ __('What\'s on your mind?') }}"
             :label="__('Chirp')"
-            {{-- required --}}
+            required
             class="w-full"
         />

次はContinue to start showing Chirps...

モッモッ

Chirp の作成ができるようになったので、次はそれらを画面に表示する

chirps.blade.php を更新して、Chirp の一覧を表示できるように

resources/views/chirps/index.blade.php へ投稿を表示するlivewireコンポーネントを挿入する

resources/views/chirps/index.blade.php
@@ -1,5 +1,6 @@
 <x-layouts.app :title="__('Dashboard')">
     <div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8">
         <livewire:chirps.create />
+        <livewire:chirps.list />
     </div>
 </x-layouts.app>

即座にmake:voltする

php artisan make:volt chirps/list --class

そうしたらresources/views/livewire/chirps/list.blade.phpを編集する

記事を取ってdd()する

ここではlivewireコンポーネントが最初に実行されるmount()というメソッドを書いていく

resources/views/livewire/chirps/list.blade.php
@@ -1,9 +1,22 @@
 <?php

 use Livewire\Volt\Component;
+use App\Models\Chirp;
+use Illuminate\Database\Eloquent\Collection;

 new class extends Component {
-    //
+
+    public Collection $chirps;
+
+    public function mount(): void
+    {
+        $this->chirps = Chirp::with('user')
+            ->latest()
+            ->get();
+
+        dd($this->chirps);
+    }
+
 }; ?>

 <div>

この段階で画面は以下のようになるだろう

ddを解除して、記事をloop表示

以下のようにする

resources/views/livewire/chirps/list.blade.php
@@ -14,11 +14,25 @@ public function mount(): void
             ->latest()
             ->get();

-        dd($this->chirps);
     }

 }; ?>

-<div>
-    //
-</div>
+<div class="mt-6 bg-white shadow-sm rounded-lg divide-y">
+    @foreach ($chirps as $chirp)
+        <div class="p-6 flex space-x-2" wire:key="{{ $chirp->id }}">
+            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-600 -scale-x-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
+                <path stroke-linecap="round" stroke-linejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
+            </svg>
+            <div class="flex-1">
+                <div class="flex justify-between items-center">
+                    <div>
+                        <span class="text-gray-800">{{ $chirp->user->name }}</span>
+                        <small class="ml-2 text-sm text-gray-600">{{ $chirp->created_at->format('j M Y, g:i a') }}</small>
+                    </div>
+                </div>
+                <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
+            </div>
+        </div>
+    @endforeach
+</div>

すると、なかなかそれっぽいものが出来あがってくる

Livewire イベントを使って、Chirp 作成時に一覧を更新する

ここで新しく投稿するとわかるんだけど

新しい投稿を行っても記事が再描画されない。これを何とかする

resources/views/livewire/chirps/create.blade.php
@@ -14,6 +14,7 @@ public function store(): void
         auth()->user()->chirps()->create($validated);

         $this->message = '';
+        $this->dispatch('chirp-created');
         session()->flash('status', __('Chirp created successfully!'));
     }
 }; ?>

このようにchirp-createdという名前のイベントをdispatch()している。この段階では何もおきないのだが、これをlistで受けとる

resources/views/livewire/chirps/list.blade.php
@@ -3,6 +3,7 @@
 use Livewire\Volt\Component;
 use App\Models\Chirp;
 use Illuminate\Database\Eloquent\Collection;
+use Livewire\Attributes\On;

 new class extends Component {

@@ -10,12 +11,15 @@

     public function mount(): void
     {
-        $this->chirps = Chirp::with('user')
-            ->latest()
-            ->get();
-
+        $this->getChirps();
     }

+    #[On('chirp-created')]
+    public function getChirps(): void
+    {
+        $this->chirps = Chirp::with('user')->latest()->get();
+    }
+
 }; ?>

 <div class="mt-6 bg-white shadow-sm rounded-lg divide-y">

見ての通りアトリビュート使っていたりして非常にクセツヨの構文であるが、、、いずれにせよこれで記事が更新されるようにはなった

モッモッ

編集

ownerとログインuser idが一致している時に編集できるようにする

listの更新

記事のownerとログインuidの比較を書いてdropdownを出している。結構長い

resources/views/livewire/chirps/list.blade.php
@@ -19,6 +19,12 @@ public function getChirps(): void
     {
         $this->chirps = Chirp::with('user')->latest()->get();
     }
+
+    public function edit(int $chirpId): void
+    {
+        dd('Edit chirp with ID: ' . $chirpId);
+    }
+
 }; ?>

 <div class="mt-6 bg-white shadow-sm rounded-lg divide-y">
@@ -33,6 +39,22 @@ public function getChirps(): void
                         <span class="text-gray-800">{{ $chirp->user->name }}</span>
                         <small class="ml-2 text-sm text-gray-600">{{ $chirp->created_at->format('j M Y, g:i a') }}</small>
                     </div>
+                    @if ($chirp->user->is(auth()->user()))
+                        <flux:dropdown position="bottom" align="end">
+                            <flux:button variant="ghost" size="sm">
+                                <flux:icon name="ellipsis-horizontal" class="h-4 w-4" />
+                            </flux:button>
+
+                            <flux:menu>
+                                <flux:menu.item  wire:click="edit({{ $chirp->id }})">
+                                    {{ __('Edit') }}
+                                </flux:menu.item>
+                                <flux:menu.item  variant="danger" wire:click="delete({{ $chirp->id }})">
+                                    {{ __('Delete') }}
+                                </flux:menu.item>
+                            </flux:menu>
+                        </flux:dropdown>
+                    @endif
                 </div>
                 <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
             </div>

deleteも書いてるけどまだ動作はしない

editを推すとdd()に移動したことが理解できるようにしてある

実際の編集処理

resources/views/livewire/chirps/list.blade.php
@@ -8,6 +8,7 @@
 new class extends Component {

     public Collection $chirps;
+    public ?Chirp $editing = null;

     public function mount(): void
     {
@@ -20,9 +21,10 @@ public function getChirps(): void
         $this->chirps = Chirp::with('user')->latest()->get();
     }

-    public function edit(int $chirpId): void
+    public function edit(Chirp $chirp): void
     {
-        dd('Edit chirp with ID: ' . $chirpId);
+        $this->editing = $chirp;
+        $this->getChirps();
     }

 }; ?>
@@ -56,7 +58,11 @@ public function edit(int $chirpId): void
                         </flux:dropdown>
                     @endif
                 </div>
-                <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
+                @if ($chirp->is($editing))
+                    ここに編集フォームを追加
+                @else
+                    <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
+                @endif
             </div>
         </div>
     @endforeach

こんな感じにしてEditを押すとこんな具合に変化する

ここに編集フォームを追加 を実際のformに置き換える

これもまたlivewireコンポーネントを使う

resources/views/livewire/chirps/list.blade.php
@@ -59,7 +59,7 @@ public function edit(Chirp $chirp): void
                     @endif
                 </div>
                 @if ($chirp->is($editing))
-                    ここに編集フォームを追加
+                    <livewire:chirps.edit :chirp="$chirp" :key="$chirp->id" />
                 @else
                     <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>
                 @endif

としたら即座にmake:voldする

php artisan make:volt chirps/edit --class

resources/views/livewire/chirps/edit.blade.phpが出来るので、これを手直ししていく

まずcancel処理から

resources/views/livewire/chirps/edit.blade.php
@@ -1,11 +1,55 @@
 <?php

 use Livewire\Volt\Component;
+use App\Models\Chirp;
+use Livewire\Attributes\Validate;

 new class extends Component {
-    //
+    public Chirp $chirp;
+    public string $message = '';
+
+    public function mount(): void
+    {
+        $this->message = $this->chirp->message;
+    }
+
+    public function cancel(): void
+    {
+        $this->dispatch('chirp-edit-canceled');
+    }
 }; ?>

 <div>
-    //
+    <form wire:submit="update">
+        <flux:textarea
+            wire:model="message"
+            name="message"
+            placeholder="{{ __('What\'s on your mind?') }}"
+            :label="__('Chirp')"
+            required
+            class="w-full"
+        />
+        {{--
+        <button class="mt-4">{{ __('Save') }}</button>
+        --}}
+        <flux:button variant="outline" wire:click.prevent="cancel">
+            {{ __('Cancel') }}
+        </flux:button>
+
+    </form>
 </div>

これによりCancelボタンが押されると

$this->dispatch('chirp-edit-canceled');

によりchirp-edit-canceledイベントが発行されるのでこれをlist.blade.phpで掴まえる

resources/views/livewire/chirps/list.blade.php
@@ -27,6 +27,15 @@ public function edit(Chirp $chirp): void
         $this->getChirps();
     }

+    #[On('chirp-edit-canceled')]
+    public function disableEditing(): void
+    {
+        $this->editing = null;
+
+        $this->getChirps();
+    }
+
 }; ?>

 <div class="mt-6 bg-white shadow-sm rounded-lg divide-y">

編集処理

ではCancelがうまくいったのでSaveボタンを付けて編集できるように

resources/views/livewire/chirps/edit.blade.php
@@ -1,11 +1,50 @@
 <?php

 use Livewire\Volt\Component;
+use App\Models\Chirp;
+use Livewire\Attributes\Validate;

 new class extends Component {
-    //
+    public Chirp $chirp;
+
+    #[Validate('required|string|max:255')]
+    public string $message = '';
+
+    public function mount(): void
+    {
+        $this->message = $this->chirp->message;
+    }
+
+    public function update(): void
+    {
+        // $this->authorize('update', $this->chirp);
+        $validated = $this->validate();
+        $this->chirp->update($validated);
+        $this->dispatch('chirp-updated');
+    }
+
+    public function cancel(): void
+    {
+        $this->dispatch('chirp-edit-canceled');
+    }
 }; ?>

 <div>
-    //
+    <form wire:submit="update">
+        <flux:textarea
+            wire:model="message"
+            name="message"
+            placeholder="{{ __('What\'s on your mind?') }}"
+            :label="__('Chirp')"
+            {{-- required --}}
+            class="w-full"
+        />
+
+        <flux:button variant="primary" type="submit">
+            {{ __('Save') }}
+        </flux:button>
+        <flux:button variant="outline" wire:click.prevent="cancel">
+            {{ __('Cancel') }}
+        </flux:button>
+    </form>
 </div>

編集するとまたchirp-updatedというイベントが飛ぶのでlistでそのイベントを受信する

resources/views/livewire/chirps/list.blade.php
@@ -28,6 +28,7 @@ public function edit(Chirp $chirp): void
     }

     #[On('chirp-edit-canceled')]
+    #[On('chirp-updated')]
     public function disableEditing(): void
     {
         $this->editing = null;

これで更新できるはずだ

authorization

laravelはポリシーの機能で

$this->authorize('update', $this->chirp);

対象のオブジェクトに対してどういう操作が出来るのかというのをモデルに定義できる。これを行うにはmake:policyする

php artisan make:policy ChirpPolicy --model=Chirp

するとapp/Policies/ChirpPolicy.phpが出来る。defaultは全て拒否なので正しくupdate可能なルールを定義してあげる

app/Policies/ChirpPolicy.php
@@ -4,7 +4,6 @@

 use App\Models\Chirp;
 use App\Models\User;
-use Illuminate\Auth\Access\Response;

 class ChirpPolicy
 {
@@ -37,7 +36,7 @@ public function create(User $user): bool
      */
     public function update(User $user, Chirp $chirp): bool
     {
-        return false;
+        return $chirp->user()->is($user);
     }

編集されたかどうかマーカーを付ける

最後に

resources/views/livewire/chirps/list.blade.php
@@ -48,6 +48,9 @@ public function disableEditing(): void
                     <div>
                         <span class="text-gray-800">{{ $chirp->user->name }}</span>
                     <small class="ml-2 text-sm text-gray-600">{{ $chirp->created_at->format('j M Y, g:i a') }}</small>
+                    @unless ($chirp->created_at->eq($chirp->updated_at))
+                        <small class="text-sm text-gray-600"> &middot; {{ __('edited') }}</small>
+                    @endunless
                 </div>
                 @if ($chirp->user->is(auth()->user()))
                     <flux:dropdown position="bottom" align="end">


これは正直やるならもう少し見た目を詰めてもいいかも

モッモッ

Chirps を削除する

削除ボタンについて

既にlist.blade.phpには削除ボタンが付いてはいるのだが

resources/views/livewire/chirps/list.blade.php
                @if ($chirp->user->is(auth()->user()))
                    <flux:dropdown position="bottom" align="end">
                        <flux:button variant="ghost" size="sm">
                            <flux:icon name="ellipsis-horizontal" class="h-4 w-4" />
                        </flux:button>

                        <flux:menu>
                            <flux:menu.item  wire:click="edit({{ $chirp->id }})">
                                {{ __('Edit') }}
                            </flux:menu.item>
                            <flux:menu.item  variant="danger" wire:click="delete({{ $chirp->id }})">
                                {{ __('Delete') }}
                            </flux:menu.item>
                        </flux:menu>
                    </flux:dropdown>
                @endif

クリックすると

Method or action [delete] does not exist on component [chirps.list].

となる。そこでdeleteメソッドを実装する

resources/views/livewire/chirps/list.blade.php
@@ -27,6 +27,14 @@ public function edit(Chirp $chirp): void
         $this->getChirps();
     }

+    public function delete(Chirp $chirp): void
+    {
+        dd($chirp);
+    }
+
     #[On('chirp-edit-canceled')]
     #[On('chirp-updated')]
     public function disableEditing(): void


deleteを押した結果

よさそうなので実際に削除する

resources/views/livewire/chirps/list.blade.php
@@ -27,6 +27,13 @@ public function edit(Chirp $chirp): void
         $this->getChirps();
     }

+    public function delete(Chirp $chirp): void
+    {
+        $chirp->delete();
+        $this->getChirps(); // reload
+        session()->flash('status', __('Chirp deleted successfully!'));
+    }


削除できるようになった

authorize

ここでもdeleteの許可設定を行っておこう。条件はeditと全く同じでよいはずだ

resources/views/livewire/chirps/list.blade.php
@@ -29,6 +29,7 @@ public function edit(Chirp $chirp): void

     public function delete(Chirp $chirp): void
     {
+        $this->authorize('delete', $chirp);
         $chirp->delete();
         $this->getChirps();
         session()->flash('status', __('Chirp deleted successfully!'));
app/Policies/ChirpPolicy.php
@@ -44,7 +44,7 @@ public function update(User $user, Chirp $chirp): bool
      */
     public function delete(User $user, Chirp $chirp): bool
     {
-        return false;
+        return $this->update($user, $chirp);
     }

削除のconfirm

resources/views/livewire/chirps/list.blade.php
@@ -70,7 +70,7 @@ public function disableEditing(): void
                             <flux:menu.item  wire:click="edit({{ $chirp->id }})">
                                 {{ __('Edit') }}
                             </flux:menu.item>
-                            <flux:menu.item  variant="danger" wire:click="delete({{ $chirp->id }})">
+                            <flux:menu.item  variant="danger" wire:click="delete({{ $chirp->id }})" wire:confirm="{{ __('Are you sure to delete this chirp?') }}">
                                 {{ __('Delete') }}
                             </flux:menu.item>
                         </flux:menu>

wire:confirm=を付けただけ

モッモッ

Notifications & Eventsは割愛

通知やeventに関して実は最後にいくつか書かれているのだけど、実装面でそこまで有意義と思えなかったので割愛。

モッモッ

git commit

このようにcopilot chatを開いて

Generate a commit message for the currently staged files in English.

とかやって生成してもらったらよい

基本的に Conventional Commitsに沿ってやってくれる

https://www.conventionalcommits.org/ja/v1.0.0/

Generate a commit message following the Conventional Commits specification.

とかでもいいかも。まあchatは日本語でもいいすよ

モッモッ

テスト

現在のテストがまず通るか確認

基本的にdevコンテナー使ってる場合は何も考えずtestは通るはず

php artisan test

テストの準備

テストデーターはfactoryで作成する必要がある。ここではUserのfactoryはdefaultで揃っているのだが、Chirpのfactoryがないので作成する。

php artisan make:factory ChirpFactory --model=Chirp

するとこんな感じでfactoryが出来るので開いて編集

database/factories/ChirpFactory.php
@@ -2,6 +2,7 @@

 namespace Database\Factories;

+use App\Models\User;
 use Illuminate\Database\Eloquent\Factories\Factory;

 /**
@@ -17,7 +18,8 @@ class ChirpFactory extends Factory
     public function definition(): array
     {
         return [
-            //
+            'message' => $this->faker->sentence(),
+            'user_id' => User::factory(),
         ];
     }
 }

ここではfactoryを生成すると自動的にユーザーも生成されるようになっているがまあこれは使い方次第で何とでもなるので、とりあえずこのようにしておく。