craft-content-modeling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Craft CMS 5 — Content Modeling

Craft CMS 5 — 内容建模

How to structure content in Craft CMS 5. Sections, entry types, fields, Matrix, relations, asset management, and strategic patterns for real projects.
This skill covers content architecture — what goes in the CP, how it's organized, and how templates access it. For extending Craft with PHP (plugins, modules, custom element types), see the
craftcms
skill. For Twig template patterns, see
craft-site
and
craft-twig-guidelines
.
如何在 Craft CMS 5 中搭建内容结构。涵盖 Sections、entry types、fields、Matrix、关联关系、资源管理,以及适用于实际项目的策略模式。
本技能覆盖内容架构相关内容——控制面板(CP)中包含什么、如何组织,以及模板如何访问这些内容。如果要使用 PHP 扩展 Craft(插件、模块、自定义元素类型),请参考
craftcms
技能。如果需要 Twig 模板相关的模式,请参考
craft-site
craft-twig-guidelines

Documentation

文档

The Craft 5 Mental Model

Craft 5 核心设计理念

Everything is an entry. Entry types are global (shared across sections and Matrix fields). Fields come from a global pool. Categories, tags, and globals are being phased out — use entries instead.
Three decisions define your content architecture:
  1. Which section type organizes the content (Single, Channel, Structure)
  2. Which entry types define its shape (global, reusable across contexts)
  3. Which relation strategy connects content together (Entries fields, Matrix, or both)
所有内容都是条目。 Entry type 是全局的(在 sections 和 Matrix 字段之间共享)。字段来自全局字段池。分类、标签和全局变量正在逐步淘汰——请改用条目。
三个决策决定了你的内容架构:
  1. 使用哪种 section 类型来组织内容(Single、Channel、Structure)
  2. 使用哪些 entry type来定义内容结构(全局、可跨场景复用)
  3. 使用哪种关联策略来连接内容(条目字段、Matrix,或两者结合)

Section Type Decision

Section 类型选择指南

