API Reference

Auto-gegenereerd via Reflection — blijft altijd in sync met src/.

Filter: alleAppCacheCmsDbDebugDynamicEventsFilesFormHtmlHttpImageLogMediaSecuritySessionStdlibSupportView

DynamicCacheBuilder

Framework\Dynamic\Cache\DynamicCacheBuilder
final class

Pure 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): array

Bouw cache voor meerdere talen tegelijk uit één rij-set.

static buildForLanguage(array $fields, iterable $rows, string $language): array

Bouw `name → value` voor één taal. Taalspecifieke rows winnen van
`language=''` rows; non-cacheable velden worden weggelaten.

static shouldCache(\DynamicTypeField $field): bool

Mag deze veldwaarde in de cache landen?

DynamicCacheWarmer

Framework\Dynamic\DynamicCacheWarmer
final class

Vult `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): void

Warm één item op.

warmType(string|int $typeId, ?array $languages = NULL, int $batchSize = 200): int

Warm alle items van een type op.

DynamicItem

Framework\Dynamic\DynamicItem
final class

Typed, 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): ?string

Geeft de gecachede veldwaarde, of null als het veld ontbreekt.

values(): array

Alle gecachede veldwaarden als array.

DynamicQuery

Framework\Dynamic\DynamicQuery
final class

Immutable, 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(): static

Selecteer 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(): int

Geef het aantal rijen (negeert limit/offset).

fields(string ...$names = ?): static

Welke 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(): ?\DynamicItem

Geef de eerste rij, of null als er geen match is.

get(): array

Voer de query uit en geef alle rijen terug.

language(string $iso): static

Taal voor de JSON-cache path ($.cache.values.{lang}.*).
Alleen lowercase letters, 2–5 tekens (bijv. 'nl', 'en', 'de').

limit(int $limit, int $offset = 0): static
onlyActive(bool $active = true): static

Filter op active-vlag in metadata root.
onlyActive(false) haalt alle items op, ongeacht status.

orderBy(string ...$columns = ?): static

Kolomnamen worden letterlijk in de query gezet — vertrouw alleen
op waarden die vanuit code komen, nooit op user input.

parent(?int $parentId): static

Filter op parent_id.
parent(null) → WHERE parent_id IS NULL (root-items).

toSql(): array

Geef de opgebouwde SQL en parameters terug — handig voor debugging.

type(string|int $typeId): static

Filter 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\DynamicQueryEav
final class

EAV-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(): int
fields(string ...$names = ?): static

Welke velden uit DynamicItemsValues te lezen.
Geef '*' als enkel argument om alle velden van het type op te halen
(resolved via DynamicStructure::fieldsForType()).

first(): ?\DynamicItem
get(): array
getStrategy(): \EavStrategy
language(string $iso): static
limit(int $limit, int $offset = 0): static
onlyActive(bool $active = true): static
orderBy(string ...$columns = ?): static
parent(?int $parentId): static
toSql(): array
type(string|int $typeId): static

DynamicQueryInterface

Framework\Dynamic\DynamicQueryInterface
interface

Gedeelde 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(): int
fields(string ...$names = ?): static
first(): ?\DynamicItem
get(): array
language(string $iso): static
limit(int $limit, int $offset = 0): static
onlyActive(bool $active = true): static
orderBy(string ...$columns = ?): static
parent(?int $parentId): static
toSql(): array
type(string|int $typeId): static

DynamicStructure

Framework\Dynamic\DynamicStructure
final class

Lichtgewicht 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(): array
fieldDetailsForType(int $typeId): array

Volledige veld-info per type (incl. datatype + encrypted-flag).
Inclusief geërfde velden via metadata.extends.

fieldNamesForType(int $typeId): array
fieldsForType(int $typeId): array
getTypeById(int $id): ?\DynamicType
getTypeByName(string $name): ?\DynamicType
hasType(string $name): bool
idForName(string $name): int
static invalidate(string $cacheKey, string $cacheDir = 'data/cache/structures'): void

Verwijder de cache-file voor een specifieke key.

static load(PDO $pdo, bool $enabledOnly = true): self

Laad 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): self

Laad uit file-cache als beschikbaar; anders {@see load()} + cache schrijven.

DynamicType

Framework\Dynamic\DynamicType
final class

Immutable waarde-object dat één rij uit DynamicTypes vertegenwoordigt.

__construct(int $id, string $name, string $metadataJson = '{}')
2 public methods
isEnabled(): bool

Is dit type actief/enabled?
Absent of true/1 = enabled; false/0 = disabled.

title(): string

Geeft de weergavenaam uit metadata['title'], of de technische naam als fallback.

DynamicTypeField

Framework\Dynamic\DynamicTypeField
final class

Immutable 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\DynamicWriter
final class

Schrijfpad 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): int

Maak een nieuw DynamicItem met values. Returneert de nieuwe id.

delete(int $itemId, bool $cascade = true): void

Verwijder item + bijbehorende DynamicItemsValues (en optioneel children).

find(int $itemId, string $language = 'nl'): ?\DynamicItem

Find éé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): void

Schakel `metadata.active` om — geen value-mutaties.

transactional(callable $fn): ?mixed
update(int $itemId, array $values, string $language = 'nl'): void

Update bestaande values van een item. Geeft alleen de gegeven velden door —
de rest blijft staan. Cache wordt automatisch opnieuw gewarmd.

withoutAutoWarm(): self

Eén-shot setting voor de volgende call: skip cache-warm.

EavStrategy

Framework\Dynamic\EavStrategy
enum

Welke 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(): array
static from(string|int $value): static
static tryFrom(string|int $value): ?static
Cases: Subselect, LeftJoin, Pivot