Практика PageBlocks: собираем страницы Услуг и Контактов как конструктор

Практика PageBlocks Курс

В предыдущем уроке мы проделали огромную работу — собрали Главную страницу. Теперь мы пожнем плоды этого труда.

В этом уроке мы применим принцип переиспользования. Мы увидим, что 80% внутренних страниц (Услуги, Контакты) собираются из уже готовых блоков, как конструктор Lego. Нам останется лишь создать пару специфических секций.

Наша цель — собрать 3 шаблона:

  1. Услуги (Services List): общий список услуг.
  2. Детальная услуга (Service Detail): страница конкретной услуги.
  3. Контакты (Contact Us): страница с контактной информацией.

Этап 1: «Универсальный солдат» (Page Header)

Если вы откроете исходный код файлов services.tpl , services_detail.tpl или contacts.tpl , вы увидите абсолютно одинаковую верхнюю секцию: серый фон, декоративные круги, заголовок и хлебные крошки.

Page Header

Чтобы не дублировать код, мы создадим один универсальный блок, который будем ставить на все внутренние страницы. Для этого:

  1. Создайте блок «Заголовок страницы» (pb.page_header).
  2. Поля: title (Текст), bg_class (Текст), значение по умолчанию custom-bg-color-light-1 (можно менять цвет) и show_breadcrumbs (Да/Нет), чтобы можно было скрыть крошки, при этом не скрывая заголовок.
  3. Код: скопируйте HTML верхней секции из любого файла (например, services.tpl).
  4. Модернизация: вместо статичной верстки <ul>...</ul> вставьте вызов сниппета pdoCrumbs, чтобы они строились автоматически.

В конечном итоге должен примерно такой получится чанк pb.page_header:

<section class="page-header page-header-lg {$bg_class ?: 'custom-bg-color-light-1'} border-0 m-0">
    <div class="container position-relative pt-5 pb-4 mt-5">
        {* --- Декоративные круги (оставляем как есть, это часть дизайна) --- *}
        <div class="custom-circle custom-circle-wrapper custom-circle-big custom-circle-pos-1 custom-circle-pos-1-1 appear-animation" data-appear-animation="expandInWithBlur" data-appear-animation-delay="900" data-appear-animation-duration="2s">
            <div class="bg-color-tertiary rounded-circle w-100 h-100" data-plugin-float-element data-plugin-options="{ 'startPos': 'bottom', 'speed': 0.5, 'transition': true, 'transitionDuration': 1000 }"></div>
        </div>
        {* ... остальные круги (копируем из исходника contacts.tpl строки 393-396) ... *}
        <div class="custom-circle custom-circle-medium custom-circle-pos-2 custom-circle-pos-2-2 appear-animation" data-appear-animation="expandInWithBlur" data-appear-animation-delay="1450" data-appear-animation-duration="2s">
            <div class="custom-bg-color-grey-2 rounded-circle w-100 h-100" data-plugin-float-element data-plugin-options="{ 'startPos': 'bottom', 'speed': 0.2, 'transition': true, 'transitionDuration': 2000 }"></div>
        </div>
        {* ... (сократил код кругов для читаемости) ... *}

        <div class="row py-5 mb-5 mt-2 p-relative z-index-1">
            <div class="col">
                {if $show_breadcrumbs != 0}
                <div class="overflow-hidden">
                    {* Вызов хлебных крошек MODX *}
                    {'pdoCrumbs' | snippet : [
                        'showHome' => 1,
                        'tplWrapper' => '@INLINE <ul class="breadcrumb d-block text-center appear-animation" data-appear-animation="maskUp" data-appear-animation-delay="800">{$output}</ul>',
                        'tpl' => '@INLINE <li><a href="{$link}">{$menutitle}</a></li>',
                        'tplCurrent' => '@INLINE <li class="active">{$menutitle}</li>'
                    ]}
                </div>
                {/if}
                
                <div class="overflow-hidden mb-4">
                    <h1 class="d-block text-color-quaternary font-weight-bold text-center mb-0 appear-animation" data-appear-animation="maskUp" data-appear-animation-delay="1000">
                        {$title ?: $_modx->resource.pagetitle}
                    </h1>
                </div>
            </div>
        </div>
    </div>
</section>

Этап 2: Раздел услуг

А. Страница «Все услуги» (services.tpl)

Services

