04. 显示 Chirps
在前面的步骤中,我们添加了创建 Chirps 的功能,现在我们准备显示它们!
检索 Chirps
让我们更新 ChirpController
类的 index
方法,将每个用户的 Chirps 传递到我们的索引页面
app/Http/Controllers/ChirpController.php
<?php
namespace App\Http\Controllers; use App\Models\Chirp; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Inertia; use Inertia\Response; class ChirpController extends Controller { /** * Display a listing of the resource. */ public function index(): Response { return Inertia::render('Chirps/Index', [- //+ 'chirps' => Chirp::with('user:id,name')->latest()->get(), ]); }
/** * Show the form for creating a new resource. */ public function create() { // } /** * Store a newly created resource in storage. */ public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'message' => 'required|string|max:255', ]); $request->user()->chirps()->create($validated); return redirect(route('chirps.index')); } /** * Display the specified resource. */ public function show(Chirp $chirp) { // } /** * Show the form for editing the specified resource. */ public function edit(Chirp $chirp) { // } /** * Update the specified resource in storage. */ public function update(Request $request, Chirp $chirp) { // } /** * Remove the specified resource from storage. */ public function destroy(Chirp $chirp) { // } }
在这里,我们使用了 Eloquent 的 with
方法来 预加载 每个 Chirp 关联的用户 ID 和姓名。我们还使用了 latest
作用域来按逆时间顺序返回记录。
一次性返回所有 Chirps 在生产环境中无法扩展。查看 Laravel 的强大 分页 功能来提高性能。
将用户连接到 Chirps
我们已经指示 Laravel 从 user
关系中返回 id
和 name
属性,以便我们可以显示 Chirp 作者的姓名,而不会返回其他可能敏感的信息,例如作者的电子邮件地址。但是,Chirp 的 user
关系尚未定义。为了解决这个问题,让我们在 Chirp
模型中添加一个新的 "属于" 关系
app/Models/Chirp.php
<?php
namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model;+use Illuminate\Database\Eloquent\Relations\BelongsTo; class Chirp extends Model {
use HasFactory; protected $fillable = [ 'message', ]; + public function user(): BelongsTo+ {+ return $this->belongsTo(User::class);+ } }
这种关系是我们之前在 User
模型上创建的 "拥有多个" 关系的逆关系。
更新我们的组件
接下来,让我们为我们的前端创建一个 Chirp
组件。这个组件将负责显示单个 Chirp
resources/js/Components/Chirp.vue
<script setup>defineProps(['chirp']);</script> <template> <div class="p-6 flex space-x-2"> <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">{{ new Date(chirp.created_at).toLocaleString() }}</small> </div> </div> <p class="mt-4 text-lg text-gray-900">{{ chirp.message }}</p> </div> </div></template>
resources/js/Components/Chirp.jsx
import React from 'react'; export default function Chirp({ chirp }) { return ( <div className="p-6 flex space-x-2"> <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 text-gray-600 -scale-x-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"> <path strokeLinecap="round" strokeLinejoin="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 className="flex-1"> <div className="flex justify-between items-center"> <div> <span className="text-gray-800">{chirp.user.name}</span> <small className="ml-2 text-sm text-gray-600">{new Date(chirp.created_at).toLocaleString()}</small> </div> </div> <p className="mt-4 text-lg text-gray-900">{chirp.message}</p> </div> </div> );}
最后,我们将更新我们的 Chirps/Index
页面组件,使其接受 chirps
属性,并使用我们的新组件在我们的表单下方渲染 Chirps
resources/js/Pages/Chirps/Index.vue
<script setup> import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';+import Chirp from '@/Components/Chirp.vue'; import InputError from '@/Components/InputError.vue'; import PrimaryButton from '@/Components/PrimaryButton.vue'; import { useForm, Head } from '@inertiajs/vue3'; +defineProps(['chirps']); const form = useForm({ message: '', }); </script> <template> <Head title="Dashboard" /> <AuthenticatedLayout> <div class="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8"> <form @submit.prevent="form.post(route('chirps.store'), { onSuccess: () => form.reset() })"> <textarea v-model="form.message" placeholder="What's on your mind?" 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> <InputError :message="form.errors.message" class="mt-2" /> <PrimaryButton class="mt-4">Chirp</PrimaryButton> </form> + <div class="mt-6 bg-white shadow-sm rounded-lg divide-y">+ <Chirp+ v-for="chirp in chirps"+ :key="chirp.id"+ :chirp="chirp"+ />+ </div> </div> </AuthenticatedLayout> </template>
resources/js/Pages/Chirps/Index.jsx
import React from 'react'; import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';+import Chirp from '@/Components/Chirp'; import InputError from '@/Components/InputError'; import PrimaryButton from '@/Components/PrimaryButton'; import { useForm, Head } from '@inertiajs/react'; -export default function Index({ auth }) {+export default function Index({ auth, chirps }) { const { data, setData, post, processing, reset, errors } = useForm({ message: '', }); const submit = (e) => { e.preventDefault(); post(route('chirps.store'), { onSuccess: () => reset() }) }; return ( <AuthenticatedLayout auth={auth}> <Head title="Chirps" /> <div className="max-w-2xl mx-auto p-4 sm:p-6 lg:p-8"> <form onSubmit={submit}> <textarea value={data.message} placeholder="What's on your mind?" className="block w-full border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm" onChange={e => setData('message', e.target.value)} ></textarea> <InputError message={errors.message} className="mt-2" /> <PrimaryButton className="mt-4" disabled={processing}>Chirp</PrimaryButton> </form> + <div className="mt-6 bg-white shadow-sm rounded-lg divide-y">+ {chirps.map(chirp =>+ <Chirp key={chirp.id} chirp={chirp} />+ )}+ </div> </div> </AuthenticatedLayout> ); }
现在看看你的浏览器,看看你之前发出的消息!
额外奖励:相对日期
在我们的 Chirp
组件中,我们格式化了日期以使其易于阅读,但我们可以更进一步,使用流行的 Day.js 库显示相对日期。
首先,安装 dayjs
NPM 包
npm install dayjs
然后我们可以在我们的 Chirp
组件中使用这个库来显示相对日期
resources/js/Components/Chirp.vue
<script setup>+import dayjs from 'dayjs';+import relativeTime from 'dayjs/plugin/relativeTime'; +dayjs.extend(relativeTime); defineProps(['chirp']); </script> <template> <div class="p-6 flex space-x-2"> <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">{{ new Date(chirp.created_at).toLocaleString() }}</small>+ <small class="ml-2 text-sm text-gray-600">{{ dayjs(chirp.created_at).fromNow() }}</small> </div> </div> <p class="mt-4 text-lg text-gray-900">{{ chirp.message }}</p> </div> </div> </template>
resources/js/Components/Chirp.jsx
import React from 'react';+import dayjs from 'dayjs';+import relativeTime from 'dayjs/plugin/relativeTime'; +dayjs.extend(relativeTime); export default function Chirp({ chirp }) { return ( <div className="p-6 flex space-x-2"> <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6 text-gray-600 -scale-x-100" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2"> <path strokeLinecap="round" strokeLinejoin="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 className="flex-1"> <div className="flex justify-between items-center"> <div> <span className="text-gray-800">{chirp.user.name}</span>- <small className="ml-2 text-sm text-gray-600">{new Date(chirp.created_at).toLocaleString()}</small>+ <small className="ml-2 text-sm text-gray-600">{dayjs(chirp.created_at).fromNow()}</small> </div> </div> <p className="mt-4 text-lg text-gray-900">{chirp.message}</p> </div> </div> ); }
看看浏览器,看看你的相对日期。
随意发布更多 Chirp,甚至注册另一个帐户并开始对话!