跳至内容

05. 编辑 Chirps

让我们添加一个其他流行的以鸟类为主题的微博平台中缺少的功能——编辑 Chirps 的功能!

更新我们的组件

让我们从更新现有的 chirp.list Livewire 组件开始,为现有的 Chirps 添加一个编辑表单。编辑表单将是一个嵌套的 Livewire 组件,我们将在稍后创建它。

首先,我们将使用 Breeze 附带的 `x-dropdown` 组件。此外,我们将使此下拉菜单仅对 Chirp 的原始作者可见。在此下拉菜单中,我们将添加一个链接,该链接将触发组件上的 `edit` 操作。此方法将设置我们要编辑的 Chirp 的 `editing` 属性。我们将使用此属性有条件地显示编辑表单。

我们还将通过比较 Chirp 的 `created_at` 日期与其 `updated_at` 日期来显示 Chirp 是否已被编辑的指示。

resources/views/livewire/chirps/list.blade.php
<?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"> &middot; {{ __('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>
resources/views/livewire/chirps/list.blade.php
<?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"> &middot; {{ __('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。

resources/views/livewire/chirps/edit.blade.php
<?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>
resources/views/livewire/chirps/edit.blade.php
<?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`,以便不再显示编辑表单。

resources/views/livewire/chirps/list.blade.php
<?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"> &middot; {{ __('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>
resources/views/livewire/chirps/list.blade.php
<?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"> &middot; {{ __('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。

app/Policies/ChirpPolicy.php
<?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 的作者可以编辑它。

An editted chirp

继续允许删除 Chirp...