Back to Course |
Vue.js 3 + Laravel 11 + Vite: SPA CRUD

Category: API Resource with Relationship

In this lesson, let's show a column from the relationship in the posts table. We will add a category for every post.

finished category relation


First, we need to create a Category model and migration.

php artisan make:model Category -m

database/migrations/xxxx_create_categories_table.php:

public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}

app/Models/Category.php:

class Category extends Model
{
protected $fillable = [
'name',
];
}

Next, we need to create a migration to add a category_id column to the Posts table.

php artisan make:migration "add category to posts table"

database/migrations/xxxx_add_category_to_posts_table.php:

public function up(): void
{
Schema::table('posts', function (Blueprint $table) {
$table->foreignId('category_id')->after('content')->constrained();
});
}

In the model, we also need a relationship.

app/Models/Post.php:

use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Post extends Model
{
protected $fillable = [
'title',
'content',
'category_id',
];
 
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
}

Now we don't want to make a N+1 issue. So in the PostController we need to eager load categories.

app/Http/Controllers/Api/PostController.php:

class PostController extends Controller
{
public function index()
{
$posts = Post::with('category')->paginate(10);
 
return PostResource::collection($posts);
}
}

We are using API Resources. So it means we need to category to it, otherwise, API won't return the category.

app/Http/Resources/PostResource.php:

class PostResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => substr($this->content, 0, 50) . '...',
'category' => $this->category->name,
'created_at' => $this->created_at->toDateString()
];
}
}

All that is left to show the category in the frontend. Let's show it near the title.

resources/js/components/Posts/Index.vue:

<template>
<div class="overflow-hidden overflow-x-auto p-6 bg-white border-gray-200">
<div class="min-w-full align-middle">
<table class="min-w-full divide-y divide-gray-200 border">
<thead>
<tr>
// ...
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Title</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Category</span>
</th>
// ...
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200 divide-solid">
<tr v-for="post in posts.data">
// ...
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ post.title }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ post.category }}
</td>
// ...
</tr>
</tbody>
</table>
 
<TailwindPagination :data="posts" @pagination-change-page="getPosts" class="mt-4" />
</div>
</div>
</template>
 
// ...

That's it. We now show a category in the posts table.

finished category relation