Как стать автором
Обновить

Как я попробовал Grav CMS

Время на прочтение13 мин
Количество просмотров8.5K

Несколько лет назад во время учебы я достаточно активно брал фрилансерские заказы и поработал тогда с различными движками для создания сайтов. Постепенно я свел подобные заказы к минимуму - когда ко мне обращаются сделать небольшой сайт на WordPress или сверстать одностраничник, то я стараюсь либо перенаправить на кого-то из знакомых, либо отказываюсь. Иногда, всё-таки, я за что-то берусь от скуки или если присутствует какой-нибудь интерес. И вот об опыте выполнения одного из таких заказов будет рассказано под катом. 

На днях ко мне обратились с уже готовой темой (html + css + js) одностраничника (landing, лэндинг) и попросили сделать каждый текст и изображение в ней редактируемыми пользователем (заказчиками) в админке на разных языках. Получается, мне необходимо было сделать форму с полями разных типов (text, textarea, number и т.д.) и возможностью заливать картинки на разных языках. В шаблоне есть несколько слайдеров, в одном из которых, помимо изображений, еще и разные наборы свойств для продукта, то есть, в админке необходима возможность создавать динамические списки форм - массивы объектов.

Для реализации поставленной задачи первая мысль была взять WP + ACF PRO + WPML и сделать из одностраничника тему для WordPress, но поскольку я давно подобное не делал, решил проверить наличие альтернатив. Моей целью было найти инструмент (библиотеку, движок, фреймворк) или наработку (например, репозиторий на гитхабе), с которой я смогу выполнить поставленную задачу быстрее, чем на WordPress. Также мне хотелось еще OpenSource решение, и на php, чтобы можно было разместить это на простых хостингах (у заказчика как раз такой уже есть). Каким-то запросом в гугле я наткнулся на Grav CMS, отдельно мне рекомендовали OctoberCMS, но в случае с OctoberCMS необходимо платить за лицензию для каждого сайта, а Grav бесплатный, поэтому решил начать с него. 

Чуть позже я нагуглил топ, в котором Grav еще и оказался на первом месте. Не буду зарекаться, что я тщательно изучил этот топ, но на пару десятков сайтов CMS из топа я заходил, в итоге ничего более простого и подходящего под нужды, чем Grav, я не нашел.

Grav - flat-file CMS, то есть все данные (страницы, настройки, пользователи) хранятся в файлах, без использования базы данных. На странице загрузок предлагается скачать чистую CMS или с уже предустановленным плагином для админки. В чистом виде ведение сайта на Grav больше напоминает работу с генераторами статических сайтов (для добавления страниц необходимо создавать markdown файлы), только компилировать и деплоить не нужно, а админка уже очень напоминает WordPress.

Установка

Когда я стал пробовать Grav у себя на компе, просто сделал git clone репозитория в папку веб-сервера, открыл в браузере, и сайт на CMS сразу открылся. Плагин для админки я установил отдельно. Учитывая, что там в топе тысяча с лишним разных CMS, проблема при установке или запуске могла бы меня спугнуть. В документации я заведомо прочитал про строение темы и про кастомные поля. Как и в WordPress, для создания темы необходимо создать папку в нужном месте (в случае с Grav это user/themes/), положить в нее файлы с объявлениями темы и активировать в админке. Рядом с информацией о полях в документации есть и туториал по быстрому созданию темы: необходимо установить плагин devtools и с помощью него можно создать простую тему для редактирования. В консоли это делается таким образом:

bin/gpm install devtools
bin/plugin devtools new-theme

Необходимо ввести данные для темы, и готово. Внутренняя конфигурация не только темы, но и в целом всего в Grav прописывается в .yaml файлах. Для генерации html используется twig, также можно на php подвязаться на события, но мне так и не пришлось ничего писать на php.

Настройки темы

В файле в корне темы в blueprints.yaml описываются общие настройки темы и дополнительно можно добавить form секцию. В моем случае, в самом верху лэндинга есть слайдер, и я прописал следующее:

form:
 fields:
   top_slider:
     type: file
     label: 'Slider images'
     multiple: true
     description: 'Dimensions 1200 x 757'
     destination: 'theme://assets/'
     accept:
       - image/

Тут я добавил одно поле для заливки файлов (type: file), в accept можно прописать список допустимых файлов. Меня интересуют только картинки, поэтому я прописал image/; с помощью multiple я указал, что их может быть несколько; label и description - это то, что будет отображено на форме в админке пользователю; в destination я указал, куда всё будет сохраняться - в папку assets внутри моей темы. Этот кусочек я взял из примеров в доке (уже после я прочитал про все возможные свойства). 

И так это выглядит в админке:

Заказчика такой вариант редактирования более чем устраивает. 

Дальше мне потребовалось отобразить изображения на самой странице в слайдере. В Grav можно указывать шаблон для страниц, и для добавления шаблонов в теме в папке templates нужно добавлять файл <название шаблона>.html.twig. Поскольку я создавал тему с помощью devtools, я воспользовался уже имеющимися там заготовками, скопировал весь html лэндинга в default.html.twig и там, где находится слайдер, прописал на твиге следующее:

{% for slide in theme.top_slider %}
   <div class="image">
       <img src="{{ url(slide.path) }}" alt="">
   </div>
{% endfor %}

Думаю, и так понятно, что top_slider - это ключ, который я прописал в blueprints.yaml. Циклом я прохожусь по массиву файлов и url возвращает релативный путь к изображению.

С использованием функции url в других местах лэндинга я прописал пути на ассеты темы, предварительно сложив их в папку темы. Пример: 

<link href="{{ url('theme://fonts/font-awesome.css') }}" rel="stylesheet" type="text/css">

Про префикс theme:// я узнал из примеров в доке, как минимум есть еще plugin://

Кстати, для добавления css и js на страницу в Grav предусмотрен Asset manager и вот как у меня выглядит добавление css файла:

{% do assets.addCss('theme://css/style.css') %}

Но рендерятся все добавленные стили при вызове:

{{ assets.css()|raw }}

Достаточно быстро я интегрировал html тему в Grav тему, попробовал настройки и оставалось только разобраться с мультиязычностью. Уже на текущем этапе мне понравилось работать с Grav и если бы возникли сложности с тем, чтобы это дело переводить, было бы даже обидно отсекать её и рассматривать другую CMS. 

Мультиязычность

Если в WordPress для поддержки мультиязычности нужно выбирать между плагинами WPML, qTranslate (X), Polylang и другими, устанавливать и настраивать, то в Grav всё работает из коробки. На странице настроек есть таб (чисто формальности ради: вертикальный таб “Languages” внутри горизонтального таба “System”) для настройки языков:

Как-то интуитивно, без чтения доки, я ввел двухбуквенные обозначения языков через запятую, сохранил, зашел на редактирование страницы и нашел там возможность сохранить страницу для отдельного языка:

Настройки страницы

В настройках темы не появилось возможности сохранять настройки для разных языков, и потому я стал разбираться, как добавить пользовательские поля для редактирования на страницу редактирования страницы. Да простят меня филологи за последнее сообщение (и за всю статью тоже). 

По умолчанию на странице уйма табов с полями для редактирования, но Grav позволяет добавлять свои поля для каждого шаблона. Для этого необходимо внутри темы в папке blueprints создать .yaml файл с названием шаблона и прописывать настройки в нем. В моем случае я добавил файл default.yaml, не стал использовать extends@, как в примерах в доке, а перетер все табы, дабы не возникало путаницы, оставил только необходимые настройки для лэндинга и обрадовался, увидев в админке поля, которые сохраняются по отдельности для каждого языка, а потом еще и отображаются по-разному для http://domain/en и http://domain/ru.

Как уже писал ранее если бы админки не было, то работа с CMS напоминала бы работу с генераторами статических сайтов: для страниц пришлось бы создавать папку, в ней файлы с markdown разметкой, в которых вверху (header) можно описывать настройки:

---
form: 
name: contact-form
custom_key: value
---

Page context here.

Всё, что между шестью (3 * 2) черточками - это настройки в формате yaml. В контексте страниц form служит для описания контактной формы. А вот custom_key - это уже моя настройка, которую я могу вывести где-нибудь в шаблоне страницы через ключ {{ page.header.custom_key }}. Отсюда получается, что для описания кастомных настроек в blueprints их необходимо помещать в page header, то есть ключи для всех настроек должны начинаться с header..

Пример настраиваемого блока

Самый, наверное, структурно сложный момент для реализации в моей задаче выглядит так:

Список апартаментов, у каждого из них есть картинка, а также динамический список свойств (Bedrooms, Garages и т.д.) внутри слайдера. Вот как я это прописал в .yaml:

header.apartments:
 type: fieldset
 title: 'Apartments block'
 collapsible: true
 collapsed: true
 fields:
   header.apartments_heading:
     type: text
     label: 'Heading'
   header.apartments_area_caption:
     type: text
     label: 'Area caption'
   header.apartments_button:
     type: text
     label: 'Contact button text'
   header.apartments_items:
     type: list
     label: 'Items'
     style: horizontal
     fields:
       .heading:
         type: text
         label: 'Heading'
       .text:
         type: textarea
         label: 'Text'
       .attributes:
         type: array
         label: 'Apartment'
         placeholder_key: 'Attribute'
         placeholder_value: 'Count'
       .area:
         type: number
         label: 'Area'
       .image:
         type: file
         label: 'Image'
         description: 'Dimensions 945 x 550'

