ActionItemAssigneeResolver
in package
ActionItemAssigneeResolver - resolves "who can act" for an action item.
Mirrors the assignee vocabulary used by the workflow approval subsystem
(permission, role, member, dynamic, policy) so existing eligibility concepts
are reused. Permission scope honours branch scoping via PermissionsLoader.
Office-based assignment is expressed through the dynamic type backed by a
plugin-provided resolver service, keeping core free of plugin dependencies.
Table of Contents
Properties
- $dynamicServiceCache : array<string, object|null>
- Per-instance cache of instantiated dynamic resolver services keyed by class name, so the per-item eligibility loop does not re-instantiate the same plugin resolver for every action item it evaluates.
- $memberIdMemo : array<string, array<string|int, int>>
- Per-instance memo of resolved eligible member-id lists keyed by assignee identity (type + config + branch). A single resolution pass — e.g. the "My To-Dos" badge / list iterating every open item — typically references the same handful of (permission, branch) groups thousands of times; this collapses those duplicate reverse lookups to one query per distinct group for the lifetime of the resolver (a single request/operation), so it never serves stale "who can act" data across requests.
Methods
- getEligibleMemberIds() : array<string|int, int>
- Resolve the list of eligible member IDs for an action item.
- isMemberEligible() : bool
- Determine whether a member is eligible to act on an action item.
- activeRoleConditions() : array<string, mixed>
- Temporal "active role" conditions shared by eligibility queries.
- getDynamicService() : object|null
- Resolve (and per-instance cache) the dynamic resolver service for an item.
- isDynamicMemberEligible() : bool
- Member-centric dynamic eligibility.
- isEligibleByPermission() : bool
- Permission eligibility for a single member, branch-scoped when possible.
- isEligibleByRole() : bool
- Role eligibility for a single member.
- membersByPermission() : array<string|int, int>
- Branch-scoped reverse permission lookup returning member IDs.
- membersByRole() : array<string|int, int>
- Reverse role lookup returning member IDs.
- permissionCoversBranch() : bool
- Whether a loaded permission's resolved scope covers a branch.
- resolveDynamicMemberIds() : array<string|int, int>
- Resolve eligible member IDs via a configured dynamic resolver service.
- roleCoversBranch() : bool
- Whether a loaded role assignment covers a branch.
Properties
$dynamicServiceCache
Per-instance cache of instantiated dynamic resolver services keyed by class name, so the per-item eligibility loop does not re-instantiate the same plugin resolver for every action item it evaluates.
protected
array<string, object|null>
$dynamicServiceCache
= []
$memberIdMemo
Per-instance memo of resolved eligible member-id lists keyed by assignee identity (type + config + branch). A single resolution pass — e.g. the "My To-Dos" badge / list iterating every open item — typically references the same handful of (permission, branch) groups thousands of times; this collapses those duplicate reverse lookups to one query per distinct group for the lifetime of the resolver (a single request/operation), so it never serves stale "who can act" data across requests.
protected
array<string, array<string|int, int>>
$memberIdMemo
= []
Methods
getEligibleMemberIds()
Resolve the list of eligible member IDs for an action item.
public
getEligibleMemberIds(ActionItem $item) : array<string|int, int>
Parameters
- $item : ActionItem
-
The action item
Return values
array<string|int, int> —Distinct eligible member IDs
isMemberEligible()
Determine whether a member is eligible to act on an action item.
public
isMemberEligible(ActionItem $item, int $memberId) : bool
Parameters
- $item : ActionItem
-
The action item
- $memberId : int
-
The member to test
Return values
boolactiveRoleConditions()
Temporal "active role" conditions shared by eligibility queries.
protected
activeRoleConditions(DateTime $now) : array<string, mixed>
Parameters
- $now : DateTime
-
Reference time
Return values
array<string, mixed>getDynamicService()
Resolve (and per-instance cache) the dynamic resolver service for an item.
protected
getDynamicService(ActionItem $item) : object|null
Parameters
- $item : ActionItem
-
The action item
Return values
object|null —The resolver service, or null when misconfigured
isDynamicMemberEligible()
Member-centric dynamic eligibility.
protected
isDynamicMemberEligible(ActionItem $item, int $memberId) : bool
Prefers an optional member-centric entry point on the dynamic resolver
service (isMemberEligible(ActionItem, int): bool). Plugin resolvers that
implement it resolve the acting member's scope once and match in memory,
avoiding a reverse "who can act" lookup per item — the cause of badge/list
query storms. Resolvers without it fall back to the list-based check.
Parameters
- $item : ActionItem
-
The action item
- $memberId : int
-
The member to test
Return values
boolisEligibleByPermission()
Permission eligibility for a single member, branch-scoped when possible.
protected
isEligibleByPermission(array<string, mixed> $config, int|null $branchId, int $memberId) : bool
Parameters
- $config : array<string, mixed>
-
Assignee config
- $branchId : int|null
-
Resolved branch scope
- $memberId : int
-
Member to test
Return values
boolisEligibleByRole()
Role eligibility for a single member.
protected
isEligibleByRole(array<string, mixed> $config, int|null $branchId, int $memberId) : bool
Parameters
- $config : array<string, mixed>
-
Assignee config
- $branchId : int|null
-
Resolved branch scope
- $memberId : int
-
Member to test
Return values
boolmembersByPermission()
Branch-scoped reverse permission lookup returning member IDs.
protected
membersByPermission(array<string, mixed> $config, int|null $branchId) : array<string|int, int>
Parameters
- $config : array<string, mixed>
-
Assignee config
- $branchId : int|null
-
Resolved branch scope
Return values
array<string|int, int>membersByRole()
Reverse role lookup returning member IDs.
protected
membersByRole(array<string, mixed> $config, int|null $branchId) : array<string|int, int>
Parameters
- $config : array<string, mixed>
-
Assignee config
- $branchId : int|null
-
Resolved branch scope
Return values
array<string|int, int>permissionCoversBranch()
Whether a loaded permission's resolved scope covers a branch.
protected
permissionCoversBranch(object $permission, int|null $branchId) : bool
A Global permission carries branch_ids === null (every branch); a scoped
permission lists the concrete branch ids it grants.
Parameters
- $permission : object
-
A permission object from PermissionsLoader
- $branchId : int|null
-
Branch to test, or null to ignore branch scope
Return values
boolresolveDynamicMemberIds()
Resolve eligible member IDs via a configured dynamic resolver service.
protected
resolveDynamicMemberIds(ActionItem $item) : array<string|int, int>
Expects assignee_config: {"service": "Plugin\Service", "method": "resolve"}. The callback receives the ActionItem and must return int[] of member IDs. This is how office-based assignment is supported without a core→plugin dependency.
Parameters
- $item : ActionItem
-
The action item
Return values
array<string|int, int>roleCoversBranch()
Whether a loaded role assignment covers a branch.
protected
roleCoversBranch(object $role, int|null $branchId) : bool
Parameters
- $role : object
-
Role object from PermissionsLoader.
- $branchId : int|null
-
Branch to test, or null to ignore branch scope.