Ui5Registry
The Ui5Registry is the single place that knows every UI5 module and artifact in your application. Controllers resolve requests through it, the manifest generator reads it, and the URL of every artifact is derived from it.
It is built once per request, at boot, from two sources — the modules listed in config/ui5.php and the infrastructure modules contributed by installed packages — and is immutable thereafter. There is no runtime registration API.
Which implementation is used is the registry key in config/ui5.php; it must satisfy [Ui5RegistryInterface]{target="_self"}.
What it holds
| Map | Keyed by | Holds |
|---|---|---|
| Modules | module namespace | Ui5ModuleInterface instances |
| Modules by class | module FQCN | the same instances |
| Artifacts | artifact namespace | Ui5ArtifactInterface instances |
| Artifacts by class | artifact FQCN | the same instances |
| Settings | artifact namespace → key | the #[Setting] catalog |
| Slots | slot name | the #[Slot] catalog (SlotCatalogEntry) |
Namespaces are the identity. A module is found by the namespace it declares, an artifact by its own — there is no separate slug or short name, and nothing in config/ui5.php acts as a lookup key. The array there is a plain list of module classes; see Registered UI5 Modules.
How it boots
loadFromArray() runs in four passes, and the order matters.
Pass 1 — modules. The infrastructure modules collected from service providers are merged ahead of the ones declared in config/ui5.php, and each is instantiated with the source strategy resolved for it.
Pass 2 — artifacts. Every module's getAllArtifacts() is walked and each artifact is registered: indexed by namespace and by class, mapped back to its owning module, and scanned for #[Setting] declarations.
Pass 3 — slots. The #[Slot] declarations are read off the module classes and merged into the slot catalog, with the conflict check described below.
Pass 4 — the settings bridge. Every slot becomes a slot.{name} setting.
Modules and artifacts come first because an artifact may reference another module's artifacts; everything must exist before anything is reflected.
afterLoad() is a no-op extension hook for subclasses — the seam the SDK's SdkRegistry uses to layer database-backed state on top.
Lookup
$registry->modules(); // array<string, Ui5ModuleInterface>
$registry->getModule('com.example.showcase'); // ?Ui5ModuleInterface
$registry->getModuleByClass(ShowcaseModule::class); // throws if unknown
$registry->artifacts(); // array<string, Ui5ArtifactInterface>
$registry->getArtifact('com.example.showcase.revenue'); // ?Ui5ArtifactInterface
$registry->getArtifactByClass(RevenueTile::class); // throws if unknownThe get* accessors return null for a missing namespace; the *ByClass accessors throw a LogicException. The asymmetry is deliberate: a namespace can come from a URL and may legitimately be wrong, a class-string comes from your own code and cannot.
Settings
#[Setting] declarations are collected per artifact at boot. A duplicate key within one artifact is a LogicException at boot — not a silent overwrite.
$registry->settings(); // every artifact's settings
$registry->settings('com.example.showcase'); // one artifact's settingsEach entry carries default, type, scope, level, note, and model — a plain array, not the attribute instance. What the declaration means, and the second (injection) path that never reaches this catalog, is documented under Settings.
The slot catalog
#[Slot] declarations live on the module class and are validated as they are read: the name must match ^[a-z][a-zA-Z0-9_]*$, the note must be non-empty, the type must not be Model, and the default must not be null. Two modules may declare the same slot, but only if type, default, note, and editable level agree — any mismatch raises a conflict at boot naming both declaring modules.
$registry->hasSlot('period'); // bool
$registry->getSlot('period'); // SlotCatalogEntry, throws UnknownSlotException
$registry->getAllSlots(); // array<string, SlotCatalogEntry>A SlotCatalogEntry carries name, type, default, note, editable, and declaredBy — the module classes that declared it, in registration order.
Every slot additionally auto-expands into a slot.{name} Setting, so a slot is addressable through the settings channel without being declared twice. Inspect the catalog from the command line with php artisan ui5:slot (bare: the whole catalog; with a name: that slot's catalog entry). The pipeline that consumes the catalog is documented under Slots.
URLs and namespace paths
Namespaces are dot-separated; URLs use slashes. The registry owns that translation and every artifact URL derived from it.
$registry->namespaceToPath('com.example.showcase'); // 'com/example/showcase'
$registry->pathToNamespace('com/example/showcase'); // 'com.example.showcase'resolve() returns an artifact's route base — prefix, type segment, path, and version — or null if the namespace is unknown:
$registry->resolve('com.example.showcase');
// '/ui5/app/com/example/[email protected]'The type segment comes from the artifact type itself: app, lib, card, dashboard, report, resource, api. A non-routable type (a Tile, a Dashboard Group) throws NonRoutableArtifactException.
Two convenience forms build on it. Both throw MissingArtifactException for an unknown namespace, rather than returning null:
$registry->resolveManifestUrl('com.example.showcase');
// '/ui5/app/com/example/[email protected]/manifest.json'
$registry->resolveIndexUrl('com.example.showcase');
// '/ui5/app/com/example/[email protected]/index.html'
$registry->resolveIndexUrl('com.example.showcase', 'orders/42');
// '/ui5/app/com/example/[email protected]/index.html#/orders/42'resolveIndexUrl() is applications-only — anything else throws NonRoutableArtifactException, because only an application has an entry page.
resolveRoots() maps a list of namespaces to their route bases in one call, the shape the UI5 bootstrap needs for its resource roots:
$registry->resolveRoots(['com.example.showcase', 'com.example.offers']);
// [
// 'com.example.showcase' => '/ui5/app/com/example/[email protected]',
// 'com.example.offers' => '/ui5/app/com/example/[email protected]',
// ]Export
exportToCache() returns the registry's built state as a plain array — modules, artifacts, namespaceToModule, artifactToModule, settings, and slots. It is what a caching registry implementation persists to skip the reflection pass on subsequent boots. The slot catalog entries implement CacheSerializable so they round-trip structurally rather than collapsing to a class name.
Core ships the in-memory Ui5Registry only. A database-backed registry and a compiled cache are part of the SDK.
Core holds no identity
The registry knows artifacts, settings, and slots. It does not know users, roles, or permissions — Core owns no identity model. Authorization metadata (#[Access], #[Act], role assignments) and the runtime that resolves it belong to the SDK, which extends this registry rather than replacing it.