Статьи

Как создать тему OctoberCMS

Октябрьская CMS — новая звезда в небе CMS. Построенный на вершине Laravel, он обещает радостное кодирование и возврат к основам. Прочитайте наше введение здесь и узнайте, как создать плагины для него здесь . В этой статье мы увидим, как мы можем создать тему.

octobercms

Что мы собираемся построить

Мы собираемся создать тему для блогов. Тема будет зависеть от плагина блога rainlab и будет содержать:

  • Макет страницы по умолчанию
  • О странице
  • Главная страница
  • Страница сообщений
  • Страница категорий
  • Одна страница поста

Это основные части блога сайта. В конце этой статьи вы сможете создавать свои собственные страницы и расширять тему новыми функциями.

Весь исходный код, упомянутый в этом посте, доступен на Github , поэтому лучше всего, если вы откроете репозиторий на другой вкладке и будете следовать подробному описанию каждого файла.

Создание темы

OctoberCMS хранит темы в каталоге themes , и они автоматически загружаются при посещении внутреннего интерфейса темы.

Чтобы ускорить процесс, я буду использовать бесплатную тему от startbootstrap.com .

Структура тематических папок OctoberCMS

theme_folder_structure

На скриншоте показана окончательная структура нашей темы. Структура папок не слишком сложна — читайте документы для получения дополнительной информации.

Установка необходимых плагинов

Поскольку мы собираемся создать тему блогов, мы установим плагин rainlab для блогов, который предоставляет несколько компонентов, которые мы можем использовать. Если вы не знакомы с компонентами, обязательно ознакомьтесь с моей предыдущей статьей о создании плагинов OctoberCMS .

Леса наша тема

Чтобы начать строить нашу тему, нам нужно только создать папку с нашим уникальным именем. Я назову мою rafietheme . Если вы посетите /backend/cms/themes , вы увидите, что новая тема была добавлена ​​в список.

На данный момент тема не имеет описания или имени, а October использует только имя нашей папки.

Файл theme.yaml внутри корневого каталога тем будет содержать информацию о нашей теме.

 // rafietheme/theme.yaml name: RAFIETHEME author: RAFIE Younes authorUrl: http://younesrafie.com description: Simple blogging theme 

Если вы посетите интерфейс управления темой, вы увидите нашу тему с новым описанием и информацией об авторе. Теперь мы добавим скриншот заполнителя. OctoberCMS будет искать скриншот в rafietheme/assets/images/theme-preview.png по умолчанию.

Файл version.yaml рассказывает историю версий вашей темы, и потому что это только наша первая версия, которую мы можем добавить:

 1.0.1: First version 

Структура шаблона

Октябрьские страницы шаблона разделены на три части. Каждый раздел отделен от других с помощью == .

Раздел конфигурации

В этом разделе мы описываем наш шаблон для CMS. Мы можем указать URL страницы, заголовок и т. Д.

 url = "/posts" title = "Latest Posts" description = "Just another posts page" layout = "default" 

Мы также можем использовать компоненты для инициализации и их настройки. Подробнее об этом в документах .

PHP раздел

Этот раздел будет помещен в кэшированный класс, поэтому нам разрешается использовать функции или ключевое слово use по вашему желанию.

Вы часто будете видеть метод onStart используемый здесь. Этот метод запускается автоматически, потому что он является частью жизненного цикла страницы. Проверьте список доступных функций , а также некоторые глобальные переменные ( layout , page , component ).

Раздел разметки

Октябрь использует шаблонный движок Symfony Twig и наследует все основные функции. Переменные, переданные из раздела PHP, доступны в представлении. Эта переменная содержит информацию о нашей page , controller , layout , environment и params .

dump_this

Использование заполнителей

Заполнители — это способ добавить контент на страницу. Например, их можно использовать как один из способов внедрения ваших сценариев.

 // defining the placeholder ... {% placeholder scripts %} </body> </html> 

Здесь мы определили заполнитель. Перед закрытием тега body мы даем пользователю возможность добавить некоторые сценарии, например:

 {% put scripts %} <script src="js/slider.min.js"></script> {% endput %} 

Заполнители имеют другие функции, такие как внедрение содержимого по умолчанию или тестирование, если заполнитель существует. Не забудьте проверить документы для большего количества примеров.

Использование Partials

