short-edition/comment-bundle
A lightweight Laravel bundle for adding threaded comments to your models, with nested replies, moderation-ready structure, and simple Blade helpers. Minimal setup, sensible defaults, and clean integration for small projects that need commenting without the overhead.
The reality check you need on day one. short-edition/comment-bundle is a republished fork of friendsofsymfony/comment-bundle — a Symfony bundle, not a Laravel package. It ships no ServiceProvider, no publishable config/comment.php, no Blade views, and no Illuminate dependencies. It will not boot in a Laravel app, and composer require is expected to fail at the resolver level because the bundle's Symfony 2/3-era constraints collide with the Symfony 6/7 components Laravel 10/11 requires. As a Laravel developer, your leverage comes from reading it as a reference implementation of the threading domain model, not from installing it.
Where to look first (in this order, ~90 minutes total):
composer.json — the require block tells you immediately whether it can coexist with your PHP version, and confirms the Symfony/Doctrine dependency chain.Entity/Comment.php and Entity/Thread.php — the actual domain model. This is the package's real value: how a comment relates to a thread, how a parent/child tree is represented, what fields exist for moderation state and voting.Manager/CommentManagerInterface.php and its Doctrine implementation — the abstraction seam the bundle is built around. This is the boundary you would replicate in Laravel as a contract + Eloquent implementation.DependencyInjection/Configuration.php — every tunable knob (nesting depth, sorting, pagination, moderation). This becomes your config/comments.php requirements doc.Resources/views/ (the Twig templates) — how threading is rendered recursively, how pagination is surfaced, and where moderation controls attach.Your first concrete use case. Do not try to run the package. Instead, extract a Laravel-shaped spec: a Comment model with thread_id, parent_id, body, state, author_id; a Thread model with a polymorphic commentable morph; configurable max_depth; and moderation states as a PHP enum. Then implement threaded replies once with kalnoy/nestedset (or a closure table) and prove you can render a 3-level thread in Blade. That one-day spike gives you 80% of the bundle's value with 0% of its maintenance burden.
The composer spike (gated, 1–2 hours max). If a stakeholder mandates evaluation, attempt composer require short-edition/comment-bundle in a throwaway Laravel app. Record the resolver error verbatim and stop. Everything downstream is wasted effort if that gate fails — and it is expected to fail.
Pattern 1 — Blueprint port (the default, recommended). Use the bundle as a design document and reimplement in Laravel idiom. The four things worth copying verbatim:
kalnoy/nestedset on top of Eloquent or a hand-rolled closure table — do not hand-roll nested-set bounds math.App\Contracts\CommentManager in a service provider and implement with Eloquent. This preserves the package's clean seam and makes swapping storage later cheap.Configuration.php into config/comments.php: max_depth, default_sort, per_page, allow_voting, moderation.enabled, moderation.default_state.Pending, Approved, Rejected, Spam) rather than a boolean — the bundle's state handling is the part most worth inheriting conceptually. Filter at the query layer with a scope, never in the view.Pattern 2 — Behavioral parity tests. If you ever migrate off a real FOSCommentBundle installation, write feature tests against the semantics before you build UI: create root comment, reply to depth N, reject reply at N+1, soft-delete a parent with children, paginate a thread at depth 3. These tests become the receipt that the port is faithful.
Pattern 3 — Event-driven side effects. The bundle dispatches Symfony events on comment creation/moderation/deletion. In Laravel, emit CommentCreated, CommentModerated, CommentDeleted and hang notifications, spam scoring, and audit logging off listeners. Keep notification dispatch queued (ShouldQueue) — comment traffic is spiky and you do not want SMTP in the request path.
Pattern 4 — Polymorphic attachment. The bundle attaches threads to "any entity." In Eloquent that is a morphMany(Comment::class, 'commentable') on the parent model, plus a morphTo on Comment. Add an index on (commentable_type, commentable_id) and a deleted_at-aware scope so threads don't leak soft-deleted parents.
Pattern 5 — Rendering. Rebuild the Twig recursion as a Blade recursive component (<x-comment :comment="$comment" :depth="$depth" />) that hard-stops at max_depth. Paginate at the thread level, not per node; use cursor pagination for deep threads.
Pattern 6 — Strangler port (only if you're on a legacy Symfony estate). If existing production FOSCommentBundle data must be preserved, sequence it: schema migration with legacy_id columns → domain port with parity tests → event hooks reimplemented → Blade UI → moderation/rate-limiting/GDPR erasure added → frozen-writes cutover with post-import tree integrity verification (node counts, max depth, path validity).
Framework-fit routing. Do not build an adapter layer that runs Symfony components inside Laravel. Either read-and-reimplement or, if you genuinely need the bundle, run Symfony as the host — that is a stack decision, not an integration task.
composer require will likely fail immediately. Treat that as the decision gate, not an obstacle to route around.{{ }} are not semantically identical in every edge case (raw filters, HTML in comments). Sanitize comment bodies at write time with an HTML purifier; never rely on the template layer alone.morphMany + explicit cascade or a queued purge job tied to parent model deletion, and a user_id-based purge for account erasure.with('replies.replies') capped by max_depth) — or accept N+1 and cache aggressively; the latter is often cheaper than over-engineering.config/comments.php when you port — divergence between those layers is the classic source of "why is this comment visible" bugs.composer.json. Not as a dependency, not as a dev dependency. Vendor it as read-only reference material if your team needs it on disk, with a code-owner annotation and a review date.How can I help you explore Laravel packages today?