Розробка кастомного шаблону Sulu

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомного шаблону Sulu
Середня
~5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка користувацького шаблону Sulu

Шаблон в Sulu складається з двох частин: XML-описання (що редагує менеджер) та Twig-шаблона (як це відображається). XML реєструє тип сторінки в системі, Twig виконує контент. Не потрібно писати контролер — Sulu використовує DefaultController, або напишіть користувацький для додання даних.

Повний XML-шаблон

<!-- config/templates/service.xml -->
<?xml version="1.0" encoding="utf-8"?>
<template xmlns="http://schemas.sulu.io/template/template"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://schemas.sulu.io/template/template
          http://schemas.sulu.io/template/template-1.0.xsd">

    <key>service</key>
    <view>pages/service</view>
    <controller>App\Controller\Website\ServiceController::indexAction</controller>
    <cacheLifetime type="seconds">1800</cacheLifetime>

    <properties>
        <property name="title" type="text_line" mandatory="true">
            <meta><title lang="uk">Заголовок</title></meta>
            <tag name="sulu.rlp.part"/>
        </property>

        <property name="intro" type="text_area" colspan="12">
            <meta><title lang="uk">Вступний текст</title></meta>
            <params>
                <param name="rows" value="3"/>
            </params>
        </property>

        <block name="content_blocks" default-type="text" colspan="12">
            <meta><title lang="uk">Блоки контенту</title></meta>
            <types>
                <type name="text">
                    <meta><title lang="uk">Текст</title></meta>
                    <properties>
                        <property name="text" type="text_editor">
                            <meta><title lang="uk">Текст</title></meta>
                        </property>
                    </properties>
                </type>
                <type name="image_text">
                    <meta><title lang="uk">Зображення + текст</title></meta>
                    <properties>
                        <property name="image" type="single_media_selection">
                            <meta><title lang="uk">Зображення</title></meta>
                        </property>
                        <property name="text" type="text_editor">
                            <meta><title lang="uk">Текст</title></meta>
                        </property>
                        <property name="image_position" type="select">
                            <meta><title lang="uk">Позиція зображення</title></meta>
                            <params>
                                <param name="values" type="collection">
                                    <param name="left" title="Ліворуч"/>
                                    <param name="right" title="Праворуч"/>
                                </param>
                            </params>
                        </property>
                    </properties>
                </type>
                <type name="cta">
                    <meta><title lang="uk">Заклик до дії</title></meta>
                    <properties>
                        <property name="heading" type="text_line">
                            <meta><title lang="uk">Заголовок</title></meta>
                        </property>
                        <property name="button_text" type="text_line">
                            <meta><title lang="uk">Текст кнопки</title></meta>
                        </property>
                        <property name="button_link" type="text_line">
                            <meta><title lang="uk">Посилання</title></meta>
                        </property>
                    </properties>
                </type>
            </types>
        </block>

        <section name="sidebar">
            <meta><title lang="uk">Бічна панель</title></meta>
            <properties>
                <property name="show_form" type="checkbox">
                    <meta><title lang="uk">Показати форму зворотного зв'язку</title></meta>
                    <params>
                        <param name="defaultValue" value="true"/>
                    </params>
                </property>
                <property name="related_services" type="smart_content">
                    <meta><title lang="uk">Подібні послуги</title></meta>
                    <params>
                        <param name="provider" value="pages"/>
                        <param name="types" value="service"/>
                        <param name="max_per_page" value="4"/>
                    </params>
                </property>
            </properties>
        </section>
    </properties>
</template>

Twig-шаблон

