# Запросы ← [К разделу «Как собирается сайт»](README.md) Запрос — сохранённая выборка документов и правила её представления. Он подходит для новостей, статей, карточек, связанных материалов и других списков. ## Части запроса | Часть | Назначение | | --- | --- | | Рубрики и условия | Определяют документы результата. | | Сортировка | Определяет порядок результата. | | Количество и пагинация | Ограничивают выдачу и создают страницы списка. | | Шаблон элемента | Рендерит один документ. | | Основной шаблон | Оборачивает готовые элементы и пагинацию. | | Контракт результата | Явно перечисляет системные свойства и поля, доступные структурированным потребителям. | | Кеш | Сохраняет результат на заданное время. | Запрос вставляется по alias: ```text [tag:request:latest_news] ``` ## Обычный и расширенный режимы Редактор открывается в **обычном режиме**. Для большинства списков этого достаточно: здесь находятся название, рубрика, количество материалов, сортировка, пагинация, шаблоны и условия. **Расширенный режим** нужен, когда вы настраиваете кеш, интеграцию, старый PHP-шаблон, Native executor или точный контракт данных. Если смысл параметра непонятен, оставьте его без изменения и вернитесь в обычный режим. Переключатель меняет только вид формы. Он не удаляет скрытые настройки и не меняет публичную страницу. Выбранный режим запоминается в текущем браузере. Простой порядок работы: 1. Выберите рубрику. 2. Укажите количество материалов и порядок сортировки. 3. Добавьте условия, если нужно отобрать не все документы рубрики. 4. Проверьте шаблон одного материала и общий шаблон списка. 5. Запустите предпросмотр и только затем сохраните запрос. ## Как показать результат В обычном режиме есть отдельный блок **«Как показать результат»**. Он позволяет посмотреть одну и ту же выборку в нескольких видах: | Вид | Для чего подходит | | --- | --- | | **Карточки данных** | Проверить все выбранные свойства каждого материала. | | **Компактный список** | Быстро просмотреть названия и несколько основных значений. | | **Таблица** | Сравнить одинаковые свойства нескольких материалов по колонкам. | | **JSON** | Проверить структурированные данные для API и интеграций. | Выбор сохраняется вместе с запросом, но относится только к предпросмотру в админке. Чтобы применить его, нажмите **«Обновить»** в блоке предпросмотра. Публичный сайт продолжает использовать сохранённые шаблоны `main` и `item`. Переключение карточек, списка или таблицы не меняет HTML сайта. Для изменения публичной разметки нажмите **«Редактировать шаблон сайта»**: редактор перейдёт в расширенный режим и откроет текущие шаблоны. Это разделение позволяет проверять данные, не рискуя случайно изменить работающую страницу. ## Условия и группы Простой плоский список недостаточен, когда части фильтра должны работать с разной логикой. Используйте группы и явно задавайте `И`/`ИЛИ`: ```text Опубликован И ( Рубрика = Новости ИЛИ Рубрика = Статьи ) И ( Тег = Важное ИЛИ Просмотры > 1000 ) ``` Сначала спроектируйте выражение, затем создавайте группы. Перемещение условия между группами меняет смысл выборки даже при тех же значениях. Условие `И` должно сужать текущий набор, `ИЛИ` — добавлять альтернативу внутри своей группы. После изменения обязательно проверьте реальный результат, а не только сохранение формы. ### Как читать одну строку условия Редактор читает каждую строку слева направо: ```text Поле документа → сравнение → откуда взять значение → значение ``` Например, строка `Цена → больше или равно → параметр min_price → число` означает: посетитель может передать `min_price=1000`, после чего запрос оставит документы с ценой от 1000. Если `min_price` не передан, эта необязательная строка не участвует в фильтрации. 1. **Поле** — данные документа, которые проверяются. 2. **Сравнение** — правило проверки: равно, содержит, больше, входит в список и так далее. 3. **Источник** — готовое значение редактора либо параметр текущей страницы или формы. 4. **Значение** — редактор сам предлагает подходящий элемент для типа поля: переключатель, список, число, дату или выбор связанного документа. Коды старых операторов (`==`, `%%`, `FRE`) хранятся для совместимости, но в редакторе показываются человекопонятные названия. Для новых условий свободный SQL и PHP не требуются. ### Как работают группы Переключатель в заголовке группы относится ко **всем непосредственным элементам этой группы**: - **И** — должны совпасть все строки и вложенные группы; - **ИЛИ** — достаточно одной совпавшей строки или вложенной группы; - вложенная группа — это скобки, содержимое которых вычисляется отдельно. Например, выражение `Показывать на главной = Да И (Тип = Новость ИЛИ Тип = Статья)` собирается из корневой группы **И** и вложенной группы **ИЛИ**. Если оставить всё в одной группе, смысл будет другим. ### Как выбрать сравнение | Сравнение | Практический смысл | | --- | --- | | **Равно / не равно** | Проверяет полное значение поля. | | **Содержит / не содержит текст** | Ищет фрагмент внутри строки. Для точного элемента составного поля используйте обработку `\|значение\|`. | | **Начинается / не начинается с** | Проверяет начало текстового значения. | | **Больше / меньше** | Сравнивает обычное сохранённое значение. | | **Число больше / меньше** | Использует числовой индекс поля и подходит для цены, веса, количества и других чисел. | | **Входит / не входит в список** | Сравнивает значение с несколькими допустимыми вариантами. | | **Диапазон** | Проверяет нижнюю и верхнюю границу. Для фильтра из формы выберите обработку диапазона. | | **Legacy SQL** | Оставлен для старых проектов. Новые правила через него не создавайте. | Если результат не совпал с ожиданием, сначала проверьте не шаблон, а тип поля и формат сохранённого значения. Число, текст и составное значение с разделителями сравниваются по-разному. ### Значение условия У значения есть три источника: | Источник | Когда использовать | | --- | --- | | **Готовое значение** | Запрос всегда сравнивает поле с выбранным или введённым значением. | | **Параметр URL или формы** | Фильтр получает значение из адреса страницы или формы посетителя. | | **Код совместимости** | Старое условие с PHP. Показывается в расширенном режиме и не нужно для новых запросов. | В режиме **Готовое значение** вид поля зависит от его типа: | Тип поля | Что показывает редактор | | --- | --- | | Флажок | Две понятные кнопки **Да** и **Нет**. | | Список | Готовые варианты из настроек поля; для сравнения со списком можно выбрать несколько. | | Число | Числовой ввод без стрелок увеличения и уменьшения. | | Дата | Календарь; старый формат хранения сохраняется автоматически. | | Связанный документ | Поиск по ID, названию и адресу с учётом рубрик-источников поля. | | Обычный текст | Строка ввода; для оператора «входит в список» значения указываются через запятую. | После выбора поля список сравнений также сокращается. Например, у флажка остаются только «равно» и «не равно», а у числа доступны числовые сравнения и диапазоны. Существующий нестандартный оператор не удаляется при открытии формы: его можно проверить и заменить вручную. Для публичного параметра укажите его имя. Под строкой редактор сразу показывает итоговое правило обычной фразой: какое поле сравнивается, откуда берётся значение и что с ним произойдёт. Например: > Поле «Каталог» содержит параметр «catalog», преобразованный в > `|значение|`. Пустой параметр не участвует в фильтрации. Служебное представление можно раскрыть отдельно: [tag:cond:catalog] Это не обычный шаблонный тег и не строковая подстановка. Редактировать его вручную не требуется. Он сообщает компилятору, что значение нужно безопасно прочитать из параметра `catalog`. В обычном режиме редактор выбирает обработку по типу поля. Изменить её вручную можно в **Расширенном** режиме. | Обработка значения | Что получает движок | | --- | --- | | Взять текст как есть | Одно текстовое значение. | | Взять целое число | Одно число без дробной части. | | Взять десятичное число | Одно число с дробной частью. | | Взять список | Несколько значений, любое из которых может дать совпадение. | | Взять диапазон от/до | Массив с ключами `min` и `max`; пустая граница не применяется. | | Обернуть значение: `|значение|` | Точное значение внутри составного поля, например ID раздела `95` превращается в `|95|`. | | Обернуть список: `|значение|` | Несколько точных значений внутри составного поля. | | Подставить константу, если заполнено | Пока параметр пуст, условия нет; при заполнении сравнивается указанная константа. Только в этом режиме показывается поле константы. | Пустой необязательный параметр полностью исключает условие из дерева. Поэтому не добавляйте разделители или SQL вокруг тега вручную: способ хранения задаётся вариантом **Как читать**. Alias поля подставляется при выборе источника, но его можно заменить, если публичный URL исторически использует другое имя, например oldprice для поля old. Каталог создаёт такие же условия, а не отдельный фильтр в обход запросов. Одиночные значения, флажки, множественный выбор и диапазоны остаются элементами общего дерева И/ИЛИ и редактируются в запросе. ### Пример: раздел каталога Условие запроса товаров обычно выглядит так: 1. Поле — **Каталог**. 2. Сравнение — **содержит**. 3. Источник — **параметр URL или формы**. 4. Имя параметра — `catalog`. 5. Обработка — **обернуть значение: `|значение|`**. При значении `catalog=95` движок ищет в поле каталога точный маркер `|95|`. Это не даёт разделу `9` случайно совпасть с разделом `95`. Если параметр отсутствует или пуст, условие не возвращает ложный результат, а полностью пропускается. Остальные активные условия запроса продолжают работать. ## Шаблон элемента Шаблон получает текущий документ и его поля. Типичный элемент: ```html

