操作
Livewire 操作是组件中的方法,可以通过前端交互(如单击按钮或提交表单)触发。它们为开发人员提供了直接从浏览器调用 PHP 方法的体验,让您专注于应用程序的逻辑,而无需陷入编写连接应用程序前端和后端的样板代码的困境。
我们来探索一个在 CreatePost
组件上调用 save
操作的基本示例
<?php namespace App\Livewire; use Livewire\Component;use App\Models\Post; class CreatePost extends Component{ public $title = ''; public $content = ''; public function save() { Post::create([ 'title' => $this->title, 'content' => $this->content, ]); return redirect()->to('/posts'); } public function render() { return view('livewire.create-post'); }}
<form wire:submit="save"> <input type="text" wire:model="title"> <textarea wire:model="content"></textarea> <button type="submit">Save</button></form>
在上面的示例中,当用户单击“保存”提交表单时,wire:submit
会拦截 submit
事件并在服务器上调用 save()
操作。
从本质上说,操作是一种将用户交互轻松映射到服务器端功能的方法,而无需手动提交和处理 AJAX 请求的麻烦。
刷新组件
有时您可能希望触发组件的简单“刷新”。例如,如果您有一个组件正在检查数据库中某项内容的状态,您可能希望向用户显示一个按钮,允许他们刷新显示的结果。
您可以在通常引用您自己的组件方法的任何地方使用 Livewire 的简单 $refresh
操作来执行此操作
<button type="button" wire:click="$refresh">...</button>
当触发 $refresh
操作时,Livewire 将进行服务器往返并重新渲染您的组件,而无需调用任何方法。
请注意,组件中的任何待定数据更新(例如 wire:model
绑定)将在组件刷新时应用到服务器上。
在内部,Livewire 使用名称“commit”来指代 Livewire 组件在服务器上更新的任何时间。如果您喜欢此术语,则可以使用 $commit
帮助器,而不是 $refresh
。两者是相同的。
<button type="button" wire:click="$commit">...</button>
您还可以在 Livewire 组件中使用 AlpineJS 触发组件刷新
<button type="button" x-on:click="$wire.$refresh()">...</button>
通过阅读 在 Livewire 中使用 Alpine 的文档 了解更多信息。
确认操作
当允许用户执行危险操作(例如从数据库中删除帖子)时,您可能希望向他们显示一个确认警报,以验证他们是否希望执行该操作。
Livewire 通过提供一个名为 wire:confirm
的简单指令,让此操作变得简单
<button type="button" wire:click="delete" wire:confirm="Are you sure you want to delete this post?"> Delete post </button>
当 wire:confirm
添加到包含 Livewire 操作的元素时,当用户尝试触发该操作时,他们将看到一个包含所提供消息的确认对话框。他们可以按“确定”确认操作,也可以按“取消”或按 Escape 键。
有关更多信息,请访问 wire:confirm
文档页面。
事件侦听器
Livewire 支持各种事件侦听器,允许您响应各种类型的用户交互
侦听器 | 描述 |
---|---|
wire:click |
当元素被点击时触发 |
wire:submit |
当表单提交时触发 |
wire:keydown |
当按下键时触发 |
wire:keyup |
当释放键时触发 |
wire:mouseenter |
当鼠标进入元素时触发 |
wire:* |
wire: 后面的任何文本都将用作侦听器的事件名称 |
由于 wire:
后面的事件名称可以是任何内容,因此 Livewire 支持您可能需要侦听的任何浏览器事件。例如,要侦听 transitionend
,可以使用 wire:transitionend
。
侦听特定键
您可以使用 Livewire 的一个便捷别名将按键事件侦听器缩小到特定键或键组合。
例如,要在用户在搜索框中输入后按 Enter
时执行搜索,可以使用 wire:keydown.enter
<input wire:model="query" wire:keydown.enter="searchPosts">
您可以在第一个键别名之后链接更多键别名来监听键的组合。例如,如果您只想在按下 Enter
键时监听 Shift
键,您可以编写以下内容
<input wire:keydown.shift.enter="...">
以下是所有可用的键修饰符列表
修饰符 | 键 |
---|---|
.shift |
Shift |
.enter |
Enter |
.space |
Space |
.ctrl |
Ctrl |
.cmd |
Cmd |
.meta |
Mac 上的 Cmd,Windows 上的 Windows 键 |
.alt |
Alt |
.up |
向上箭头 |
.down |
向下箭头 |
.left |
向左箭头 |
.right |
向右箭头 |
.escape |
Escape |
.tab |
Tab |
.caps-lock |
Caps Lock |
.equal |
等号,= |
.period |
句号,. |
.slash |
正斜杠,/ |
事件处理程序修饰符
Livewire 还包括一些有用的修饰符,可以轻松完成常见的事件处理任务。
例如,如果您需要从事件侦听器内部调用 event.preventDefault()
,则可以在事件名称后加上 .prevent
<input wire:keydown.prevent="...">
以下是所有可用的事件侦听器修饰符及其功能的完整列表
修饰符 | 键 |
---|---|
.prevent |
相当于调用 .preventDefault() |
.stop |
相当于调用 .stopPropagation() |
.window |
在 window 对象上侦听事件 |
.outside |
仅侦听元素“外部”的点击 |
.document |
在 document 对象上侦听事件 |
.once |
确保侦听器仅被调用一次 |
.debounce |
默认情况下以 250 毫秒为单位对处理程序进行防抖 |
.debounce.100ms |
在特定时间内对处理程序进行防抖 |
.throttle |
将处理程序限制为至少每 250 毫秒调用一次 |
.throttle.100ms |
以自定义持续时间限制处理程序 |
.self |
仅在事件源自该元素(而不是子元素)时调用侦听器 |
.camel |
将事件名称转换为驼峰式大小写(wire:custom-event -> "customEvent") |
.dot |
将事件名称转换为点表示法(wire:custom-event -> "custom.event") |
.passive |
wire:touchstart.passive 不会阻止滚动性能 |
.capture |
在“捕获”阶段侦听事件 |
由于 wire:
在幕后使用 Alpine 的 x-on
指令,因此这些修饰符由 Alpine 提供给您。有关何时应使用这些修饰符的更多背景信息,请参阅 Alpine 事件文档。
处理第三方事件
Livewire 还支持监听第三方库触发的自定义事件。
例如,假设你在项目中使用了 Trix 富文本编辑器,并且希望监听 trix-change
事件以捕获编辑器内容。你可以使用 wire:trix-change
指令来实现此目的
<form wire:submit="save"> <!-- ... --> <trix-editor wire:trix-change="setPostContent($event.target.value)" ></trix-editor> <!-- ... --></form>
在此示例中,每当触发 trix-change
事件时,都会调用 setPostContent
操作,使用 Trix 编辑器的当前值更新 Livewire 组件中的 content
属性。
$event
访问事件对象
在 Livewire 事件处理程序中,你可以通过 $event
访问事件对象。这对于引用事件中的信息很有用。例如,你可以通过 $event.target
访问触发事件的元素。
上面的 Trix 演示代码不完整,仅可用作事件侦听器的演示。如果逐字使用,则每次击键都会触发网络请求。性能更高的实现方式是
<trix-editor x-on:trix-change="$wire.content = $event.target.value"></trix-editor>
监听分发的自定义事件
如果你的应用程序从 Alpine 分发自定义事件,你也可以使用 Livewire 监听这些事件
<div wire:custom-event="..."> <!-- Deeply nested within this component: --> <button x-on:click="$dispatch('custom-event')">...</button> </div>
在上述示例中,当单击按钮时,会分发 custom-event
事件并冒泡到 Livewire 组件的根部,wire:custom-event
会捕获该事件并调用给定的操作。
如果你想监听应用程序其他位置分发的事件,则需要等待事件冒泡到 window
对象,然后在那里监听它。幸运的是,Livewire 通过允许你向任何事件侦听器添加一个简单的 .window
修饰符,从而使此操作变得容易
<div wire:custom-event.window="..."> <!-- ... --></div> <!-- Dispatched somewhere on the page outside the component: --><button x-on:click="$dispatch('custom-event')">...</button>
在提交表单时禁用输入
考虑我们之前讨论的 CreatePost
示例
<form wire:submit="save"> <input wire:model="title"> <textarea wire:model="content"></textarea> <button type="submit">Save</button></form>
当用户单击“保存”时,会向服务器发送网络请求以调用 Livewire 组件上的 save()
操作。
但是,假设用户在网速较慢的连接上填写此表单。用户单击“保存”,但最初什么也没有发生,因为网络请求花费的时间比平时长。他们可能会怀疑提交是否失败,并在第一个请求仍在处理时再次尝试单击“保存”按钮。
在这种情况下,将同时处理针对同一操作的两个请求。
为了防止这种情况,Livewire 在处理 wire:submit
操作时会自动禁用 <form>
元素内的提交按钮和所有表单输入。这可确保不会意外地提交表单两次。
为了进一步减少连接速度较慢的用户遇到的困惑,通常有帮助的做法是显示一些加载指示器,例如微妙的背景颜色更改或 SVG 动画。
Livewire 提供了一个 wire:loading
指令,可轻松地在页面上的任何位置显示和隐藏加载指示器。以下是一个使用 wire:loading
在“保存”按钮下方显示加载消息的简短示例
<form wire:submit="save"> <textarea wire:model="content"></textarea> <button type="submit">Save</button> <span wire:loading>Saving...</span> </form>
wire:loading
是一个强大的功能,具有各种更强大的功能。 查看完整的加载文档以获取更多信息。
传递参数
Livewire 允许您将参数从 Blade 模板传递到组件中的操作,从而让您有机会在调用操作时从前端提供操作附加数据或状态。
例如,假设您有一个 ShowPosts
组件,允许用户删除帖子。您可以将帖子的 ID 作为参数传递给 Livewire 组件中的 delete()
操作。然后,该操作可以获取相关帖子并将其从数据库中删除
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class ShowPosts extends Component{ public function delete($id) { $post = Post::findOrFail($id); $this->authorize('delete', $post); $post->delete(); } public function render() { return view('livewire.show-posts', [ 'posts' => Auth::user()->posts, ]); }}
<div> @foreach ($posts as $post) <div wire:key="{{ $post->id }}"> <h1>{{ $post->title }}</h1> <span>{{ $post->content }}</span> <button wire:click="delete({{ $post->id }})">Delete</button> </div> @endforeach</div>
对于 ID 为 2 的帖子,Blade 模板中上面的“删除”按钮将在浏览器中呈现为
<button wire:click="delete(2)">Delete</button>
单击此按钮时,将调用 delete()
方法,并且 $id
将传递,其值为“2”。
操作参数应像 HTTP 请求输入一样对待,这意味着不应信任操作参数值。在数据库中更新实体之前,您应始终授权其所有权。
有关更多信息,请参阅我们关于 安全问题和最佳实践 的文档。
作为一种额外的便利,您可以通过作为参数提供给操作的相应模型 ID 自动解析 Eloquent 模型。这与 路由模型绑定 非常相似。要开始,请使用模型类提示操作参数的类型,然后将自动从数据库中检索适当的模型并将其传递给操作,而不是 ID
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class ShowPosts extends Component{ public function delete(Post $post) { $this->authorize('delete', $post); $post->delete(); } public function render() { return view('livewire.show-posts', [ 'posts' => Auth::user()->posts, ]); }}
依赖注入
您可以通过在操作签名中提示参数类型来利用 Laravel 的依赖注入 系统。Livewire 和 Laravel 将自动从容器中解析操作的依赖项
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Repositories\PostRepository; class ShowPosts extends Component{ public function delete(PostRepository $posts, $postId) { $posts->deletePost($postId); } public function render() { return view('livewire.show-posts', [ 'posts' => Auth::user()->posts, ]); }}
<div> @foreach ($posts as $post) <div wire:key="{{ $post->id }}"> <h1>{{ $post->title }}</h1> <span>{{ $post->content }}</span> <button wire:click="delete({{ $post->id }})">Delete</button> </div> @endforeach</div>
在此示例中,delete()
方法在接收提供的 $postId
参数之前,接收通过 Laravel 服务容器 解析的 PostRepository
实例。
从 Alpine 调用操作
Livewire 与 Alpine 无缝集成。事实上,在幕后,每个 Livewire 组件也是一个 Alpine 组件。这意味着你可以在组件中充分利用 Alpine,以添加由 JavaScript 驱动的客户端交互性。
为了让这种配对更加强大,Livewire 向 Alpine 公开了一个神奇的 $wire
对象,该对象可以被视为 PHP 组件的 JavaScript 表示。除了 通过 $wire
访问和更改公共属性 之外,你还可以调用操作。当在 $wire
对象上调用操作时,将在后端 Livewire 组件上调用相应 PHP 方法
<button x-on:click="$wire.save()">Save Post</button>
或者,为了说明一个更复杂的示例,你可以使用 Alpine 的 x-intersect
实用程序,当给定元素在页面上可见时触发 incrementViewCount()
Livewire 操作
<div x-intersect="$wire.incrementViewCount()">...</div>
传递参数
你传递给 $wire
方法的任何参数也将传递给 PHP 类方法。例如,考虑以下 Livewire 操作
public function addTodo($todo){ $this->todos[] = $todo;}
在组件的 Blade 模板中,你可以通过 Alpine 调用此操作,提供应提供给操作的参数
<div x-data="{ todo: '' }"> <input type="text" x-model="todo"> <button x-on:click="$wire.addTodo(todo)">Add Todo</button></div>
如果用户在文本输入框中输入“倒垃圾”,然后按下“添加待办事项”按钮,则 addTodo()
方法将被触发,$todo
参数值为“倒垃圾”。
接收返回值
为了获得更大的功能,在网络请求处理期间,调用的 $wire
操作会返回一个 Promise。当收到服务器响应时,Promise 会使用后端操作返回的值进行解析。
例如,考虑一个具有以下操作的 Livewire 组件
use App\Models\Post; public function getPostCount(){ return Post::count();}
使用 $wire
,可以调用操作并解析其返回值
<span x-init="$el.innerHTML = await $wire.getPostCount()"></span>
在此示例中,如果 getPostCount()
方法返回“10”,则 <span>
标记也将包含“10”。
使用 Livewire 时不需要 Alpine 知识;但是,它是一个非常强大的工具,了解 Alpine 将增强你的 Livewire 体验和生产力。
Livewire 的“混合”JavaScript 函数
有时,组件中的操作不需要与服务器通信,并且可以使用仅 JavaScript 更有效地编写。
在这些情况下,您可以让组件操作返回 JavaScript 函数作为字符串,而不是在 Blade 模板或其他文件中编写操作。如果操作标记了 #[Js]
属性,则可以从应用程序的前端调用它
例如
<?php namespace App\Livewire; use Livewire\Attributes\Js;use Livewire\Component;use App\Models\Post; class SearchPosts extends Component{ public $query = ''; #[Js] public function resetQuery() { return <<<'JS' $wire.query = ''; JS; } public function render() { return view('livewire.search-posts', [ 'posts' => Post::whereTitle($this->query)->get(), ]); }}
<div> <input wire:model.live="query"> <button wire:click="resetQuery">Reset Search</button> @foreach ($posts as $post) <!-- ... --> @endforeach</div>
在上面的示例中,当按下“重置搜索”按钮时,文本输入将被清除,而不会向服务器发送任何请求。
求值一次性 JavaScript 表达式
除了指定要在 JavaScript 中求值的方法外,您还可以使用 js()
方法求值更小的单个表达式。
这通常在执行服务器端操作后执行某种客户端后续操作时很有用。
例如,以下是一个 CreatePost
组件的示例,该组件在将帖子保存到数据库后触发客户端警报对话框
<?php namespace App\Livewire; use Livewire\Component; class CreatePost extends Component{ public $title = ''; public function save() { // ... $this->js("alert('Post saved!')"); }}
JavaScript 表达式 alert('Post saved!')
现在将在服务器上将帖子保存到数据库后在客户端执行。
就像 #[Js]
方法一样,您可以在表达式中访问当前组件的 $wire
对象。
神奇操作
Livewire 提供了一组“神奇”操作,允许您在组件中执行常见任务,而无需定义自定义方法。这些神奇操作可以在 Blade 模板中定义的事件侦听器中使用。
$parent
$parent
神奇变量允许您从子组件访问父组件属性并调用父组件操作
<button wire:click="$parent.removePost({{ $post->id }})">Remove</button>
在上面的示例中,如果父组件有 removePost()
操作,则子组件可以使用 $parent.removePost()
直接从其 Blade 模板中调用它。
$set
$set
神奇操作允许您直接从 Blade 模板更新 Livewire 组件中的属性。要使用 $set
,请提供您要更新的属性和新值作为参数
<button wire:click="$set('query', '')">Reset Search</button>
在此示例中,当单击按钮时,将分派一个网络请求,将组件中的 $query
属性设置为 ''
。
$refresh
$refresh
操作触发 Livewire 组件的重新渲染。这在不更改任何属性值的情况下更新组件视图时很有用
<button wire:click="$refresh">Refresh</button>
单击按钮时,组件将重新渲染,让你看到视图中的最新更改。
$toggle
$toggle
操作用于切换 Livewire 组件中布尔属性的值
<button wire:click="$toggle('sortAsc')"> Sort {{ $sortAsc ? 'Descending' : 'Ascending' }}</button>
在此示例中,单击按钮时,组件中的 $sortAsc
属性将在 true
和 false
之间切换。
$dispatch
$dispatch
操作允许你在浏览器中直接分派 Livewire 事件。以下是单击时将分派 post-deleted
事件的按钮示例
<button type="submit" wire:click="$dispatch('post-deleted')">Delete Post</button>
$event
$event
操作可以在事件侦听器(如 wire:click
)中使用。此操作允许你访问触发的实际 JavaScript 事件,让你可以引用触发元素和其他相关信息
<input type="text" wire:keydown.enter="search($event.target.value)">
当用户在上述输入框中键入时按下回车键,输入框的内容将作为参数传递给 search()
操作。
使用 Alpine 中的魔术操作
你还可以使用 $wire
对象从 Alpine 中调用魔术操作。例如,你可以使用 $wire
对象调用 $refresh
魔术操作
<button x-on:click="$wire.$refresh()">Refresh</button>
跳过重新渲染
有时,你的组件中可能存在一个操作,该操作没有任何副作用,在调用该操作时会更改渲染的 Blade 模板。如果是这样,你可以通过在操作方法上方添加 #[Renderless]
属性来跳过 Livewire 生命周期中的 render
部分。
为了演示,在下面的 ShowPost
组件中,当用户滚动到帖子的底部时,会记录“查看次数”
<?php namespace App\Livewire; use Livewire\Attributes\Renderless;use Livewire\Component;use App\Models\Post; class ShowPost extends Component{ public Post $post; public function mount(Post $post) { $this->post = $post; } #[Renderless] public function incrementViewCount() { $this->post->incrementViewCount(); } public function render() { return view('livewire.show-post'); }}
<div> <h1>{{ $post->title }}</h1> <p>{{ $post->content }}</p> <div x-intersect="$wire.incrementViewCount()"></div></div>
上面的示例使用了 x-intersect
,这是一个 Alpine 实用程序,当元素进入视口时调用表达式(通常用于检测用户何时滚动到页面下方的元素)。
如您所见,当用户滚动到帖子的底部时,将调用 incrementViewCount()
。由于 #[Renderless]
已添加到操作,因此视图已记录,但模板不会重新渲染,并且页面的任何部分都不会受到影响。
如果您不想使用 method 属性或需要有条件地跳过渲染,则可以在组件操作中调用 skipRender()
方法
<?php namespace App\Livewire; use Livewire\Component;use App\Models\Post; class ShowPost extends Component{ public Post $post; public function mount(Post $post) { $this->post = $post; } public function incrementViewCount() { $this->post->incrementViewCount(); $this->skipRender(); } public function render() { return view('livewire.show-post'); }}
安全问题
请记住,即使没有关联的 wire:click
处理器调用它,也可以从客户端调用 Livewire 组件中的任何公共方法。在这些情况下,用户仍然可以从浏览器的 DevTools 触发操作。
以下是 Livewire 组件中三个容易被忽视的漏洞示例。每个示例都会首先显示易受攻击的组件,然后显示安全的组件。作为练习,请尝试在查看解决方案之前发现第一个示例中的漏洞。
如果您难以发现漏洞,并且这会让您担心自己保护应用程序安全的能力,请记住所有这些漏洞都适用于使用请求和控制器的标准 Web 应用程序。如果您将组件方法用作控制器方法的代理,并将其参数用作请求输入的代理,则应该能够将现有的应用程序安全知识应用到 Livewire 代码中。
始终授权操作参数
就像控制器请求输入一样,授权操作参数非常重要,因为它们是任意的用户输入。
下面是一个 ShowPosts
组件,用户可以在其中在一个页面上查看其所有帖子。他们可以使用帖子的“删除”按钮删除任何他们喜欢的帖子。
以下是组件的易受攻击版本
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class ShowPosts extends Component{ public function delete($id) { $post = Post::find($id); $post->delete(); } public function render() { return view('livewire.show-posts', [ 'posts' => Auth::user()->posts, ]); }}
<div> @foreach ($posts as $post) <div wire:key="{{ $post->id }}"> <h1>{{ $post->title }}</h1> <span>{{ $post->content }}</span> <button wire:click="delete({{ $post->id }})">Delete</button> </div> @endforeach</div>
请记住,恶意用户可以直接从 JavaScript 控制台调用 delete()
,将他们希望的操作传递给任何参数。这意味着查看其一篇帖子的用户可以通过将未拥有的帖子 ID 传递给 delete()
来删除另一位用户的帖子。
为了保护这一点,我们需要授权用户拥有即将被删除的帖子
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class ShowPosts extends Component{ public function delete($id) { $post = Post::find($id); $this->authorize('delete', $post); $post->delete(); } public function render() { return view('livewire.show-posts', [ 'posts' => Auth::user()->posts, ]); }}
始终授权服务器端
与标准 Laravel 控制器一样,即使 UI 中没有调用操作的承受力,任何用户也可以调用 Livewire 操作。
考虑以下 BrowsePosts
组件,其中任何用户都可以查看应用程序中的所有帖子,但只有管理员可以删除帖子
<?php namespace App\Livewire; use Livewire\Component;use App\Models\Post; class BrowsePosts extends Component{ public function deletePost($id) { $post = Post::find($id); $post->delete(); } public function render() { return view('livewire.browse-posts', [ 'posts' => Post::all(), ]); }}
<div> @foreach ($posts as $post) <div wire:key="{{ $post->id }}"> <h1>{{ $post->title }}</h1> <span>{{ $post->content }}</span> @if (Auth::user()->isAdmin()) <button wire:click="deletePost({{ $post->id }})">Delete</button> @endif </div> @endforeach</div>
如您所见,只有管理员才能看到“删除”按钮;但是,任何用户都可以从浏览器的 DevTools 在组件上调用 deletePost()
。
要修补此漏洞,我们需要像这样在服务器上授权操作
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class BrowsePosts extends Component{ public function deletePost($id) { if (! Auth::user()->isAdmin) { abort(403); } $post = Post::find($id); $post->delete(); } public function render() { return view('livewire.browse-posts', [ 'posts' => Post::all(), ]); }}
通过此更改,只有管理员才能从此组件中删除帖子。
保持危险方法受保护或私有
Livewire 组件中的每个公共方法都可以从客户端调用。即使您没有在 wire:click
处理程序中引用的方法。为了防止用户调用不打算在客户端调用的方法,您应该将它们标记为 protected
或 private
。通过这样做,您可以将该敏感方法的可见性限制为组件的类及其子类,确保它们不能从客户端调用。
考虑我们之前讨论过的 BrowsePosts
示例,其中用户可以在您的应用程序中查看所有帖子,但只有管理员可以删除帖子。在 始终授权服务器端 部分中,我们通过添加服务器端授权来确保操作安全。现在想象一下,我们将帖子的实际删除重构为一个专用方法,就像您可能为了简化代码而做的那样
// Warning: This snippet demonstrates what NOT to do...<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class BrowsePosts extends Component{ public function deletePost($id) { if (! Auth::user()->isAdmin) { abort(403); } $this->delete($id); } public function delete($postId) { $post = Post::find($postId); $post->delete(); } public function render() { return view('livewire.browse-posts', [ 'posts' => Post::all(), ]); }}
<div> @foreach ($posts as $post) <div wire:key="{{ $post->id }}"> <h1>{{ $post->title }}</h1> <span>{{ $post->content }}</span> <button wire:click="deletePost({{ $post->id }})">Delete</button> </div> @endforeach</div>
如您所见,我们将帖子删除逻辑重构为一个名为 delete()
的专用方法。即使此方法在我们的模板中没有任何引用,但如果用户了解它的存在,他们将能够从浏览器的 DevTools 中调用它,因为它为 public
。
为了解决这个问题,我们可以将该方法标记为 protected
或 private
。一旦该方法被标记为 protected
或 private
,如果用户尝试调用它,将抛出一个错误
<?php namespace App\Livewire; use Illuminate\Support\Facades\Auth;use Livewire\Component;use App\Models\Post; class BrowsePosts extends Component{ public function deletePost($id) { if (! Auth::user()->isAdmin) { abort(403); } $this->delete($id); } protected function delete($postId) { $post = Post::find($postId); $post->delete(); } public function render() { return view('livewire.browse-posts', [ 'posts' => Post::all(), ]); }}