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

Comment Bundle Laravel Package

ezsystems/comment-bundle

A Symfony bundle that adds a full comment system to your application, with threaded comment trees, configurable sorting, a REST API, event extensibility, optional ACL protection, Akismet spam filtering, and Doctrine ORM or MongoDB persistence.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install: composer require ezsystems/comment-bundle. Expect the package to be flagged abandoned on Packagist and to carry Symfony 3.4|4.4 constraints — check composer why-not and your PHP platform before assuming it resolves. On Symfony 5+/PHP 8 you will likely need a fork or replace in composer.json.
  • Register it in config/bundles.php as FOS\CommentBundle\FOSCommentBundle::class => ['all' => true]. The package name is misleading: the namespace is still FOS\CommentBundle and the config root is fos_comment, not ezsystems_comment. Verify with bin/console debug:container | grep fos_comment before debugging anything else.
  • Read the right docs: the README is a pointer. The real install walkthrough is Resources/doc/index.md (plus integration, acl, spam_detection, markup_parser, rest pages) in the vendor directory. It's the only reliable reference; there is no maintained docs site.
  • Minimal config in config/packages/fos_comment.yml: set db_driver: orm (or mongodb), and map class.model.comment / class.model.thread to your own entity FQCNs. Without the class block you'll get the "child config must be configured" error immediately.
  • Create your entities: extend FOS\CommentBundle\Model\Comment and Thread as mapped superclasses (the doc shows a MappedSuperclass base + @ORM\Entity subclass), then bin/console make:migration / doctrine:schema:validate. You must implement the abstract bits and add a parent/children self-association for the tree.
  • First use case: a threaded discussion under an article. Embed the async thread template with a stable identifier:
    {% include '@FOSComment/Thread/async.html.twig' with {'id': 'article-' ~ article.id} %}
    
    The thread is created lazily on first request from the identifier. Check Resources/views/ in vendor for the exact template path in your version.
  • Look first at: Model/Thread.php and Model/Comment.php (what's denormalized), Entity/CommentManager.php (the write path), Controller/ (the route surface), and Resources/doc/. If debug:router | grep fos_comment shows nothing, the bundle wasn't registered or routing wasn't imported.

Implementation Patterns

Thread-per-content-entity with namespaced identifiers. The Thread.identifier is your join key to the host entity. Always prefix by content type (article-42, product-7), never bare IDs — one thread table shared across content types will collide. Keep a helper (ThreadLocator::forArticle($article)) so every entry point resolves the same string.

Async rendering for cacheable pages. The async template loads the tree over JSON and keeps your page fragments cacheable; the server-rendered variant couples comment state to page caching and is usually a mistake. Pass id, and if you customize routes also pass the thread URL.

Write through the manager, never the EntityManager. The manager owns tree maintenance (path/level/depth), parent/child wiring, and the thread's numComments / lastCommentAt counters. From your own controller or messaging consumer:

$comment = $commentManager->createComment($thread);
$comment->setBody($body);
$comment->setAuthor($user); // or your own author relation
$commentManager->saveComment($comment);

If you need custom moderation, set state (STATE_VALID / STATE_PENDING / STATE_SPAM / STATE_DELETED) rather than deleting rows — the tree and counters assume comments still exist.

Events are the business-logic seam. Register listeners for the lifecycle events (FOSCommentEvents::COMMENT_CREATE, COMMENT_PRE_PERSIST, COMMENT_POST_PERSIST, THREAD_CREATE, etc.) for: notification emails, moderation queueing, search indexing, auto-closing comment threads past N days, and rejecting comments from banned users. Prefer PRE_PERSIST for validation, POST_PERSIST for side effects that need an ID.

Authorization: pick one layer. Either use the bundle's Symfony ACL integration (fos_comment.acl.* — configure permissions per thread) or, more often, a small Voter that checks thread->getIdentifier() against the underlying entity (canComment(Article $article)). Don't enable both; double-denial is a painful hour to debug.

Persistence customization. For a hot comments query, override the repository via your entity's repositoryClass (eager-join authors to avoid N+1 on the tree endpoint) or override class.model.comment. Add DB indexes on thread_id, parent_id, createdAt.

API-first consuming. The FOSRestBundle endpoints give you a JSON tree. Configure serialization exclusion strategies/groups so author emails and internal fields don't leak. If you're building a SPA, skip the bundled JS client entirely and call the REST routes with your own frontend; if you don't need JSON beyond a thin wrapper, you can drop FOSRestBundle from the dependency graph and write two small controllers around the managers.

Gotchas and Tips

  • Archived = frozen. Pin exact versions, audit transitive deps, and keep a plan to vendor the bundle in. The Symfony 3.4/4.4 support in the README means runtime breakage on 5/6 is likely (event classes, getSubscribedServices, controller signatures, FOSRest API changes).
  • Name confusion costs the most time. ezsystems/comment-bundle installs FOS\CommentBundle with root config fos_comment and Twig namespace @FOSComment. Searching config for "ezsystems" gives a false negative.
  • Entities are yours to write. The Model/ classes are plain PHP. Miss a mapping field (state, depth, numComments) and failures surface as cryptic null counters or broken tree ordering, not schema errors.
  • Tree state is precomputed. Changing parent or children directly through Doctrine (bulk DQL, fixtures, imports) leaves path/depth stale. Always go through fos_comment.manager.comment.saveComment(). If you deleted rows in bulk, write a console command that recomputes thread counters.
  • Thread counters only update via the manager. Badges showing "12 comments" drift silently after a raw delete. Backfill and test this explicitly.
  • ACL off by default, and all-or-nothing when on. Enabling fos_comment.acl demands ACEs for every thread; threads without ACEs deny everyone. Add it only after threads exist and seed permissions in a command.
  • FOSUserBundle integration is optional and dated. With modern UserInterface, write an event listener that maps your user to the comment's author; don't call fos_user.user_manager.
  • Akismet does network I/O on persist. Disable it in test/dev or mock fos_comment.spam_detector; otherwise test suites become slow and flaky.
  • XSS is opt-in. Comment.body is stored raw. Configure a markup parser (html_purifier or sundown) or sanitize at render time — never |raw. A purifier service is also where you enforce a whitelist of allowed HTML.
  • Routing customization is a silent breaker. The bundled JS calls specific route names (fos_comment_comment_tree, fos_comment_comment_new_head, etc.). Change the prefix/host and the async thread renders empty with a 404 in devtools — nothing in PHP logs.
  • Serializer recursion. Comment.parent ↔ children self-reference loops through JMS/json_encode when you serialize a thread. Set @MaxDepth / groups, or serialize a flattened tree assembled in a listener.
  • Debugging checklist: debug:router | grep fos_comment (routes imported?), debug:container fos_comment.manager.comment (services wired?), step through Entity/CommentManager::saveComment (which events fired, in what order?), then check var/log for event-listener exceptions swallowed by the controller.
  • Safety net: because upstream is dead, keep one HTTP-level integration test (post a head comment and a reply via the bundle route) plus one manager-level test. That pair catches the breakage you'll actually hit when a Symfony or Doctrine upgrade lands.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings