Skip to content

Examples

Create and revise a Descriptor

Publish a typed Approval binding and document schema for the Workshop Note, then observe its domain-form entry.

Example type Recipe

Overview

Create and Change a Descriptor

This recipe makes the same Workshop Note available to Electronic Approval by adding an ApprovalFormBindingDescriptor and an ApprovalDocumentSchema. It assumes the previous Contributor recipe added workshop.note.publish.

Scope of this recipe

Descriptors are the common way Apps expose capabilities to Core runtimes, but this recipe covers only the binding and document schema required by Electronic Approval. To execute an App-owned action from a Service Task in tenant-authored BPMN, publish a separate ProcessWorkActionDescriptor instead of reusing an Approval descriptor.

ProcessWorkActionDescriptor publishes the action to the BPMN picker
  → the service provider registers a handler factory with ProcessWorkActionRegistrar
  → ProcessWorkActionHandler::handle() receives the restored context and input
  → ProcessWorkActionResult returns the action output

A descriptor declaration publishes a catalog and execution contract; it does not execute the App's business logic. The App-owned handler performs the actual work. Process start bindings, user-task forms, and importable Process templates use the separate ProcessStartBindingDescriptor, ProcessUserTaskFormDescriptor, and ProcessTemplateDescriptor contracts. Continue with Contribute to Business Process for the descriptor, handler, and service-provider registration. See Business Process and Business Process contracts for the complete BPMN integration choices and runtime signatures.

What appears when you finish

ResultWhere to see it
workshop.note.submit bindingE-Approval → Template management → New template → Business form → Domain form
Name-and-status snapshot schemaPublic document structure used by preview and approval detail
New menuNone. Core owns the E-Approval menu

Workshop Submit Note descriptor

This is the actual catalog result. Searching for Workshop shows Submit Note under Other Apps → Workshop. This stage gives administrators a binding choice; the next Approval contribution recipe connects the user-facing composer widget.

Which contract it implements and how Core receives it

WorkshopApprovalDescriptors also does not extend a Core class. It implements the App SDK AppDescriptorContribution interface and returns an AppDescriptorSet from appDescriptors(). ApprovalFormBindingDescriptor and ApprovalDocumentSchema are final SDK descriptor value objects, not base classes that App code subclasses.

WorkshopApprovalDescriptors implements AppDescriptorContribution
  → NexiaContributionRegistry discovers the implementing class
  → AppDescriptorCatalog calls appDescriptors()
  → catalog rejects duplicate descriptor type + key pairs and records Workshop ownership
  ├─ ApprovalBindingRegistry consumes ApprovalFormBindingDescriptor
  └─ ApprovalDocumentSchemaRegistry consumes ApprovalDocumentSchema
     (both registries recheck tenant App installation and descriptor lifecycle)

Adding objects to appDescriptors() does not render UI directly. Core's Approval registries interpret the static contracts as an available domain form binding and the shape of document values to freeze. Because ContributionOwner comes from the Workshop namespace where the class was discovered, an App cannot publish a descriptor under another App's ownership.

1. Add the Descriptor

Code example
Shell
cp docs/developers/examples/descriptor/WorkshopApprovalDescriptors.php \
  packages/workshop/src/Descriptors/WorkshopApprovalDescriptors.php

The binding is:

Code example
PHP
new ApprovalFormBindingDescriptor(
    appKey: 'workshop',
    resourceKey: 'note',
    actionKey: 'submit',
    labelKey: 'workshop.approval.note.submit.label',
    entryModes: ['link'],
    formWidgetKey: 'workshop.note.submit',
    documentSchemaKey: 'workshop.note',
    descriptionKey: 'workshop.approval.note.submit.description',
    submitPermissionKey: 'workshop.note.publish',
)

link means that composition selects an existing Note. The submit permission filters catalog visibility but never replaces record-level backend Policy checks.

Publish the referenced schema in the same set:

Code example
PHP
new ApprovalDocumentSchema(
    appKey: 'workshop',
    resourceKey: 'note',
    titleKey: 'workshop.approval.note.document.title',
    sections: [[
        'title_key' => 'workshop.approval.note.document.summary',
        'fields' => [
            ['key' => 'name', 'label_key' => 'workshop.note.name.label', 'type' => 'string'],
            ['key' => 'status', 'label_key' => 'workshop.note.status.label', 'type' => 'string'],
        ],
    ]],
)

