pbStudio: Создаём сайт с PageBlocks – настройка и главная страница
В этом уроке мы установим MODX, настроим необходимые компоненты и системные параметры для работы с PageBlocks. Затем создадим главную страницу и добавим на неё два блока. Это часть серии уроков, в которых мы пошагово создадим полноценный сайт для студии с использованием PageBlocks.

Демо сайта.
Навигация по урокам:
1. Устанавливаем MODX — следуйте официальной инструкции.
2. Устанавливаем компоненты:
На этом базовая настройка завершена.
1. Создаем наш первый маршрут в core/App/routes/web.php:
3. Создаем базовый шаблон core/App/elements/templates/base.tpl:
Примеры готовых чанков:
4. Добавляем блоки в метод getBlocks в core/App/Models/Resource.php.
В результате получаем блок с таблицей:

и форму для слайда:

Теперь для отображения блока нам нужно создать чанк core/App/elements/chunks/slider.tpl:
Верстку возьмем отсюда
Вот и все, наш первый блок готов. Результат можно увидеть на сайте или на первой картинке в статье.

Настройка:
Описание полей:

Давайте улучшим конфигурацию колонок таблицы для блока Services:
Результат:

Создаем чанк coreApp/elements/chunks/services.tpl
Вот и всё. Главная страница готова.
Все, кто дочитал до конца, ставьте лайк! Встретимся в следующем уроке, где продолжим создавать наш сайт.

Навигация по урокам:
- pbStudio: Создаём сайт с PageBlocks – настройка и главная страница
- pbStudio: Меню и страница «О нас»
- pbStudio: Чистый контроллер или FetchIt — два способа обработки форм
- pbStudio: Портфолио, Услуги и Контакты
- pbStudio: Подключаем мультиязычность
Настройка
1. Устанавливаем MODX — следуйте официальной инструкции.
2. Устанавливаем компоненты:
- PageBlocks – основной компонент (без него никуда)
- ColorPicker – инструмент для выбора цветов
- pThumb – оптимизация изображений
- TinyMCE Rich Text Editor –мощный текстовый редактор
- pageblocks_development_mode – Developer (режим разработчика)
- pageblocks_routing – Full API (полный контроль маршрутизации)
- pageblocks_parser – pbFenom (шаблонизатор Fenom)
- Для Apache: переименуйте ht.access в .htaccess
- Для Nginx: настройте соответствующие правила
На этом базовая настройка завершена.
Главная страница
1. Создаем наш первый маршрут в core/App/routes/web.php:
use PageBlocks\App\Http\Controllers\ResourceController;
Route::get('/{alias?}', [ResourceController::class, 'index']);
Этот маршрут обрабатывает:- / — главная страница
- some_alias — другие страницы.
<?php
namespace PageBlocks\App\Http\Controllers;
class ResourceController extends Controller
{
public $classKey = \modResource::class;
public function index(string $alias = '')
{
// Условия для поиска ресурса
$where = [
'published' => 1, // ресурс должен быть опубликованный
'deleted' => 0, // не удален
];
// Если $alias пустой, значит это главная страница, иначе ищем ресурс по алиасу.
if (empty($alias)) {
$where['id'] = $this->modx->getOption('site_start', null, 1, true);
} else {
$where['alias'] = $alias;
}
// Если ресурс не найден то отдаем 404 ошибку
if (!$resource = $this->modx->getObject($this->classKey, $where)) {
abort();
}
// Можем сделать просто так:
// $this->modx->resource = $resource; // для того чтобы в шаблоне и у сниппетов был доступ к ресурсу
// но мы сделаем так, для того, чтобы поля созданные через PageBlocks тоже учитывались
$this->modx->resource = updateResource($resource);
// Возвращаем базовый шаблон
return response()->view('templates/base');
}
}
3. Создаем базовый шаблон core/App/elements/templates/base.tpl:
<!doctype html>
<html lang="en">
<head>
{block 'head'}
{insert 'chunks/head.tpl'}
{/block}
</head>
<body>
{block 'header'}
{insert 'chunks/header.tpl'}
{/block}
{*Выводим блоки*}
{block 'content'}
<div class="container">
{'!pbBlocks'|snippet: [
'fileElements' => 1,
]}
</div>
{/block}
{block 'footer'}
{insert 'chunks/footer.tpl'}
{/block}
{block 'modal'}
{insert 'chunks/modal.tpl'}
{/block}
</ body>
</html>
Это единственный шаблон сайта, который будет выводить уникальные блоки для каждой страницы.Примеры готовых чанков:
4. Добавляем блоки в метод getBlocks в core/App/Models/Resource.php.
Блок Slider
Block::make('Slider') // название блока
// chunk можем не указывать, ведь make задает не только название блока,
// но и название чанка в нижнем регистре.
// ->chunk('slider') // явно указываем имя чанка
->fields([
Field::make('Slider')
->type('table')
->fields([
Field::make('Background')
->type('image')
->sourcePath('/assets/images/')
->required(),
Field::make('Title')
->required(),
Field::make('Description')
->type('textarea'),
])
->columns([
Column::make('Background')
->width(100)
->render('image'),
Column::make('Title')
])
])
В результате получаем блок с таблицей:

и форму для слайда:

Теперь для отображения блока нам нужно создать чанк core/App/elements/chunks/slider.tpl:
Верстку возьмем отсюда
<div id="pbSlider" class="carousel slide">
<div class="carousel-indicators">
{foreach $slider as $idx => $slide}
<button type="button"
data-bs-target="#pbSlider"
data-bs-slide-to="{$idx}"
class="{!$idx ? 'active' : ''}"
aria-current="{!$idx ? 'true' : 'false'}"
aria-label="{$slide.title}"></button>
{/foreach}
</div>
<div class="carousel-inner">
{foreach $slider as $idx => $slide}
<div class="carousel-item{!$idx ? ' active' : ''}">
<img src="{$slide.background.url|pthumb:'w=1296&h=500&zc=1&f=webp'}"
class="d-block w-100 img-fluid object-fit-cover"
width="1296"
height="500"
style="min-height: 240px;"
alt="{$slide.title}">
<div class="carousel-caption mx-auto top-50 start-50 translate-middle w-100" style="max-width:650px;">
<h1>{$slide.title}</h1>
{if $slide.description}
<p class="d-none d-md-block">{$slide.description}</p>
{/if}
</div>
</div>
{/foreach}
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#pbSlider" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#pbSlider" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>
</div>
Вот и все, наш первый блок готов. Результат можно увидеть на сайте или на первой картинке в статье.
Блок Services
Этот блок немного сложнее, так как нам нужно для каждого элемента задать:- Фон блока
- Цвет текста
- Расположение текста
- Ссылку
- Картинку

Настройка:
Block::make('Services')
->fields([
// Создаем поле с типом таблица
Field::make('Services')
->type('table')
->fields([
// Фон блока
Field::make('Background')
->type('colorpicker')
->width(50)
->required(),
// Цвет текста
Field::make('Color')
->type('colorpicker')
->width(50)
->default('#ffffff')
->required(),
// Тип блока: Image или Text. Определяет набор отображаемых полей.
Field::make('Type')
->type('select')
->options([
'image' => 'Image',
'text' => 'Text',
])
->required(),
// Картинка (только для типа Image)
Field::make('Image')
->type('image')
->sourcePath('/assets/images/')
->required()
->hidden('type', '!=', 'image'),
// Заголовок (только для типа Text)
Field::make('Title')
->required()
->hidden('type', '!=', 'text'),
// Позиция текста (только для типа Text)
Field::make('Position')
->type('select')
->values([
'' => 'Center',
'mt-auto' => 'Bottom'
])
->required()
->hidden('type', '!=', 'text'),
// Описание (только для типа Text)
Field::make('Description')
->type('richtext')
->hidden('type', '!=', 'text'),
// ID ресурса для ссылки, прячем поле, если тип не выбран
Field::make('resource_id')
->label('Resource')
->type('resourcelist')
->hidden('type', '=', ''),
])
// Настройка колонок для таблицы
->columns([
Column::make('Background')
->render('color'),
Column::make('Color')
->width(100)
->render('color'),
Column::make('Title')
->default('---')
])
]),
Описание полей:
- Background: Выбор цвета фона блока с помощью colorpicker. Обязательное поле.
- Color: Выбор цвета текста с помощью colorpicker. По умолчанию #ffffff (белый). Обязательное поле.
- Type: Выбор типа блока:
- Image: Отображает только изображение.
- Text: Отображает заголовок, описание и позволяет выбрать позицию текста. Обязательное поле. В зависимости от выбранного типа, отображаются соответствующие поля.
- Image: Загрузка изображения. Отображается только если Type равен Image. Указывает путь к папке с изображениями (sourcePath).
- Title: Заголовок текста. Отображается только если Type равен Text. Обязательное поле.
- Position: Выбор позиции текста (центр или низ). Используется для вертикального выравнивания текста. Отображается только если Type равен Text. Обязательное поле.
- Description: Описание блока. Отображается только если Type равен Text. Использует редактор richtext для форматирования текста.
- Resource (resource_id): Выбор ресурса (страницы) для создания ссылки. Отображается если выбран какой-то Type.

