s_skaidocsTài liệu
Mô hình field nested-v1. Theme khai báo "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.

Key khác type. Ví dụ field key 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

LINGO
{% 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 typeGiá trị trong themeContent Type hiện tại
textstringBật
long_textstringBật
ckeditor_editorstring chứa HTMLBật
tiptap_editorJSON document (hợp đồng lưu trữ)Tắt; chỉ bật cho field taxonomy
numbernumberBật
booleanbooleanBật
datestring YYYY-MM-DDBật
datetimestring ngày và giờBật
selectstring hoặc string[] khi chọn nhiềuBật
statusMã trạng thái; không tự resolve objectKhông cho tạo custom field
relationUUID hoặc UUID[] theo hợp đồng lưu trữTắt
taxonomyobject[]; không chọn: []Bật
mediaUUID[]; không chọn: []Bật
emailstringBật
urlstringBật
jsonGiá 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>
Giới hạn hiện tại: rich_text xử lý chuỗi JSON Tiptap, nhưng escape chuỗi HTML thuần của CKEditor. Chưa coi rich_text(item.content) là cách render đầy đủ CKEditor HTML. Không dùng safe hoặc tắt autoescape để vượt chính sách runtime; phần render HTML cần helper được kiểm soát riêng.

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.

Field ảnh hệ thống của resource post đã đổi từ 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.