Siren

CollaboratorGroupMemberResolverRegistryInitiated

Broadcast once when the CollaboratorGroupMember field-resolver registry is first read. Listeners register per-field getter closures.

Last updated: June 3, 2026

CollaboratorGroupMemberResolverRegistryInitiated is broadcast exactly once, on the first read of the CollaboratorGroupMember field-resolver registry. It’s the registration seam for any module that wants to expose a new field on the membership rows returned by the CollaboratorGroup resource. Adding a field here surfaces it in the members list returned alongside the group.

The event ID is collaborator_group_member_resolver_registry_initiated, and its fully qualified class is Siren\Plus\Core\Groups\Events\CollaboratorGroupMemberResolverRegistryInitiated. To run code when it fires, register a handler with Siren’s event system. Because the registry is built exactly once on first read, register your handler during plugin initialization, the same way the core resolvers are wired, so it is in place before that first read. A handler added after the first read misses the window and its field never appears. See listeners, the events introduction, and the collaborator group events reference.

What does this event carry?

The event extends the framework’s FieldResolverRegistryInitiatedEvent abstract and exposes one writer: addResolver(string $field, callable $resolver). The resolver closure receives the CollaboratorGroupMember model and returns whatever the field should expose on the API row.

use PHPNomad\Events\Interfaces\CanHandle;
use PHPNomad\Events\Interfaces\Event;
use Siren\Plus\Core\Groups\Events\CollaboratorGroupMemberResolverRegistryInitiated;
use Siren\Plus\Core\Groups\Models\CollaboratorGroupMember;

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

        $event->addResolver('lifetimePayout', function (CollaboratorGroupMember $member) {
            // Derive the value from whatever store you keep it in
            return 0.0;
        });
    }
}

How does it fit?

This is the same registry pattern as the group-level field registry and the structure-resolver registry, scoped to the per-member rows. Core fields and any custom fields are registered the same way, by listening to this event and calling addResolver. The core resolvers (id, groupId, collaboratorId, metadata, dateCreated, dateModified plus collaboratorName, collaboratorEmail, and collaboratorStatus) are all registered through RegisterCoreCollaboratorGroupMemberResolvers listening to this event.

The first six fields read straight off the membership row. The last three are joined from the collaborator record. A resolver that joins another datastore runs once per member on every list request, so keep it cheap and defend against missing rows (return null rather than throwing) so the list endpoint keeps working against orphaned memberships. On a large group a careless join becomes an N+1 query.

Field names are unique in the registry, so registering a resolver for a name that already exists (a core field like metadata) replaces it. Use a distinct name unless you mean to override, and return JSON-friendly data, since the value is serialized onto the API row. See custom structure resolvers for the same registry shape applied to structures.