Demand Source Registry
Contract for registering availability demand definitions and their container-resolved resolvers.
Overview
App\Services\DemandSourceRegistry maps availability demand source types to display metadata and resolver classes.
Core binds the registry as a singleton in AppServiceProvider. Repair actions resolve their source-specific handler through it, while Gantt output uses registered display names and colours.
Public surface
| Method | Purpose |
|---|---|
register() |
Store one DemandSourceDefinition under its type |
get() |
Return a definition for a type or throw |
resolve() |
Resolve the definition's resolver class from Laravel's container |
has() |
Check whether a source type is registered |
all() |
Return the complete keyed definition map |
Accepted definition and resolver contracts
The accepted registration value is App\Services\DemandSourceDefinition:
new DemandSourceDefinition(
type: 'rental_item',
displayName: 'Bookings',
resolverClass: RentalItemDemandResolver::class,
colour: '#3B82F6',
icon: 'calendar',
);
type is the stable registry key. displayName, colour, and icon provide presentation metadata. resolverClass is a class-string for an App\Contracts\DemandResolverContract implementation.
That resolver contract requires:
sourceType(): stringsyncDemands(Model $source): voidreleaseDemands(Model $source): voidresolvePhase(Model $source): DemandPhasebuildMetadata(Model $source): array
syncDemands() must be idempotent: repeated calls for the same source converge on the same demand set instead of creating duplicates. Resolvers are intentionally stateful application services that create, update, or release demand records; they are not pure functions.
Identity, collisions, and failure behavior
register() stores a definition under its type. A later definition with the same type replaces the earlier definition; duplicate registration is not rejected.
Both get($type) and resolve($type) throw InvalidArgumentException with Unknown demand source: {type} when the type is missing. resolve() first calls get(), then asks Laravel's container to instantiate the declared resolver class.
The registry does not validate that a resolver's sourceType() matches its definition key at registration time. A registration is responsible for keeping those values aligned and for supplying a container-resolvable class that implements DemandResolverContract.
Current core registrations
The complete built-in set is:
| Type | Display name | Resolver | Colour | Icon |
|---|---|---|---|---|
rental_item |
Bookings | RentalItemDemandResolver |
#3B82F6 |
calendar |
flightcase |
Flightcases | FlightcaseDemandResolver |
#0EA5E9 |
cube |
virtual_stock_intake |
Virtual Stock | VirtualStockDemandResolver |
#8B5CF6 |
arrow-down-circle |
repair |
Repair | RepairDemandResolver |
#EF4444 |
shield-exclamation |
warehouse_transfer |
Warehouse Transfers | WarehouseTransferDemandResolver |
#F59E0B |
arrows-right-left |
There is no priority or sorting contract. all() returns the keyed definition map, while current write workflows resolve the exact type they own. Catalogue order does not determine demand precedence.
Runtime consumption
Current consumers use the registry in two forms:
- repair actions call
resolve('repair')and thensyncDemands() AvailabilityServicereads registered colours and display names for Gantt demand bars, guarding withhas()so unknown historical types can fall back safely
Other current core workflows for rental items, flightcases, virtual-stock intakes, and warehouse transfers resolve their concrete resolver classes directly. Their definitions are still part of this registry's complete source catalogue, but the current framework does not route every built-in write through resolve().
When resolve() is used, the flightcase constructs the resolver so its dependencies remain injectable and the registry stores definitions rather than resolver instances.
Worked example
The framework's repair actions use the current core definition like this:
app(DemandSourceRegistry::class)
->resolve('repair')
->syncDemands($repair);
The registry resolves RepairDemandResolver through the container. The resolver then synchronises the demand rows for that repair according to the idempotent resolver contract.