Посмотрите на макет страницы списка услуг. Он состоит из:

  1. Шапки.
  2. Сетки услуг (точно такой же, как на Главной).
  3. Формы SEO Аудита (почти точно такой же, как на Главной).

Так как форма содержит тот же набор полей, что и на главной, поступим следующим образом:

  1. Сделайте копию конфигурации блока Форма захвата (после нажмите Да).
    Делаем копию
  2. Далее заходим в копию конфигурации и переименовываем ее в «Форма захвата перед Footer» и пишем новое название чанка pb.footer_form и сохраняем.
    Переименовываем и сохраняем конфигурацию
  3. Создаем чанк pb.footer_form со следующим кодом:
    <section class="section section-height-3 bg-color-secondary position-relative border-0 m-0">
        <div class="container position-relative z-index-1 pt-2 pb-5 mt-3 mb-5">
            <div class="row justify-content-center mb-3">
                <div class="col-md-8 col-lg-6 text-center">
                    <div class="overflow-hidden mb-2">
                        <h2 class="font-weight-bold text-color-light text-7 line-height-2 mb-0 appear-animation" data-appear-animation="maskUp" data-appear-animation-delay="250">{$title}</h2>
                    </div>
                    <div class="overflow-hidden mb-1">
                        <p class="lead custom-text-color-light-1 mb-0 appear-animation" data-appear-animation="maskUp" data-appear-animation-delay="400">{$subtitle_1}</p>
                    </div>
                    {if $subtitle_2}<div class="overflow-hidden mb-3">
                        <p class="custom-text-color-light-1 mb-0 appear-animation" data-appear-animation="maskUp" data-appear-animation-delay="550">{$subtitle_2}</p>
                    </div>{/if}
                </div>
            </div>
            <div class="row mb-3">
                <div class="col">
                    {* Здесь будет вызов FetchIt *}
                </div>
            </div>
        </div>
    </section>

Сборка:

  1. Создайте ресурс «Услуги».
  2. В PageBlocks добавьте:
    • Блок «Заголовок страницы» (создан на Этапе 1).
    • Блок «Сетка Услуг» (создан в уроке про Главную).
    • Блок «Форма захвата перед Footer».

Готово. Страница собрана.

Б. Детальная страница услуги (services_detail.tpl)

Здесь уже есть полу-уникальный контент:

Контент услуги

Подобный блок мы уже создавали на более широком блоке на главной (Блок 3: О нас + Фичи), по сути одно и тоже, единственное нужно убрать слайды и добавить поле контент и все). Самое простое это создать копию конфигурации блока О нас + Фичи, Назвать к примеру Контент услуги (чанк pb.service_content) и поле Логотипы переименовываем в Контент и меняем тип на Текстовый редактор.

Делаем копию конфигурации и немного правим ее

Это поле перенесите мышкой в самый низ.

Мы создаем отдельную конфигурацию, чтобы в админке этот блок имел понятное название «Форма захвата перед Footer» и менеджер не путался, хотя технически это почти копия блока с главной.

Ну и создаем чанк pb.service_content:

