Plugin Operation Registry
Contract for mapping declared plugin operation names onto invocable core action classes.
Overview
App\Sdk\Data\PluginOperationRegistry maps declared plugin operation names to invocable core action classes.
Core seeds a conservative set of low-risk actions that retain their own Gate checks. The Signals::operation() / PluginDataAccess::operation() facade never bypasses authorisation — operations run as the current authenticated user (or the ambient system/job context when no user is bound).
Manifest operations[] is a separate allow-list gate: a name must be both registered here and declared on the plugin manifest before the facade will invoke it.
Public surface
| Method | Purpose |
|---|---|
register() |
Map one operation name to an action class string |
has() |
Check whether an operation name is registered |
get() |
Return the action class for a name or throw |
all() |
Return the complete name => class-string map |
Accepted registration shape
Identity is the operation name string in resource.action form (for example activities.complete).
$registry->register('activities.complete', CompleteActivity::class);
The value is a class-string for an invocable action. The registry stores the string without instantiating or runtime-checking the class; the container resolves it when PluginDataAccess invokes the operation.
register() assigns directly into an associative map. Registering another class for the same name replaces the earlier mapping; duplicate names are not rejected.
Seeded operations
The constructor currently seeds:
| Operation | Action class |
|---|---|
activities.create |
App\Actions\Activities\CreateActivity |
activities.complete |
App\Actions\Activities\CompleteActivity |
favourites.toggle |
App\Actions\Favourites\ToggleFavourite |
Lookup and facade gating
has($name)is the optional guardget($name)throwsApp\Sdk\Data\PluginDataExceptionwithOperation [{name}] is not registered in the plugin operation registry.when missingall()returns the current map in registration / replacement order
PluginDataAccess::operation($package, $action, ...$args) enforces two gates before resolution:
$actionmust appear in the plugin's stored manifestoperations[]$actionmust passhas()/get()on this registry
Only then does the facade resolve the action class from the container and invoke it with the supplied arguments. Authorisation remains inside the action.
Worked example
This is the current seeded lookup and invocation path:
$registry->has('activities.complete'); // true
$registry->get('activities.complete'); // CompleteActivity::class
$actionClass = $registry->get('activities.complete');
app($actionClass)($activity);
A name that is registered here but omitted from the calling plugin's manifest operations[] is still denied by the facade before get() runs for the happy path; an undeclared unknown name fails the manifest gate, while a declared but unregistered name fails with PluginDataException from get().