Tue. Apr 7th, 2026

Here’s a clean way to implement tags for posts so that clicking on a tag shows all posts with that tag:

Create a Tag model and migration

php artisan make:model Tag -m
Schema::create('tags', function (Blueprint $table) {
    $table->id();
    $table->string('name')->unique();
    $table->timestamps();
});

Now, create a pivot table for posts and tags

php artisan make:migration create_post_tag_table --create=post_tag
Schema::create('post_tag', function (Blueprint $table) {
    $table->id();
    $table->foreignId('post_id')->constrained()->onDelete('cascade');
    $table->foreignId('tag_id')->constrained()->onDelete('cascade');
});

php artisan migrate

2. Define relationships in models

In Post.php

public function tags()
{
    return $this->belongsToMany(Tag::class);
}

In Tag.php:

public function posts()
{
    return $this->belongsToMany(Post::class);
}

3. Update your Post form (create/edit)

Add a multi-select field for tags (assuming you already seed tags or create them elsewhere):

@extends('adminlayouts.app')

@section('content')
<div class="container mt-5">
    <h2>Create New Post</h2>

    @if($tags->isEmpty())
        <div class="alert alert-warning">
            No tags available. Please create some tags first.
        </div>
    @endif

    <form action="{{ route('posts.store') }}" method="POST">
        @csrf
        <div class="mb-3">
            <label for="title" class="form-label">Title</label>
            <input type="text" name="title" id="title" class="form-control" required>
        </div>

        <div class="mb-3">
            <label for="content" class="form-label">Content</label>
            <textarea name="content" id="content" rows="5" class="form-control" required></textarea>
        </div>

        <div class="mb-3">
            <label for="tags" class="form-label">Tags</label>
            <select name="tags[]" id="tags" class="form-control" multiple 
                {{ $tags->isEmpty() ? 'disabled' : '' }}>
                
                @foreach($tags as $tag)
                    <option value="{{ $tag->id }}">{{ $tag->name }}</option>
                @endforeach
            </select>
            
            @if($tags->isEmpty())
                <small class="text-danger">No tags available to select</small>
            @endif
        </div>

        <button type="submit" class="btn btn-primary" {{ $tags->isEmpty() ? 'disabled' : '' }}>
            Create Post
        </button>
    </form>
</div>
@endsection

Store tags with posts

In your PostController@store:

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $posts = Post::all();
        return view('posts.index', compact('posts'));
    }

    public function dashboard()
    {
        return view('dashboard');
    }

    /**
     * Show the form for creating a new resource.
     */
    public function create()
    {
        // Get all tags to display in the form
        $tags = \App\Models\Tag::all();
        return view('posts.create', compact('tags'));
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        // Validate the incoming request data
        $validatedData = $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'required|string',
            // Tags validation:
            // - Must be an array (when using multiple select)
            // - Each tag ID must exist in the tags table
            'tags' => 'array',
            'tags.*' => 'exists:tags,id',
        ]);

        // Create the post with basic fields
        $post = Post::create([
            'title' => $validatedData['title'],
            'content' => $validatedData['content']
        ]);

        // Attach tags to the post if any were selected
        // This handles the many-to-many relationship between posts and tags
        if ($request->has('tags')) {
            $post->tags()->attach($validatedData['tags']);
        }

        return redirect()->route('posts.index')->with('success', 'Post created successfully.');
    }

    /**
     * Display the specified resource.
     */
    public function show(string $id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     */
    public function edit(string $id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, string $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy(string $id)
    {
        //
    }
}

. Show tags in post views

In posts/index.blade.php or posts/show.blade.php:

@extends('userlayouts.app')

@section('content')
<br>
<br>
<div class="container">
    @foreach ($posts as $post)
        <h2>{{ $post->title }}</h2>
        <p>{{ $post->content }}</p>
        
        {{-- 
            TAG DISPLAY SECTION
            ------------------
            This section displays all tags associated with the current post
            - We loop through $post->tags (using the many-to-many relationship)
            - Each tag is displayed as a clickable badge/link
            - The link routes to a tag details page (tags.show)
            - The badge uses Bootstrap's bg-primary class for styling
        --}}
        <div class="mt-2 mb-3">
            @foreach($post->tags as $tag)
                <a href="{{ route('tags.show', $tag->id) }}" class="badge bg-primary text-decoration-none me-1">
                    {{ $tag->name }}
                </a>
            @endforeach
        </div>
        <hr>
    @endforeach
</div>
@endsection

 

Tag controller to show all posts with a tag

php artisan make:controller TagController
public function show(Tag $tag)
{
    $posts = $tag->posts()->latest()->get();
    return view('tags.show', compact('tag', 'posts'));
}

Add route

Route::get('/tags/{tag}', [TagController::class, 'show'])->name('tags.show');
View for tags (resources/views/tags/show.blade.php)
<x-app-layout>
    <div class="container mt-5">
        <h2>Posts tagged with: {{ $tag->name }}</h2>
        <ul class="list-group mt-3">
            @forelse($posts as $post)
                <li class="list-group-item">
                    <a href="{{ route('posts.show', $post) }}">{{ $post->title }}</a>
                </li>
            @empty
                <li class="list-group-item">No posts found for this tag.</li>
            @endforelse
        </ul>
    </div>
</x-app-layout>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *