跳至内容

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 从 user 关系中返回 idname 属性,以便我们可以显示 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 listing

额外奖励:相对日期

在我们的 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 listing with relative dates

随意发布更多 Chirp,甚至注册另一个帐户并开始对话!

继续允许编辑 Chirps...