# Implementing Dynamic Tagging in Laravel: Add Tags to Posts with Collections

By Dinesh Uprety
Published: 2024-11-09
Updated: 2024-11-09
Canonical: https://laranepal.com/blog/implementing-dynamic-tagging-in-laravel-add-tags-to-posts-with-collections

# Implementing Dynamic Tagging in Laravel: Add Tags to Posts with Collections

**Tagging** is a popular feature in blog engines, allowing users to categorize content for better navigation and
discovery. In this article, we’ll explore how to create a dynamic tagging system in Laravel, where tags can be created
on the fly or selected from an existing list. We’ll use Laravel collections to handle mixed data inputs, making the
process both efficient and clean.

Imagine you’re building a blogging platform where users can tag posts with predefined tags or create new ones as needed.
We’ll start by setting up the database structure, creating the Tag model, and then building a controller to add tags to
a new post using collections.

A nice UI pattern for something like this is to use an autocomplete multiselect box like
[Select2](https://select2.org/) or similar, and allow someone to add tags from an existing list or create new ones on
the fly.

**Prerequisites**

Before you start, make sure you have a Laravel project set up with a database connection configured. We’ll walk through
the steps from creating the migrations and models to using a dedicated controller to handle tagging.

### Step 1: Create the Tag Model

To interact with the tags table, we need a Tag model. Run the following command to generate it:

```bash
php artisan make:model Tag
```

Open the generated `Tag.php` file and define the **many-to-many** relationship with **Post**:

**Tag.php**

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Tag extends Model
{
    use HasFactory;

    protected $fillable = ['name'];

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

```

### Step 2: Create the Tag Table Migration

Let’s create a tags table to store tag names. Additionally, we’ll set up a pivot table called post_tag to establish a many-to-many relationship between `posts` and `tags`.

Run the following commands to generate migrations:

```bash
php artisan make:migration create_tags_table
php artisan make:migration create_post_tag_table
```

Then, open the migration files and update them as shown below.

**`create_tags_table.php`**

In this file, we’ll create the tags table, which has a unique name field to store each tag’s name.

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('tags', function (Blueprint $table) {
            $table->id();
            $table->string('name')->unique();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('tags');
    }
};

```

**`create_post_tag_table.php`**

In this pivot table migration, we’ll link posts and tags through their IDs.

```php
<?php

use App\Models\Tag;
use App\Models\Post;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;


return new class extends Migration
{
    public function up(): void
    {
        Schema::create('post_tag', function (Blueprint $table) {
            $table->foreignIdFor(Tag::class)->constrained()->cascadeOnDelete();
            $table->foreignIdFor(Post::class)->constrained()->cascadeOnDelete();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('post_tag');
    }
}

```

Run the migrations to create the tables:

```bash
php artisan migrate
```

### Step 3: Update the Post Model

Assuming you already have a `Post model`, add a similar **many-to-many relationship** with `Tag`.

**Post.php**

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class Post extends Model
{
use HasFactory;

    protected $fillable = ['title', 'content']; // Add other fields as needed

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

```

Now, we’re ready to create a controller to handle tag creation.

### Step 4: Create the PostTagsController with a store Method

We’ll set up a `store` method in `PostTagsController` to handle creating a new post and adding tags to it. This method will
use [Laravel collections](https://laravel.com/docs/11.x/eloquent#collections) to process each tag, checking if it’s an existing tag ID or a new tag name, and then attaching
it to the post.

Create the controller:

```bash
php artisan make:controller PostTagsController
```

Update `PostTagsController.php` as follows:

**`PostTagsController.php`**

```php
<?php

namespace App\Http\Controllers;

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

class PostTagsController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        // Create a new Post
        $post = Post::create($request->only(['title', 'content'])); // Add other fields as needed

        // Process tags
        $tagIds = collect($request->input('tags'))->map(function ($nameOrId) {
            // Check if it's an ID or a new tag name, create if new
            return is_numeric($nameOrId)
                ? $nameOrId
                : Tag::firstOrCreate(['name' => $nameOrId])->id;
        });

        // Attach tags to the new post
        $post->tags()->sync($tagIds);

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

```

Explanation

1. **Create Post**: `$post = Post::create($request->only(['title', 'content']));` creates a new post using only the
   specified
   fields.
2. **Process Tags**: The `collect($request->input('tags'))->map()` function iterates over each tag. If it’s a number, it
   assumes it’s an ID; if it’s a name, it creates the tag using `firstOrCreate`.
3. **Attach Tags**: Finally, sync attaches the list of tag IDs to the new post, ensuring only the provided tags are
   associated with the post.

Testing the Tagging System
To test, send a POST request to the store route with the following sample data:

```json
{
  "title": "New Blog Post",
  "content": "This is the content of the post.",
  "tags": [
    17,
    "recipes",
    32,
    "kitchen"
  ]
}
```

The store method will:

* Create a new post with the provided title and content.
* Process the tags, attaching existing tags by ID and creating any new ones as necessary.

### Conclusion

This setup enables a dynamic tagging system for posts in Laravel, using collections to handle mixed data for tags. By
leveraging Laravel’s `firstOrCreate` and `collection methods`, you can efficiently create and associate tags in a clean,
readable way. This approach not only simplifies server-side logic but also ensures an optimized tagging experience for
users.

With the models, migrations, and controller in place, you’re ready to implement tagging in your Laravel blog platform!
