Довідка та документація

Pubner пропонує два способи побудувати сайт: описати, що вам потрібно, і доручити це AI-асистенту, або відкрити режим Expert і створити шаблони самостійно. Цей посібник охоплює обидва — почніть з основ, а далі перейдіть до чистих URL, SEO та конструкторів даних.

Створення з AI

Найшвидший спосіб працювати з Pubner — просто описати, що вам потрібно. У панелі сайту перемкніть редактор у режим AI, напишіть запит звичайною мовою — і асистент побудує сторінку за вас, показуючи живий попередній перегляд поряд із чатом.

Асистент може створювати й редагувати сторінки, макети та партіали, а також налаштовувати маршрути. Уточнюйте в діалозі — «зроби ширше», «додай блок відгуків» — і ніщо не торкнеться живого сайту, доки ви не натиснете Зберегти. У будь-який момент можна перемкнутися в режим Expert і доопрацювати згенерований код вручну.

Приклади запитів:

{# Опишіть сторінку так, як пояснили б дизайнеру #}
“Create a contact page with a form and a map.”
“Add a blog section with categories and pagination.”
“Make the header sticky and add a language switcher.”
Кожну зміну видно в попередньому перегляді до публікації, а попередні версії зберігаються — тож можна вільно експериментувати й відкочуватися будь-коли.

Режим Expert: файли вашої теми

Тема Pubner — це лише Twig-шаблони та один YAML-конфіг, без PHP. Є чотири типи файлів: pages/ (один шаблон на URL), partials/ (повторні блоки), layouts/ (HTML-оболонка навколо кожної сторінки) та config.yml (який URL завантажує яку сторінку). Ви редагуєте їх прямо в панелі та перемикаєтесь між режимами Expert і AI будь-коли.

Оскільки теми не містять PHP, шаблон ніколи не виконає довільний код — найгірше, що може статися від помилки, — це зламана HTML-розмітка. Кожне збереження спершу перевіряється, а попередні версії зберігаються, тож ви завжди можете відкотитися.

Як влаштована тема:

my-theme/
├─ config.yml {# маршрути #}
├─ layouts/
│  └─ layout.twig {# HTML-оболонка #}
├─ pages/
│  ├─ home.twig {# один файл на сторінку #}
│  └─ post.twig
└─ partials/
   ├─ header.twig {# повторні блоки #}
   └─ footer.twig

Сторінки

Сторінки — найважливіші елементи вашого сайту на Pubner. Кожен URL-маршрут відповідає певному шаблону сторінки. Коли відвідувач запитує URL, система завантажує відповідну сторінку, обробляє її Twig-код і вставляє результат у визначений макет.

Уявляйте сторінку як «тіло» вашого контенту. Вона містить лише те, що унікальне для конкретного URL (як-от текст статті чи певна форма), тоді як глобальні елементи (навігація чи футер) обробляються макетами й партіалами.

Вивести контент поточної сторінки всередині макета:

{% page %}

Партіали

Партіали — це повторно використовувані шматки HTML і Twig-логіки. Розбиваючи дизайн на партіали, ви тримаєте код за принципом DRY і добре структурованим. Щоб оновити футер сайту, ви змінюєте лише один файл партіала — і він миттєво оновлюється по всьому сайту.

Типові випадки використання партіалів: хедери, футери, віджети бічної панелі, картки товарів або складні UI-компоненти, що використовуються на різних сторінках.

Підключити партіал у макеті чи сторінці:

{% partial 'navigation.twig' %}

Передати дані в партіал:

{% partial 'card.twig' product=item, featured=true %}
Іменовані параметри стають змінними всередині партіала — тут product та featured доступні прямо в card.twig.

Макети та плейсхолдери

Макети визначають основну HTML-оболонку сайту (теги <html>, <head> та <body>). Один проєкт може мати кілька макетів (наприклад, один для публічного сайту, інший для кабінету клієнта).

Щоб зробити макети гнучкими, Pubner використовує плейсхолдери. Вони діють як «гачки», куди окремі сторінки можуть вставляти власний контент: SEO-метатеги, додаткові CSS-стилі чи скрипти трекінгу.

Визначити плейсхолдер у макеті:

{% placeholder styles %}

Передати контент у плейсхолдер з конкретної сторінки:

{% put styles %}
    <style> .custom-hero { background: blue; } </style>
{% endput %}
Контент між тегами put буде автоматично вставлено саме там, де плейсхолдер визначено у вашому макеті.

Змінні та керівна логіка

Pubner працює на сучасному рушії шаблонів Twig. Це дозволяє писати чисті, безпечні та дуже динамічні шаблони без чистого PHP-коду.

Ви легко виводите змінні, застосовуєте фільтри для перетворення тексту, проходите масиви даних (як-от пости чи товари) у циклах і використовуєте умовну логіку, щоб показувати чи приховувати елементи залежно від стану чи сесії користувача.

Вивід змінних та використання умов:

{% if site().name %}
    <h1>Welcome to {{ site().name }}</h1>
{% endif %}

Цикл по колекції:

{% for post in records({'area': 'blog'}).get() %}
    <article>{{ post.name }}</article>
{% else %}
    <p>No posts yet.</p>
{% endfor %}

Чисті URL та звʼязування маршрутів

У config.yml кожен маршрут будується із сегментів, а не з вручну набраного шляху. Сегмент — це або фіксоване слово (literal, як-от news), або захоплене значення (param, як-от slug). Сегмент також можна звʼязати з вашим контентом — областю, категорією чи записом — і Pubner автоматично знайде його за slug.

Коли сегмент звʼязано, Pubner сам завантажує цей елемент і вставляє його в сторінку: запис доступний як record, а також area й category, якщо ви їх звʼязали. Позначте одне звʼязування як primary — і його поля додатково розкриваються на верхній рівень, тож можна писати просто name. Якщо за slug нічого не знайдено, Pubner автоматично повертає чисту 404 — без ручного пошуку й без сторінки помилки, яку треба будувати.

Звʼязаний маршрут деталей у config.yml:

- page: post.twig
  layout: layout.twig
  segments:
    - { type: literal, value: news, bind: area }
    - { type: param, name: slug, bind: record, primary: true }
Це обслуговує /news/{slug}. Область news перевіряється, запис завантажується за slug, а відсутній slug сам повертає 404. Не хочете чіпати YAML? Візуальний конструктор URL у панелі напише ці сегменти за вас.

Використання звʼязаної моделі в post.twig:

{# record вставлено — запит не потрібен #}
<h1>{{ record.name }}</h1>
{{ record.content | raw }}

{# primary-звʼязування також розкривається в корінь #}
<title>{{ name }}</title>
Жодного set, жодного firstOrFail(). Звʼязаний запис надходить готовим до використання — і це та сама модель, яку SEO-блок бере для заголовка сторінки.

Керований SEO

Одна функція — seo_head() — рендерить увесь керований SEO-блок <head>: <title>, мета-опис, тег robots, коли індексацію вимкнено, та ваш фрагмент аналітики. Додайте її в <head> вашого макета один раз — і вона лишається коректною на кожній сторінці.

Заголовки заповнюються самі. На звʼязаній сторінці деталей seo_head() бере meta_title та meta_description запису, відкочуючись до його назви, а потім до назви сайту. Щоб перевизначити на конкретній сторінці, викличте seo_title() / seo_description() у тілі сторінки. Суфікс бренду, увімкнення/вимкнення індексації та ID аналітики задаються один раз у Налаштуваннях сайту (seo_title_suffix, seo_indexing, seo_ga). Потрібні частини окремо? seo('title'), seo('description'), seo('robots') та seo('analytics') повертають кожну окремо.

Додайте керований head у макет:

<head>
    {% placeholder meta %} {# ваш блок charset / viewport #}
    {{ seo_head() }}
</head>
Один виклик рендерить теги title, description, robots та аналітики разом.

Перевизначення title та description на сторінці:

{# викликайте це в тілі сторінки #}
{{ seo_title('Summer Sale — up to 50% off') }}
{{ seo_description('Our biggest discounts of the year, ending Sunday.') }}
Звʼязаним сторінкам деталей це зазвичай не потрібно — meta_title / meta_description запису беруться автоматично.

Редіректи та навігація

Переміщення користувачів вашим застосунком має бути безпечним і передбачуваним. Pubner надає набір Twig-функцій для внутрішніх і зовнішніх редіректів прямо з шаблонів.

Крім того, ви можете використати хелпер request_is(), щоб легко визначати активний стан пунктів навігації — з підтримкою шаблонів-масок і локалізованих маршрутів.

Доступні хелпери редіректів:

{# Redirect to a specific URL #}
{{ redirect('/about') }}

{# Redirect to the previous page #}
{{ redirect_back() }}

{# Redirect only if the user is logged in (Guest protection) #}
{{ redirect_if_auth('/dashboard') }}

{# Safe redirect (Prevents external open-redirect vulnerabilities) #}
{{ redirect_safely(user_provided_url) }}

Перевірка активного маршруту (зручно для навбарів):

{% if request_is('about*', 'contact') %}
    {# This will be true for /about, /about/team, and /contact #}
    <a class="active">Company</a>
{% endif %}

Локалізовані URL через page_url():

{# Link to a record detail page (locale-aware) #}
<a href="{{ page_url('/news', record.slug) }}">Read more</a>

{# Link to a static page #}
<a href="{{ page_url('/about') }}">About Us</a>

{# Single-language → /news/my-post   Multilingual → /ua/news/my-post #}

Форми, ассети та UI-хелпери

Створення інтерактивних елементів на кшталт форм контактів чи підключення статичних ассетів (CSS/JS) спрощено завдяки вбудованим хелперам. Вони автоматично дбають про безпеку (CSRF-токени), маршрутизацію та стан сесії.

Ми також надаємо автоматичний генератор пагінації, що вставляє чистий адаптивний компонент сторінкування там, де потрібно, створюючи необхідні файли партіалів «на льоту», якщо їх ще немає.

Ініціалізувати безпечну форму:

{{ form({'action': 'contact.send', 'redirect': '/thanks'}).open() }}
    {# Form inputs go here #}
{{ form().close() }}

Додайте поля до форми:

{{ form({'action': 'contact.send'}).open() }}
    {{ form().text('name') }}
    {{ form().email('email') }}
    {{ form().textarea('message') }}
    {{ form().select('topic', {'sales': 'Sales', 'support': 'Support'}) }}
    {{ form().submit('Send message') }}
{{ form().close() }}
Поля автоматично відновлюють значення з останньої відправки й показують помилки валідації. Також доступні: password(), file(), checkbox(), radio(), hidden(), label() та button().

Перевірити сповіщення про успіх/помилку форми:

{% if success() %}
    <div class="alert-success">Message sent successfully!</div>
{% endif %}

Згенерувати посилання пагінації:

{{ paginator_links(records) }}
Передайте сюди пагіновану колекцію. Pubner автоматично згенерує партіал pagination.twig у вашій темі, якщо його ще немає.

Локалізація та мови

Pubner із самого початку створений для глобальної аудиторії. Нативна система локалізації дозволяє перекладати статичні рядки тексту й будувати розумні перемикачі мов з мінімумом зусиль.

Об'єкт lang() дає повний контроль над структурою URL, автоматично додаючи чи оновлюючи мовний префікс у поточному URL зі збереженням параметрів запиту.

Переклад рядків тексту:

{{ t('btn_checkout', 'Proceed to Checkout') }}
Рядок-запасний варіант за замовчуванням гарантує, що макет не зламається, навіть якщо ключ перекладу ще не збережено в базі даних.

Логіка перемикача мов:

{# Get current language code (e.g., 'en') #}
{{ lang().current() }}

{# Check if specific language is active #}
{% if lang().is('ua') %}...{% endif %}

{# Generate a URL for a different language (Keeps current path) #}
<a href="{{ lang().url('ua') }}">Українська</a>

Хелпери глобальних даних

Щоб побудувати справді динамічну платформу, потрібен доступ до середовища. Pubner надає глобальні хелпери для отримання конфігурації поточного сайту, даних авторизованого користувача та даних URL.

Ці хелпери миттєво отримують дані без потреби писати складні бекенд-контролери для кожного окремого подання.

Дані конфігурації сайту:

Site Name: {{ site('name') }}
Primary Email: {{ site().email }}
Available Locales: {{ site().locales | join(', ') }}

Авторизований користувач та запити:

{# Get logged-in user name (Returns null if guest) #}
{{ auth('name') }}

{# Get current URL Path parameter #}
{{ path('slug') }}

{# Get $_GET or $_POST request data #}
{{ request('search_query') }}

Конструктор записів

Справжня сила CMS — у запитах до даних. Білдери дозволяють запитувати записи, категорії та області вашої бази даних прямо на рівні шаблонів. Вони надзвичайно швидкі й автоматично враховують мультитенантні межі вашого сайту.

Є два способи робити запити. Передайте масив фільтрів — records({...}) — для швидких декларативних вибірок, або складайте методи ланцюжком для більшого контролю. У будь-якому разі завершуйте через .get() (масив елементів), .first(), .paginate() чи .count().

Стиль масиву фільтрів (напр., пости чи товари):

{% set posts = records({
    'area': 'blog',
    'limit': 10,
    'sort': '-published_at'
}).get() %}
Передайте 'area' зі slug області, щоб обмежити запит. Для sort додайте перед полем - для спадання (напр. -published_at) або без нього для зростання.

Ланцюжок методів для точнішого контролю:

{% set posts = records()
    .where('category', 'guides')
    .search(request('q'))
    .latest()
    .limit(6)
    .get() %}
Фільтруйте через .where() / .whereNot() / .whereIn() / .like() / .search(); сортуйте через .latest() / .oldest() / .sort('-field') / .rand(); а .with() довантажує повʼязані дані одним запитом.

Пагінація результатів:

{% set page = records({'area': 'blog'}).paginate() %}
{% for post in page.data %} ... {% endfor %}
{{ paginator_links(page) }}
.paginate() повертає елементи в page.data плюс метадані сторінкування; передайте весь результат у paginator_links(), щоб відрендерити елементи керування.

Запит категорій та областей:

{# Fetch Categories for a specific Area #}
{% set blog_categories = categories({'area': 'blog'}).get() %}

{# Fetch a specific structural Area #}
{% set portfolio_area = areas({'slug': 'portfolio'}).first() %}

Виведення контенту записів

Записи та категорії зберігають насичений контент як структуровані блоки, що пишуться в блоковому редакторі панелі — окремо для кожної мови. Шаблони ніколи не працюють із сирими блоками: платформа рендерить їх у чистий HTML для поточної локалі.

Читайте відрендерений HTML з поля content і виводьте його Twig-фільтром raw — однаково для записів і категорій. На власній сторінці запису (запис, прив'язаний до маршруту) коротка змінна content доступна напряму, без префікса.

Вивести контент запису:

{# Fetch the record and render its content #}
{% set post = records({'slug': path('slug')}).first() %}

<article>
    {{ post.content | raw }}
</article>
HTML походить лише з дозволених блоків редактора — заголовки, абзаци, списки, зображення й посилання — тому його безпечно виводити через raw.

Telegram-асистент

Підключіть Telegram один раз — і сайт почне приходити до вас сам: нові заявки прилітають повідомленнями, тижневий дайджест звітує цифрами, а AI-асистент виконує ваші прохання прямо в чаті — пише пости, перекладає, оновлює контент і готує відповіді на заявки.

Щоб підключити: відкрийте Панель → Профіль, натисніть Підключити біля Telegram і тапніть Start у чаті, що відкриється. Кожен адміністратор підключає свій чат; кожен бачить лише те, що дозволяють його права в панелі. Надішліть боту /stop, щоб відвʼязати.

Асистент ніколи не діє мовчки: все, що змінює живий сайт або надсилає лист реальній людині, повертається превʼю з кнопками ✅ Підтвердити / ❌ Скасувати. Надіслані фото потрапляють у Cloud сайту; голосове стає командою.

AI-чат для відвідувачів

Ваш сайт може відповідати відвідувачам сам. AI-чат знає ваш опублікований контент — послуги, ціни, пости — і відповідає мовою відвідувача, 24/7. Він бачить лише те, що і так публічно на сайті.

Увімкніть його в Налаштування → AI-чат: оберіть акцентний колір, напишіть привітання для кожної мови, задайте денний ліміт — і чат зʼявиться на сайті. Працює з вашого AI-балансу; якщо баланс порожній — віджет просто ховається.

Автопілот контенту

Автопілот пише пости за вашим розкладом: «стаття про кавові тренди щопʼятниці», «новини нашої галузі двічі на місяць». Він може шукати свіжі факти в живому вебі й підбирає відповідні фото.

Все написане зберігається як чернетка — без вас нічого не публікується. Налаштовується в Налаштування → Автопілот контенту; коли нова чернетка готова до перегляду, прийде нотифікація (і пінг у Telegram, якщо підключений).

Пошта сайту

Ваш сайт надсилає листи від вашого імені — сповіщення про нові заявки та відповіді, які ви схвалюєте. Для цього він використовує вашого власного поштового провайдера: листи йдуть з вашої адреси і потрапляють у вхідні, а не в спам.

Налаштуйте в Налаштування → Пошта: оберіть провайдера (будь-який SMTP, Brevo, SendGrid, Mailgun), вставте його ключі й натисніть Надіслати тестовий лист. Без цього сайт просто не шле пошту — заявки все одно приходять у панель і Telegram.