"field_contract": "nested-v1" trong config/theme.json. Field hệ thống giữ ở cấp gốc (item.title, term.name); field tùy chỉnh đọc qua item.fields.{key}, term.fields.{key}. Các field hệ thống như categories vẫn ở cấp gốc dù lưu dạng quan hệ. Section settings, block settings và configs không đổi. Bản release chưa chuyển đổi vẫn dùng mô hình cũ; thay đổi draft chỉ lên runtime sau khi publish.Field types
Tham chiếu các kiểu field: dữ liệu trả về cho theme, cách đọc và ví dụ Lingo. Áp dụng cho field của Content Type qua entries, model catalog, products và field của taxonomy qua terms; không phải danh sách kiểu của section schema.
thumbs có type media: đọc bằng item.fields.thumbs, không phải item.media. Các key trong ví dụ cần được tạo trong Content Type của bạn. themes cũng là key ví dụ, không phải resource mặc định.Lấy bản ghi trước khi đọc field
{% set item = entries.find(type="themes", slug=slug) %}
{% if item %}
<h1>{{ item.title }}</h1>
{% endif %}entries.find/get/paginate chỉ trả bản ghi được phép đọc trong runtime. Field bị tắt hoặc type không được phép với owner bị ẩn, không đồng nghĩa với giá trị rỗng. Với field thông thường, giá trị thiếu có thể là null, chuỗi rỗng hoặc không có key; engine không tự áp dụng giá trị mặc định khi đọc. Không dùng kiểm tra truthy để phân biệt số 0 hoặc boolean false với dữ liệu thiếu.
Tra cứu nhanh — toàn bộ 16 kiểu đã khai báo
| Field type | Giá trị trong theme | Content Type hiện tại |
|---|---|---|
| text | string | Bật |
| long_text | string | Bật |
| ckeditor_editor | string chứa HTML | Bật |
| tiptap_editor | JSON document (hợp đồng lưu trữ) | Tắt; chỉ bật cho field taxonomy |
| number | number | Bật |
| boolean | boolean | Bật |
| date | string YYYY-MM-DD | Bật |
| datetime | string ngày và giờ | Bật |
| select | string hoặc string[] khi chọn nhiều | Bật |
| status | Mã trạng thái; không tự resolve object | Không cho tạo custom field |
| relation | UUID hoặc UUID[] theo hợp đồng lưu trữ | Tắt |
| taxonomy | object[]; không chọn: [] | Bật |
| media | UUID[]; không chọn: [] | Bật |
| string | Bật | |
| url | string | Bật |
| json | Giá trị JSON (hợp đồng khai báo) | Tắt toàn hệ thống |
text — Văn bản một dòng
Dùng cho tên, tiêu đề, tác giả. Ví dụ trả về "Skai Team". Xuất trực tiếp; autoescape bảo vệ HTML.
<span>{{ item.fields.author }}</span>
long_text — Văn bản nhiều dòng
Trả chuỗi, ví dụ "Dòng một\nDòng hai", không phải HTML. Có thể dùng CSS giữ xuống dòng.
<div style="white-space:pre-line">{{ item.excerpt }}</div>
ckeditor_editor — Nội dung HTML
Trả chuỗi HTML, ví dụ "<p>Giới thiệu</p>". Xuất trực tiếp sẽ escape markup. Nếu chỉ cần văn bản tóm tắt, dùng striptags:
<p>{{ item.content | striptags }}</p>
tiptap_editor — Tài liệu rich text JSON
Chưa bật cho Content Type. Đang bật ở owner taxonomy; không suy ra entries có thể trả field này. Dữ liệu editor dạng {"type":"doc","content":[...]}. Helper rich_text hiện nhận chuỗi JSON Tiptap; không tự JSON-encode object/map.
{{ rich_text(document_json_string) }}
document_json_string là biến chuỗi JSON được cung cấp, không phải object toàn cục có sẵn. terms.get trả field tùy chỉnh trong term.fields theo kiểu dữ liệu đã lưu. Không truyền object đã decode trực tiếp cho rich_text hoặc tự bật type chưa được hỗ trợ.
number — Số nguyên hoặc thập phân
Trả số JSON, ví dụ 999000 hoặc 4.5; không phải chuỗi và không tự gắn đơn vị tiền. Số 0 là giá trị hợp lệ.
<span>{{ item.fields.price }} ₫</span>
boolean — Đúng / sai
Trả true hoặc false, không phải chuỗi "true"/"false". Dùng điều kiện:
{% if item.fields.install %}
Cài đặt tự động
{% else %}
Cài đặt thủ công
{% endif %}
date — Ngày
Custom field trả chuỗi như "2026-09-28". Có thể xuất bằng thẻ time. Filter date hiện yêu cầu time.Time, không nhận trực tiếp chuỗi custom field.
{% if item.fields.event_date %}
<time datetime="{{ item.fields.event_date }}">{{ item.fields.event_date }}</time>
{% endif %}
datetime — Ngày và giờ
Custom field trả chuỗi, ví dụ "2026-09-28T09:30"; một số luồng nhận RFC3339 có múi giờ. Không tự quy đổi múi giờ hoặc chuyển thành time.Time khi đọc.
<time datetime="{{ item.fields.starts_at }}">{{ item.fields.starts_at }}</time>
Khác với custom date/datetime, published_at của post được adapter chuyển thành time.Time khi có giá trị, nên có thể dùng item.published_at | date: "d/m/Y". Hướng dẫn date.
select — Lựa chọn cấu hình sẵn
Chọn một trả mã string như "premium"; chọn nhiều trả mảng mã như ["red","blue"]. Không trả label hoặc object option. Chưa chọn có thể là null/chuỗi rỗng; với nhiều lựa chọn có thể là [] tùy luồng lưu. Theme không tự chuyển dữ liệu cũ khi đổi cờ multiple.
{% if item.fields.plan == "premium" %}Gói cao cấp{% endif %}
{% for color in item.fields.colors %}
<span>{{ color }}</span>
{% endfor %}
status — Trạng thái
Không được tạo như custom field trong Content Type hiện tại. Trạng thái hệ thống item.status là mã string, ví dụ "published"; không phải object có .name. Entries đang giới hạn bản ghi published, nên không dùng entries để duyệt các bản ghi draft.
{{ item.status }}
relation — Liên kết bản ghi
Đang tắt cho Content Type. Hợp đồng lưu trữ là UUID đơn hoặc mảng UUID theo multiple, không có bước tự resolve thành object như taxonomy. Không dựa vào item.fields.related.title.
Nếu có UUID từ một nguồn dữ liệu được phép, tra bản ghi tường minh bằng entries.find; đây không phải cam kết field relation hiện đã được bật.
{% set related = entries.find(type="posts", id=record_id) %}
{% if related %}{{ related.title }}{% endif %}
taxonomy — Danh mục
Luôn trả mảng object, dù chọn một hay nhiều. Không chọn, chưa liên kết taxonomy hoặc không có term hợp lệ trả []. Không trả UUID đơn.
[{"id":"uuid-term","name":"Doanh nghiệp","slug":"doanh-nghiep", ...}]
Mỗi object có id, name, slug, description, taxonomy_id, taxonomy, parent_id, position, locale, is_fallback. Tên/slug theo locale hiện tại và fallback của theme; chỉ resolve term hợp lệ, đang hoạt động trong taxonomy được cấu hình và đúng phạm vi truy cập.
{% for category in item.categories %}
<span>{{ category.name }}</span>
{% endfor %}
{% set category = item.categories | first %}
{% if category %}{{ category.name }}{% endif %}
Lấy ID đầu tiên: item.categories.0.id. Không cần gọi terms.get chỉ để lấy tên của từng UUID nữa. Tham chiếu terms.
media — Ảnh và file
Luôn trả mảng UUID, gồm cả images. Chọn một: ["uuid-file"]; chọn nhiều: ["uuid-1","uuid-2"]; không chọn: []. Không tự tải metadata, URL hoặc ảnh gốc.
featured_image sang images. Cột lưu là mảng JSONB, dữ liệu cũ được giữ nguyên. Tùy chọn Nhiều giá trị có thể bật/tắt, mặc định không check. Ảnh đầu tiên là ảnh đại diện; theme cũ cần đổi sang item.images. Product vẫn dùng featured_media.{% if item.images %}
<img src="{{ item.images | first | image_url: width: 450 }}" alt="{{ item.title }}">
{% endif %}
{% for file_id in item.fields.thumbs %}
<img src="{{ file_id | image_url: width: 192, height: 160, crop: 'top' }}" loading="lazy" alt="">
{% endfor %}
Có thể dùng item.fields.thumbs.0 thay cho first. Không dùng split cho mảng. first trên mảng rỗng trả chuỗi rỗng. image_url chỉ nhận UUID ảnh public hợp lệ, không dùng cho ZIP/PDF; UUID file không đồng nghĩa URL tải công khai.
Cấu hình media_type: "image" hoặc "all", cùng extensions: ["png","webp"] hay ["pdf","zip"]. multiple chỉ giới hạn lựa chọn khi lưu, không đổi kiểu kết quả theme. Dữ liệu UUID đơn/chuỗi xuống dòng cũ vẫn được chuẩn hóa khi đọc.
first và cấu hình media · image_url, resize và crop
email — Địa chỉ email
Trả string như "[email protected]". Không có object liên hệ đi kèm.
{% if item.fields.contact_email %}
<a href="mailto:{{ item.fields.contact_email }}">{{ item.fields.contact_email }}</a>
{% endif %}
url — Đường dẫn web
Trả string URL như "https://example.com", không phải object link. Kiểm tra rỗng trước khi xuất nút.
{% if item.fields.preview_url %}
<a href="{{ item.fields.preview_url }}" target="_blank" rel="noopener noreferrer">Xem thực tế</a>
{% endif %}
json — Dữ liệu có cấu trúc
Đang tắt toàn hệ thống, không có hợp đồng field đang hoạt động trong entries. Cấu hình khai báo kiểu JSON có thể chứa object, array, string, number, boolean hoặc null, nhưng không nên tạo field hoặc dựa vào giá trị cũ của type bị tắt.
Với một object được cung cấp bởi nguồn khác, cú pháp đọc thuộc tính vẫn là data.label, phần tử mảng là data.items.0; đây chỉ là cú pháp Lingo, không tự bật JSON field.
Cờ field và những điểm cần nhớ
- required: kiểm tra lúc ghi, không phải bảo đảm mọi bản ghi cũ luôn có giá trị.
- multiple: select thay đổi scalar/mảng; media và taxonomy luôn là mảng.
- translatable: chọn giá trị theo locale hiện tại, không trả object gom mọi ngôn ngữ. Không giả định mọi custom field đều tự fallback.
- unique: ràng buộc lúc lưu, không thay đổi kiểu trả về.
- inactive: field bị ẩn khỏi dữ liệu runtime; dữ liệu lưu không bị xóa.
- Field hệ thống: title, slug, excerpt, content, images, categories có quy tắc riêng theo resource; key không quyết định type của mọi custom field.
Danh sách phản ánh cấu hình hiện tại trong field.go, field_owners.go và adapter theme. Kiểu bị tắt được ghi ở đây để phân biệt hỗ trợ thực tế với khai báo, không phải đề nghị bật tính năng.