
Een schema is geen documentatie, het is de admin
20 Apr 2026
De meeste adminpanelen worden een tweede keer handmatig gebouwd, nadat het datamodel al bestaat: een lijstweergave hier, een formulier daar, validatie die tussen de API en de UI wordt gedupliceerd. Phlo CMS begint vanuit de positie dat dit verspilde moeite is. Je declareert eenmaal hoe je data eruitziet, en het CMS genereert de rest.
static schema => arr(
id: field(type: 'token', title: 'ID', list: false, record: false),
title: field(type: 'text', required: true, search: true),
body: field(type: 'wysiwyg', list: false),
cover: field(type: 'image', list: false),
author: field(type: 'parent', obj: 'author', required: true),
topics: field(type: 'many', obj: 'topic', table: 'article_topic'),
)
Van die ene declaratie produceert de CMS een zijbalkitem, een gepagineerde lijst met tellingen en zoekfunctie, een recordweergave met opgeloste relaties, aanmaak- en bewerkingsformulieren met de juiste invoer per veldtype, en een REST API (POST/PUT/PATCH/DELETE) die exact dezelfde validatie deelt als de formulieren. Niets aan de lijstweergave of de API is met de hand geschreven; beide lezen dezelfde schema.
Relaties zijn gewoon veldtypes
parent, child en many worden precies zoals elk ander veld gedeclareerd en worden teruggelezen als gewone eigenschapstoegang: $article->author, $author->articles, $article->topics. Er is geen join SQL in de applicatiecode en geen apart relatie-configuratiebestand; de relatie leeft op de kolom die het vertegenwoordigt, in het schema.
Betaal alleen voor wat je gebruikt
field(type: 'x', ...) is een verkorte notatie voor de fields/x resource. Het laden van een veldtype is gewoon het vermelden ervan in de resources van je app, dus een app die nooit een wysiwyg veld gebruikt, verzendt nooit de code van de wysiwyg editor. Een blog met vijf velden en een backoffice met vijftig velden halen precies zoveel binnen als ze daadwerkelijk gebruiken, niets gedeeld wordt meegetrokken.
Dashboardwidgets, ook gratis
Een model kan een widget op de CMS-home vastpinnen:
static objWidgets => [
'Articles' => obj(type: 'gauge', data: static::item(columns: 'COUNT(id)')),
]
De CMS-home stopt met een menu en wordt een live overzicht van de gegevens erachter, vanuit hetzelfde schema, opnieuw.
Begin met een echt schema, niet een blanco pagina
Het handmatig kiezen van een database, een tabelnaam en een lijst met velden is het grootste deel van het werk bij het schrijven van een model. De CMS builder verandert precies die keuze in een kant-en-klaar modelbestand, inclusief de relatieverbindingen tussen de modellen die je samen definieert, zodat het model op schijf overeenkomt met wat je net hebt beschreven in plaats van opnieuw uit het hoofd te worden getypt.