05. 编辑 Chirps
让我们添加一个其他流行的以鸟类为主题的微博平台中缺少的功能——编辑 Chirps 的功能!
更新我们的组件
让我们从更新现有的 chirp.list
Livewire 组件开始,为现有的 Chirps 添加一个编辑表单。编辑表单将是一个嵌套的 Livewire 组件,我们将在稍后创建它。
首先,我们将使用 Breeze 附带的 `x-dropdown` 组件。此外,我们将使此下拉菜单仅对 Chirp 的原始作者可见。在此下拉菜单中,我们将添加一个链接,该链接将触发组件上的 `edit` 操作。此方法将设置我们要编辑的 Chirp 的 `editing` 属性。我们将使用此属性有条件地显示编辑表单。
我们还将通过比较 Chirp 的 `created_at` 日期与其 `updated_at` 日期来显示 Chirp 是否已被编辑的指示。
<?php use App\Models\Chirp; use Illuminate\Database\Eloquent\Collection; use Livewire\Attributes\On; use Livewire\Volt\Component; new class extends Component { public Collection $chirps;+ + public ?Chirp $editing = null; public function mount(): void { $this->getChirps(); } #[On('chirp-created')] public function getChirps(): void { $this->chirps = Chirp::with('user') ->latest() ->get(); }+ + public function edit(Chirp $chirp): void+ {+ $this->editing = $chirp;+ + $this->getChirps();+ } }; ?> <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>+ @unless ($chirp->created_at->eq($chirp->updated_at))+ <small class="text-sm text-gray-600"> · {{ __('edited') }}</small>+ @endunless </div>+ @if ($chirp->user->is(auth()->user()))+ <x-dropdown>+ <x-slot name="trigger">+ <button>+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor">+ <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" />+ </svg>+ </button>+ </x-slot>+ <x-slot name="content">+ <x-dropdown-link wire:click="edit({{ $chirp->id }})">+ {{ __('Edit') }}+ </x-dropdown-link>+ </x-slot>+ </x-dropdown>+ @endif </div>- <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>+ @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 </div> </div> @endforeach </div>
<?php use App\Models\Chirp; use function Livewire\Volt\{on, state}; $getChirps = fn () => $this->chirps = Chirp::with('user')->latest()->get(); -state(['chirps' => $getChirps]); +state(['chirps' => $getChirps, 'editing' => null]); on(['chirp-created' => $getChirps]);+ +$edit = function (Chirp $chirp) {+ $this->editing = $chirp;+ + $this->getChirps();+}; ?> <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>+ @unless ($chirp->created_at->eq($chirp->updated_at))+ <small class="text-sm text-gray-600"> · {{ __('edited') }}</small>+ @endunless </div>+ @if ($chirp->user->is(auth()->user()))+ <x-dropdown>+ <x-slot name="trigger">+ <button>+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor">+ <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" />+ </svg>+ </button>+ </x-slot>+ <x-slot name="content">+ <x-dropdown-link wire:click="edit({{ $chirp->id }})">+ {{ __('Edit') }}+ </x-dropdown-link>+ </x-slot>+ </x-dropdown>+ @endif </div>- <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p>+ @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 </div> </div> @endforeach </div>
创建编辑表单
接下来,让我们创建 `chirps.edit` Livewire 组件
php artisan make:volt chirps/edit --class
php artisan make:volt chirps/edit
这将在 `resources/views/livewire/chirps/edit.blade.php` 中创建一个新的 Livewire 组件。让我们更新 Livewire 组件内容以显示用于编辑 Chirp 的表单。
请注意,即使我们只向 Chirp 的作者显示编辑按钮,我们还需要在服务器上授权请求,以确保它是 Chirp 的作者请求更新 Chirp。
<?php +use App\Models\Chirp; +use Livewire\Attributes\Validate; use Livewire\Volt\Component; 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"> + <textarea+ wire:model="message"+ class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"+ ></textarea>+ + <x-input-error :messages="$errors->get('message')" class="mt-2" />+ <x-primary-button class="mt-4">{{ __('Save') }}</x-primary-button>+ <button class="mt-4" wire:click.prevent="cancel">Cancel</button>+ </form> </div>
<?php -use function Livewire\Volt\{state}; +use function Livewire\Volt\{mount, rules, state}; + +state(['chirp', 'message']);+ +rules(['message' => 'required|string|max:255']);+ +mount(fn () => $this->message = $this->chirp->message);+ +$update = function () {+ $this->authorize('update', $this->chirp);+ + $validated = $this->validate();+ + $this->chirp->update($validated);+ + $this->dispatch('chirp-updated');+};+ +$cancel = fn () => $this->dispatch('chirp-edit-canceled'); ?> <div>- // + <form wire:submit="update"> + <textarea+ wire:model="message"+ class="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm"+ ></textarea>+ + <x-input-error :messages="$errors->get('message')" class="mt-2" />+ <x-primary-button class="mt-4">{{ __('Save') }}</x-primary-button>+ <button class="mt-4" wire:click.prevent="cancel">Cancel</button>+ </form> </div>
最后,我们需要更新 `chirp.list` 组件以监听 `chirp-updated` 和 `chirp-edit-canceled` 事件。
如果分发了 `chirp-updated` 事件,我们需要更新 Chirp 列表。如果分发了 `chirp-edit-canceled` 事件,我们需要将 `editing` 属性设置为 `null`,以便不再显示编辑表单。
<?php
use App\Models\Chirp; use Illuminate\Database\Eloquent\Collection; use Livewire\Attributes\On; use Livewire\Volt\Component; new class extends Component {
public Collection $chirps; public ?Chirp $editing = null; public function mount(): void { $this->getChirps(); } #[On('chirp-created')] public function getChirps(): void { $this->chirps = Chirp::with('user') ->latest() ->get(); } public function edit(Chirp $chirp): void { $this->editing = $chirp; $this->getChirps(); }+ + #[On('chirp-edit-canceled')]+ #[On('chirp-updated')] + public function disableEditing(): void+ {+ $this->editing = null;+ + $this->getChirps();+ } }; ?> <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> @unless ($chirp->created_at->eq($chirp->updated_at)) <small class="text-sm text-gray-600"> · {{ __('edited') }}</small> @endunless </div> @if ($chirp->user->is(auth()->user())) <x-dropdown> <x-slot name="trigger"> <button> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor"> <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" /> </svg> </button> </x-slot> <x-slot name="content"> <x-dropdown-link wire:click="edit({{ $chirp->id }})"> {{ __('Edit') }} </x-dropdown-link> </x-slot> </x-dropdown> @endif </div> <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p> @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 </div> </div> @endforeach </div>
<?php use App\Models\Chirp; use function Livewire\Volt\{on, state}; $getChirps = fn () => $this->chirps = Chirp::with('user')->latest()->get(); +$disableEditing = function () { + $this->editing = null;+ + return $this->getChirps();+}; state(['chirps' => $getChirps, 'editing' => null]); -on(['chirp-created' => $getChirps]); +on([ + 'chirp-created' => $getChirps,+ 'chirp-updated' => $disableEditing,+ 'chirp-edit-canceled' => $disableEditing,+]); $edit = function (Chirp $chirp) { $this->editing = $chirp; $this->getChirps(); }; ?> <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> @unless ($chirp->created_at->eq($chirp->updated_at)) <small class="text-sm text-gray-600"> · {{ __('edited') }}</small> @endunless </div> @if ($chirp->user->is(auth()->user())) <x-dropdown> <x-slot name="trigger"> <button> <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-gray-400" viewBox="0 0 20 20" fill="currentColor"> <path d="M6 10a2 2 0 11-4 0 2 2 0 014 0zM12 10a2 2 0 11-4 0 2 2 0 014 0zM16 12a2 2 0 100-4 2 2 0 000 4z" /> </svg> </button> </x-slot> <x-slot name="content"> <x-dropdown-link wire:click="edit({{ $chirp->id }})"> {{ __('Edit') }} </x-dropdown-link> </x-slot> </x-dropdown> @endif </div> <p class="mt-4 text-lg text-gray-900">{{ $chirp->message }}</p> @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 </div> </div> @endforeach </div>
授权
默认情况下,`authorize` 方法将阻止所有人更新 Chirp。我们可以通过以下命令创建 模型策略 来指定谁被允许更新它。
php artisan make:policy ChirpPolicy --model=Chirp
这将在 `app/Policies/ChirpPolicy.php` 中创建一个策略类,我们可以更新它以指定只有作者被授权更新 Chirp。
<?php
namespace App\Policies; use App\Models\Chirp; use App\Models\User; use Illuminate\Auth\Access\HandlesAuthorization; class ChirpPolicy {
use HandlesAuthorization; /** * Determine whether the user can view any models. */ public function viewAny(User $user): bool { // } /** * Determine whether the user can view the model. */ public function view(User $user, Chirp $chirp): bool { // } /** * Determine whether the user can create models. */ public function create(User $user): bool { // } /** * Determine whether the user can update the model. */ public function update(User $user, Chirp $chirp): bool {- //+ return $chirp->user()->is($user); }
/** * Determine whether the user can delete the model. */ public function delete(User $user, Chirp $chirp): bool { // } /** * Determine whether the user can restore the model. */ public function restore(User $user, Chirp $chirp): bool { // } /** * Determine whether the user can permanently delete the model. */ public function forceDelete(User $user, Chirp $chirp): bool { // } }
测试一下
是时候测试一下了!继续使用下拉菜单编辑一些 Chirp。如果您注册另一个用户帐户,您会看到只有 Chirp 的作者可以编辑它。