Давайте улучшим конфигурацию колонок таблицы для блока Services:
->columns([
Column::make('Colors')
// Группируем фон и цвет текста в одну колонку
->group([
Column::make('Background')
->render('color')
->withLabel(), // Добавляем заголовок, который равен имени колонки (Background)
Column::make('Color')
->render('color')
->withLabel('Color text'), // Добавляем кастомный заголовок
]),
Column::make('Title / Image')
// Добавляем условие, показываем заголовок или картинку
->renderIf([
Column::make('Title'),
Column::make('Image')->render('image')
])
])
Результат:

Создаем чанк coreApp/elements/chunks/services.tpl
<section class="services d-flex flex-wrap">
{foreach $services as $item}
<div class="service-item col-12 col-xl-6"
style="background-color: {$item.background};color:{$item.color?:'#fff'}">
{if $item.type === 'text'}
<h2 class="{$item.position}">{$item.title}</h2>
{if $item.description}
<div class="mb-0 {$item.position}">{$item.description}</div>
{/if}
{else}
<img loading="lazy"
src="{$item.image.url}"
class="img-fluid mx-auto"
width="236"
height="236"
alt="{$item.image.title}">
{/if}
{if $item.resource_id}
<a href="{$item.resource_id|url}"
class="service-link text-decoration-none d-flex align-items-center gap-3 position-absolute bottom-0 end-0">
{if $item.type === 'image'}
<span>{$item.resource_id|resource:'pagetitle'}</span>
{/if}
<svg fill="{$item.color}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
<path d="M502.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-128-128c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L402.7 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l370.7 0-73.4 73.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l128-128z"/>
</svg>
</a>
{/if}
</div>
{/foreach}
</section>
Вот и всё. Главная страница готова.
Все, кто дочитал до конца, ставьте лайк! Встретимся в следующем уроке, где продолжим создавать наш сайт.
Поблагодарить автора
Отправить деньги
Комментарии: 6
Инструкция! Вот чего не хватало, спасибо!!!
Зачем нужен пункт 1 и 2? Ведь это решается стандартными полями ресурса в MODX?
Зачем нужен пункт 1 и 2? Ведь это решается стандартными полями ресурса в MODX?
Зачем нужен пункт 1 и 2? Ведь это решается стандартными полями ресурса в MODX?Для полного контроля над страницами и логикой отображения.
Block::make('Services')
->fields([
// Создаем поле с типом таблица
Field::make('Services')
->type('table')
->fields([
Учить еще один язык програмирования, который типа облегчает контроль над html и css. Как говориться плох тот разработчик который не написал свой фрейворк.PageBlocks может и не плох но как же уже достал это зоопарк фреймворков :-)
- Режим менеджера — можно делать тоже самое, только клацать в админке
- Режим разработчика — управлять всем через файлы, но нужно немного поучиться, зато получаем чистый и управляемый код, который можно хранить на гитхабе
PageBlocks может и не плох но как же уже достал это зоопарк фреймворков :-)PageBlocks имеет 2 режима разработки:
Как говорится, плох тот разработчик, который не учится новому!Новое так быстро выходит что не успеваешь написать программу как подоспел новый фреймворк и надо переписывать прогу уже с новым фреймворком.
П.С. Хочестя в тикеты при сохранении комментария добавить кнопку «проверить на ошибки» и при нажатии на нее проверить комментарий на ошибки в нейросети сохранить проверенный вариант. Но пока еще не настолько хочется чтоб взять и сделать :-)
Хочестя в тикеты при сохранении комментария добавить кнопку «проверить на ошибки» и при нажатии на нее проверить комментарий на ошибки в нейросети сохранить проверенный вариант.Хорошая идея. Запишу себе и реализую, когда буду делать уроки по созданию блога на PageBlocks.
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.