Weave Code
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

Doctrine Extensions Taggable Laravel Package

fpn/doctrine-extensions-taggable

Doctrine 2 Taggable extension that lets you add and manage tags on your entities. Implement the Taggable interface on your models, register the package XML metadata, and hook up the TagListener/TagManager to persist tags automatically.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps for Laravel Integration

  1. Install the Package

    composer require fpn/doctrine-extensions-taggable
    

    Note: Requires Doctrine ORM. Install via:

    composer require doctrine/orm
    
  2. Set Up Doctrine in Laravel

    • Use laravel-doctrine/orm (experimental) or manually configure Doctrine in config/doctrine.php:
      $config = new \Doctrine\ORM\Configuration();
      $driverImpl = new \Doctrine\ORM\Mapping\Driver\DriverChain();
      $driverImpl->addDriver(new \Doctrine\ORM\Mapping\Driver\XmlDriver(__DIR__.'/vendor/fpn/doctrine-extensions-taggable/metadata'), 'DoctrineExtensions\Taggable\Entity');
      $config->setMetadataDriverImpl($driverImpl);
      
  3. Register the TagListener In a Laravel service provider (e.g., AppServiceProvider):

    public function boot()
    {
        $em = DoctrineHelper::getEntityManager();
        $tagManager = new \DoctrineExtensions\Taggable\TagManager($em);
        $em->getEventManager()->addEventSubscriber(new \DoctrineExtensions\Taggable\TagListener($tagManager));
    }
    
  4. Create a Taggable Eloquent Model Extend a model to implement Taggable via a trait (since Laravel doesn’t support interfaces directly):

    use DoctrineExtensions\Taggable\Taggable;
    use Doctrine\Common\Collections\ArrayCollection;
    
    class Article implements Taggable
    {
        // Eloquent fields...
        protected $tags;
    
        public function getTaggableType(): string
        {
            return 'article';
        }
    
        public function getTaggableId()
        {
            return $this->id;
        }
    
        public function getTags()
        {
            $this->tags = $this->tags ?: new ArrayCollection();
            return $this->tags;
        }
    }
    
  5. First Use Case: Tag an Article

    $article = Article::find(1);
    $tagManager = app(\DoctrineExtensions\Taggable\TagManager::class);
    $tag = $tagManager->loadOrCreateTag('laravel');
    $tagManager->addTag($tag, $article);
    $tagManager->saveTagging($article);
    

Implementation Patterns

Workflows

1. Tagging an Eloquent Model

  • Pattern: Use a service layer to bridge Eloquent and Doctrine:
    class TagService
    {
        protected $tagManager;
    
        public function __construct(\DoctrineExtensions\Taggable\TagManager $tagManager)
        {
            $this->tagManager = $tagManager;
        }
    
        public function tagResource($resource, array $tagNames)
        {
            $tags = $this->tagManager->loadOrCreateTags($tagNames);
            $this->tagManager->replaceTags($tags, $resource);
            $this->tagManager->saveTagging($resource);
        }
    }
    
  • Usage:
    $service = new TagService($tagManager);
    $service->tagResource($article, ['laravel', 'php']);
    

2. Querying Tagged Resources

  • Pattern: Use the TagRepository for efficient queries:
    $tagRepo = $em->getRepository('DoctrineExtensions\Taggable\Entity\Tag');
    $articleIds = $tagRepo->getResourceIdsForTag('article', 'laravel');
    $articles = Article::whereIn('id', $articleIds)->get();
    
  • Alternative: Custom DQL for complex queries:
    $query = $em->createQuery(
        'SELECT t FROM DoctrineExtensions\Taggable\Entity\Tag t
         JOIN t.resources r WHERE r.type = :type AND t.name = :name'
    )->setParameters(['type' => 'article', 'name' => 'laravel']);
    

3. Bulk Tagging

  • Pattern: Process collections of resources:
    $articles = Article::where('published', true)->get();
    $tagManager->loadOrCreateTags(['news', 'update']);
    foreach ($articles as $article) {
        $tagManager->addTag($newsTag, $article);
    }
    $tagManager->saveTagging($articles); // Bulk save
    