NeedSection TypeURI Example
One-off page (homepage, about, contact)Single
__home__
,
about
Site-wide settings (footer, header config)Single (no URI,
preloadSingles
)
Flat collection (blog, news, events)Channel
blog/{slug}
Hierarchical pages (docs, services)Structure
{parent.uri}/{slug}
Taxonomy (topics, categories)Structure (replaces categories)
topics/{slug}
Flat tagsChannel (replaces tags)
需求Section 类型URI 示例
单页页面(首页、关于页、联系页)Single
__home__
,
about
全站设置(页脚、头部配置)Single(无URI,开启
preloadSingles
扁平集合(博客、新闻、活动)Channel
blog/{slug}
层级页面(文档、服务)Structure
{parent.uri}/{slug}
分类体系(主题、分类)Structure(替代原有分类功能)
topics/{slug}
扁平标签Channel(替代原有标签功能)

Singles replace globals

Singles 替代全局变量

Set
preloadSingles => true
in
config/general.php
to access singles as global Twig variables by handle — identical to the old globals behavior but with drafts, revisions, live preview, and scheduling.
twig
{# With preloadSingles enabled #}
{{ siteSettings.footerText }}
{{ siteSettings.socialLinks.all() }}
Caveat: Singles always propagate to all sites. This is hard-coded.
config/general.php
中设置
preloadSingles => true
,即可通过 handle 将 singles 作为全局 Twig 变量访问——和旧的全局变量行为完全一致,但额外支持草稿、修订版、实时预览和定时发布功能。
twig
{# 开启 preloadSingles 后即可使用 #}
{{ siteSettings.footerText }}
{{ siteSettings.socialLinks.all() }}
注意: Singles 会默认传播到所有站点,这是硬编码的逻辑。

Structure queries for navigation

用于导航的 Structure 查询

twig
{% set topLevel = craft.entries.section('pages').level(1).all() %}
{% set children = craft.entries.descendantOf(entry).descendantDist(1).all() %}
{% set breadcrumbs = craft.entries.ancestorOf(entry).all() %}
{% set siblings = craft.entries.siblingOf(entry).all() %}
twig
{% set topLevel = craft.entries.section('pages').level(1).all() %}
{% set children = craft.entries.descendantOf(entry).descendantDist(1).all() %}
{% set breadcrumbs = craft.entries.ancestorOf(entry).all() %}
{% set siblings = craft.entries.siblingOf(entry).all() %}

Entry Types in Craft 5

Entry Types in Craft 5

Entry types are defined globally (Settings → Entry Types), then attached to sections and Matrix fields. One entry type can serve multiple contexts.
Key implications:
  • Changing an entry type's field layout affects every section and Matrix field using it
  • Fields come from the global pool — same field definition reused everywhere
  • Local name/handle overrides per context available (5.6.0+)
  • The global pool demands careful field naming — use specific handles
Entry type 是全局定义的(设置 → Entry Types),之后可以关联到 sections 和 Matrix 字段。同一个 entry type 可以在多个场景下使用。
核心影响:
  • 修改一个 entry type 的字段布局会影响所有使用它的 section 和 Matrix 字段
  • 字段来自全局字段池——同一个字段定义可以在所有地方复用
  • 支持在不同场景下覆盖名称/handle(5.6.0及以上版本支持)
  • 全局字段池要求字段命名要谨慎——使用明确的 handle

Reserved handles (will collide with native attributes)

保留 handle(会和原生属性冲突)

title
,
slug
,
id
,
uid
,
dateCreated
,
dateUpdated
,
status
,
url
,
uri
,
enabled
,
archived
,
siteId
,
level
,
lft
,
rgt
,
root
,
postDate
,
expiryDate
title
,
slug
,
id
,
uid
,
dateCreated
,
dateUpdated
,
status
,
url
,
uri
,
enabled
,
archived
,
siteId
,
level
,
lft
,
rgt
,
root
,
postDate
,
expiryDate

Common Pitfalls

常见误区

  • Over-using Matrix — if content needs its own URL, independent querying, or permissions, it should be a separate section with an Entries relation field, not a Matrix block.
  • Vague field handles
    image
    ,
    text
    ,
    link
    collide fast in the global pool. Use
    blogFeaturedImage
    ,
    serviceDescription
    ,
    ctaLink
    .
  • Not planning multi-site from the start — propagation method, field translation methods, and site settings must be configured before content exists. Changing propagation later resaves all entries.
  • Using categories/tags in new projects — they're deprecated. Use Structure sections (hierarchical taxonomy) and Channel sections (flat taxonomy) with Entries fields.
  • Forgetting
    preloadSingles
    — without it, singles aren't available as global variables and you need explicit queries.
  • Matrix for everything — 15+ entry types in one Matrix field is a red flag. Deeply nested Matrix hits
    max_input_vars
    limits and degrades CP performance.
  • Not using
    .eagerly()
    — every relational field access inside a loop should use
    .eagerly()
    to prevent N+1 queries.
  • Editing project config YAML manually — let Craft manage
    config/project/
    . Use
    php craft project-config/rebuild
    to regenerate from DB if needed.
  • Using database IDs in URI formats — IDs differ across environments. Use
    {slug}
    ,
    {canonicalUid}
    , or custom fields.
  • Not setting
    allowAdminChanges => false
    in production
    — without this, production schema changes won't sync back to dev.
  • 过度使用 Matrix——如果内容需要独立URL、独立查询或者权限控制,它应该是一个独立的 section,通过条目关联字段关联,而不是放在 Matrix 块中。
  • 字段 handle 命名模糊——
    image
    text
    link
    这类命名在全局字段池中很容易冲突。请使用
    blogFeaturedImage
    serviceDescription
    ctaLink
    这类命名。
  • 一开始没有规划多站点功能——传播方式、字段翻译方法、站点设置必须在内容录入前配置完成。之后修改传播方式会导致所有条目重新保存。
  • 在新项目中使用分类/标签——这两个功能已经废弃。请使用 Structure section(层级分类)和 Channel section(扁平分类)搭配条目字段。
  • 忘记开启
    preloadSingles
    ——不开启的话,singles 不能作为全局变量使用,你需要显式写查询语句获取。
  • 所有内容都用 Matrix——一个 Matrix 字段里有15个以上的 entry type 是危险信号。深度嵌套的 Matrix 会触发
    max_input_vars
    限制,降低控制面板的性能。
  • 不使用
    .eagerly()
    ——循环中每次访问关联字段都应该使用
    .eagerly()
    来避免N+1查询问题。
  • 手动编辑项目配置YAML文件——让 Craft 自行管理
    config/project/
    目录。如有需要,可以使用
    php craft project-config/rebuild
    从数据库重新生成配置。
  • 在URI格式中使用数据库ID——不同环境的ID不一致。请使用
    {slug}
    {canonicalUid}
    或者自定义字段。
  • 生产环境没有设置
    allowAdminChanges => false
    ——不设置的话,生产环境的 schema 变更无法同步回开发环境。

Reference Files

参考文件

Read the relevant reference file(s) for your task.
Task examples:
  • "Plan a blog content architecture" → read
    content-patterns.md
  • "Which field type should I use for X?" → read
    field-types.md
  • "Set up relatedTo queries" → read
    relations-and-eager-loading.md
  • "Configure Matrix with nested entries" → read
    field-types.md
    (Matrix section)
  • "Plan a multi-site content model" → read
    content-patterns.md
    + propagation in SKILL.md
  • "Understand project config workflow" → this SKILL.md covers the essentials
ReferenceScope
references/field-types.md
All 26 built-in field types: settings, Twig access patterns, query syntax, gotchas. Matrix configuration, view modes, nesting.
references/relations-and-eager-loading.md
relatedTo() shapes (4 forms), .with() eager loading, .eagerly() lazy eager loading, nested eager loading, native eager-loadable attributes.
references/content-patterns.md
Strategic patterns for blog, portfolio, multi-site corporate, e-commerce. Section/field/relation architecture per pattern. Categories-to-entries migration.
根据你的任务阅读对应的参考文件。
任务示例:
  • "规划博客内容架构" → 阅读
    content-patterns.md
  • "X场景应该用哪种字段类型?" → 阅读
    field-types.md
  • "设置 relatedTo 查询" → 阅读
    relations-and-eager-loading.md
  • "配置带嵌套条目的 Matrix" → 阅读
    field-types.md
    (Matrix部分)
  • "规划多站点内容模型" → 阅读
    content-patterns.md
    + SKILL.md中的传播相关内容
  • "了解项目配置工作流" → 本SKILL.md已经覆盖了核心要点
参考文件覆盖范围
references/field-types.md
全部26种内置字段类型:设置、Twig访问模式、查询语法、注意事项。Matrix配置、视图模式、嵌套。
references/relations-and-eager-loading.md
relatedTo() 的4种写法、.with()预加载、.eagerly()懒预加载、嵌套预加载、原生可预加载属性。
references/content-patterns.md
博客、作品集、多站点企业站、电商的策略模式。每种模式对应的section/field/关联架构。分类转条目的迁移方法。

Propagation Methods (Multi-Site)

传播方法(多站点)

Available for channels and structures. Singles always propagate to all sites.
MethodBehavior
Only save to site created inEntries exist in one site only
Same site groupEntries propagate within the same site group
Same languageEntries propagate to sites sharing the same language
All enabled sitesEntries exist in all enabled sites (default)
Let each entry choosePer-entry control via Status sidebar
Matrix fields have their own propagation method, independent of the section's.
适用于 channel 和 structure。Singles 总是会传播到所有站点。
方法行为
仅保存到创建时的站点条目仅存在于单个站点
同一站点组条目在同一个站点组内传播
同一语言条目传播到使用相同语言的站点
所有已启用站点条目存在于所有已启用站点(默认)
每个条目自行选择通过状态侧边栏为每个条目单独设置
Matrix 字段有自己独立的传播方法,和所属 section 的传播方法无关。

Field Translation Methods

字段翻译方法

Per-field setting controlling how values behave across sites:
MethodBehavior
Not translatableSame value across all sites
Per siteIndependent value per site
Per site groupShared within site group, independent across groups
Per languageShared across sites with same language
CustomUser-defined grouping key
Configure translation methods before populating content.
每个字段的设置,控制字段值在不同站点间的行为:
方法行为
不可翻译所有站点使用相同的值
按站点区分每个站点的值独立
按站点组区分站点组内共享,不同组之间独立
按语言区分使用相同语言的站点共享值
自定义用户自定义分组key
请在录入内容之前配置翻译方法。

Project Config Essentials

项目配置核心要点

All schema changes (sections, entry types, fields, volumes, transforms, sites, plugins, permissions) are stored as YAML in
config/project/
.
所有 schema 变更(sections、entry types、fields、资源卷、转换规则、站点、插件、权限)都以 YAML 格式存储在
config/project/
目录下。

Workflow

工作流

  1. Make CP changes in development environment
  2. YAML auto-updates in
    config/project/
  3. Commit to Git
  4. Deploy to staging/production
  5. Run
    ddev craft up
    (applies migrations + project config)
  1. 开发环境的控制面板中做修改
  2. config/project/
    下的YAML文件会自动更新
  3. 提交到Git
  4. 部署到预发布/生产环境
  5. 运行
    ddev craft up
    (应用迁移 + 项目配置)

Rules

规则

  • Never manually edit YAML — let Craft manage it
  • Always set
    allowAdminChanges => false
    in production
  • Use UIDs (not IDs) — they're stable across environments
  • After resolving Git merge conflicts in YAML:
    ddev craft project-config/touch
    then
    ddev craft project-config/apply
  • Use
    $ENV_VAR
    syntax in YAML for environment-specific values
  • 永远不要手动编辑YAML——让Craft自行管理
  • 生产环境务必设置
    allowAdminChanges => false
  • 使用UID(而不是ID)——它们在不同环境之间是稳定的
  • 解决YAML的Git合并冲突后:执行
    ddev craft project-config/touch
    然后执行
    ddev craft project-config/apply
  • 在YAML中使用
    $ENV_VAR
    语法来定义环境特定的值

Asset Volumes and Transforms

资源卷和图片转换

Volumes define content organization. Filesystems define storage (local, S3, Google Cloud, Azure). Multiple volumes can share one filesystem.
资源卷定义内容的组织方式。文件系统定义存储位置(本地、S3、Google Cloud、Azure)。多个资源卷可以共享同一个文件系统。

Image Transforms

图片转换

twig
{# Named transform (defined in Settings → Assets → Image Transforms) #}
<img src="{{ asset.getUrl('thumb') }}" alt="{{ asset.alt }}">

{# Ad-hoc transform #}
{% set transform = { width: 300, height: 200, mode: 'crop', format: 'webp' } %}
<img src="{{ asset.getUrl(transform) }}" alt="{{ asset.alt }}">

{# Srcset #}
{{ asset.getImg({ width: 300 }, ['1.5x', '2x', '3x']) }}
Always add the Alternative Text field layout element to asset volumes for accessibility.
twig
{# 命名转换规则(在 设置 → 资源 → 图片转换 中定义) #}
<img src="{{ asset.getUrl('thumb') }}" alt="{{ asset.alt }}">

{# 临时转换规则 #}
{% set transform = { width: 300, height: 200, mode: 'crop', format: 'webp' } %}
<img src="{{ asset.getUrl(transform) }}" alt="{{ asset.alt }}">

{# Srcset #}
{{ asset.getImg({ width: 300 }, ['1.5x', '2x', '3x']) }}
请务必在资源卷的字段布局中添加替代文本字段,以满足无障碍要求。