NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Tickets for Laravel

Assignment and watchers

One assignee per ticket: a morph to any model implementing Contracts\Assignee.

Tickets::for($ticket)->by($lead)->assign($agent);    // TicketAssigned
Tickets::for($ticket)->by($agent)->claim();          // the actor takes it
Tickets::for($ticket)->by($lead)->unassign();        // TicketUnassigned
$ticket->assignTo($agent, $lead);                    // model convenience, through the manager
  • The assignee must be eligible (isEligibleTicketAssignee()), else InvalidAssigneeException. With Concerns\IsTicketAssignee, an Authenticatable is eligible when it is an agent for this ticket — it passes the agent ability and the AgentScope lets it work on the ticket — so nobody is assigned a ticket they could not open. Any other model always is.
  • The same assignee again is a no-op: no activity, no event.
  • Assigning a ticket in the initial state runs the Assigned automation (new → open), and the assignee starts watching (tickets.watchers.auto.assignee).
  • claim() needs an actor who is an eligible assignee. In the API, assigning someone else needs the assign ability; any agent may claim; the holder may unassign themselves.
  • At creation without an assignee, Contracts\AssignmentStrategy::assigneeFor($ticket) is asked — by default the nearest category’s default assignee.

A team as assignee

Its members are the ones notified:

use RoundlyConsulting\Tickets\Concerns\IsTicketAssignee;
use RoundlyConsulting\Tickets\Contracts\Assignee;
use RoundlyConsulting\Tickets\Models\Ticket;

final class Team extends Model implements Assignee
{
    use IsTicketAssignee;

    public function ticketNotifiables(): iterable
    {
        return $this->members()->where('active', true)->get();
    }

    public function isEligibleTicketAssignee(Ticket $ticket): bool
    {
        return true;
    }
}

Add the class to tickets.assignables (['team' => Team::class]) for the API. teams-for-laravel works as the team model.

Your own assignment rule

Bind a Contracts\AssignmentStrategy (assigneeFor(Ticket): ?Model) through tickets.assignment.strategy or the container — round-robin, load-based, whatever your desk needs. It runs on the stored ticket, with its project, category, priority and channel already set.

Watchers

Followers of a ticket — users, any model, or a TicketGuest (a CC’d guest):

Tickets::for($ticket)->by($agent)->watchers()->add($colleague)->remove($former);
Tickets::for($ticket)->by($user)->watchers()->watch();      // the actor; ->unwatch()
Tickets::for($ticket)->watchers()->has($colleague);         // bool
Tickets::for($ticket)->watchers()->all();                   // TicketWatcher rows, watcher loaded
  • Unique per (ticket, watcher); adding twice is a no-op. watch() without an actor throws InvalidWatcherException.
  • Auto-watch (tickets.watchers.auto.*): the requester on creation, the assignee on assignment, agents who write, and (off by default) a mentioned reader. Auto-watches are silent — no activity, no event. TicketWatcher::$auto tells them apart; add() on someone already auto-watching makes the watch a chosen one.
  • A requester hand-over (changeRequester()) removes the previous requester’s automatic watch, so they no longer view the ticket or receive its notifications; a watch an agent added on purpose stays. A previous guest requester’s links are revoked too.
  • A non-agent watcher may view the ticket and its public thread and reply as a participant (tickets.watchers.can_view). Watcher lists are agent-only in the API.
  • TicketWatcherAdded / TicketWatcherRemoved fire for explicit changes; their activity entries are internal.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.