Download the PHP package middag-io/ui without Composer
On this page you can find all versions of the php package middag-io/ui. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Package ui
Short Description MIDDAG UI contract builders — transport-agnostic PageContract system for contract-driven rendering
License Apache-2.0
Homepage https://github.com/middag-io/middag-php-ui
Informations about the package ui
Transport-agnostic PHP contract builder system for MIDDAG's contract-driven UI. Produces PageContract (JSON) consumed by @middag-io/react via Inertia or any transport layer.
Zero external dependencies. PHP 8.2+ only.
Open source — Apache-2.0. The foundation layer of the MIDDAG framework stack (ui → framework → moodle / wordpress adapters; @middag-io/react on the client). Host-agnostic and transport-agnostic by design: this package knows nothing about Moodle, WordPress, or any transport — it only describes pages.
[!NOTE] Documentation — the MIDDAG open-source project lives at middag.dev; full docs will be at docs.middag.dev (coming soon).
Until the docs site is live, this README plus the
@apidocblocks insrc/are the reference.[!IMPORTANT]
0.6.0— concern-first layout (BREAKING). EveryMiddag\Ui\*FQN moved from the old stereotype-first layout (Contract/ Data/ Builder/ Enum/ Schema/) to concern-first: each concern (Page,Form,Table,Block,Region,Action, …) owns its interfaces + value objects + builders, with cross-cutting types underShared/{Data,Enum,Schema}. The wire JSON is unchanged — only PHP namespaces moved. Consumers on^0.5must update imports (e.g.Middag\Ui\Data\Fragment→Middag\Ui\Region\Fragment,Middag\Ui\Enum\FieldType→Middag\Ui\Shared\Enum\FieldType). Pre-1.0, no compatibility shims.
What It Does
Pages in the MIDDAG stack are described by a PageContract — a JSON document declaring shell, page metadata, layout template, regions, and blocks. This library provides the PHP side: builders that produce that document. The React side (@middag-io/react) consumes it to render the actual UI.
This means PHP never renders HTML for pages — it declares structure, and React renders.
Features
| Feature | What you get |
|---|---|
| Transport-agnostic contracts | Builders produce JsonSerializable → PageContract (JSON). No Inertia/transport dependency; works with any wire. |
| Zero dependencies | PHP ^8.2 only. Consumers inherit no transitive packages. |
| Host-agnostic | No Moodle / WordPress / mform / capability coupling. Host-specifics live in the adapter, never here. |
| 3 levels of composition | Convention (CrudBuilder) → convention + overrides → free composition (PageBuilder). |
| CRUD convention builder | index/create/edit/show pages from an entity class: i18n titles, filters, search, per-action capability gating. |
| Block types | denseTable, formPanel, detailPanel, metricCard, emptyState, statusStrip, activityTimeline, markdownPanel, cardGrid, actionGrid, linkList, chart, tabs — via BlockBuilder or the fluent RegionBuilder. |
| Typed value objects | final readonly + JsonSerializable. camelCase wire keys, omit-empty payloads, immutable witters. |
| Partial fragments | Server-push slices: Fragment, RegionUpdate, ActionResult (push + pull), ResourcePatch. |
| Navigation tree | 3-level NavigationNode (group / section / item), capability-filtered, drill-down + collapsible. |
| Form system | Contracts + VOs (FieldDefinition, Condition, FormState, Section, Group). Renderers live in adapters. |
| i18n intents | Translatable {key, domain, params}. The library never resolves translations — the client does. |
| Quality gates | Full test suite, high coverage, PHPStan L6, php-cs-fixer, Rector — enforced per commit. |
Three Levels of Composition
Level 1 — Convention (CrudBuilder)
Full CRUD pages from an entity class name:
Level 2 — Convention + Overrides (CrudBuilder)
Customize columns, actions, layout without leaving the convention:
The class basename is treated as a singular noun. The library never
fabricates a plural: the slug defaults to the singular, and for(class, slug:)
overrides it for a plural URL. Titles resolve via ->label() (explicit) →
<key>_plural i18n convention → singular fallback; create/edit verbs are
emitted as crud_create/crud_edit intents in a shared UI domain.
Level 3 — Free Composition (PageBuilder)
Full control over every block and region:
Inertia Props
When you need overlay or help panel metadata alongside the contract:
Partial Fragments (server push)
A page can be served two ways, and both share one envelope contract (ContractEnvelopeInterface, carrying the same version):
- Full page in PHP —
PageContractdeclares the whole page (shell, layout, regions, blocks). The default. - Page owned by the client, partial props from PHP — when React owns the layout, the server returns a
Fragment: one ready, self-describing slice of the contract (a block, a table, a region update, notifications) plus its routingkind. A fragment is a node of the contract with its own header, not a smaller page.
RegionUpdate modes: replace / append / prepend / remove (by key) / update (match by key).
Mutations return an ActionResult, which carries both update strategies — push and pull:
A ResourcePatch rides along on a Fragment or ActionResult to push a partial change to preferences / capabilities / feature flags without resending the whole PageResources.
Block Types
Static factories in BlockBuilder:::
| Method | React Component |
|---|---|
BlockBuilder::denseTable($key, $columns, $rows) |
Dense data grid |
BlockBuilder::formPanel($key, $action, $method, $schema, $values) |
Form panel |
BlockBuilder::detailPanel($key, $sections) |
Read-only detail view |
BlockBuilder::metricCard($key, $value, $label, $delta, $icon, $href) |
KPI card |
BlockBuilder::emptyState($key, $variant, $description, $cta) |
Empty state |
BlockBuilder::statusStrip($key, $items, $tone) |
Status bar |
BlockBuilder::activityTimeline($key, $groups, $hasMore, $loadMoreHref) |
Activity feed |
BlockBuilder::markdownPanel($key, $content, $maxHeight) |
Markdown body |
BlockBuilder::cardGrid($key, $columns, $rows, $variant) |
Card grid |
BlockBuilder::actionGrid($key, $items, $flash) |
Action card grid |
BlockBuilder::linkList($key, $items) |
Link list |
BlockBuilder::chart($key, $type, ChartSeries[], ...) |
Chart (ChartType enum) |
BlockBuilder::tabs($key, Tab[]) |
Tabs container |
Or via RegionBuilder fluent API inside a ->region() closure. Each method
mirrors its BlockBuilder factory one-to-one, so the same block type produces
an identical descriptor whichever entry point you use:
Navigation
NavigationNode is the @api value object for nav tree entries. Serializes to the shape consumed by SidebarNav in @middag-io/react:
Form System
This library provides contracts and value objects only — renderers live in middag-io/framework (Inertia) and the host adapters.
Contracts
| Interface | Role |
|---|---|
FormInterface |
schema() → hydrate() → validate() → validated() |
FieldInterface |
toDefinition(): FieldDefinition — produces the boundary object |
FormRendererInterface |
target(): RenderTarget + render(Form): RendererOutput |
LayoutElementInterface |
id() + children() — Section and Group implement this |
Value Objects
| Class | Notes |
|---|---|
FieldDefinition |
Immutable boundary object between DSL and renderers. No JsonSerializable — renderers map manually. |
Condition |
field + operator (ConditionOperator enum) + value + kind. Kinds: visible_when, hidden_when, required_when, disabled_when. |
FormState |
Immutable readonly VO. withValues() / withErrors() return a new instance. Carries values, errors, submitted. |
RendererOutput |
Static factories ::html() and ::props() for the two render targets. |
Layout Primitives
Section::label() takes a string|Translatable like every other label in the
contract; labelData() serializes it via Label (a {key, domain} payload for
an intent, a raw string for a literal).
Field Types (FieldType enum)
A closed backed enum of field types (TEXT, TEXTAREA, SELECT, DATE,
RICHTEXT, TIME, AUTOCOMPLETE, TAGS, …) — see src/Shared/Enum/FieldType.php
for the full catalogue.
Adding a type requires a new field class, matching renderer-side mappers, and the client component — most of which live downstream.
Table Builder
Fluent API for producing TableConfig consumed by dense table blocks:
Installation
Requires PHP ^8.2. Install via Composer:
Development
The dev toolchain may run a newer PHP than the supported floor (^8.2). Newer
syntax such as new X()->method() parses on 8.4 but is a fatal error on 8.2, so
composer check alone will not catch it. Two guards keep the floor honest:
composer lint:php82 runs php -l under a real PHP 8.2 interpreter over the
source, tests, and the .php-cs-fixer.dist.php / .php-rector.php configs; and
the CI Static analysis & style job runs entirely on PHP 8.2. PHPStan is
configured for the 8.2–8.4 range for version-sensitive type checks (it does
not catch syntax-level issues — that is the lint's job).
Git hooks configured automatically via post-install-cmd. commit-msg enforces Conventional Commits.
Releases managed by release-please.
License
Licensed under the Apache License, Version 2.0. See NOTICE.
Contributing
See CONTRIBUTING.md.