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

Ticket Bundle Laravel Package

hackzilla/ticket-bundle

A simple ticketing/helpdesk bundle for Symfony apps. Users can create and reply to tickets with priorities, categories, statuses, attachments, and email notifications, while admins manage queues and respond from the backend. Integrates with existing user providers.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package. There is no service provider, no Eloquent model, no Blade views, and no config/ticket.php. Everything you read below assumes a Symfony application (or a Symfony service inside a hybrid stack); a pure Laravel app cannot composer require this and get a working ticketing system — see Gotchas first if that is your situation.

Minimum steps in a Symfony app:

composer require hackzilla/ticket-bundle
  1. Register it in config/bundles.php:
    Hackzilla\Bundle\TicketBundle\HackzillaTicketBundle::class => ['all' => true],
    
  2. Create the bundle config. As of 3.8.0 the bundle's own examples and test app use PHP config, so config/packages/hackzilla_ticket.php is the format to copy from first; YAML still works if that is your house style (use one format per bundle, never both):
    return static function (HackzillaTicketConfig $config): void {
        $config->userClass(App\Entity\User::class);
        // ticket/message/attachment/category/priority/status class overrides,
        // templates, mailer + from_email
    };
    
    Whatever you copy, DependencyInjection/Configuration.php is the authoritative key list, not the README, and not a blog post.
  3. You write your own migrations. The bundle ships entities, not schema. Run bin/console doctrine:schema:validate then doctrine:migrations:diff and inspect the generated ticket, ticket_message, ticket_attachment, ticket_category, ticket_priority, ticket_status tables before running them.
  4. Seed categories/priorities/statuses — they are entities, not enums, and an empty status table means every create form fails validation.

Where to look first, in this order: Manager/TicketManagerInterface (the entire public API), Manager/UserManager (new findUserByUsername() in 3.8.0 — the supported way to resolve a user from a string, useful for assignment widgets and imports), Entity/TicketInterface + Entity/MessageInterface (your extension surface), Resources/views/ (what you will override), Event/ (the hooks). Ignore the rest until you need it.

First use case: an internal support inbox. Inject TicketManagerInterface, create a ticket from a form, list tickets by status, and render the bundle's Twig templates from your own controller. There is no bundled CRUD UI; the routing definitions shipped with the bundle were modernized in 3.8.0 and an XML variant was added, but treat those as reference resources and check Resources/config/routing* for actual route names before importing anything. Routing, authorization, and pagination are yours to build — roughly 100 lines of controller is the fastest path to a working prototype.

Implementation Patterns

Always go through the manager, never the repository. TicketManager::createTicket(), updateTicket(), getTicket(), and the message/attachment helpers wrap persistence and dispatch events and trigger notifications. Persisting a Ticket with the EntityManager directly silently skips both, which is the number-one source of "why didn't the email go out" bugs.

public function __construct(private TicketManagerInterface $tickets) {}

public function open( User $user, string $subject, string $body ): TicketInterface
{
    $ticket = $this->tickets->createTicket();
    $ticket->setSubject($subject)
           ->setCreatedBy($user)
           ->setPriority($defaultPriority)
           ->setCategory($defaultCategory)
           ->setStatus($openStatus);

    $this->tickets->updateTicket($ticket);
    $this->tickets->createMessage($ticket, $user, $body); // initial body is a message

    return $ticket;
}

Custom entities via interface + config. Subclass the bundle entities (or implement the interfaces and add #[ORM\Entity]), then point hackzilla_ticket.ticket_class etc. at yours. This is the supported extension point — do not fork the bundle. For the polymorphic createdBy/assignedTo relation, wire Doctrine's ResolveTargetEntityListener so user_class resolves at runtime; that is what makes the bundle reusable across apps, and it is entirely independent of which user provider you use (3.8.0 dropped the FOSUserBundle assumption — do not re-add it).

Resolving users by name. UserManager::findUserByUsername() (3.8.0) is the new, provider-agnostic entry point. Use it in admin "assign to" form types, CSV imports, or CLI commands instead of injecting your own UserRepository — it respects the configured user_class and keeps the bundle swappable.

Reuse the forms, don't rebuild them. Form/TicketType, TicketMessageType, and the filter form already encode the relation choices. Extend them with ->add() in your own form class; a from-scratch form usually forgets the status/priority EntityType query builders and produces empty dropdowns on fresh databases.

Override templates through config or convention. Either set paths under hackzilla_ticket.templates or use Symfony's standard templates/bundles/HackzillaTicketBundle/ override directory. The bundled markup is Bootstrap-3-ish and opinionated; most teams override the list, show, and form partials within a day.

Events are the integration seam. Subscribe to the ticket and ticket-message events to push to Slack, fire webhooks, or write an audit log. If you need email beyond the bundled notifier, disable the built-in mailer in config and handle it in a listener — you get retries, queuing, and testability that the synchronous built-in mailer does not offer.

Attachments need an owner. The attachment entity handles the uploaded file and move-on-persist via lifecycle callbacks. You must set upload_dir to a writable path and provide your own controller/route to serve files back with authorization checks — the bundle will not do that for you, and serving an unauthenticated upload directory is the classic leak here.

Multilingual, precisely: the bundle translates its interface through Symfony's translator catalogs, not user content. Add translations/HackzillaTicketBundle.<locale>.yaml for your locale. If you need genuinely per-locale ticket bodies, model that yourself as one message per locale or a translatable field — the package gives you the UI plumbing only.

Hybrid Laravel stacks: the only sane pattern is to run the Symfony app as the ticketing service and have Laravel call it over HTTP (or share the database read-only). Do not try to boot the bundle inside Laravel.

Gotchas and Tips

  • Composer will fight you in a Laravel app. The bundle pulls symfony/* and doctrine/* constraints that conflict with the versions Laravel already requires. Even when install succeeds, none of the wiring (DI container, event dispatcher, Twig) exists. Treat it as reference architecture, not a dependency.
  • Release cadence is slow — 3.8.0 is the current tag. 3.8.0 loosened several dependency constraints to allow some major versions, but that is not a blanket Symfony 6 / Doctrine 3 guarantee. Read composer.json in the tag you install and run a real composer update --dry-run in a throwaway branch before committing to an upgrade. Budget for a fork if you need a version pair the constraints do not cover.
  • Config format churn. 3.8.0 moved the bundle's own configuration to PHP. Older snippets, README examples, and every blog post you will find are YAML. Both formats are valid Symfony; pick one, and do not define the same bundle in both hackzilla_ticket.yaml and hackzilla_ticket.php — the merge order will surprise you. Verify what actually loaded with bin/console debug:config hackzilla_ticket.
  • UserLoad constructor fix (#201). If you were pinned to a 3.7.x patch and had subclassed or decorated the user loader, re-check your signature after upgrading; the argument list changed.
  • No bundled migrations means every entity-subclass change requires a fresh migrations:diff. Regenerate and diff carefully after changing any *_class config key — table names and FKs shift underneath you.
  • Statuses/priorities/categories are rows. Deleting a category still referenced by tickets throws a FK violation; soft-delete or reassign instead. Seed them in fixtures so tests and dev environments start sane.
  • user_class must be a Doctrine entity. Laravel's Authenticatable is not one. In Symfony, the bundle assumes the security token's user; anonymous ticket creation needs an explicit fallback user or an early access_denied.
  • The mailer is synchronous. A dead SMTP host throws inside updateTicket(). Disable it in test and dev, and wrap in production or replace it with a listener + queue.
  • Debugging commands worth memorizing: bin/console debug:container hackzilla, debug:config hackzilla_ticket, debug:event-dispatcher (filter on the ticket events), debug:router after importing any bundle routing resource, doctrine:mapping:info, and doctrine:schema:validate after any config change.
  • Extension points, ranked by safety: event subscribers → template overrides → custom entity classes in config → decorator around TicketManagerInterface → fork. Reaching for the fork first is how these projects die; the interface decorator covers 90% of "I need behavior X on every ticket write."
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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core