4. Synchronizing with Eloquent

  • Pattern: Use model observers to sync tags:
    class ArticleObserver
    {
        public function saved(Article $article)
        {
            if ($article->isDirty('tags')) {
                $tagManager = app(\DoctrineExtensions\Taggable\TagManager::class);
                $tagManager->replaceTags(
                    $tagManager->loadOrCreateTags($article->tags),
                    $article
                );
                $tagManager->saveTagging($article);
            }
        }
    }
    

Integration Tips

  1. Avoid ORM Conflicts:

    • Isolate Doctrine entities to a separate namespace (e.g., App\Doctrine\Entity).
    • Use a single database connection for both Eloquent and Doctrine.
  2. Leverage Laravel’s Service Container:

    • Bind TagManager and TagListener as singletons:
      $this->app->singleton(\DoctrineExtensions\Taggable\TagManager::class, function ($app) {
          return new \DoctrineExtensions\Taggable\TagManager($app['doctrine.orm.entity_manager']);
      });
      
  3. Tag Validation:

    • Add validation in TagManager or a custom service:
      public function loadOrCreateTag(string $name): Tag
      {
          if (!preg_match('/^[a-z0-9_-]+$/i', $name)) {
              throw new \InvalidArgumentException('Invalid tag format.');
          }
          return parent::loadOrCreateTag($name);
      }
      
  4. Caching Tag Queries:

    • Cache results of getTagsWithCountArray:
      $cacheKey = 'tags:article:count';
      $tags = Cache::remember($cacheKey, now()->addHours(1), function () use ($tagRepo) {
          return $tagRepo->getTagsWithCountArray('article');
      });
      
  5. Testing:

    • Use Laravel’s testing helpers to mock TagManager:
      $this->mock(TagManager::class)->shouldReceive('addTag')->once();
      

Gotchas and Tips

Pitfalls

  1. Doctrine vs. Eloquent Schema Conflicts:

    • Issue: Doctrine’s Tag table may conflict with Eloquent migrations.
    • Fix: Exclude Doctrine tables from Eloquent migrations or use a separate schema.
  2. Event Listener Overhead:

    • Issue: TagListener adds event subscribers globally, which may impact performance.
    • Fix: Disable listeners for non-taggable entities or use conditional logic.
  3. Taggable Interface Incompatibility:

    • Issue: Laravel models can’t implement interfaces directly (PHP 8.0+ supports this, but older versions require traits).
    • Fix: Use a trait to wrap the interface:
      trait TaggableTrait
      {
          public function getTaggableType(): string { return 'article'; }
          public function getTaggableId() { return $this->id; }
          public function getTags() { return $this->tags ?: new ArrayCollection(); }
      }
      
  4. Circular Dependencies:

    • Issue: Tagging logic may create circular references (e.g., TagArticle).
    • Fix: Use lazy loading or fetch="EXTRA_LAZY" in Doctrine mappings.
  5. Transaction Management:

    • Issue: saveTagging() may fail silently if not wrapped in a transaction.
    • Fix: Use Laravel’s transactions:
      DB::transaction(function () use ($tagManager, $article) {
          $tagManager->saveTagging($article);
      });
      

Debugging

  1. Tag Not Persisting:

    • Check: Ensure saveTagging() is called after the entity is persisted ($article->save()).
    • Debug: Enable Doctrine logging:
      $config->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
      
  2. Duplicate Tags:

    • Cause: Case-sensitive tag names or race conditions.
    • Fix: Normalize tags in loadOrCreateTag:
      $name = strtolower(trim($name));
      
  3. Performance Issues:

    • Tool: Use Doctrine’s query profiler:
      $profiler = $em->getConnection()->getConfiguration()->getSQLLogger();
      $profiler->start Profiling();
      
    • Optimize: Add indexes to `taggable
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.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope