Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tagging Bundle Laravel Package

toro/tagging-bundle

Toro Tagging Bundle adds flexible tagging to Symfony applications: attach tags to any entity, query and filter by tag, and manage tags through a simple service and Doctrine integration. Includes taggable behavior, tag clouds, and helpers for building tag-based search and navigation.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is not a Laravel package. toro/tagging-bundle is a Symfony bundle — the Bundle suffix, the DependencyInjection/ + Resources/config/ layout, and Doctrine ORM entity mapping. There is no TaggingServiceProvider, no publishable config, no facade. composer require toro/tagging-bundle in a Laravel app will either fail dependency resolution or install classes that nothing autoloads into the container.

Second reality check: last release 2017-07-27, 0 stars, 0 dependents. It targets the Symfony 2.x/3.x + PHP 5.6/7.0 era. There is no issue triage, no security response path, and no one to ask when a Doctrine version bump breaks it.

If you still need to inspect it (e.g. you inherited a database written by it):

  1. Clone the repo locally; do not require it into the app.
  2. Read DependencyInjection/ and Resources/config/*.xml|yml to learn what services and parameters it registers.
  3. Read the Entity/ (or Model/) directory for the Tag entity and the taggable interface/trait.
  4. Read the mapping files for the join table name and column names — that schema is the only artifact worth preserving.

The first use case it was designed for: a single polymorphic many-to-many tagging table attached to several entity types, with a manager service for find-or-create and query. The Laravel equivalent is ~20 minutes of work:

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

Schema::create('taggables', function (Blueprint $t) {
    $t->foreignId('tag_id')->constrained()->cascadeOnDelete();
    $t->morphs('taggable');           // taggable_id + taggable_type + index
    $t->unique(['tag_id', 'taggable_id', 'taggable_type']);
});

That is the honest conclusion: start here, not with the bundle.

Implementation Patterns

The patterns below are the bundle's concepts translated into Laravel idioms, which is how you'd actually leverage the idea day to day.

Trait on the taggable side. Mirror whatever the bundle's taggable interface promised:

trait HasTags
{
    public function tags(): MorphToMany
    {
        return $this->morphToMany(Tag::class, 'taggable')->withTimestamps();
    }
}

Tag resolution with find-or-create by slug. The bundle's manager service did this; in Laravel put it in a TagResolver action and normalize on write, not on read:

public function resolve(string $name): Tag
{
    $slug = Str::slug($name);
    return Tag::firstOrCreate(['slug' => $slug], ['name' => trim($name)]);
}

Syncing from a request. Translate a comma-separated tags input into IDs and sync():

$ids = collect(explode(',', $request->input('tags', '')))
    ->map(fn ($n) => trim($n))->filter()
    ->map(fn ($n) => $this->resolver->resolve($n)->id);

$post->tags()->sync($ids);

Querying — AND semantics. The bundle exposed manager queries; the Laravel version is whereHas plus a grouped having. This is the part people get wrong most often:

Post::whereHas('tags', fn ($q) => $q->whereIn('slug', $slugs), '>=', count($slugs))
    ->get();

Use '=' for exact-AND, '>=' when tags are hierarchical or you allow supersets.

Denormalized tag cloud. Keep a taggables_count on tags, incremented in an observer or a queued job. Recomputing withCount('posts') on every page render is the usual performance cliff.

Interop with an existing bundle-written schema. If you're migrating off a Symfony app that used this bundle, keep the bundle's exact table and column names in your new migration. Don't rename the pivot mid-flight, and don't add foreign keys that the old writer doesn't expect — it inserts directly and will start throwing on constraint violations during the overlap period.

Where to extend, if you fork. The only sensible extension points are the manager service (wrap it, don't edit it) and the taggable interface. Everything else is mapping, which you're better off re-expressing as Laravel migrations.

Gotchas and Tips

  • Framework mismatch is the headline gotcha. Every tutorial, every debug:container tip, every services.yml snippet you'll find for this package assumes Symfony. In Laravel, php artisan about and tinker tell you nothing about it. Confirm early that you're not chasing Symfony-shaped answers in a Laravel app.
  • Composer will not save you. With a 2017 constraint set, composer require may resolve to a Symfony 2.x/3.x doctrine chain or fail outright. If you must inspect it, clone it out-of-tree and read the source.
  • Duplicate pivot rows on concurrent writes. sync() in Laravel is safe; raw inserts are not. The unique index on (tag_id, taggable_id, taggable_type) is not optional — without it, retries and parallel requests silently multiply rows and every count is wrong.
  • Normalization is the #1 source of duplicate tags. "Laravel", "laravel ", and "Laravel " produce three tags if you slug at read time. Slug and trim at write time, enforce unique on slug, and treat the display name as cosmetic.
  • Orphaned pivot rows on delete. Deleting a post does not delete its taggables rows unless you use cascadeOnDelete or a deleting observer. Tag clouds drift upward forever and nobody notices for months.
  • N+1 in tag rendering. Every $post->tags inside a loop is a query. Always with('tags') on index pages and withCount where you need totals.
  • Case sensitivity differs by collation. On MySQL utf8mb4_unicode_ci your slug unique index is case-insensitive; on Postgres it isn't. A tag set that's deduplicated in dev can split in production if you switch drivers.
  • Debugging the bundle itself (Symfony host only): bin/console debug:container toro_tagging and debug:doctrine to confirm what's actually registered and mapped. In a Laravel app there is nothing to debug — which is itself the answer.
  • Forking is a debt, not a fix. With 0 stars and 0 dependents, you are the only maintainer the moment you fork. If the schema is the only thing you need, copy the migration and abandon the code.
  • Recommendation: do not add this as a dependency. Implement tagging natively (three files, one migration), and if you're migrating an existing Symfony database, treat the bundle's schema as the contract and everything else as disposable.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib