PageCharm

Разработчикам

Запросы к контенту

Руководство разработчика PageCharm

Полный набор приемов IPageCharmContentQuery: несколько блоков и шаблонов, поиск по полям, сортировка, пейджинг, картинки и файлы.

Зачем нужен IPageCharmContentQuery

IPageCharmContentQuery — публичный способ читать CMS-данные из Razor, сервисов сайта или виджетов. Реализация sample работает поверх EF Core, но код сайта зависит от контракта PageCharm, а не от конкретной базы.

Фильтры контекста

  • Active() оставляет активный и уже опубликованный контент. Если у шаблона включено управление датой публикации, материалы с пустой или будущей датой скрываются. Active(false) выбирает неактивный контент; если метод не вызывать, вернутся оба состояния без проверки расписания.
  • FromBlocks("pages", "news") выбирает один или несколько блоков.
  • WithTemplates("article", "product") выбирает один или несколько шаблонов.
  • ChildrenOf(parentId) выбирает дочерний контент конкретного родителя.
  • FindByIdAsync(id), FindByUrlAsync(url), FirstOrDefaultAsync(), ToListAsync() и CountAsync() завершают запрос.

Поиск и фильтры по полям

Search(text) ищет по всем текстовым и JSON-значениям атрибутов. Он не ищет по URL и системному Name, потому что Name нужен админке, а публичные заголовки лучше хранить в полях шаблона. SearchFields(text, "summary", "body") ищет только в указанных полях. На PostgreSQL и MySQL поиск использует отдельную read-модель и provider-specific full-text индексы; на SQLite остается fallback через LIKE. Для точной фильтрации есть WhereFieldContains, WhereFieldEquals для строк, чисел, bool, дат и времени, range-методы: WhereIntegerFieldBetween, WhereDecimalFieldBetween, WhereDateFieldBetween, WhereDateTimeFieldBetween, а также фильтры по тегам: WithAnyTags, WithAllTags и WhereTagsContain.

Сортировка и пейджинг

Для даты из пользовательского поля используйте OrderByFieldOrCreatedAt(alias, sortType, descending). Метод принимает CalendarDate или Instant и подставляет CreatedAt, если поле пустое.

Если сортировку не указать, PageCharm сортирует по ручному порядку CMS: Order, затем Id. OrderByField(alias, type, descending) переопределяет это и сортирует по значению поля. Типы сортировки: Text, WholeNumber, DecimalNumber, Flag, CalendarDate, ClockTime, Instant. Для новостных лент используйте OrderByPublishAt(descending: true): при отсутствии системной даты запасным значением служит CreatedAt. Для страниц списка используйте Skip(count) и Take(count).

Словари, теги и списки

Раздел «Словари» хранит наборы значений с Name и Alias. Поля и настройки типа Tags редактируются как chips с автодополнением по выбранному словарю. Если в настройках поля включено «Разрешить новые значения», редактор может набрать новое значение прямо в форме контента или настройки; если выключено — можно выбрать только уже существующее значение словаря.

Тип Select тоже может брать варианты из словаря. В этом режиме в базе хранится alias выбранного значения, а список вариантов управляется централизованно в разделе «Словари». Это удобно для категорий, брендов, статусов и любых повторяемых справочников, которые нужны сразу в нескольких шаблонах или настройках.

Для вывода поля Tags используйте GetTags(alias); для выборок по тегам — WithAnyTags("news"), WithAllTags("news", "featured") или WhereTagsContain("topics", "news"). Это методы именно для множественных тегов и join-таблицы CmsContentTag. Для Select со словарным источником хранится один alias значения, поэтому фильтруйте его как обычное поле: WhereFieldEquals("brand", "jetbrains"). Сами словари и значения во view можно получить через IPageCharmDictionaryQuery.

Картинки, видео и файлы из полей

Медиа может быть привязано к самому контенту или к конкретному полю. В публичных запросах PageCharm загружает и Content.MediaAssets, и Attributes[].MediaAssets. Для Razor есть helpers: GetMedia() для файлов контента без поля, GetMedia("gallery"), GetFirstMedia("cover"), GetImages("gallery"), GetFiles("downloads"). В разметке используйте Url, Alt, FileName и ContentType. Для локального storage Url хранится без схемы и домена, например /uploads/file.jpg; для CDN/S3 он может быть абсолютным.

