PHPackages                             humweb/taggables - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. humweb/taggables

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

humweb/taggables
================

A powerful and flexible tagging package for Laravel with polymorphic relationships and user-scoped tags support

1.1(1y ago)11.2k↓76.4%MITPHPPHP ^8.3CI passing

Since May 24Pushed 1y agoCompare

[ Source](https://github.com/humweb/taggables)[ Packagist](https://packagist.org/packages/humweb/taggables)[ Docs](https://github.com/humweb/taggables)[ RSS](/packages/humweb-taggables/feed)WikiDiscussions main Synced 2d ago

READMEChangelog (2)Dependencies (12)Versions (3)Used By (0)

Taggables Package for Laravel
=============================

[](#taggables-package-for-laravel)

[![tests](https://github.com/humweb/taggables/actions/workflows/run-tests.yml/badge.svg)](https://github.com/humweb/taggables/actions/workflows/run-tests.yml)[![codecov](https://camo.githubusercontent.com/80b8126aca777cffb5f771187acdbf4b44b70cc88aef977f7c11071ab86ff5f8/68747470733a2f2f636f6465636f762e696f2f67682f68756d7765622f7461676761626c65732f67726170682f62616467652e737667)](https://codecov.io/gh/humweb/taggables)

A powerful and flexible tagging package for Laravel applications with polymorphic relationships support and user-scoped tags.

Features
--------

[](#features)

- 🏷️ **Polymorphic tagging** - Tag any Eloquent model
- 👤 **User-scoped tags** - Personal tags for each user alongside global tags
- 📁 **Tag types** - Organize tags into categories
- 🔍 **Advanced queries** - Filter models by tags with ease
- 🚀 **Performance optimized** - Eager loading and query optimization
- 📊 **Tag statistics** - Popular tags, tag clouds, and more
- 🎯 **Type hinting** - Full IDE support with proper return types
- ✨ **Laravel conventions** - Follows Laravel best practices

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require humweb/taggables
```

Publish the migrations:

```
php artisan vendor:publish --tag="taggable-migrations"
```

Run the migrations:

```
php artisan migrate
```

Optionally, publish the config file:

```
php artisan vendor:publish --tag="taggable-config"
```

Usage
-----

[](#usage)

### Making a Model Taggable

[](#making-a-model-taggable)

Add the `HasTags` trait to any model you want to make taggable:

```
use Humweb\Taggables\Traits\HasTags;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasTags;
}
```

### Basic Tagging Operations

[](#basic-tagging-operations)

```
$post = Post::find(1);

// Add global tags (available to all users)
$post->tag('laravel');
$post->tag(['php', 'javascript']);

// Add user-specific tags
$post->tagAsUser(['favorite', 'important'], $user);

// Remove tags
$post->untag('laravel');
$post->untag(['php', 'javascript']);
$post->untag(); // Remove all tags

// Remove user-specific tags
$post->untagAsUser(['favorite'], $user);

// Replace all tags
$post->retag(['vue', 'tailwind']);
$post->retagAsUser(['personal', 'work'], $user);

// Sync tags (like Laravel's sync method)
$post->syncTags(['laravel', 'php', 'mysql']);
$post->syncTagsAsUser(['todo', 'urgent'], $user);
```

### User-Scoped Tags

[](#user-scoped-tags)

The package supports both global tags (available to all users) and user-specific tags:

```
// Create a global tag
$post->tag('laravel'); // Available to all users

// Create a user-specific tag
$post->tagAsUser('personal-project', $user);

// Mix global and user tags
$post->tag(['php', 'laravel']); // Global
$post->tagAsUser(['favorite', 'todo'], $user); // User-specific

// Get only user tags
$userTags = $post->userTags($user->id);

// Get only global tags
$globalTags = $post->globalTags();

// Get all tags (user + global)
$allTags = $post->tags; // Returns both by default
```

### Checking Tags

[](#checking-tags)

```
// Check if the model has a specific tag (checks user + global by default)
if ($post->hasTag('laravel', $user->id)) {
    // ...
}

// Check for global tag only
if ($post->hasGlobalTag('laravel')) {
    // ...
}

// Check for user tag only
if ($post->hasUserTag('favorite', $user)) {
    // ...
}

// Check if the model has any of the given tags
if ($post->hasAnyTag(['laravel', 'php'], $user->id)) {
    // ...
}

// Check if the model has all of the given tags
if ($post->hasAllTags(['laravel', 'php'], $user->id)) {
    // ...
}
```

### Querying by Tags

[](#querying-by-tags)

```
// Get all posts with any of the given tags (includes user + global tags)
$posts = Post::withAnyTags(['laravel', 'php'], null, $userId)->get();

// Get posts with user-specific tags only
$posts = Post::withUserTags(['favorite', 'todo'], $user)->get();

// Get posts with global tags only
$posts = Post::withGlobalTags(['laravel', 'php'])->get();

// Get all posts with all of the given tags
$posts = Post::withAllTags(['laravel', 'php'], null, $userId)->get();

// Get all posts without the given tags
$posts = Post::withoutTags(['draft', 'archived'], null, $userId)->get();

// Simple single tag query
$posts = Post::taggedWith('laravel', $userId)->get();
```

### Using Tag Types

[](#using-tag-types)

Tag types allow you to categorize your tags:

```
// Tag with type
$post->tag(['important', 'urgent'], 'priority');
$post->tagAsUser(['personal', 'work'], $user, 'category');

// Get tags of a specific type
$priorityTags = $post->tagsWithType('priority');
$userCategories = $post->tagsWithType('category', $user->id);

// Query by tags with type
$posts = Post::withAnyTags(['important', 'urgent'], 'priority', $userId)->get();
```

### Working with the Tag Model

[](#working-with-the-tag-model)

```
use Humweb\Taggables\Models\Tag;

// Find or create a tag
$tag = Tag::findOrCreate('laravel'); // Global tag
$tag = Tag::findOrCreateForUser('personal', $user); // User tag
$tag = Tag::findOrCreateGlobal('php'); // Explicitly global

// Find or create with type
$tag = Tag::findOrCreate('important', 'priority', $user->id);

// Find or create multiple tags
$tags = Tag::findOrCreateMany(['laravel', 'php', 'mysql'], null, $user->id);

// Search tags
$tags = Tag::containing('lara')->get();

// Get tags by user
$userTags = Tag::forUser($user->id)->get();
$globalTags = Tag::global()->get();
$mixedTags = Tag::forUserWithGlobal($user->id)->get();

// Get tags by type
$priorityTags = Tag::withType('priority')->get();

// Get popular tags
$popularTags = Tag::popularTags(10, $user->id); // Mixed popular tags
$userPopular = Tag::popularUserTags(10, $user->id); // User's popular tags
$globalPopular = Tag::popularGlobalTags(10); // Global popular tags

// Get tag cloud (tags with usage weight)
$tagCloud = Tag::tagCloud($user->id); // Mixed cloud
$globalCloud = Tag::tagCloud(); // Global only

// Get unused tags
$unusedTags = Tag::unusedTags($user->id)->get();

// Check tag ownership
$tag = Tag::find(1);
if ($tag->isGlobal()) {
    // This is a global tag
} elseif ($tag->isOwnedBy($user)) {
    // This is the user's tag
}
```

### Tag Suggestions

[](#tag-suggestions)

```
// Get tag suggestions based on partial input
$suggestions = Tag::suggestTags('lara', $user->id); // Returns user + global tags
$suggestions = Tag::suggestTags('lara'); // Returns only global tags

// Get related tags (tags often used together)
$tag = Tag::findOrCreate('laravel');
$relatedTags = $tag->relatedTags($user->id);
```

### Events

[](#events)

The package fires events during tagging operations:

- `TagAttached` - Fired when a tag is attached to a model
- `TagDetached` - Fired when a tag is detached from a model
- `TagsSynced` - Fired when tags are synced

```
use Humweb\Taggables\Events\TagAttached;

// In your EventServiceProvider
protected $listen = [
    TagAttached::class => [
        SendTagNotification::class,
    ],
];
```

### Artisan Commands

[](#artisan-commands)

Clean up unused tags:

```
php artisan tags:cleanup

# Clean up only user tags
php artisan tags:cleanup --user=123

# Clean up only global tags
php artisan tags:cleanup --global
```

Advanced Usage
--------------

[](#advanced-usage)

### Custom Tag Model

[](#custom-tag-model)

You can extend the Tag model for additional functionality:

```
namespace App\Models;

use Humweb\Taggables\Models\Tag as BaseTag;

class Tag extends BaseTag
{
    // Add your custom methods
    public function isPublic(): bool
    {
        return $this->isGlobal() || $this->metadata['public'] ?? false;
    }
}
```

Update the config to use your custom model:

```
// config/taggable.php
return [
    'tag_model' => \App\Models\Tag::class,
];
```

### Eager Loading

[](#eager-loading)

```
// Eager load all tags
$posts = Post::with('tags')->get();

// Eager load only user tags
$posts = Post::with(['tags' => function ($query) use ($userId) {
    $query->where('user_id', $userId);
}])->get();

// Eager load only global tags
$posts = Post::with(['tags' => function ($query) {
    $query->whereNull('user_id');
}])->get();

// Eager load tags with specific type
$posts = Post::with(['tags' => function ($query) {
    $query->where('type', 'technology');
}])->get();
```

### Caching

[](#caching)

The package supports caching for better performance:

```
// config/taggable.php
return [
    'cache' => [
        'enabled' => true,
        'key_prefix' => 'taggable',
        'ttl' => 3600, // 1 hour
    ],
];
```

Configuration
-------------

[](#configuration)

The full configuration file:

```
return [
    // The tag model to use
    'tag_model' => \Humweb\Taggables\Models\Tag::class,

    // Table names
    'tables' => [
        'tags' => 'tags',
        'taggables' => 'taggables',
    ],

    // Slug generation
    'slugger' => null, // null defaults to Str::slug

    // Tag name validation rules
    'rules' => [
        'name' => ['required', 'string', 'max:255'],
    ],

    // Auto-delete unused tags
    'delete_unused_tags' => false,

    // User scoping configuration
    'user_scope' => [
        // Enable user-scoped tags
        'enabled' => true,

        // Allow creation of global tags (null user_id)
        'allow_global_tags' => true,

        // Include global tags when querying user tags
        'mix_user_and_global' => true,
    ],

    // Cache configuration
    'cache' => [
        'enabled' => true,
        'key_prefix' => 'taggable',
        'ttl' => 3600,
    ],
];
```

Migration Notes
---------------

[](#migration-notes)

The tags table includes a `user_id` column for user-scoped tags:

```
Schema::create('tags', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('slug');
    $table->foreignId('user_id')->nullable()->constrained()->cascadeOnDelete();
    $table->string('type')->nullable()->index();
    $table->integer('order_column')->default(0);
    $table->json('metadata')->nullable();
    $table->timestamps();

    // Same slug can exist for different users/types
    $table->unique(['slug', 'user_id', 'type']);
    $table->index(['user_id', 'type', 'order_column']);
});
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Ryan Shofner](https://github.com/ryun)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance46

Moderate activity, may be stable

Popularity20

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

406d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8b536bae26e43306745d195a34b8dff510bc05419cdc3503f96c5cb3fd4e7239?d=identicon)[ryun](/maintainers/ryun)

---

Top Contributors

[![ryun](https://avatars.githubusercontent.com/u/227672?v=4)](https://github.com/ryun "ryun (12 commits)")

---

Tags

laraveltagstaggingpolymorphichumwebtaggablesuser-scoped-tags

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/humweb-taggables/health.svg)

```
[![Health](https://phpackages.com/badges/humweb-taggables/health.svg)](https://phpackages.com/packages/humweb-taggables)
```

###  Alternatives

[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M47](/packages/spatie-laravel-pdf)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

329530.5k29](/packages/codewithdennis-filament-select-tree)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.6k](/packages/rawilk-profile-filament-plugin)[worksome/exchange

Check Exchange Rates for any currency in Laravel.

124603.0k](/packages/worksome-exchange)[tarfin-labs/event-machine

Event-driven state machines for Laravel with event sourcing, type-safe context, and full audit trail.

199.4k](/packages/tarfin-labs-event-machine)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