[tag:doctitle]

[tag:rfld:excerpt][0]
``` Откройте кнопку **Теги** именно у редактора **Шаблон одного материала**. Первая группа **Поля рубрики** строится по выбранной рубрике запроса. В ней рядом с названием и типом поля показан готовый тег: ```text [tag:rfld:title][0] [tag:rfld:image][0] ``` По возможности используется системный alias поля, а если его нет — ID. Суффикс `[0]` включает обычный публичный шаблон поля для списка. Для изображения внутри тега превью можно заменить его на `[img]`, например: ```text [tag:c760x460:[tag:rfld:image][img]] ``` Группа полей не показывается в палитре общего шаблона: там ещё нет текущего документа, а готовые карточки вставляются тегом `[tag:content]`. Не выполняйте отдельный SQL-запрос для каждого элемента списка. ## Основной шаблон Основной шаблон получает уже собранные элементы и отвечает за контейнер, заголовок и пагинацию. Не дублируйте в нём разметку одной карточки. Проверьте отдельно обычную и постраничную выдачу: у пагинации могут быть иной URL и активное состояние. ## Контракт результата Контракт отвечает не за поиск документов и не за HTML. Он перечисляет данные, которые разрешено передать API, предпросмотру или renderer'у: - системные свойства документа: ID, название, alias, тизер, состояние и даты; - выбранные поля рубрики; - стабильные системные ключи и alias полей. Например, для списка новостей достаточно названия, alias, тизера, даты и поля с обложкой. Цена и остаток в такой контракт не попадут, если они не выбраны. Пустой старый контракт автоматически получает безопасный базовый набор системных свойств. Выбор полей сохраняется вместе с запросом и переносится контент-пакетом. Удалённое или чужое поле при сохранении отбрасывается. Контракт первой итерации не меняет теги и HTML существующего сайта. Публичный вывод по-прежнему выполняют шаблоны `main/item`, помеченные в редакторе как **Legacy renderer**. Рядом можно выбрать renderer предпросмотра: - **Карточки данных** удобны для визуальной проверки выбранных свойств; - **JSON** показывает тот же контракт в машинном формате. Выбор сохраняется у запроса, но не переключает публичную страницу. Поэтому можно проверять будущий API-формат, не меняя карточки и листинги сайта. ## Режим выполнения У запроса есть три режима executor. Они меняют только получение списка документов. Шаблоны `main/item`, разметка карточек и публичные URL остаются прежними. | Режим | Что происходит | | --- | --- | | **Legacy** | Работает прежняя выборка со всеми историческими PHP- и SQL-возможностями. Это безопасное состояние по умолчанию. | | **Shadow** | Публичный сайт продолжает использовать Legacy. В панели управления тот же запрос дополнительно выполняется Native executor'ом и результаты сравниваются. | | **Native** | Список выбирается по параметризованному плану без исполнения PHP из значений условий. Старые шаблоны продолжают рендерить результат. | Native нельзя выбрать сразу. Сначала сохраните запрос, включите **Shadow** и запустите предпросмотр. Система сравнит: - общее количество документов; - ID документов в проверяемом окне; - порядок документов; - текущую версию условий, групп, рубрики и сортировки. Если всё совпало, появится состояние **Native подтверждён** и режим станет доступен. Подтверждение связано с хешем плана. Изменение условия, группы, рубрики или сортировки автоматически снимает его; после сохранения нужно снова запустить сравнение. Для автоматического подтверждения порядок должен заканчиваться уникальным системным полем **ID**. Одинаковая дата публикации, позиция или цена может быть у нескольких документов. Без финального ID MySQL вправе переставить такие строки после очистки кеша, даже если состав списка не изменился. В этом случае Панель управления покажет **Порядок не закреплён** и сохранит Shadow/Legacy. Это защищает пагинацию, меню и карточки от незаметной перестановки. Для текстовой сортировки равенство определяется той же collation базы, что и в реальном `ORDER BY`. Например, `MODEL-X` и `Model-X` обычно считаются одним значением. В диагностике они будут показаны как два исходных текста, но причина останется той же: таким строкам нужен отдельный финальный порядок. В конструкторе порядка нажмите **«Закрепить по ID»**. Система добавит ID последним уровнем по возрастанию. При необходимости направление можно поменять. Это не заменяет предыдущие уровни: дата, цена или позиция остаётся главным правилом, ID только стабилизирует строки с одинаковым значением. У случайного порядка с заполненным ключом ID добавляется автоматически как незаметный финальный признак; вручную добавлять его не нужно. Автоподтверждение сравнивает до 5000 документов целиком. Более крупная выборка помечается как неполная и остаётся на Legacy, даже если её начало совпало. Native использует декларативные источники HTTP-параметров: необязательное целое, десятичное число, строку, список, диапазон, составное значение и константу по наличию параметра. PHP при этом не исполняется. Аудит проверяет состояние без параметров, каждый параметр отдельно и их сочетание; для теста берётся реальное значение соответствующего поля, когда оно доступно. Запрос останется на Legacy, если использует произвольный `FRE`, `ANY`, произвольный PHP, `[field]`, случайную сортировку без постоянного ключа или переданные при вызове сырые `USER_WHERE`, `USER_FROM`, `USER_JOIN`, `ORDER`, `SQL_QUERY`. Панель управления показывает конкретные причины. Эти возможности не интерпретируются приближённо. Декларативный диапазон и множественный фильтр могут хранить исторический код оператора `FRE`, но внутри у них уже нет свободного SQL: Native строит параметризованное сравнение по сохранённому типу. Даже в режиме Native действует защитный fallback: неподдерживаемый runtime- параметр или неподтверждённая версия плана выполняются через Legacy. Для быстрого ручного отката достаточно вернуть режим **Legacy**. ### Массовая проверка На странице списка запросов откройте **Проверка Native**. Эта страница нужна для безопасного перехода существующего сайта: 1. **Проверить все** переводит запросы в Shadow. Распознанным старым условиям записывается типизированное описание рядом с исходным PHP, но публичный HTML продолжает собираться Legacy executor'ом. 2. Панель управления последовательно сравнивает до 5000 документов каждого запроса, не создавая одновременную нагрузку на БД. Если состав Legacy-выборки совпал во всех состояниях параметров, PHP заменяется декларативным тегом и Legacy проверяется ещё раз по хешу полного списка ID. При отличии значение автоматически возвращается. 3. В таблице отдельно видны точное совпадение, несовпадение порядка, незакреплённая сортировка и legacy-код. Для несовпадения порядка показывается первая отличающаяся позиция, ID из Legacy и Native и фактические значения ключа сортировки. Например, одинаковая дата или цена сразу показывает, что фильтр совпал, а запросу не хватает только финального ID. 4. **Закрепить порядок** обрабатывает только незакреплённые запросы. Для каждого из них и запросов с отличием порядка система целиком сравнивает прежний Legacy-порядок сначала с `Id ASC`, затем с `Id DESC`. Подходящее направление сохраняется только при точном совпадении всех ID. После записи выполняется повторная контрольная сверка; при любом отличии настройка и прежний режим автоматически восстанавливаются. 5. **Включить подтверждённые** меняет executor только у запросов с актуальным хешем, полным совпадением и уникальным финальным порядком. Условия запросов без стабильной сортировки уже могут быть переведены на теги, но сам запрос останется в Shadow/Legacy. 6. **Вернуть Legacy** одним действием откатывает все запросы. Хеши проверки сохраняются, но после изменения запроса всё равно потребуют новой сверки. Если ни одно направление ID не воспроизводит существующую выдачу, запрос остаётся в Shadow/Legacy. Это нормальный результат: система не пытается «приблизительно» исправить порядок и не меняет публичную страницу. Смена режима очищает кеш настроек и элементов запросов. Поэтому контрольные страницы после активации проверяйте заново, а не по старому закешированному HTML. ## Предпросмотр данных Кнопка **Обновить** выполняет сохранённую выборку, не запускает шаблоны `main/item` и одновременно проводит теневое сравнение Legacy/Native. Поэтому результат показывает данные и совместимость, а не дизайн сайта. В предпросмотре доступны: - общее количество найденных документов; - первые 5, 10 или 20 результатов; - время SQL; - рубрика, число групп и активных условий, сортировка и публикационные ограничения; - только свойства, выбранные в контракте; - SQL для пользователя с правом управления запросами. - состояние Native-плана, причины несовместимости и различающиеся ID. Если вы изменили рубрику, сортировку или основные параметры запроса, сначала сохраните форму. Выбор полей контракта передаётся в preview сразу и может быть проверен до сохранения. Каждый показанный документ уже прошёл всё дерево условий. Чтобы проверить исключённый материал, найдите его по ID, названию или alias в строке **Почему документ попал или не попал?** и нажмите **Объяснить**. В правой панели отдельно показаны: - рубрика, состояние, удаление и срок публикации; - результат каждой группы И/ИЛИ; - фактическое значение поля и ожидаемое значение; - окончательный ответ реального executor. Свободное условие `FRE` может содержать PHP, SQL или runtime-параметры. Инспектор не исполняет такой код повторно и честно помечает условие как неопределённое, но итог «входит / не входит» всё равно получает из настоящей выборки. ## Сортировка Порядок собирается сверху вниз. Первое правило является главным, второе применяется при совпадении первого, третье — при совпадении первых двух. Пример: ```text 1. Дата публикации — по убыванию 2. Название документа — по возрастанию 3. ID документа — по возрастанию ``` Так новые материалы показываются первыми, материалы с одинаковой датой упорядочиваются по названию, а ID делает итог стабильным для пагинации. Нажмите **«Добавить уровень»**, выберите системное свойство документа или поле рубрики и задайте направление. Строки можно переставлять за левый маркер. Одинаковое поле нельзя добавить дважды; доступно не больше восьми уровней. Случайный порядок используется отдельно и не сочетается с другими правилами. После его выбора появляется **Ключ порядка**. Пустой ключ перемешивает материалы заново, как прежний `RAND()`. Одинаковый числовой ключ всегда даёт одинаковый порядок и подходит для пагинации: переход на следующую страницу не перемешивает уже показанные материалы. Например, ключ `2026` можно оставить постоянным, а для ежедневной подборки менять его планировщиком раз в день. SQL для этого писать не требуется. Для больших списков выбирайте поля с однозначным типом и числовым индексом. SQL писать не требуется. Кнопка **«Закрепить по ID»** добавляет безопасный последний уровень и предотвращает перестановку элементов между страницами. Старые запросы открываются в новом конструкторе без миграции вручную. Их историческая последовательность «поле рубрики → системное поле → ID» сохраняется. После сохранения полный порядок записывается в JSON, а старые колонки продолжают заполняться для совместимости с прежними интеграциями. Поле **Шаблон пагинации** показывает реальные шаблоны из настроек по названию и ID. Удалённый или несуществующий шаблон сохранить нельзя: сначала выберите действующий либо создайте новый в настройках пагинации. ## Кеш Время жизни `0` отключает долговременный кеш результата. Большой срок подходит стабильным спискам, но задерживает появление новых документов. Сохранение документа очищает связанные ключи там, где зависимость известна; проектные внешние данные могут потребовать явной инвалидации. Перед выводом списка движок сам собирает ID документов и пакетно подготавливает их JSON-снимки и поля. Поэтому шаблон элемента может продолжать использовать обычные `[tag:doc:*]` и `[tag:rfld:*]`: переносить поля в SQL запроса ради скорости не требуется. Если снимка ещё нет, первая страница создаст его; далее используется готовый файл. Сохранение документа обновляет снимок автоматически. Не включайте общий кеш для результата, который зависит от пользователя, сессии или непредставленных в ключе параметров. ## Внешний и AJAX-вызов Эти флаги разрешают получить запрос вне обычной страницы. Включайте их только для реально используемого endpoint и проверяйте права, входные параметры и кеширование. Внутреннему тегу `[tag:request:alias]` они не нужны. ## Проверка запроса 1. Выберите минимальный контракт результата. 2. Запустите структурированный предпросмотр и сверьте первые документы. 3. Сверьте количество результатов без кеша. 4. Проверьте каждую группу условий отдельно. 5. Проверьте пустую выдачу и один элемент. 6. Проверьте первую, вторую и последнюю страницу пагинации. 7. Проверьте активность и дату публикации документов. 8. После включения кеша повторите изменение одного документа. 9. Оставьте режим Shadow на реальном трафике, затем включайте Native отдельно для каждого подтверждённого запроса.