liuggio/help-desk-bundle
A Symfony bundle for adding help desk and support ticket functionality to your application. It provides building blocks for customer requests, conversations, and ticket workflows, helping teams manage and track support inquiries within Symfony projects.
Day one reality check: you cannot composer require this. liuggio/help-desk-bundle (published as tvision/HelpDeskBundle) is a Symfony 2.x bundle marked NOT MAINTAINED, with zero stars and zero dependents. It has no service provider, no facade, no Laravel package-discovery contract, and it hard-depends on Symfony 2.x, Doctrine ORM 2.2/2.3, FOSUserBundle-style user entities, and Twig. Installing it into a Laravel 10/11/12 app either fails resolution or drags in abandoned transitive dependencies with known CVEs. The only legitimate day-to-day use is cloned out of tree as a read-only design reference.
Minimal path to get value from it:
git clone the repo somewhere outside your project — do not add it to composer.json.Entity/ first. Read the ticket, message/comment, and status classes plus their Doctrine mappings. This is the only durable asset: ticket ↔ requester, optional assignee, threaded replies, status and priority fields.Ticket belongsTo requester, nullable assignee_id, hasMany comments, enum status/priority — and hang a policy and a status-change event off it. That's a half-day to a support inbox with none of the baggage.If you need a working help desk this week, stop here and evaluate a maintained Laravel-native package or a standalone system (Zammad, FreeScout) behind SSO instead.
These are the patterns worth carrying from the bundle into a Laravel codebase; the bundle's own wiring is not reusable.
Status as an explicit state machine. The bundle stores status as a discrete field and drives transitions from the controller. In Laravel, make that a single source of truth — a backed enum plus guarded transitions enforced in an action class, never in the controller. Dispatch a TicketStatusChanged event on every transition so mail, audit log, and SLA timers all attach to one hook.
enum TicketStatus: string {
case Open = 'open';
case Pending = 'pending';
case Resolved = 'resolved';
case Closed = 'closed';
}
Two foreign keys, not one polymorphic owner. The bundle ties tickets to the FOSUser entity as owner. In Laravel keep user_id (requester) and a nullable assignee_id (agent) separate. Queries stay simpler and policies become explicit — requester sees own tickets, agents see the queue, assignee sees assigned.
public function view(User $user, Ticket $ticket): bool {
return $user->id === $ticket->user_id
|| $user->id === $ticket->assignee_id
|| $user->hasRole('agent');
}
Flat threaded comments. The bundle models replies as a flat hasMany with author and body — no nesting. Keep that flatness. It makes pagination, unread markers, and inbound-email threading (Message-ID / In-Reply-To) tractable. Add an is_internal boolean for agent-only notes early; you will want it within a week.
Events over controller side effects. The bundle leans on Symfony's event dispatcher for notifications. Map each of those hooks to a Laravel event + queued listener, and route outbound mail through notifications rather than inline Mail::send calls in controllers. That's what makes email ingestion and Slack-style integrations additive later.
Actions, not fat controllers. Reproduce the bundle's flows (create, reply, assign, transition, close) as single-purpose invokable action classes. It keeps authorization in policies and business rules out of HTTP concerns, which is exactly where the original Symfony controllers blurred the line.
NOT MAINTAINED banner plus PHP 8.1+ incompatibility. Porting is a rewrite, not an integration.How can I help you explore Laravel packages today?