Siren

ProgramBoundToCollaboratorGroup

Fires when a program is wired to a collaborator group via the program edit endpoint.

Last updated: June 3, 2026

ProgramBoundToCollaboratorGroup fires when an operator wires a program to a collaborator group. The binding is what tells the cascade machinery which group to walk when a triggering event lands against the program. Without it, a cascade calc strategy has no group to traverse.

The event ID is program_bound_to_collaborator_group, and its fully qualified class is Siren\Plus\Core\Groups\Events\ProgramBoundToCollaboratorGroup. It fires after the binding is saved, from the program edit endpoint. To run code when it fires, register a handler with Siren’s event system. See listeners and the events introduction.

What does this event carry?

The event carries the program id and the group id that was just bound to it. A program can be bound to at most one collaborator group at a time, so this event names the single binding now in effect. It is the program twin of DistributorBoundToCollaboratorGroup: the same payload shape and one-group rule, and the same config pattern, differing only by type=program in place of type=distributor.

use PHPNomad\Events\Interfaces\CanHandle;
use PHPNomad\Events\Interfaces\Event;
use Siren\Plus\Core\Groups\Events\ProgramBoundToCollaboratorGroup;

class HandleProgramBinding implements CanHandle
{
    public function handle(Event $event): void
    {
        if (!$event instanceof ProgramBoundToCollaboratorGroup) {
            return;
        }

        $programId = $event->getProgramId();
        $groupId = $event->getGroupId();

        // The next trigger fired against this program will walk this group
    }
}

How does it fit?

The binding is a config row keyed (type='program', subtype=<programId>, configKey='collaboratorGroupId'). The activity feed listens here to record the new relationship. Any reporting layer that wants to show “which programs are wired to this group” can rebuild its lookup off the same signal. Re-binding a program to a different group fires this event again with the new group id, after firing ProgramUnboundFromCollaboratorGroup for the previous one. Both fire synchronously in the same request, so the unbind always precedes the bind.

If a trigger lands against a program with no bound group, the cascade has no group to walk and credits no one, with no error raised. If the bound group’s structure cannot support the selected calc (a cascade calc on a flat group, for example), the calc fails closed at run time and credits no one, so a bind-time handler is a good place to catch an incompatible pairing early.

ProgramUnboundFromCollaboratorGroup is the unbind side, and DistributorBoundToCollaboratorGroup and DistributorUnboundFromCollaboratorGroup are the distributor-side pair. The collaborator group events reference lists the full family.