{# templates/pages/service.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}{{ content.title }} | {{ app.request.host }}{% endblock %}

{% block body %}
<div class="page-service">
    <div class="container">
        <header class="page-header">
            <h1>{{ content.title }}</h1>
            {% if content.intro %}
                <p class="lead">{{ content.intro }}</p>
            {% endif %}
        </header>

        <div class="service-layout {% if content.show_form %}service-layout--with-sidebar{% endif %}">
            <main class="service-content">
                {% for block in content.content_blocks %}
                    {% if block.type == 'text' %}
                        <div class="block block--text">
                            {{ block.text|raw }}
                        </div>

                    {% elseif block.type == 'image_text' %}
                        {% set img = sulu_resolve_media(block.image, locale) %}
                        <div class="block block--image-text block--image-{{ block.image_position }}">
                            {% if img %}
                                <figure>
                                    <img
                                        src="{{ img.thumbnails['service-block'] }}"
                                        alt="{{ img.title }}"
                                        loading="lazy"
                                    >
                                </figure>
                            {% endif %}
                            <div class="block__text">{{ block.text|raw }}</div>
                        </div>

                    {% elseif block.type == 'cta' %}
                        <div class="block block--cta">
                            {% if block.heading %}
                                <h2>{{ block.heading }}</h2>
                            {% endif %}
                            <a href="{{ block.button_link }}" class="btn btn--primary">
                                {{ block.button_text }}
                            </a>
                        </div>
                    {% endif %}
                {% endfor %}
            </main>

            {% if content.show_form %}
                <aside class="service-sidebar">
                    {% include 'snippets/contact-form.html.twig' %}

                    {% if content.related_services %}
                        <div class="related-services">
                            <h3>Подібні послуги</h3>
                            {% for service in content.related_services %}
                                <a href="{{ service.url }}" class="related-link">
                                    {{ service.title }}
                                </a>
                            {% endfor %}
                        </div>
                    {% endif %}
                </aside>
            {% endif %}
        </div>
    </div>
</div>
{% endblock %}

Користувацький контролер

// src/Controller/Website/ServiceController.php
namespace App\Controller\Website;

use Sulu\Bundle\WebsiteBundle\Resolver\TemplateAttributeResolverInterface;
use Sulu\Component\Content\Compat\StructureInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ServiceController extends AbstractController
{
    public function __construct(
        private readonly TemplateAttributeResolverInterface $resolver,
        private readonly TestimonialRepository $testimonials
    ) {}

    public function indexAction(
        StructureInterface $structure,
        bool $preview = false,
        bool $partial = false
    ): Response {
        $attributes = $this->resolver->resolve(
            [
                'content'      => $structure->getContent(),
                'testimonials' => $this->testimonials->findByService(
                    $structure->getUuid(),
                    limit: 3
                ),
            ],
            $structure,
            !$partial,
            $preview
        );

        $view = $partial ? 'pages/service_partial.html.twig' : 'pages/service.html.twig';

        return $this->render($view, $attributes);
    }
}

Базовий шаблон та блоки Twig

{# templates/base.html.twig #}
<!DOCTYPE html>
<html lang="{{ app.request.locale }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block title %}{{ sulu_content_path('/', webspace, locale) }}{% endblock %}</title>
    {% block stylesheets %}
        <link rel="stylesheet" href="{{ asset('build/app.css') }}">
    {% endblock %}
</head>
<body>
    {% block header %}
        {% include 'snippets/header.html.twig' %}
    {% endblock %}

    {% block body %}{% endblock %}

    {% block footer %}
        {% include 'snippets/footer.html.twig' %}
    {% endblock %}

    {% block javascripts %}
        <script src="{{ asset('build/app.js') }}" defer></script>
    {% endblock %}
</body>
</html>

Розширення Sulu Twig

{# навігація #}
{% set navigation = sulu_navigation_root_flat('main', 3) %}
{% for item in navigation %}
    <a href="{{ item.url }}"
       class="{{ item.uuid == content.uuid ? 'active' : '' }}">
        {{ item.title }}
    </a>
{% endfor %}

{# навігаційна ціль #}
{% set breadcrumb = sulu_breadcrumb() %}
{% for crumb in breadcrumb %}
    <a href="{{ crumb.url }}">{{ crumb.title }}</a>
{% endfor %}

{# фрагмент з системи фрагментів #}
{{ sulu_snippet('footer_info', 'default', locale)|raw }}

{# URL сторінки за UUID #}
<a href="{{ sulu_content_path(content.link, webspace, locale) }}">Посилання</a>

Реєстрація шаблону в Webspace

<!-- config/packages/webspaces/example.xml -->
<templates>
    <template type="page">service</template>
</templates>

Після додання шаблону — очистити кеш та переіндексувати:

php bin/console cache:clear
php bin/console sulu:document:initialize

Часові рамки

Один шаблон з блочним редактором, користувацьким контролером та Twig: 2–3 дні. Повний набір шаблонів (5–8 типів) для корпоративного сайту: 1,5–2 тижні.