Частицы очень полезны для разделения частей нашей страницы, header footer , нижнего footer и т. Д. Как мы уже говорили, они хранятся в каталоге partials и могут быть вложены с помощью подкаталогов.

 |---- partials/ |-- header.htm |-- footer.htm |-- navigation.htm |-- comments.htm |-- ajax/ |- posts.htm 
  • header.htm : содержит определение типа документа и заголовка, а также ссылки на некоторые ресурсы.
  • footer.htm : будет содержать текст с информацией об авторских правах и закрывать документ, а также некоторые ресурсы JavaScript.
  • navigation.htm : будет содержать только наше меню навигации.
  • comments.htm : поскольку мы собираемся использовать только Disqus , давайте представим его в своей части.
 // partials/header.htm <!DOCTYPE html> <html lang="en"> <head> <title>{{ this.page.title }}</title> {% styles %} <link href="{{ [ 'assets/css/bootstrap.min.css', 'assets/css/clean-blog.min.css' ]|theme }}" rel="stylesheet"> </head> <body> 

Внутри каждой части или страницы мы можем получить доступ к this переменной, которая содержит переменные page , layout , controller , param и environment , как указано в документации .

При загрузке ресурсов мы передаем массив ссылок на фильтр тем, который объединяет файлы и создает кэшированную ссылку.

Тег {% styles %} позволяет разработчикам плагинов вставлять свои ресурсы на страницу, и то же самое относится к {% scripts %} .

Чтобы внедрить ваши активы на страницы, у вас есть два способа:

  • Используя функцию onStart в вашей части PHP-кода:

     function onStart(){ $this->addCss('assets/css/main.css'); $this->addJs('assets/js/app.js'); } 
  • Использование заполнителей компонентов:

     {% put scripts %} <script type="text/javascript" src="{{ ['assets/js/app.js']|theme }}"></script> {% endput %} 
 // partials/footer.htm <script src="{{ [ '@jquery' 'assets/javascript/bootstrap.min.js', 'assets/javascript/clean-blog.min.js']|theme }}"></script> {% scripts %} </body> </html> 

Вы заметили, что мы добавили @jquery вместо пути к файлу!
Это просто означает, что мы хотим использовать файл jQuery, доступный во внутренней части. Эти обозначения называются псевдонимами .

 <ul class="nav navbar-nav navbar-right"> <li class="{% if( this.page.id == 'home') %}active {% endif %}"> <a href="{{ 'home'|page }}">Home</a> </li> <li class="{% if( this.page.id == 'about') %}active {% endif %}"> <a href="{{ 'about'|page }}">About</a> </li> <li class="{% if( this.page.id == 'posts') %}active {% endif %}"> <a href="{{ 'posts'|page }}">Posts</a> </li> </ul> 

Ранее мы говорили, что объект page доступен для использования внутри любой страницы или частично. Мы используем page.id для установки активного пункта меню, а для атрибута href ссылок передаем идентификатор page фильтр page , который сгенерирует полный URL-адрес. Обратите внимание, что идентификатор страницы — это имя файла шаблона.

Использование макетов

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

 description = "Default Layout" == {% partial 'header' %} {% partial 'navigation' %} {% page %} {% partial 'footer' %} 

В части конфигурации мы можем указать описание для нашего макета. Внутри части разметки ветки мы включаем необходимые части. Тег page будет выводить содержимое страницы с использованием этого макета.

Мы можем использовать большинство частичных функций и функций страницы, а также включать разметку HTML, ресурсы и т. Д.

Создание страниц

OctoberCMS хранит определенные пользователем страницы внутри папки pages . Давайте сначала создадим нашу страницу о программе, и на этом пути мы рассмотрим различные возможности.

О странице

Поскольку это наша первая страница, мы постараемся сделать ее максимально простой, а первая часть — использовать раздел конфигурации.

 title = "About" url = "/about" layout = "default" description = "Just another about page" 

Раздел шаблона будет содержать только некоторое содержимое HTML Lorem ipsum внутри контейнера.

 <div class="container"> Lorem ipsum..... </div> 

Это оно! Вы можете посетить новую страницу, нажав /about URL внешнего интерфейса вашего блога.

aboutpage

Домашняя страница

домашняя страница