<section class="section bg-color-light position-relative border-0 pt-0 m-0">
    <svg class="custom-page-header-curved-top-1" width="100%" height="700" xmlns="http://www.w3.org/2000/svg">
        <path transform="rotate(-3.1329219341278076 1459.172607421877,783.5322875976566) " d="m-12.54488,445.11701c0,0 2.16796,-1.48437 6.92379,-3.91356c4.75584,-2.42918 12.09956,-5.80319 22.45107,-9.58247c20.70303,-7.55856 53.43725,-16.7382 101.56202,-23.22255c48.12477,-6.48434 111.6401,-10.27339 193.90533,-7.05074c41.13262,1.61132 88.20271,5.91306 140.3802,12.50726c230.96006,32.89734 314.60609,102.57281 635.26547,59.88645c320.65938,-42.68635 452.47762,-118.72154 843.58759,3.72964c391.10997,122.45118 553.23416,-82.15958 698.49814,-47.66481c-76.25064,69.23438 407.49874,281.32592 331.2481,350.5603c-168.91731,29.52009 85.02254,247.61162 -83.89478,277.13171c84.07062,348.27313 -2948.95065,-242.40222 -2928.39024,-287.84045" stroke-width="0" stroke="#000" fill="#FFF" id="svg_2"/>
    </svg>
    <div class="container pb-2 mb-4">
        <div class="row justify-content-center align-items-center">
            <div class="col-md-10 col-lg-6 mb-5 mb-lg-0">
                <h2 class="text-color-dark font-weight-semibold text-6 line-height-3 mb-0 pe-5 me-5 appear-animation" data-appear-animation="fadeInUpShorter" data-appear-animation-delay="1600">{$title}</h2>
                <span class="d-block mb-3 appear-animation" data-appear-animation="fadeInUpShorter" data-appear-animation-delay="1800">{$subtitle}</span>
                <p class="lead pe-5 mb-4 pb-2 appear-animation" data-appear-animation="fadeInUpShorter" data-appear-animation-delay="2000">{$lead}</p>
                
                {* Список фич *}
                {foreach $features as $item}
                <div class="feature-box appear-animation" data-appear-animation="fadeInUpShorter" data-appear-animation-delay="2200">
                    <div class="feature-box-icon custom-feature-box-icon-size-1 bg-color-{$item.color} top-0">
                        <i class="{$item.icon} position-relative left-1"></i>
                    </div>
                    <div class="feature-box-info mb-4 pb-3">
                        <h4 class="font-weight-bold line-height-3 custom-font-size-1 mb-1">{$item.title}</h4>
                        <p class="mb-0">{$item.text}</p>
                    </div>
                </div>
                {/foreach}
            </div>

            {* ПРАВАЯ КОЛОНКА (Изображение) *}
            <div class="col-md-6 col-lg-5 offset-lg-1 ps-5 pb-lg-3 mb-md-4 mb-lg-5 appear-animation" data-appear-animation="fadeInRightShorter" data-appear-animation-delay="2200">
                {if $image}
                    <img src="{$image.url}" class="img-fluid mb-4 pb-2" width="100%" alt="" />
                {/if}
            </div>

            {* НИЖНИЙ ТЕКСТ *}
            <div class="col-md-10 col-lg-12 mt-4">
                <div class="appear-animation" data-appear-animation="fadeInUpShorter" data-appear-animation-delay="250">
                    {$content}
                </div>
            </div>
        </div>
    </div>
</section>

И еще у нас есть полу уникальный блок: Простой слайдер отзывов, который точно такой же как кусок блока на главной (Блок 6: Статистика и отзывы). Думаю самостоятельно справитесь с конфигурацией полей этого блока (таблица уже создана, можно использовать ее), я назвал его чанк pb.testimonials_simple и вот такой код у меня получился:

<section class="section bg-color-light position-relative border-0 pb-4 m-0">
	<svg class="custom-section-curved-top-6" width="100%" height="600px" xmlns="http://www.w3.org/2000/svg">
	  	<path id="svg_1" d="m-12.66406,442.40068c352.72654,-76.36348 565.45337,5.45453 696.36219,19.99996c130.90882,14.54542 270.90852,-23.63632 367.27196,-47.27263c96.36344,-23.63631 379.99921,-154.54513 527.27163,-209.09047c147.27242,-54.54535 381.813,-92.55755 406.36076,-99.00598c12.27388,-3.22421 917.96684,-113.93032 715.00991,10.61478c-202.95693,124.5451 -210.46055,521.28714 -198.64021,540.29354c11.82034,19.0064 -2500.90899,-15.53962 -2500.0019,-16.36399c-0.90709,0.82437 -9.99798,-180.99343 -9.09089,-181.8178" stroke-opacity="null" stroke-width="0" stroke="#000" fill="#f7f8f9"></path>
	  	<path id="svg_2" d="m-116.90461,507.88064c314.5448,-112.72704 523.63527,-21.81814 878.17999,12.72724c354.54471,34.54538 632.72595,-225.45407 978.17978,-294.54484c172.72691,-34.54538 291.36195,-62.7275 368.52007,-78.40952c77.15812,-15.68202 352.84215,-22.50036 359.66142,-7.04537c13.63854,30.90997 97.72734,614.54347 50.90961,639.99858c-46.81772,25.4551 -855.68236,4.54593 -1433.63569,1.81866c-577.95334,-2.72727 -1155.90718,-5.45466 -1155.45364,-5.45491" stroke-opacity="null" stroke-width="0" stroke="#000" fill="#fbfcfc"></path>
	  	<path id="svg_3" d="m-115.93584,623.27542c234.54496,-132.72699 429.09001,-112.72703 678.1804,-83.63619c249.09039,29.09085 389.09011,30.90903 656.36228,-107.2725c267.27217,-138.18153 816.36193,-207.2723 1121.81584,-170.90873c305.45391,36.36356 -292.72666,-19.99996 -293.63778,-18.18228c71.36548,8.18218 627.05432,68.63506 626.48637,265.22584c-0.56794,196.59079 -20.11364,456.59134 -31.02284,531.13767c-10.90919,74.54633 -1561.82313,-36.3646 -1565.45948,-34.54642c-3.63636,1.81818 -1249.08831,-1.81818 -1248.18122,-1.81869c-0.90709,0.00051 39.09282,-234.54445 39.99992,-234.54496c-0.9071,0.00051 -4.54345,-76.36297 -3.63636,-76.36348" stroke-opacity="null" stroke-width="0" stroke="#000" fill="#ffffff"></path>
	</svg>
	<div class="container position-relative z-index-1">
		<div class="row mt-5 mb-5">
			<div class="col-lg-4 pe-lg-0">
				<h2 class="text-color-dark font-weight-semibold text-6 line-height-3 mb-3">{$reviews_title}</h2>
				<p>{$reviews_text}</p>
			</div>
            <div class="col-lg-8 ps-lg-4">
                <div class="owl-carousel custom-carousel-style-1 custom-carousel-dots-style-1" data-plugin-options="{ 'responsive': { '0': { 'items': 1 }, '479': { 'items': 1 }, '979': { 'items': 2 }, '1199': { 'items': 2 } }, 'margin': 0, 'loop': true, 'dots': true, 'nav': false, 'autoplay': true }">
                    {foreach $reviews as $review}
                        <div>
                            <div class="testimonial testimonial-style-3 custom-testimonial-style-1">
                                <blockquote>
                                    <p class="mb-0">{$review.text}</p>
                                </blockquote>
                                <div class="testimonial-author">
                                    <div class="testimonial-author-thumbnail">
                                        <img src="{$review.photo.url}" class="img-fluid rounded-circle" alt="{$review.author}">
                                    </div>
                                    <p><strong class="font-weight-semibold text-4 mb-1">{$review.author}</strong><span class="text-2">{$review.role}</span></p>
                                </div>
                            </div>
                        </div>
                    {/foreach}
                </div>
            </div>
	</div>
	</div>
</section>

Сборка. В ресурсе услуги собираем конструктор: Заголовок страницы + Контент услуги + Тарифы (с главной) + Простой слайдер отзывов + Форма захвата перед Footer.

Этап 3: Страница контактов (contacts.tpl)

На этой странице у нас только 1 уникальный блок с контактной информацией (остальные уже есть):

Блок с контактной информацией

Первым делом создайте таблицу: info_boxes — Контактные данные (Адрес, Телефон, Email), со следующим набором полей:

  • icon (Текст, класс иконки, например icon-globe icons), title (Текст), content (RichText — чтобы можно было вставить ссылку <a>).
  • color (Select: secondary, quaternary — для цвета иконки).

Теперь создаем блок «Контакты + Форма». Чанк: pb.seo_contacts. со следующим набором полей:

  1. title (Текст) — Заголовок (Free Website Audit…).
  2. subtitle (Текст) — Надзаголовок (CONTACT US TODAY).
  3. description (Textarea) — Описание.
  4. icon (Таблица) — Контакты.
  5. form_title (Текст) — Заголовок над формой.
  6. form_text (Textarea) — Текст над формой.

Чанк pb.contacts:

<section class="section bg-color-light position-relative border-0 pt-3 m-0">
    {* SVG Background *}
    <svg class="custom-page-header-curved-top-1" width="100%" height="700" xmlns="http://www.w3.org/2000/svg">
        <path d="..." fill="#FFF" id="svg_2"/> 
        {* (Полный путь SVG берем из contacts.tpl строки 399) *}
    </svg>

    <div class="container pb-2 mb-4">
        <div class="row justify-content-center">
            
            {* ЛЕВАЯ КОЛОНКА: ИНФОРМАЦИЯ *}
            <div class="col-md-10 col-lg-6 mb-5 mb-lg-0">
                <div class="overflow-hidden mb-1">
                    <h2 class="text-color-dark font-weight-semibold text-6 line-height-3 mb-0 pe-5 me-5 appear-animation" data-appear-animation="maskUp" data-appear-animation-delay="1200">{$title}</h2>
                </div>
                <div class="overflow-hidden mb-3">
                    <span class="d-block mb-0 appear-animation" data-appear-animation="maskUp" data-appear-animation-delay="1400">{$subtitle}</span>
                </div>
                <p class="lead pe-5 mb-4 pb-2 appear-animation" data-appear-animation="fadeInUpShorter" data-appear-animation-delay="1600">{$description}</p>

                {* Цикл по инфо-боксам *}
                {foreach $info_boxes as $item}
                <div class="feature-box align-items-center mb-4 pb-1 appear-animation" data-appear-animation="fadeInUpShorter" data-appear-animation-delay="1800">
                    <div class="feature-box-icon custom-feature-box-icon-size-1 bg-color-{$item.color} top-0">
                        <i class="{$item.icon} position-relative"></i>
                    </div>
                    <div class="feature-box-info">
                        <h4 class="font-weight-bold line-height-1 custom-font-size-1 mb-1">{$item.title}</h4>
                        <p class="mb-0">{$item.content}</p>
                    </div>
                </div>
                {/foreach}
            </div>

            {* ПРАВАЯ КОЛОНКА: ФОРМА *}
            <div class="col-md-10 col-lg-6 appear-animation" data-appear-animation="fadeInRightShorter" data-appear-animation-delay="2000">
                <div class="card border-0 custom-border-radius-1 box-shadow-1 p-2">
                    <div class="card-body p-4 z-index-1">
                        <h4 class="text-color-dark font-weight-semibold text-5 line-height-3 ls-0 mb-1 mt-2 pe-5 me-5">{$form_title}</h4>
                        <p class="pb-2 mb-4">{$form_text}</p>

                        {* Здесь место для вызова FetchIt. Пока оставляем верстку *}
                        <form class="contact-form custom-form-style-1" action="{$_modx->resource.id | url}" method="POST">
                            <div class="contact-form-success alert alert-success d-none mt-4">
                                <strong>Success!</strong> Your message has been sent to us.
                            </div>
                            <div class="contact-form-error alert alert-danger d-none mt-4">
                                <strong>Error!</strong> There was an error sending your message.
                            </div>

                            <div class="row">
                                <div class="form-group col mb-3">
                                    <input type="text" maxlength="100" class="form-control custom-bg-color-light-1 border-0" name="name" placeholder="Your Name" required>
                                </div>
                            </div>
                            <div class="row">
                                <div class="form-group col mb-3">
                                    <input type="email" maxlength="100" class="form-control custom-bg-color-light-1 border-0" name="email" placeholder="E-mail Address" required>
                                </div>
                            </div>
                            <div class="row">
                                <div class="form-group col mb-3">
                                    <textarea maxlength="5000" rows="6" class="form-control custom-bg-color-light-1 border-0" name="message" placeholder="Your Message" required></textarea>
                                </div>
                            </div>
                            <div class="row">
                                <div class="form-group col mb-3">
                                    <input type="submit" value="SEND NOW" class="btn btn-gradient btn-rounded font-weight-bold px-5 py-3 text-3">
                                </div>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
</section>

Форму мы оставили HTML-кодом, но подготовим её для работы с FetchIt (добавим необходимые классы и ID, если нужно).

Сборка:

  1. Создайте ресурс «Контакты».
  2. В PageBlocks добавьте:
    • Блок «Заголовок страницы» (создан на Этапе 1).
    • Блок «Контакты + Форма» (создан только что).
    • Блок «Форма захвата перед Footer» (создана в этом уроке).

Готово. Страница собрана.

Итоги урока

Поздравляю! Только что вы на практике ощутили главную мощь компонентного подхода в MODX.

Мы собрали три абсолютно разные по структуре страницы (Список услуг, Детальная услуга, Контакты), потратив минимум времени на написание нового кода. Мы создали всего пару уникальных блоков, а остальные 80% контента собрали из уже готовых «кубиков», которые сделали для Главной страницы.

Именно так выглядит современная и быстрая разработка сайтов.

Что дальше? Сейчас наши страницы выглядят отлично, но формы на них пока «мертвые» — это просто HTML-каркас. В следующем уроке мы вдохнем в них жизнь: интегрируем компонент FetchIt, настроим отправку заявок на почту и сделаем валидацию полей, не ломая при этом красивую верстку.

Оцените статью
MODX 3
Добавить комментарий

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.