А в админке это выглядит так:

Не буду копировать весь html из .twig файла с посторонней версткой, а покажу самое интересное:

{% for apartment in page.header.apartments_items %}
   <img src="{{ url(apartment.image|first.path) }}">
{% endfor %}

{% for apartment in page.header.apartments_items %}
   <div class="slide">
       <h3>{{ apartment.heading }}</h3>
       <div class="description">
           <div class="row">
               <div class="col-md-6 col-sm-6">
                   <dl>
                       {% for attr, value in apartment.attributes %}
                           <dt>{{ attr }}:</dt>
                           <dd>{{ value }}</dd>
                       {% endfor %}
                       <dt>{{ page.header.apartments_area_caption }}:</dt>
                       <dd>{{ apartment.area }} m<sup>2</sup></dd>
                   </dl>
               </div>
               <div class="col-md-6 col-sm-6">
                   <p>{{ apartment.text }}</p>
               </div>
           </div>
       </div>
   </div>
{% endfor %}

Выходит, я прописал динамический список объектов, у которого, помимо картинки и текста, еще есть массив (здесь важно понимать, что в php array - это и массив, и объект/hashmap), в который можно добавлять ключ-значение пары.

Контактная форма

Лэндинг не лэндинг без контактной формы. В Grav CMS предусмотрена достаточно гибкая функциональность по настройке контактных форм, но в админке для этого удобного UI не реализовано. 

Редактирование страницы в админке доступно в двух режимах: Normal и Expert.

В expert режиме редактирование контента предоставляется с возможностью редактировать Frontmatter. Можно сказать это редактирование исходника страницы - точно также можно открыть файл страницы (user/pages/01.home/default.<lang>.md в моем случае) в редакторе. 

На этом скриншоте полностью описана моя форма. 

Каждое заполнение формы сохраняется в файл на файловую систему, а также отправляется по указанной в настройках плагина Email (ставится вместе с админкой) почте. Там же я прописал свой SMTP сервер. Для просмотра заполнений формы (файлов в файловой системе) потребовалось установить Data manager плагин. 

Для содержимого файла и содержимого письма прописываются шаблоны в твиге, которые можно перекрыть в своей теме в твиге: получается, если в теме нет файла forms/data.txt.twig, то будет использоваться одноименный из плагина Form (plugins/form/templates/forms/data.txt.twig). 

Я не описывал свои шаблоны для содержимого письма и файла, но у меня уже была готовая верстка для отображения пользователю и тут я немного схитрил. Grav CMS позволяет не только переназначить шаблон, но также и каждое поле в нем, там еще уйма настроек, можно добавить классы в css. Я же сначала создал дефолтную форму, посмотрел на аттрибут name у тегов input и по примеру из html name=”data[message]” прописал имена для моих атрибутов в готовой верстке. 

Подключается форма на страницу в твиге вот таким образом:

{% include "forms/custom.html.twig" with {form: forms('contact-form')} %}

forms/custom.html.twig - этот файл я создал внутри темы и перенес в него верстку контактной формы с моими изменениями. Если же своего шаблона нет, то необходимо подключать forms/form.html.twig. Я пытался создать в теме form.html.twig файл, но столкнулся с переполнением памяти (что-то видимо зацикливалось), поэтому сделал другое название для своего файла шаблона. 

В моем конфиге формы action (это action для тега form в html) указан /forms/contact. По умолчанию идет пустая строка, то есть POST запрос с формой уходит на саму страницу и в таком случае каждый refresh (F5/Ctrl + R) страницы будет повторять заполнение формы. Но и для того, чтобы по /forms/contact происходило создание формы мне пришлось создать страницу в файловой системе /user/pages/forms/contact и поместить там пустой файл form.md. Такое решение я нашел где-то в гугле и оно сработало, но лично я считаю это костылем, который даже мне не очень нравится, хотя костылить я люблю =) 

После заполнения можно указать редирект или содержимое на отдельной странице, но я делал ajax-форму, для чего я воспользовался инструкций в доке: скопировал оттуда Vanilla JS пример и добавил в код темы, поменяв айдишки элементов на те, что указаны в моем html. 

Процесс создания контактной формы мне не очень понравился в Grav CMS. Здесь уже потребовалось подольше ковыряться. Разобравшись (хоть и не до конца), я понимаю, что это гибко и даже удобно, хоть и в некотором роде костыльно. 

Конспект