Внутри нашей части конфигурации мы отображаем нашу страницу на URL и используем определенный макет вместе с заголовком и описанием.

 title = "HOME PAGE" url = "/" layout = "default" description = "Blog Home Page" 

Внутри нашего раздела PHP мы используем метод onStart чтобы установить готовые переменные нашей страницы. Этот метод выполняется после инициализации всех компонентов.

 <?php use RainLab\Blog\Models\Post; function onStart(){ $this['posts'] = Post::isPublished() ->orderBy('published_at', 'desc') ->take(5) ->with('categories') ->get(); } ?> 

Плагин rainlab предоставляет набор моделей, которые мы можем использовать для запроса базы данных. Мы берем только пять последних опубликованных сообщений из нашей базы данных и устанавливаем ее как глобальную переменную.

 <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1"> {% for post in posts %} <div class="post-preview"> <a href="{{ 'post'|page({slug: post.slug}) }}"> <h2 class="post-title"> {{ post.title }} </h2> <h3 class="post-subtitle"> {{ post.excerpt }} </h3> </a> <span class="post-meta">Published on {{ post.published_at|date("F jS, Y") }}</span> <br/> {% for category in post.categories %} <a href="{{ 'category'|page({slug: category.slug }) }}"> <span class="label label-primary">{{ category.name }}</span> </a> {% endfor %} </div> <hr> {% else %} <h2>No posts found.</h2> {% endfor %} <ul class="pager"> <li class="next"> <a href="{{ 'posts'|page }}">Older Posts &rarr;</a> </li> </ul> </div> 

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

Фильтр page генерирует URL для данной страницы, но вы также можете передать некоторые параметры URL для включения. Проверьте документы для получения дополнительной информации.

Страница с одним сообщением

 title = "Blog Post" url = "/post/:slug" layout = "default" description = "Single post page" 

Единственное отличие от домашней страницы — это часть URL. Параметр slug — это параметр в базе данных. Если мы хотим быть более явными, мы можем использовать регулярные выражения, чтобы соответствовать нашим правилам.

 url = "/post/:slug|^[a-zA-Z0-9]$" 

Вы можете проверить документацию для получения дополнительной информации о синтаксисе URL и использовании регулярных выражений.

 <?php use RainLab\Blog\Models\Post; function onStart(){ $slug = $this->param('slug'); $this['post'] = Post::where('slug', '=', $slug) ->isPublished() ->with('categories') ->first(); if( !$this['post'] ) return Redirect::to('/404'); } ?> 

Внутри нашего PHP-раздела мы смотрим на базу данных для публикации, используя параметр slug . Если сообщение не существует, мы перенаправляем на нашу страницу 404.

 <header class="intro-header" style=" {% if post.featured_images.isEmpty() %} background-image: url('{{ "/assets/images/post-sample-image.jpg"|theme }}') {% else %} background-image: url('{{ post.featured_images.first().getPath() }}') {% endif %}"> <div class="container"> <div class="row"> <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1"> <div class="post-heading"> <h1>{{ post.title }}</h1> <span class="meta">Published on {{ post.published_at|date("F jS, Y") }}</span><br/> {% for category in post.categories %} <a href="{{ 'category'|page({slug: category.slug }) }}"> <span class="label label-primary">{{ category.name }}</span> </a> {% endfor %} </div> </div> </div> </div> </header> <article> <div class="container"> <div class="row"> <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1"> {{ post.content|raw }} </div> </div> <div class="row"> <hr/> <div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1"> {% partial 'comments' %} </div> </div> </div> </article> 

Мы проверяем, есть ли в блоге какие-либо посты. Если это так, мы берем первый, иначе мы показываем изображение по умолчанию. После этого мы печатаем заголовок, дату, категории и экранируем содержимое перед его выводом.

partial тег загрузит наши комментарии. На данный момент он просто выводит <h2>Comments</h2> , но вы можете использовать Disqus, если хотите.

Страница сообщений категории

categorypage

Раздел конфигурации нашей страницы в основном такой же.

 title = "Category" url = "/category/:slug" layout = "default" description = "Filter posts by category" 
 use RainLab\Blog\Models\Post; use RainLab\Blog\Models\Category; function onStart(){ $slug = $this->param('slug'); $this['category'] = Category::where('slug', '=', $slug)->first(); if( $this['category'] ){ $post = new Post; $query = $post->isPublished() ->orderBy('published_at', 'desc') ->with('categories'); $this['posts'] = $post->scopeFilterCategories($query, [ $this['category']->id ])->get(); } } 

