# Fields {#fields}

::: info หน้านี้สอนอะไร
field ทุกประเภท ตัวเลือก หน้าตาในหน้า admin และวิธีจัดเก็บ

**ควรอ่านก่อน:** [การตั้งค่า](./configuration)
:::

ทุก field มี `name` และ `type` ตัวเลือกที่ใช้ร่วมกัน:

| ตัวเลือก | |
|---|---|
| `label` | string หรือ `{ en, th }` ค่าเริ่มต้นคือชื่อ field ที่แปลงให้อ่านง่าย |
| `required` | ต้องมีค่า (ข้ามการตรวจสอบขณะบันทึก [ฉบับร่าง (draft)](./drafts)) |
| `defaultValue` | ใช้เมื่อสร้างเอกสารโดยไม่มี field นี้ |
| `unique` | เอกสารสองรายการใช้ค่าซ้ำกันไม่ได้ (field ระดับบนสุด) |
| `index` | สร้าง index ในฐานข้อมูล |
| `validate` | `(value, { data, operation }) => true \| 'error message'` เป็น async ได้ |
| `access` | `{ read, update }` [สิทธิ์](./access-control#field-access)ระดับ field |
| `hidden` | ถูกจัดเก็บ แต่ API จะไม่ส่งคืนและไม่รับเป็น input |
| `localized` | เก็บค่าแยกตามภาษา ดู [หลายภาษา](./localization) |
| `position` | `'sidebar'`: แสดงในแถบข้างของหน้าแก้ไข (สำหรับ field ระดับบนสุด เช่น หมวดหมู่ แท็ก หรือวันที่) |
| `admin` | `{ component, after }`: [admin components](./plugins#admin-components) แทนช่องกรอก และต่อท้าย field |

## ประเภท {#types}

| ประเภท | ค่า | ตัวเลือก |
|---|---|---|
| `text` | `string` | `minLength`, `maxLength` |
| `textarea` | `string` | `minLength`, `maxLength` |
| `email` | `string` จัดเก็บเป็นตัวพิมพ์เล็ก | |
| `number` | `number` | `min`, `max` |
| `boolean` | `boolean` | |
| `date` | `string` แบบ ISO 8601 (รับ `Date` ได้) | |
| `select` | หนึ่งในตัวเลือก หรือ array เมื่อใช้ `hasMany` | `options`, `hasMany` |
| `slug` | `string` ที่ปลอดภัยสำหรับ URL และไม่ซ้ำภายใน collection | `from` |
| `json` | ค่า JSON ใดก็ได้ | |
| `richText` | เอกสาร JSON ของ Tiptap | ดู [Rich text](./rich-text) |
| `upload` | id ของเอกสาร `media` หรือ array เมื่อใช้ `hasMany` | `hasMany`, `mimeTypes` ดู [การอัปโหลด](./uploads#several-files-galleries) |
| `relationship` | id ของเอกสารใน collection อื่น | `to`, `hasMany`, `minRows`, `maxRows` |
| `array` | รายการของแถว แต่ละแถวมี `id` และ field ย่อย | `fields`, `minRows`, `maxRows` |
| `group` | object ซ้อน | `fields` |
| `blocks` | รายการของแถวที่มีหลายชนิด แต่ละแถวมี `id` และ `blockType` | `blocks`, `minRows`, `maxRows` |

### select {#select}

```ts
{ name: 'kind', type: 'select', options: ['news', { label: { en: 'Blog', th: 'บล็อก' }, value: 'blog' }] }
{ name: 'tags', type: 'select', options: ['vue', 'react'], hasMany: true }
```

### slug {#slug}

```ts
{ name: 'slug', type: 'slug', from: 'title' }
```

เติมค่าจาก `title` เมื่อว่าง ตัวอักษรของทุกภาษาจะถูกเก็บไว้ (`สวัสดี ชาวโลก` →
`สวัสดี-ชาวโลก`) ค่าที่ซ้ำจะได้ `-2`, `-3` การเปลี่ยน title ภายหลังจะไม่เปลี่ยน slug

`uniqueWithin: 'parent'` ทำให้ slug ห้ามซ้ำเฉพาะเอกสารที่มีค่าของอีก field เหมือนกัน เช่น หน้าที่อยู่ใต้หน้าแม่เดียวกัน
(แบบที่ plugin [หน้าย่อย](./nested-docs) ใช้)

### relationship {#relationship}

```ts
{ name: 'author', type: 'relationship', to: 'users' }
{ name: 'related', type: 'relationship', to: 'posts', hasMany: true }
```

id จะถูกตรวจสอบว่ามีอยู่จริงเมื่อบันทึก การอ่านจะดึงเอกสารที่เกี่ยวข้องมาแทนที่ตามจำนวนระดับ `depth`
(ค่าเริ่มต้น 1 สูงสุด 3) ที่ depth 0 จะได้เพียง id เอกสารที่ถูกลบหรืออ่านไม่ได้จะกลายเป็น `null` หรือ
ถูกตัดออกจากรายการ `hasMany`

`filterOptions` จำกัดว่า relationship ชี้ไปเอกสารใดได้บ้าง ทำงานฝั่ง server โดยได้รับ id ของเอกสาร
(`undefined` ตอนสร้าง) และผู้ใช้ แล้วคืน `where` ของ collection ปลายทาง หรือ `true` ถ้าเลือกได้ทุกเอกสาร
ช่องเลือกใน admin แสดงเฉพาะเอกสารเหล่านั้น และตรวจซ้ำตอนบันทึก

```ts
{
  name: 'manager',
  type: 'relationship',
  to: 'users',
  filterOptions: ({ id }) => ({ role: { equals: 'admin' } }),
}
```

### array และ group {#array-and-group}

```ts
{
  name: 'links',
  type: 'array',
  maxRows: 5,
  fields: [
    { name: 'label', type: 'text', required: true },
    { name: 'url', type: 'text' },
  ],
}
{ name: 'seo', type: 'group', fields: [{ name: 'title', type: 'text' }] }
```

การอัปเดต array จะแทนที่แถวทั้งหมด หากต้องการคงตัวตนของแถวไว้ ให้คง `id` ของแถวนั้นไว้

### blocks {#blocks}

แถวที่มีหลายชนิด สำหรับหน้าที่ผู้แก้ไขเนื้อหาจัดวางเอง:

```ts
{
  name: 'layout',
  type: 'blocks',
  blocks: [
    {
      slug: 'hero',
      labels: { singular: 'Hero' },
      fields: [
        { name: 'heading', type: 'text', required: true },
        { name: 'image', type: 'upload' },
      ],
    },
    { slug: 'text', fields: [{ name: 'body', type: 'richText' }] },
  ],
}
```

แต่ละแถวคือ `{ id, blockType, ...fields }` โดย `blockType` ใช้เลือก block แถวจะถูกตรวจสอบความถูกต้อง
relationship และ upload ภายในแถวจะถูก populate และ type ที่สร้างขึ้นจะเป็น union ของ
block แต่ละชนิด

blocks จัดเก็บเป็น JSON และ `where` ค้นข้างในได้: ชื่อ field หมายถึง field นั้นในบล็อกชนิดใดก็ตามที่มี
และเอกสารจะตรงเงื่อนไขเมื่อมีบล็อกใดบล็อกหนึ่งตรง

```ts
await cms.find('pages', { where: { 'layout.blockType': { equals: 'hero' } } })
await cms.find('pages', { where: { 'layout.heading': { like: 'sale' } } })
await cms.find('pages', { where: { 'layout.heading.en': { equals: 'Hello' } } }) // ระบุภาษา
await cms.find('pages', { where: { layout: { exists: false } } }) // ไม่มีบล็อกเลย
```

list ที่อยู่ในบล็อกก็ค้นได้แบบเดียวกัน โดยต้องมีบางแถวตรงเงื่อนไข

```ts
await cms.find('pages', { where: { 'layout.meta.tone': { equals: 'warm' } } }) // group
await cms.find('pages', { where: { 'layout.items.title': { equals: 'One' } } }) // array
await cms.find('pages', { where: { 'layout.tags': { in: ['sale'] } } }) // ค่า hasMany
await cms.find('pages', { where: { 'layout.content.blockType': { equals: 'quote' } } }) // blocks
```

`sort` ใช้ค่าแรกที่พบ โดยไล่ตามลำดับบล็อก (และแถวของ list ข้างในบล็อก): `sort: '-layout.columns'`,
`sort: 'layout.items.title'` การค้นแบบนี้ต้องอ่าน JSON ของทุกเอกสาร จึงเหมาะกับการกรองมากกว่ารายการใหญ่ที่ถูกเรียกบ่อย

## วิธีจัดเก็บ field {#how-fields-are-stored}

แต่ละ collection คือตารางหนึ่งตาราง field คือคอลัมน์ (field ใน group จะถูกแผ่ออกเป็นระดับเดียว: `seo.title` →
`seo_title`) ค่าของ array และ `hasMany` อยู่ในตารางลูก ส่วน blocks เป็นคอลัมน์ JSON field สองรายการที่จะแมปไปยังคอลัมน์
เดียวกันจะถูกรายงานเป็นข้อผิดพลาดของ config

## ขั้นต่อไป {#next-steps}

- [ชนิด field เพิ่มเติม](./field-types): ชนิดอื่นจากแพ็กเกจ เช่น `color` หรือเขียนเอง
- [การควบคุมสิทธิ์](./access-control): สิทธิ์ระดับ field
- [Rich text](./rich-text): แสดง rich text บนหน้าเว็บ