Пресеты изображений настраиваются в разделе «Медиа» через кнопку настроек. В настройках поля или настройки типа Image/Images можно выбрать допустимые пресеты и пресет по умолчанию. Если пресеты не заданы или не выбраны, CMS работает с оригинальным изображением без ресайза. Для вывода варианта используйте image.GetPresetUrl("card"); первый запрос лениво создаст файл по публичному пути /_pagecharm/images/....

Для видео используйте поля Video/Videos. В админке вставляется ссылка на youtube.com, vk.com, vkvideo.ru или rutube.ru, а CMS сохраняет объект CmsVideo: публичная ссылка лежит в video.Url, картинка предпросмотра берется через video.GetPreviewUrl(), embed-ссылка — через video.GetEmbedUrl(). Если провайдер не отдал картинку серверному запросу, URL превью можно указать вручную.

Хлебные крошки

Для хлебных крошек используйте GetBreadcrumbsAsync(content). Метод идет вверх по ParentId, защищен от циклов и возвращает активных родителей, если вызван после Active(). Если у шаблона есть отдельное поле публичного заголовка, используйте его, а Name оставьте запасным значением для админки.

Практическое правило

В админке PageCharm работает с EF напрямую, потому что там нужны транзакции, права и массовые операции. В публичном сайте читайте данные через IPageCharmContentQuery: так представления остаются переносимыми и не знают деталей хранилища.

Полный список методов, параметров и возвращаемых типов собран в справочнике Razor API.

Пример

@model PageCharmPageModel
@inject IPageCharmContentQuery Content

@{
    var baseQuery = Content.Active()
        .FromBlocks("catalog", "special-offers")
        .WithTemplates("product", "promo-card")
        .SearchFields("ноутбук", "title", "description")
        .WhereDecimalFieldBetween("price", min: 1000m, max: 250000m);

    var total = await baseQuery.CountAsync();

    var items = await baseQuery
        .OrderByField("price", CmsContentFieldSortType.DecimalNumber)
        .Skip(0)
        .Take(12)
        .ToListAsync();

    var breadcrumbs = await Content.Active().GetBreadcrumbsAsync(Model.Content);
}

<nav aria-label="Хлебные крошки">
    <a href="/">Главная</a>
    @foreach (var parent in breadcrumbs)
    {
        var title = parent.Get<string>("title") ?? parent.Name;
        <span>/</span>
        <a href="@parent.Url">@title</a>
    }
    <span>/</span>
    <span>@(Model.Content.Get<string>("title") ?? Model.Content.Name)</span>
</nav>

<p>Найдено: @total</p>

@foreach (var item in items)
{
    var title = item.Get<string>("title") ?? item.Name;
    var description = item.Get<string>("description");
    var price = item.Get<decimal>("price");
    var cover = item.GetFirstMedia("cover");
    var downloads = item.GetFiles("downloads");
    var video = item.GetVideo("video");
    var tags = item.GetTags("topics");

    <article>
        @if (cover is not null)
        {
            <img src="@cover.GetPresetUrl("kartochka-tovara")" alt="@cover.Alt">
        }
        <h2><a href="@item.Url">@title</a></h2>
        <p>@description</p>
        <strong>@price</strong>
        @foreach (var file in downloads)
        {
            <a href="@file.Url">@file.FileName</a>
        }
        @if (video is not null)
        {
            <a href="@video.Url">
                @if (video.GetPreviewUrl() is { } previewUrl)
                {
                    <img src="@previewUrl" alt="@video.Title">
                }
                Смотреть видео
            </a>
        }
        @foreach (var tag in tags)
        {
            <span>@tag.Name</span>
        }
    </article>
}

Тип сортировки

Для строковых полей используйте Text. Для Decimal-полей используйте DecimalNumber: в SQLite PageCharm сортирует такие значения через double, как и админка, чтобы не зависеть от культуры процесса.