Auto-gegenereerd via Reflection — blijft altijd in sync met src/.
DynamicCacheBuilder
Framework\Dynamic\Cache\DynamicCacheBuilderPure cache-bouwer voor `DynamicItems.metadata.cache.values.{lang}.*`.
Geen DB, geen state — alleen input → output. Zo kunnen verschillende
IO-paden (warmer, post-save-hook, easyhandling's table-class) dezelfde
regels delen voor:
1. welk veld wel/niet de cache in mag (skip-list + encrypted),
2. hoe taalvrijgestelde velden (`language=''`) gemerged worden met
taalspecifieke waarden — taalspecifiek wint.
Datatype-skiplist gemodelleerd naar
easyhandling.nl/.../Dynamic/Model/DynamicItemsTable.php::shouldCacheFieldValue().
3 public methods
static buildAll(array $fields, iterable $rows, array $languages): arrayBouw cache voor meerdere talen tegelijk uit één rij-set.
static buildForLanguage(array $fields, iterable $rows, string $language): arrayBouw `name → value` voor één taal. Taalspecifieke rows winnen van
`language=''` rows; non-cacheable velden worden weggelaten.
static shouldCache(\DynamicTypeField $field): boolMag deze veldwaarde in de cache landen?
DynamicCacheWarmer
Framework\Dynamic\DynamicCacheWarmerVult `metadata.cache.values.{lang}.{field}` in DynamicItems.
IO-laag rond {@see DynamicCacheBuilder}: leest uit DynamicItemsValues,
laat de bouwer de waarde-merge per taal doen, schrijft het resultaat
terug naar DynamicItems.metadata.
Talen kunnen expliciet meegegeven worden, of automatisch gedetecteerd
worden via een DISTINCT-query op DynamicItemsValues + de eigen
DynamicItems.language — gemodelleerd naar
easyhandling.nl/.../Dynamic/Model/DynamicItemsTable.php::resolveValuesMetadataCacheLanguages().
__construct(PDO $pdo, \DynamicStructure $structure)2 public methods
warmItem(int $itemId, ?array $languages = NULL): voidWarm één item op.
warmType(string|int $typeId, ?array $languages = NULL, int $batchSize = 200): intWarm alle items van een type op.
DynamicItem
Framework\Dynamic\DynamicItemTyped, read-only representatie van één DynamicItems-rij.
Veldwaarden (vanuit de JSON-cache) zitten in $values en zijn opvraagbaar
via get(). De meta-kolommen (id, typeId, order, ...) zijn publieke properties.
Gebruik:
$item->get('title'); // ?string — null als veld niet in cache
$item->get('media');
$item->active; // bool
__construct(int $id, int $typeId, string $typeName, int $order, ?int $parentId, bool $active, array $values)2 public methods
get(string $field): ?stringGeeft de gecachede veldwaarde, of null als het veld ontbreekt.
values(): arrayAlle gecachede veldwaarden als array.
DynamicQuery
Framework\Dynamic\DynamicQueryImmutable, fluent query builder voor DynamicItems.
Leest veldwaarden uitsluitend uit metadata→cache→values→{lang},
zodat DynamicItemsValues volledig buiten beeld blijft.
Gebruik:
$q = new DynamicQuery($pdo);
$items = $q->type(72)
->language('nl')
->onlyActive()
->fields('title', 'media', 'subtitle')
->get();
$first = $q->type(72)->language('nl')->fields('title')->first();
$total = $q->type(72)->onlyActive(false)->count();
Elke setter returnt een clone — de originele query blijft ongewijzigd.
Hierdoor kun je een basis-query hergebruiken:
$base = (new DynamicQuery($pdo))->language('nl')->onlyActive();
$nav = $base->type(72)->fields('title', 'link', 'label')->get();
$news = $base->type(18)->fields('title', 'intro')->limit(5)->get();
Active-logica (identiek aan DynamicItemsTable):
- metadata.active afwezig → actief (default)
- metadata.active = true/1 → actief
- metadata.active = false/0 → inactief
__construct(PDO $pdo)12 public methods
allCachedFields(): staticSelecteer alle gecachte velden als één JSON-blob en decodeer in PHP.
Handig als je niet weet welke velden een type heeft, of alles wilt zien.
Gebruik ->fields() voor gerichte selects (sneller, expliciet).
count(): intGeef het aantal rijen (negeert limit/offset).
fields(string ...$names = ?): staticWelke velden uit de cache te selecteren.
Alleen alphanumerieke namen + underscore zijn toegestaan.
Geef '*' als enkel argument om alle gecachte velden op te halen (JSON-blob → PHP decode).
Equivalent aan ->allCachedFields().
first(): ?\DynamicItemGeef de eerste rij, of null als er geen match is.
get(): arrayVoer de query uit en geef alle rijen terug.
language(string $iso): staticTaal voor de JSON-cache path ($.cache.values.{lang}.*).
Alleen lowercase letters, 2–5 tekens (bijv. 'nl', 'en', 'de').
limit(int $limit, int $offset = 0): staticonlyActive(bool $active = true): staticFilter op active-vlag in metadata root.
onlyActive(false) haalt alle items op, ongeacht status.
orderBy(string ...$columns = ?): staticKolomnamen worden letterlijk in de query gezet — vertrouw alleen
op waarden die vanuit code komen, nooit op user input.
parent(?int $parentId): staticFilter op parent_id.
parent(null) → WHERE parent_id IS NULL (root-items).
toSql(): arrayGeef de opgebouwde SQL en parameters terug — handig voor debugging.
type(string|int $typeId): staticFilter op type: geef een integer type_id óf een string type-naam.
Bij een string wordt een subquery gebruikt: type_id = (SELECT id FROM DynamicTypes WHERE name = ?).
DynamicQueryEav
Framework\Dynamic\DynamicQueryEavEAV-variant van DynamicQuery: leest veldwaarden direct uit DynamicItemsValues.
Bestaat naast {@see DynamicQuery} (die uit metadata.cache.values.* leest) zodat
we beide leespaden tegen elkaar kunnen benchen op echte MariaDB-data.
Drie strategieën, te kiezen via {@see EavStrategy}:
- Subselect : per gevraagd veld een gecorreleerde (SELECT value FROM
DynamicItemsValues WHERE …) — equivalent aan library/Db/Dynamic/Select.php
- LeftJoin : per veld een aparte LEFT JOIN met alias dv_<naam>
- Pivot : één LEFT JOIN op DynamicItemsValues + GROUP BY met
MAX(CASE WHEN v.field_id = N THEN v.value END) per veld
Strategy is constructor-argument: voor benchen instantieer je per strategie
een eigen DynamicQueryEav.
Bij `type(string $name)` wordt de naam direct via DynamicStructure naar een
integer type_id geresolved — anders kunnen we de field_ids niet ophalen die
de EAV-strategieën nodig hebben.
__construct(PDO $pdo, \DynamicStructure $structure, \EavStrategy $strategy = \Framework\Dynamic\EavStrategy::Subselect)12 public methods
count(): intfields(string ...$names = ?): staticWelke velden uit DynamicItemsValues te lezen.
Geef '*' als enkel argument om alle velden van het type op te halen
(resolved via DynamicStructure::fieldsForType()).
first(): ?\DynamicItemget(): arraygetStrategy(): \EavStrategylanguage(string $iso): staticlimit(int $limit, int $offset = 0): staticonlyActive(bool $active = true): staticorderBy(string ...$columns = ?): staticparent(?int $parentId): statictoSql(): arraytype(string|int $typeId): staticDynamicQueryInterface
Framework\Dynamic\DynamicQueryInterfaceGedeelde contract voor DynamicQuery-implementaties.
Bestaat in twee varianten:
- DynamicQuery — leest uit metadata.cache.values.{lang}.* (JSON)
- DynamicQueryEav — leest uit DynamicItemsValues (subselect / left-join / pivot)
Beide implementaties zijn fluent + immutable: elke setter returnt een clone.
11 public methods
count(): intfields(string ...$names = ?): staticfirst(): ?\DynamicItemget(): arraylanguage(string $iso): staticlimit(int $limit, int $offset = 0): staticonlyActive(bool $active = true): staticorderBy(string ...$columns = ?): staticparent(?int $parentId): statictoSql(): arraytype(string|int $typeId): staticDynamicStructure
Framework\Dynamic\DynamicStructureLichtgewicht registry voor DynamicTypes en hun velden.
Eén `load()` haalt zowel alle types als alle velden in twee queries op,
waarna alle lookups (id, naam, fields-per-type) zonder DB-hit werken.
Het PDO-object wordt na laden niet bewaard — de instance is daardoor
volledig serialiseerbaar en geschikt voor een file-cache.
Twee laad-modes:
$structure = DynamicStructure::load($pdo); // altijd vers
$structure = DynamicStructure::loadCached($pdo, 'site-easyhandling'); // cached
loadCached() schrijft serialize() naar
`{cacheDir}/{sanitized-key}.dat` (default cacheDir: data/cache/structures).
De cache-key is verplicht en uniek per bron — handig wanneer je met
meerdere websites/databases tegelijk werkt.
Invalideren:
DynamicStructure::invalidate('site-easyhandling');
11 public methods
all(): arrayfieldDetailsForType(int $typeId): arrayVolledige veld-info per type (incl. datatype + encrypted-flag).
Inclusief geërfde velden via metadata.extends.
fieldNamesForType(int $typeId): arrayfieldsForType(int $typeId): arraygetTypeById(int $id): ?\DynamicTypegetTypeByName(string $name): ?\DynamicTypehasType(string $name): boolidForName(string $name): intstatic invalidate(string $cacheKey, string $cacheDir = 'data/cache/structures'): voidVerwijder de cache-file voor een specifieke key.
static load(PDO $pdo, bool $enabledOnly = true): selfLaad alle (actieve) types + alle velden uit de database (twee queries).
static loadCached(PDO $pdo, string $cacheKey, ?int $ttl = NULL, string $cacheDir = 'data/cache/structures', bool $forceReload = false, bool $enabledOnly = true): selfLaad uit file-cache als beschikbaar; anders {@see load()} + cache schrijven.
DynamicType
Framework\Dynamic\DynamicTypeImmutable waarde-object dat één rij uit DynamicTypes vertegenwoordigt.
__construct(int $id, string $name, string $metadataJson = '{}')2 public methods
isEnabled(): boolIs dit type actief/enabled?
Absent of true/1 = enabled; false/0 = disabled.
title(): stringGeeft de weergavenaam uit metadata['title'], of de technische naam als fallback.
DynamicTypeField
Framework\Dynamic\DynamicTypeFieldImmutable rij uit DynamicTypesFields.
Voldoende velden om te beslissen of een waarde gecached mag worden:
datatype + encrypted bepalen samen of een veld in de
`metadata.cache.values.{lang}.*` mag landen — zie {@see Cache\DynamicCacheBuilder}.
Gemodelleerd naar de checks in:
easyhandling.nl/.../Dynamic/Model/DynamicItemsTable.php::shouldCacheFieldValue()
__construct(int $id, int $typeId, string $name, int $datatype, bool $encrypted)DynamicWriter
Framework\Dynamic\DynamicWriterSchrijfpad voor `DynamicItems` + `DynamicItemsValues`.
$writer = new DynamicWriter($pdo, $structure);
$id = $writer->create('contact', ['name' => 'Jan', 'email' => 'a@b']);
$writer->update(42, ['phone' => '06-1234']);
$writer->setActive(42, false);
$writer->delete(42);
// Voor 1-shot find tijdens een edit-flow:
$item = $writer->find(42); // ?DynamicItem (via DynamicQuery)
Auto-cache-warm na elke schrijfactie. Opt-out per call mogelijk via
`$writer->withoutAutoWarm()->update(...)` (handig bij batch-imports).
Validatie: weigert onbekende veldnamen — `fieldsForType()` van
{@see DynamicStructure} fungeert als whitelist (incl. geërfde velden).
__construct(PDO $pdo, \DynamicStructure $structure, ?\DynamicCacheWarmer $warmer = NULL)7 public methods
create(string|int $type, array $values, string $language = 'nl', ?int $parentId = NULL): intMaak een nieuw DynamicItem met values. Returneert de nieuwe id.
delete(int $itemId, bool $cascade = true): voidVerwijder item + bijbehorende DynamicItemsValues (en optioneel children).
find(int $itemId, string $language = 'nl'): ?\DynamicItemFind één DynamicItem op id. Returnt de gehydrateerde DynamicItem of null.
Gebruikt cache-pad (DynamicQuery) via een subquery op id zodat alle
fields uit metadata.cache komen.
setActive(int $itemId, bool $active): voidSchakel `metadata.active` om — geen value-mutaties.
transactional(callable $fn): ?mixedupdate(int $itemId, array $values, string $language = 'nl'): voidUpdate bestaande values van een item. Geeft alleen de gegeven velden door —
de rest blijft staan. Cache wordt automatisch opnieuw gewarmd.
withoutAutoWarm(): selfEén-shot setting voor de volgende call: skip cache-warm.
EavStrategy
Framework\Dynamic\EavStrategyWelke SQL-vorm DynamicQueryEav genereert om veldwaarden uit
DynamicItemsValues te lezen.
- Subselect : per veld een gecorreleerde subselect (zoals library/Db/Dynamic/Select.php).
Eenvoudig, maar de planner moet N subselects per rij plannen.
- LeftJoin : per veld een aparte LEFT JOIN op DynamicItemsValues met alias.
Vaak sneller bij veel velden, maar elke join is een extra index-lookup.
- Pivot : één LEFT JOIN op DynamicItemsValues + GROUP BY met MAX(CASE WHEN field_id=…).
Eén round-trip naar de waarde-tabel, beste plan bij veel velden.
3 public methods
static cases(): arraystatic from(string|int $value): staticstatic tryFrom(string|int $value): ?staticSubselect, LeftJoin, Pivot