The schema does not reflect model columns. It declares only the public documentValues shape the App freezes at submission.

2. Add locale entries

Add the exact dotted keys to every locale owned by Workshop:

Code example
JSON
{
  "workshop.approval.note.submit.label": "Submit note",
  "workshop.approval.note.submit.description": "Link an existing note to Electronic Approval.",
  "workshop.approval.note.document.title": "Note",
  "workshop.approval.note.document.summary": "Note details"
}

3. Discover and observe it

Code example
Shell
sh docs/developers/examples/descriptor/verify.sh

Then search for Workshop at the path shown in the screenshot. Installation, submit authority, lifecycle, and locale resolution all participate in catalog visibility.

4. Change the Descriptor

For wording-only changes, update locale values and keep identity and version. For a compatible optional extension, confirm consumer compatibility and review the version:

Code example
PHP
version: '1.1',

For breaking changes such as an action key, field type, or requiredness, add a new Active descriptor and deprecate the old one. Remove the old version only after stored templates and consumers have migrated.

Common errors

SymptomCauseFixConfirm
Validation succeeds but the domain form is absentWorkshop is not installed for the tenant, the user lacks submit authority, or the descriptor is not ActiveCheck installation, the workshop.note.publish assignment, and lifecycleSearching for Workshop in the same tenant and Legal Entity shows Submit Note
A raw locale key appearsOne of Workshop's supported locale catalogs lacks the same dotted keyAdd all four keys to every resources/lang/{locale}.json and refresh runtime cachesThe picker shows the translated Submit Note label and description
The item is visible but composition cannot openThe Descriptor publishes only the static catalog; the Slot Widget and frontend registry do not exist yetComplete the Electronic Approval contribution recipe and match all four workshop.note.submit identitiesThe composer loads the Workshop Widget
The schema cannot be resolvedThe binding documentSchemaKey differs from the schema's appKey.resourceKey identityNormalize both sides to workshop.noteverify.sh prints Descriptor contracts are valid.

Included files

Approval descriptors

docs/developers/examples/descriptor/WorkshopApprovalDescriptors.php
Code example
PHP
<?php

declare(strict_types=1);

namespace Amuzcorp\Nexia\Workshop\Descriptors;

use Nexia\AppDescriptors\AppDescriptorSet;
use Nexia\AppDescriptors\ApprovalDocumentSchema;
use Nexia\AppDescriptors\ApprovalFormBindingDescriptor;
use Nexia\AppDescriptors\Contracts\AppDescriptorContribution;

final class WorkshopApprovalDescriptors implements AppDescriptorContribution
{
    public static function appDescriptors(): AppDescriptorSet
    {
        return AppDescriptorSet::of(
            new ApprovalFormBindingDescriptor(
                appKey: 'workshop',
                resourceKey: 'note',
                actionKey: 'submit',
                labelKey: 'workshop.approval.note.submit.label',
                entryModes: ['link'],
                formWidgetKey: 'workshop.note.submit',
                documentSchemaKey: 'workshop.note',
                descriptionKey: 'workshop.approval.note.submit.description',
                submitPermissionKey: 'workshop.note.publish',
            ),
            new ApprovalDocumentSchema(
                appKey: 'workshop',
                resourceKey: 'note',
                titleKey: 'workshop.approval.note.document.title',
                sections: [[
                    'title_key' => 'workshop.approval.note.document.summary',
                    'fields' => [
                        [
                            'key' => 'name',
                            'label_key' => 'workshop.note.name.label',
                            'type' => 'string',
                        ],
                        [
                            'key' => 'status',
                            'label_key' => 'workshop.note.status.label',
                            'type' => 'string',
                        ],
                    ],
                ]],
            ),
        );
    }
}

Refresh and validate descriptors

docs/developers/examples/descriptor/verify.sh
Code example
Shell
#!/usr/bin/env sh
set -eu

test -f packages/workshop/src/Descriptors/WorkshopApprovalDescriptors.php

task artisan -- nexia-runtime:refresh-runtime-caches --if-route-cached --no-ansi
task artisan -- nexia-apps:validate-app-descriptors workshop --no-ansi
task artisan -- nexia-apps:doctor-package-app workshop --no-ansi

printf '%s\n' 'Descriptor contracts are valid. Confirm the new binding in the Electronic Approval domain-form catalog.'