Перейти к содержанию

Коллекция статей и устойчивые поля

Оглавление серии

Коллекция определяет, какие сведения хранятся у документов одного вида. Для «Редакционного дома» таким видом будет статья: у неё есть заголовок, адрес, краткое описание, основной текст и уровень сложности. Пока мы не добавляем автора и обложку. Сначала полезно убедиться, что каждое простое поле отвечает на отдельный вопрос и может быть использовано независимо от остальных.

После урока у проекта появится схема Article и первый черновик «Сетка карточек CSS Grid». Работаем в локальной CMS из первого урока. Архив содержит прежнее состояние схемы в states/lesson-02/; финальная схема в src/ включает изменения будущих уроков, поэтому в начале последовательности используйте именно этот snapshot.

Поля отражают решения редакции

Заголовок объясняет содержание человеку: «Сетка карточек CSS Grid». slug задаёт часть адреса: css-grid-cards. summary позволяет показать карточку в каталоге без всего текста. body хранит само объяснение. level сообщает читателю предполагаемую сложность. Такие поля удобнее одного большого HTML-фрагмента, в котором название, адрес и описание смешаны с оформлением.

Рассмотрим изменение заголовка на «Адаптивная сетка карточек CSS Grid». Краткое описание не должно автоматически превратиться в этот же текст. Адрес также не обязан меняться: на него могли сослаться другие страницы. Поэтому связь UID с title помогает сформировать первоначальное значение, но редактор должен относиться к опубликованному адресу как к отдельному решению. Не рассматривайте автоматически предложенный slug как вечную команду переименовывать страницу.

В Content-Type Builder выберите создание Collection Type, назовите его Article и проверьте singular API ID article, plural API ID articles. Эти идентификаторы будут участвовать в адресе /api/articles. Добавьте поля по схеме ниже. Конструктор модели используется в режиме разработки; он меняет структуру приложения, а не просто создаёт ещё одну редакционную запись. Официальные правила описаны в Content-Type Builder.

Полный файл состояния второго урока находится по пути states/lesson-02/src/api/article/content-types/article/schema.json.

{
  "kind": "collectionType",
  "collectionName": "articles",
  "info": {
    "singularName": "article",
    "pluralName": "articles",
    "displayName": "Article"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {},
  "attributes": {
    "title": {
      "type": "string",
      "required": true,
      "minLength": 5,
      "maxLength": 120
    },
    "slug": {
      "type": "uid",
      "targetField": "title",
      "required": true
    },
    "summary": {
      "type": "text",
      "required": true,
      "maxLength": 320
    },
    "body": {
      "type": "text",
      "required": true
    },
    "level": {
      "type": "enumeration",
      "enum": [
        "beginner",
        "intermediate"
      ],
      "default": "beginner",
      "required": true
    }
  }
}

required означает обязательность значения, minLength и maxLength задают простые границы текста. Они помогают поймать случайно пустой заголовок или слишком длинную карточку, но не определяют качество объяснения. Заголовок из нескольких бессмысленных слов всё ещё может удовлетворять схеме. Семантическая проверка остаётся работой автора и редактора.

level является enumeration: допускаются только beginner и intermediate. Это лучше свободного поля, где один автор напишет «начальный», другой — «для новичков», а третий — Beginner. Сайт будет фильтровать устойчивые значения и показывать выбранные русские подписи. Машинное значение и текст интерфейса не обязаны совпадать буквально.

Первый документ без публикации

Откройте Content Manager, найдите Articles и создайте запись. Введите заголовок «Сетка карточек CSS Grid», явно задайте css-grid-cards, добавьте summary «Размещаем карточки в адаптивной сетке». В body запишите два абзаца: «CSS Grid описывает строки и столбцы» и «Начните с трёх карточек и одного контейнера». Выберите beginner, затем сохраните черновик, не нажимая Publish.

Ожидаемый результат — документ доступен редактору в Content Manager, но ещё не имеет опубликованной версии. Мы не наблюдали этот экран при подготовке курса. Сведения для ввода входят в fixtures/articles.json, поэтому текст первого документа будет тем же при ручном заполнении и будущем импорте. Пока не импортируйте весь набор: сначала разберём связанные поля.

Обратите внимание на назначение короткого текста примера. Он нужен для изучения структуры и движения данных, а не для создания готовой учебной статьи о CSS Grid. Чем проще первые данные, тем легче объяснить, почему значение отсутствует в запросе или появилось в другом поле. В реальной редакции body будет значительно длиннее и пройдёт отдельную проверку содержания.

Для body сознательно выбран обычный text. Потребитель позже разобьёт его на абзацы и экранирует символы HTML. Если использовать Rich Text или Blocks, клиенту понадобится другой renderer, поддерживающий структуру выбранного поля. Смена типа текста — изменение контракта, которое нельзя скрыть под обещанием «любой редактор автоматически работает на сайте».

Как модель переживает изменения

Допустим, редактору нужно добавить время чтения. Не начинайте с вставки строки «5 минут» в начало body: тогда каталог должен будет разбирать текст и угадывать, где находится метаданные. Отдельное число readingMinutes можно было бы использовать на карточке и странице одинаково. Однако в нашем курсе пока нет способа обоснованно вычислить это число, поэтому такое поле не добавляем. Хорошая модель включает нужные сведения, а не все параметры, которые встречаются у похожих сайтов.

Другая ситуация — опечатка в названии самого атрибута. Схема и база содержат связанные сведения, поэтому переименование поля требует понимания выбранной версии Strapi и состояния записей. Даже когда текущий Builder поддерживает сохранение данных при переименовании, сначала нужен план копии и сравнения. Не удаляйте атрибут только потому, что его название неудобно: удаление может затронуть уже внесённое содержание.

Полезно разделить изменение определения и изменение документа. Если вы исправили summary одной статьи, изменяется редакционная запись. Если сократили разрешённую длину summary во всей модели, изменяется правило для всех будущих операций. Редактору важно знать, какие существующие значения не соответствуют новому правилу и что делать при следующем сохранении. Структурное изменение нельзя оценивать только по одной открытой карточке.

Сравните также обязательность поля с обязательностью его публикационного смысла. Summary может быть непустым, но содержать временную фразу «описание позже». Схема разрешит строку подходящей длины, тогда как редакционный выпуск должен её отклонить. Поэтому в нашем course Article проходит сначала структурное сохранение, затем содержательную проверку. Не стоит добавлять сложную автоматическую проверку каждого русского предложения в schema: удобнее задать понятные критерии выпуска и постепенно автоматизировать только объективные свойства. Для первой статьи таким свойством является совпадение адреса css-grid-cards с установленным контрактом сайта, а не точное совпадение title с filename.

Проверяемый учебный эффект такой: при вашем будущем вводе пустой обязательный заголовок должен препятствовать корректному сохранению, а допустимые данные должны дать черновик с отдельными title, slug, summary, body и level. Случайно вставленный <strong> в body остаётся текстом; наш сайт впоследствии не будет исполнять его как HTML. В следующем уроке вместо повторения имени автора в каждой статье появится самостоятельный документ Author и явная связь с ним.