Fields
Field types define how content values are decoded and rendered. Declare them per view; access them in templates via the page object.
Two different things named similarly
Fields (site/fields/{name}/index.json) are named, reusable definitions: a label, a type, and default options. Field types (phlat/fieldtypes/{type}/ or site/fieldtypes/{type}/) are the PHP classes that give a type its decode/encode/validate behaviour. A field picks a type; a type has no name of its own.
// site/fields/body/index.json, a named, reusable field definition
{
"label": "Body",
"type": "markdown",
"options": { "required": false }
}
Accessing fields in templates
Record::__get (inherited by Page) resolves any undeclared property against data.json, returning a FieldValue:
{$page->title} {* FieldValue::__toString(), decoded string *}
{$page->body|render} {* rendered via the field type's render template *}
{if !$page->cover->empty()}
<img src="{$page->cover}">
{/if}
Use ->empty() to check whether a field has content before rendering, since a FieldValue object is always truthy in PHP regardless of its value.
Accessing fields in PHP
$field = $page->body; // FieldValue
$raw = $field->value; // raw stored value from data.json
$out = $field->decode(); // decoded value (HTML for markdown, etc.)
$empty = $field->empty(); // true if decoded value is empty
Declaring fields for a view
A view's own index.json can override a field's options per-view, under a fields key keyed by field name (not an array, and no type here, type comes from the field's own definition):
// site/views/post/index.json
{
"fields": {
"title": { "required": true, "max_length": 40 },
"body": {}
}
}
Built-in field types
| Type | Description |
|---|---|
text |
Plain string |
markdown |
Markdown source rendered to HTML via CommonMark |
code |
Source code, syntax-highlighted with Tempest Highlight |
image |
Image file reference, decodes to a PageImage with src(), srcset(), base64(), and transform helpers |
datetime |
ISO 8601 timestamp |
richtext |
HTML content, passed through unescaped |
Custom field types
Create site/fieldtypes/{type}/{type}.php with a class extending one of the four primitive bases, FieldTypeText, FieldTypeNumber, FieldTypeList, or FieldTypeObject, matching the JSON shape the type stores:
<?php
namespace Phlat;
class FieldTypeRating extends FieldTypeNumber
{
public function decode(): mixed
{
return max(0, min(5, (int) $this->value));
}
}
Add site/fieldtypes/{type}/index.json with a title/description for the admin UI. To control |render output, add a template at site/views/fields/{type}.latte (checked after site/views/fields/{view}/{name}.latte and site/views/fields/{name}.latte), it receives $field and $value (the decoded value). Admin field editing is a separate template, phlat/fieldtypes/{type}/edit.latte (or site/fieldtypes/{type}/edit.latte), unrelated to |render.
site/fieldtypes/
rating/
rating.php ← class FieldTypeRating extends FieldTypeNumber
index.json ← title/description for the admin UI
edit.latte ← optional, admin field-editing UI
Field types in site/fieldtypes/ override matching types in phlat/fieldtypes/.
<?php
namespace Phlat;
class FieldTypeRating extends FieldTypeNumber
{
public function decode(): mixed
{
// clamp stored value to 0-5
return max(0, min(5, (int) $this->value));
}
public function stars(): string
{
$filled = str_repeat('★', $this->decode());
$empty = str_repeat('☆', 5 - $this->decode());
return $filled . $empty;
}
}