В процессе освоения Grav я сделал несколько личных наблюдений, которые я оформил в небольшой конспект:

  • Разработчику для использования CMS необходимо знать php, twig, markdown, yaml и для фронтенда можно использовать что угодно: vue, react, angular, jquery, vanilla и т.д.

  • Пользователю (редактору, модератору, автору) при использовании админки вероятно ничего дополнительно не надо знать кроме админки, а без админки - markdown.

  • Для кэша используется doctrine.

  • Админка - это не часть движка, а плагин, у которого в зависимостях среди прочего есть плагин Form, отвечающий за то красивое отображение редактируемых полей в админке, фронт для него написан на jquery =)

    • Лично меня воодушевляет подобная модульность: когда ядро системы достаточно маленькое и простое, а весь сок CMS находится в плагинах.

  • На момент написания статьи на странице загрузок 373 плагина, 112 тем и 50 скелетонов.

    • Не знаю, насколько это много. В списке я видел плагины с упоминанием технологий, с которыми я работал, а это значит эти плагины потенциально интересны мне в будущем, если буду брать еще подобные заказы. Попадания в мой стек есть, а значит плагинов достаточно много ;)

  • Из коробки Grav с nginx + php-fpm у меня не заработал, с apache веб-сервером работает без проблем, но в самом движке есть папка webserver-configs с примерами конфигов для разных веб-серверов, среди которых и nginx. В доке об этой папке не видел и, учитывая, что последние изменения в конфигах были несколько лет назад, подозреваю, что пришлось бы еще доводить до рабочего состояния конфиги самому. Потенциальное место для оформления своего pull request в проект. 

  • Для локализации используется расширение intl, в свою очередь у WordPress используется gettext

  • Монетизация проекта вероятно держится в основном на платных плагинах, секция спонсоров на гитхабе пустая. В свою очередь и OctoberCMS когда-то была полностью бесплатной для использования.

  • Редактор страниц в теме предлагается только markdown, есть два плагина для прокачки редактора: платный и бесплатный

OctoberCMS

Параллельно с моим опытом с Grav CMS мне предложили еще небольшой заказ на OctoberCMS: прикрутить оплату через Stripe на страницу сайта без перехода на сам Stripe. Заказчик сам делал сайт на OctoberCMS, а я, по большей части, помогал ему, и по завершении мы обсудили и сравнили возможности обеих CMS.

OctoberCMS создана на основе Laravel, что предоставляет возможность для расширений сайта, чем я и воспользовался: с помощью утилиты artisan я создал плагин, контроллер в нем, описал необходимую логику и далее в файле routes.php прописал путь для фронта. Для рендеринга html также используется twig и похожий на формат .ini файлов формат для конфигурации. Мне не очень понравилось, что перечисленные форматы, а также php код, по желанию, замешаны в одном .htm файле, ибо phpstorm плохо это дело подсвечивает. С другой стороны, это предоставляет гибкость.

Получается, на двух разных и мало чем похожих заказах я попробовал две разные CMS и, вероятно, на каждой из CMS можно было бы выполнить оба заказа, но чисто по моему мнению каждая из них отлично подошла под свой заказ, и я бы ничего не менял. Если есть необходимость выбрать только один инструмент из этих двух для выполнения самых разных задач, то стратегически лучше выбирать OctoberCMS, но Grav CMS лично мне всё равно понравилась больше, так что простые сайты я буду пилить на ней, а более сложные заказы на фрилансе буду стараться обходить стороной =)

Заключение

Grav CMS оказалась подходящим вариантом для моей задачи, поначалу мне очень понравилась гибкость настроек темы, на контактной форме я немного спотыкался, но поставленную передо мной задачу я выполнил и всё равно считаю, что получилось быстрее и лично мне приятнее, чем на WordPress. Данной статьей я поделился своим опытом - надеюсь, описанное будет полезно. Я не ручаюсь, что всё делал правильно, так что буду рад увидеть в комментариях советы, замечания и сравнения с другими CMS и с этой же целью рекомендую после прочтения заглянуть в комментарии и остальным. С таким инструментом я пересмотрю свое отношение к заказам делать небольшие сайты и может стану браться за них почаще. 

Теги:
Хабы:
Всего голосов 7: ↑5 и ↓2+3
Комментарии2

Публикации

Истории

Ближайшие события

Антиконференция X5 Future Night
Дата30 мая
Время11:00 – 23:00
Место
Онлайн
OTUS CONF: GameDev
Дата30 мая
Время19:00 – 20:30
Место
Онлайн
Конференция «IT IS CONF 2024»
Дата20 июня
Время09:00 – 19:00
Место
Екатеринбург
Summer Merge
Дата28 – 30 июня
Время11:00
Место
Ульяновская область