В части раздела PHP мы проверяем, существует ли категория. Если это так, мы запрашиваем в базе данных соответствующие сообщения.

Я собирался использовать объединение в сводной таблице между posts и categories , но плагин блога rainlab предоставляет RainLab\Blog\Models@scopeFilterCategories для фильтрации сообщений с использованием массива идентификаторов категорий: он принимает Illuminate\Query\Builder в качестве первого параметра и массив идентификаторов категорий.

Часть HTML такая же, как домашняя страница, мы изменили только заголовок, чтобы добавить название категории.

Страница сообщений

На странице постов будут напечатаны пять последних постов, а в нижней части страницы есть кнопка « load more , которая будет использовать функциональность ajax-инфраструктуры OctoberCMS для загрузки большего количества.

Чтобы иметь возможность использовать фреймворк ajax, вам нужно включить тег {% framework %} . Это выведет ссылку на файл framework.js из каталога модулей.

Тег также поддерживает параметр {% framework extra %} . Это добавит некоторые стили, такие как индикатор загрузки при использовании ajax.

AJAX Framework

Октябрь имеет хороший способ обработки некоторых из наиболее распространенных способов загрузки контента с помощью запросов AJAX. Чтобы включить инфраструктуру ajax для элемента, вы просто добавляете атрибут data-request="onMethodName" в HTML5. Метод, который будет выполняться, должен начинаться с on за которым следует имя вашего метода.

Есть два способа определить ваш метод обработчика ajax:

  • В разделе PHP:

     function onMethodName(){ //handle it here } 
  • Внутри компонента:

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

     title = "My page" url = "/" [componentName] == 

Перед отправкой запроса мы можем показать подтверждающее сообщение или перенаправить после возврата ответа. Вы можете проверить список поддерживаемых атрибутов здесь .

 <div class="col-md-2 col-md-offset-5"> <a id="load_more" href="#" class="btn btn-primary" data-request="onLoadMorePosts" data-request-update="'ajax/posts': '@#posts'" data-request-data='"postsCount": 1' data-request-success="incrementPostsCounter($el);"> Load more </a> </div> 
  • data-request : onLoadMorePosts наш обработчик onLoadMorePosts который мы определили в нашем разделе PHP.
  • data-request-update : здесь мы указываем частичное, которое будет загружено на сервер, а также идентификатор или класс целевого элемента. Если вы заметили символ @ перед идентификатором, это просто означает, что мы хотим добавить элемент, а не заменять его содержимое.
  • data-request-data : мы можем добавить больше параметров к запросу, в этом случае мы отправляем переменную количества сообщений.
  • data-request-success : код JS, который будет выполняться после каждого успешного запроса, в этом случае функция incrementPostsCounter обновит наш атрибут data-request-data .
 function onLoadMorePosts(){ $postsCount = (int) post('postsCount'); $this['posts'] = Post::isPublished() ->orderBy('published_at', 'desc') ->skip( $postsCount * 5 ) ->take(5) ->with('categories') ->get(); } 

Метод post извлекает postsCount со страницы, затем мы пропускаем указанное количество и выбираем следующие пять постов.

Страница partials/ajax/posts.htm автоматически, и список сообщений остается доступным для просмотра.

 // partials/ajax/posts.htm {% for post in posts %} <div class="post-preview"> <a href="{{ 'post'|page({slug: post.slug}) }}"> <h2 class="post-title"> {{ post.title }} </h2> <h3 class="post-subtitle"> {{ post.excerpt }} </h3> </a> <span class="post-meta">Published on {{ post.published_at|date("F jS, Y") }}</span> <br/> {% for category in post.categories %} <a href="{{ 'category'|page({slug: category.slug }) }}"> <span class="label label-primary">{{ category.name }}</span> </a> {% endfor %} </div> <hr> {% endfor %} 

На самом деле это все, что нужно для загрузки большего количества постов — анимация и вставка автоматически обрабатываются фреймворком.

Если у вас возникают проблемы с кэшированием при загрузке AJAX, попробуйте php artisan cache:clear из командной строки, чтобы очистить кеш и убедиться, что JS обновляется.

Вывод

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