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

Replies Laravel Package

lara-zeus/replies

Lara Zeus Replies adds threaded, nested replies to any Laravel model, letting users discuss records in a familiar comment-style interface. Ship conversations fast with ready-made Livewire components, flexible configuration, and seamless Filament-friendly integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Version gate first — this package's major version is hostage to Filament's. Run this before you type anything:

composer why-not filament/filament
composer show filament/filament livewire/livewire php
  • Filament 5 → composer require lara-zeus/replies:^3.0. This is the only supported path.
  • Filament 3/4 → pin 2.0.3 exactly. Do not chase 3.0.0; there is no upgrade guide and no stated schema delta.

Install and publish:

composer require lara-zeus/replies:^3.0
php artisan vendor:publish --tag="replies-config"
php artisan vendor:publish --tag="replies-migrations"
php artisan vendor:publish --tag="replies-views"   # only if restyling
php artisan migrate

Open the published migration first, before migrating. It is the only authoritative source for the table name, whether the polymorphic (replyable_type, replyable_id) pair is indexed, and whether the migration is additive and reversible. Rehearse migrate + migrate:rollback against a copy of real data — not a fresh local DB — because that is where any schema surprise actually costs you.

Register the plugin in your panel provider:

use LaraZeus\Replies\RepliesPlugin;

public function panel(Panel $panel): Panel
{
    return $panel->plugin(RepliesPlugin::make());
}

Attach the trait to your model (confirm exact namespace against src/Traits/ — the tagline/docs mismatch means you should not trust the README blindly):

class Ticket extends Model
{
    use \LaraZeus\Replies\Traits\HasReplies;
}

First use case to spike (≈1 hour, scratch panel): one model, one surface, two nesting levels. Open a Ticket's View page, render the reply component, post a parent reply, reply to it, reload, verify persistence and that a user who cannot view the Ticket also cannot see the thread. That spike will teach you more than the docs do, because the docs are thin on authorization and loading strategy.

Implementation Patterns

The polymorphism is the entire buy case. One reply implementation serving tickets, posts, records, and annotations beats N bespoke comment tables. But adopt on the second model only after the first model's rendering is verified — breadth is precisely where N+1 and authorization gaps appear; depth-one never surfaces them.

Pick your integration surface deliberately, in this order of preference:

  1. Resource View page — render the component as a below-content section or header action. Lowest friction, and the natural home for review notes and annotations.
  2. Relation manager — gives replies a first-class tab. Verify it works before committing; the docs are silent on relation-manager support, so treat this as a spike, not an assumption.
  3. Infolist entry / custom Blade partial — for read-heavy surfaces where the thread is context rather than the primary action.
  4. Raw Livewire component in a non-Filament view — possible, but you lose panel context (tenant scoping, guards, styling). Only with a concrete reason.

Authorization is yours, entirely. The package ships UI + persistence; it does not know your policy model. Decide three things up front:

  • Who can post — check whether the plugin/component exposes a canReply-style closure; if it does, use it. If not, wrap the render in @can / Gate::allows.
  • Who can see the thread — this is the one that bites. A thread rendered on a resource page is visible to anyone who can view that resource. Internal-only notes on a customer-visible record is a data leak, not a UI bug. Make visibility an explicit per-surface decision.
  • Who can edit/delete — the package likely ships neither. Do not add them without a moderation story.
// gate as a habit, even if the package offers a hook
@can('viewReplies', $ticket)
    <livewire:replies :replyable="$ticket" />
@endcan

Handle loading yourself before you trust the component's. Nested replies are a textbook N+1, and nothing in the docs states a loading strategy. Enable query logging during the spike, build a thread of ~20 replies across 3 levels, and count queries. If the count scales with reply count, eager-load in the parent query or pass a pre-loaded collection.

Scope tenancy at the panel level. With a polymorphic link hanging off a tenant-scoped parent, scoping usually follows — but verify with two tenants and a direct component render, not just through the resource, since components can be reachable by URL.

Feature-flag the surface on adoption. A config flag or ->visible() check keyed to a setting gives you a one-line rollback on a surface whose failure mode is silent (blank widget, not exception). It also lets you widen from one model to many deliberately.

Set a maximum nesting depth in the UI. "Threaded" is undocumented on depth bounds, ordering guarantees, notification semantics, and eager loading. Enforce your own recursion cap before users discover the cost.

Write your own tests — they are the contract, not the package's. Minimum four: post top-level reply, post nested reply, unauthorized user cannot see the thread, migration rolls back cleanly.

Gotchas and Tips

The tagline lies. The repo calls it "a bookmark manager for your filament resources" while every other artifact describes threaded replies. Read src/ end-to-end before writing integration code — it costs fifteen minutes and it's a reliable signal the README will not answer your questions.

The Livewire registration defect is the worst failure shape. PR #5 fixed component registration. That class of bug appears as blank or non-interactive widgets after deploy — no exception, no health check trip, and invisible in local dev where the component manifest is fresh. If your deploy caches Livewire components or Filament assets, run the spike in staging that mirrors the cached-manifest flow. Blank widgets → clear component caches and republish assets before blaming your own integration.

No upgrade guide exists — prefer fresh installs. 3.0.0 is a two-bullet changelog (Filament 5 support + the Livewire fix) with no breaking-change list and no statement of schema impact versus 2.0.3. Moving from 2.x means a written diff review of PRs #5 and #6 plus a re-read of the migration. Installing new means installing clean and never relying on the upgrade path.

The migrations are the data risk. Nothing confirms they're additive and reversible across majors. Before production: forward and back against a copy of real data, confirm no existing columns are altered, and record the table name so a future fork can migrate away from it.

Pin exact versions and write the fork procedure at adoption, not later. Single maintainer, 2 stars, 0 dependents — you are effectively the first serious user. Keep published config/views/migrations in your repo (they already are once published), pin exact constraints, and name an internal owner. The real mitigant is that the codebase is small enough to read end-to-end — actually do it during the spike.

Budget the upgrade-cadence tax. Because the major version tracks the host framework, this dependency inherits Filament's release train. Add a re-test step to your Filament upgrade runbook — and never let a small plugin justify a panel-wide migration. Sequence Filament on its own merits; this is a follow-on.

Extension points to hunt for in src/ when the published config isn't enough:

  • Plugin configuration methods — styling, model binding, authorization closures.
  • Whether the reply model is swappable via config, or hard-wired.
  • Whether views are fully publishable and overridable per-surface.

If the model isn't swappable and you need custom behaviour, that's a fork decision — make it before you build on the default model, not after. And if you hit a widget that renders but won't persist, check the migration's polymorphic columns against your actual model namespace first; a type-string mismatch fails silently.

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.
frankyso/ipaymu
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