1. Подготовка изображений: Компрессия и оптимизация
Оптимизация изображений — важный аспект ускорения загрузки страниц, так как изображения могут занимать значительное количество трафика. Существует несколько плагинов, которые помогут автоматизировать этот процесс.
Плагины для оптимизации изображений:
Smush: Один из самых популярных плагинов для сжатия изображений без потери качества. Smush автоматически оптимизирует изображения при загрузке и позволяет массово сжимать уже загруженные файлы.
Imagify: Альтернативный плагин для оптимизации изображений, предлагающий несколько уровней сжатия, включая режим без потерь, агрессивное сжатие и ультра-компрессию.
2. Оптимизация стилей и скриптов: Минификация и объединение
Минификация CSS и JavaScript файлов уменьшает их размер за счет удаления пробелов, комментариев и других ненужных символов. Объединение файлов позволяет сократить количество HTTP-запросов, что также ускоряет загрузку страницы.
Плагины для минификации и объединения файлов:
Autoptimize: Плагин, который автоматически минифицирует и объединяет CSS, JavaScript и HTML файлы. Также поддерживает отложенную загрузку скриптов, что может значительно ускорить загрузку страниц.
Fast Velocity Minify: Плагин, специализирующийся на минификации и объединении CSS и JavaScript файлов. Он также предоставляет возможность кэширования объединенных файлов для еще большего ускорения работы сайта.
3. Кэширование: Ускорение работы сайта
Кэширование помогает снизить нагрузку на сервер и ускорить загрузку страниц, предоставляя пользователям уже обработанные версии страниц. Это особенно полезно для сайтов с высоким трафиком.
Плагины для кэширования:
WP Super Cache: Один из самых популярных плагинов для кэширования, который создает статические HTML-файлы из динамических страниц WordPress и отдает их пользователям, уменьшая нагрузку на сервер.
W3 Total Cache: Мощный плагин для кэширования, который предлагает широкий набор функций, включая кэширование страниц, объектов, баз данных и браузеров. Также поддерживает интеграцию с сетями доставки контента (CDN).
Заключение
Использование правильных плагинов для SEO в WordPress позволяет не только улучшить видимость сайта в поисковых системах, но и значительно ускорить его загрузку. Оптимизация метатегов, структурированных данных и карты сайта помогает поисковым системам лучше понимать ваш сайт, а оптимизация изображений, стилей, скриптов и кэширование обеспечивает быстрый доступ пользователей к вашему контенту.
Метатеги играют ключевую роль в SEO, так как они предоставляют поисковым системам и пользователям важную информацию о содержимом страниц. Правильное использование метатегов помогает улучшить кликабельность и позиции в поисковой выдаче.
Лучшие плагины для работы с метатегами:
Yoast SEO: Один из самых популярных SEO-плагинов для WordPress. Он позволяет легко настраивать метатеги, такие как title и description, для каждой страницы и записи. Yoast также предоставляет рекомендации по оптимизации контента, что делает его идеальным выбором для начинающих и опытных пользователей.
All in One SEO Pack: Альтернатива Yoast SEO, которая также поддерживает настройку метатегов и автоматическое создание мета-описаний на основе содержимого страниц. Плагин имеет интуитивно понятный интерфейс и множество настроек для опытных пользователей.
2. Структурированные данные: Как их настроить с помощью плагинов
Структурированные данные помогают поисковым системам лучше понимать содержимое страницы, что может улучшить видимость сайта в поиске и способствовать появлению расширенных сниппетов (rich snippets). Это особенно важно для e-commerce сайтов, блогов, рецептов и других специфических типов контента.
Плагины для работы со структурированными данными:
Schema Pro: Премиум-плагин, который позволяет легко добавлять микроразметку (Schema.org) на сайт без необходимости писать код. Поддерживает множество типов схем, включая отзывы, рецепты, продукты и многое другое.
WP Review Pro: Плагин, специально предназначенный для сайтов с обзорами. Он автоматически добавляет структурированные данные в ваши обзоры, что может улучшить отображение ваших записей в поисковой выдаче.
3. Карта сайта: Зачем она нужна и как создать с помощью плагинов
Карта сайта (sitemap) — это файл, который помогает поисковым системам эффективно сканировать и индексировать ваш сайт. Она предоставляет поисковикам структуру сайта и ссылки на все важные страницы, что особенно полезно для больших веб-сайтов.
Плагины для создания карты сайта:
Google XML Sitemaps: Один из старейших и самых надежных плагинов для создания карты сайта. Он автоматически генерирует XML-карту сайта и обновляет ее каждый раз при добавлении нового контента.
Rank Math: Плагин, который совмещает функции SEO-оптимизации с созданием карты сайта. Rank Math позволяет гибко настроить, какие страницы и записи должны быть включены в карту сайта.
Yoast SEO: автоматически генерирует XML-карту сайта и обновляет ее каждый раз при добавлении нового контента.
Продолжение следует
Далее обсудим работу с картинками, оптимизацию загрузки страницы и кеширование6
Создание интернет-магазина на платформе WordPress — это популярное и доступное решение для предпринимателей, стремящихся быстро запустить свой бизнес в интернете. WordPress предоставляет мощные инструменты и гибкость, а с плагинами, такими как WooCommerce, можно настроить полноценный интернет-магазин с минимальными затратами.
Шаг 1: Установка WordPress
Выбор хостинга и домена: Для начала выберите надежного хостинг-провайдера и зарегистрируйте доменное имя. Популярные хостинги предлагают установку WordPress в один клик.
Установка WordPress: После регистрации домена и выбора хостинга, используйте панель управления хостинга для установки WordPress. Большинство хостинг-провайдеров предлагают автоматическую установку.
Шаг 2: Установка и настройка темы
Выбор темы: Для интернет-магазина подойдет тема, которая поддерживает WooCommerce. Вы можете выбрать бесплатные темы из репозитория WordPress или приобрести премиум-темы на таких платформах, как ThemeForest.
Настройка внешнего вида: После установки темы настройте её внешний вид, используя кастомайзер WordPress. Это позволит адаптировать дизайн под нужды вашего бренда. С целью построить шаблоны страниц можно использовать пэйджбилдеры, Elemento к примеру.
Шаг 3: Установка WooCommerce
Установка плагина: Перейдите в раздел «Плагины» и добавьте новый плагин. В поиске введите «WooCommerce» и установите его. После активации плагина следуйте инструкциям мастера настройки.
Базовая настройка WooCommerce: Настройте валюту, налоги, способы доставки и оплаты. WooCommerce поддерживает множество платежных шлюзов, включая PayPal, Stripe и другие.
Шаг 4: Добавление товаров
Создание продуктов: В меню WooCommerce выберите «Товары» и добавьте новый продукт. Введите название, описание, цену и загрузите изображения. Можно настроить вариативные продукты, если у вас есть товары с разными характеристиками, такими как цвет или размер.
Категории и метки: Организуйте товары по категориям и меткам, чтобы клиентам было проще их находить.
Шаг 5: Настройка страниц и навигации
Создание основных страниц: WooCommerce автоматически создает страницы для корзины, оформления заказа и аккаунта. Убедитесь, что эти страницы корректно работают.
Настройка меню: Добавьте страницы магазина в меню навигации. Это улучшит пользовательский опыт, позволяя клиентам легко перемещаться по сайту.
Шаг 6: SEO-оптимизация
Один из наиболее важных шагов для любого сайта:
Установка плагина для SEO: Используйте плагин, такой как Yoast SEO или Rank Math, чтобы оптимизировать страницы и продукты для поисковых систем. Это поможет повысить видимость магазина в поисковых системах.
Оптимизация продуктов: Заполняйте мета-заголовки, описания и ключевые слова для каждого продукта. Используйте качественные изображения с оптимизированными названиями файлов.
Шаг 7: Тестирование и запуск
Тестирование: Проверьте работу всех страниц, оформление заказа и интеграцию платежных систем. Убедитесь, что процесс покупки работает без сбоев.
Запуск сайта: После успешного тестирования сайт готов к запуску. Начните продвижение вашего магазина через социальные сети, контекстную рекламу и другие маркетинговые каналы.
Заключение
Создание интернет-магазина на WordPress — это процесс, который требует внимания к деталям, но при правильном подходе вы можете быстро запустить успешный бизнес. С правильной темой, плагинами и оптимизацией вы сможете привлечь больше клиентов и увеличить продажи.
Это продолжение статьи “WP REST API(дефолтное) — взаимодействие с WP сайтом,” в которой я повествовал о работе с дефолтным апи вордпрес. Однако, той статье я ничего не рассказываю о пагинации или получении связанных обьектов в одном запросе, ну и тд… в общем, эта статья ксть дополнением к той(по этому прочтите ее).
Параметры пагинации
Предположим, у нас появилась потребность получить обьекты постов запросом к WP REST API. В случаи если ЧПУ на сайте включены, запрос будет выглядеть так
https://guten.website/wp-json/wp/v2/posts
Cейчас мы получим JSON-строку, содержащую массив обьектов всех постов(или 100, если их больше). Но если нам не нужны они все? Здесь все просто – нужно указать параметры пагинации(аналогичные им же в обьекте WP_Query). Этот запрос ограничит нашу выборку 5-ю постами ч 2-ой страницы
Как видно, были добавлены 2 GET-параметра – per_page и page. На деле их 4(5)(если порядок сортировки результатов относить к пагинации).
Третий параметр(offset) позволяет настроить сдвиг, т.е. количество постов, которое нужно пропустить(еще не придумал ситуации где он нужен, может, для разбития самой страницы…). И четвертый(order и orderby, да, их два – обманул) – определяют поле для сортировки(второй) и порядок сортировки(первый).
В итоге, запрос, который будет выводить последние 3 поста с второй страницы, содержащей по 5 постов на страницу и в порядке убывание id, выглядит так
Сортируемые поля у каждого типа обьекта разные… и, к полной картине, заголовки ответа(в полях X-WP-Total и X-WP-TotalPages) содержится
информация о количестве записей и количестве страниц, при данном разбиении. Немного уточнений по offset – он отбрасывает не n первых постов на каждой странице, а n первых постов согласно параметров сортировки.
Ссылание и встраивание
Каждый полученный обьект содержит поле _links, представляющее собой массив ссылок на апи-пути связанных обьектов. Также, эти массивы имеют поле embeddable, несущее информацию о том, можно ли этот связанный обьект встроить в текущий.
Для примера, воспользуемся обращением к постам; ответ содержит только id изображения миниатюры, но не саму ссылку на него(которую можно вставить в тег img, к примеру). В поле _links есть следующий обьект
В этом участке информации меня интересует не так ссылка на обьект, как то, что поле embeddable имеет значение true, т.е. обьект может быть встроен в текущий. Чтобы его встроить, нужно к текущему запросу дописатьь еще один гет-параметр _embed с значением нужного обьекта; в моем случаи
при этом в каждом родительском объекте появится поле _embedded, содержащий требуемый обьект(равный тоому, который будет ответом по ссылке).
Фильтр полей
В WP REST API есть get-атрибут _fields, позволяющий заказать поля, которые нужно получить. Это нужно, как вы понимаете, для оптимизации запросов к серверу, что б он не “лег” в пиковые моменты… но не только! Также, этим параметром можно заказывать различные зарегистрированные метаполя. Рассмотрим пример получения id, title и метаполя counter; для начала зарегистрируем его в rest
https://guten.website/wp-json/wp/v2/posts?_fields=id,title,meta.counter
# или
https://guten.website/wp-json/wp/v2/posts?_fields=id,title,counter
Теперь немного об опыте использования этого на практике. Этим параметром можно получить только поля обьекта записи(или других табличных сущностей), т.е. при этом никаких вложенных обьектов не будет.
Все чаще и чаще на в сайты интегрируют элементы SPA, в частности “бесконечную страницу”… здравствуй, читатель! По ходу этой статьи будет описан принцип работы такой страницы, реализация и посадка на вордпресс.
SPA и бесконечная страница
В принципе, и SPA(Single Page Application – одно-страничное приложение) и бесконечная страница исповедуют один и тот же принцип(извиняюсь за тавтологию) – динамическая загрузка контента. Все очень просто – когда поступает событие(клик либо скролл, либо еще что-то), javascript’ом загружается новая порция контента и вставляется в страницу.
Реализация
Использовать буду jQuery(тут часть любителей хайповых React, Vue и прочего, вышли…). Обьясню свой выбор – зачем перегружать страницу яваскриптом? да, jQuery тоже библиотека без которой можно обойтись, но если он используется для многих других элементов – почему бы и нет?
Немного о ней: поскольку я черпать контент буду в ворпрессе – то и использую пагинацию для разбивки на загружаемые блоки. Первую страницу загрузим обычным способом(каждая страница – это div class="page-%n"). В теге template будет содержаться разметка нашего поста – после загрузки пачки постов я буду ее клонировать.
Далее, нужно прибегнуть к javascript. Как я говорил ранее, на странице будет jQuery, поэтому сделаем AJAX-запрос к WP REST API со списком постов и их данными. Но прежде – определим условия для начала загрузки постов. Раз уж бесконечное полотно – значит условие это достижение нижней границы текущей страницы
var prevScroll = 0,
page = 1,
windowH = $(window).height(),
positionY = $('.page-1').offset().top,
blockH = $('.page-1').height(),
template = $('#post').html(),
currentScroll, tmpl
$(window).scroll(function (event) {
currentScroll = $(this).scrollTop()
if (currentScroll > prevScroll) {
prevScroll = currentScroll
if (Math.round(blockH + positionY) === (windowH + currentScroll)) {
// Получаем посты, рисуем их и высчитываем новую границу для загрузки
}
}
})
Немного пояснений для этого куска кода. Эта строка(12) позволяет отсеять скролл вверх; можно было б отслеживать прокрутку колеса на мишке – но тогда скрипт был бы мертв на мобильниках. По поводу Math.round() свойства – высота(как и ширина) может быть дробной, в то время как значение скролла всегда целое число. Еще замечу – этот скрипт рабочий только в случаи если под блоком(не по разметке, а по позиции) с постами нет больше блоков(отсутствует футер). Дело в том, что при быстрой прокрутке scrollTop() имеет разрывы и тогда может произойти перескок нужного значения – потому, заменим его(===) простым сравниванием(>). Также, поскольку загрузка постов требует времени, используем коэффициент(что пользователю не пришлось ждать и он не покинул из-за этого сайт). В итоге, строка 15 трансформируется в
строка 1 “убивает” прослушивание события прокрутки. Это нужно потому, что js асинхронен и способен отослать несколько одинаковых запросов к серверу – что, в свою очередь, приведет к повторению постов(помимо ненужной нагрузки на сервер);
строки 4 – 33 сам запрос к серверу: первый аргумент это адрес эндпоинта WP REST API, который возвращает список постов, второй – обьект с параметрами постов, третий – функция, которая будет исполнена при получении ответа;
строки 23 – 26 – почему измерения высоты блока заперты в load’е? – у меня нет контейнера с размерами для картинки, а поскольку они грузятся отдельно от аякса – они изменяют размер блока постов через некоторое время;
строка 29 возвращаем прослушку прокрутки.
По ходу написания статьи я немного изменил скрипт, а именно, анонимную функцию слушателя присвоил переменной onScroll. Немного о том, почему в строке 14 я постоянно клонирую шаблончик – каждый клон одноразовый, т.е. один раз вставив клон его переменная стает пустой.
В общем, полный код бесконечной страницы(уже посаженой на шаблон страницы вордпрес) можно увидеть и взять здесь.
Elementor – популярный wp плагин для построения страниц; данный тип плагинов называют pagebuilder. Это плагин(как и все в его категории) оперирует специальными виджетами – именно его я и создам, по ходу статьи…
Здесья расскажу о том, что толкнуло меня на создание виджета(помимо получения опыта для написания этой писульки). Дело было так: была страница на которую выводилось видео с YouTube. К нему был список тайм-кодов, клик по которым включал воспроизведение видео с определенного момента. Временная метка(таймкод) состоит из самого времени, картинки и текста-описания. При переезде страницы на Элементор – всё взаимодействие сламалось. Задача: создать аддон к Элементор, добавляющий эти возможности в виджете.
Немного воды…
Всем, кто знаком с веб-программированием, с ходу стало понятно в чем причина – javascript; теперь для тех, кто незнаком: за прослушивание всех событий в браузере отвечает javascript. И правда, билдер изменил структуру DOM-дерева страницы и, за часик(долго разбирался в новой структуре), изменив селекторы в скрипте все заработало…
И что ж?.. на этом все? Было бы всё, если бы владелец не сказал, что неудобно так: строить ряд, в нем колонки с текстовым полем в(которых снова ряды с колонками) – сделай-ка единым блоком, чтоб в элементы управления просто вбить значения.
Приступим…
Начал я, разумеется, с оболочки плагина. На сайте плагина, в доках, есть шаблон кода основного файла для аддонов Элементор. Немного опишу главный метод, который в нем присутствует
add_action('elementor/init', function () {
add_action('elementor/widgets/widgets_registered', function () {
require_once(__DIR__ . '/class-youtube-widget.php');
ElementorPlugin::instance()->widgets_manager->register_widget_type(new Elementor_YouTube_Widget());
});
});
чтоб написать любое расширение к элементору нужно расширить его класс; чтобы получить доступ к ядру плагина(а именно там лежат они) нужно вызывать аддон после загрузки плагина. В движке есть хук(plugins_loaded), срабатывающий после загрузки всех плагинов – однако, в виду того, как устроен сам Элементор – в этот момент его ядро уже недоступно.
Плагин Элементор имеет свои хуки, позволяющие получить доступ к разным классам его ядра:
elementor/init – ко всем;
ementor/widgets/widgets_registered – к классу ElementorWidget_Base(и прочим, необходимым для создания нового виджета);
elementor/controls/control_registered – к классу ElementorControl_Base(и прочим, необходимым для создания нового типа контролера для плагина);
Это все хуки, которые нужно знать, для того чтоб построить свой виджет. На деле, из-за довольно широкой палитры встроенных контролей, 3ий хук я никогда не использовал – не было нужды создавать свои контроллеры.
Теперь можно переходить к созданию самого виджета… хотя, нет – пару строк о скрипте. Здесь, как и в вордпресс, нет метода, который подключал бы скрипты только когда виджет есть на странице(по крайней мере я таких не увидел – если ты о них знаешь – напиши в комментах, буду благодарен!) и потому я подключаю их как обычные скрипты.
Теперь немного о самом скрипте – в нем я буду использовать YouTube Iframe Player API. Начнем с html-разметки, которую будем строить на странице
Думал использовать Iframe API, но глянув, что он делает – послал его лесом. Дело в том, что беглый анализ кода показал, что апи содержит методы постройки тега iframe с его атрибутами; учитывая, что страница индексируемая(должна быть оптимизирована под скорость загрузки) я решил не захламлять ее дополнительной библиотекой(jquery и так хорошо выполняет эту задачу). В общем, вот скрипт для работы с метками с вышеприведенной разметкой
(function($) {
'use strict';
$(document).on('click', '.youtube-preview', function() {
var id = $(this).closest('.elementor-row').data('id');
YTFrame.build(id, 0, $(this).parent());
});
$(document).on('click', 'figure.stamp', function() {
console.log(this);
var id = $(this).closest('.elementor-row').data('id'),
time = $(this).find('.timestamp').text().split(':'),
seconds = 60 * parseInt(time[0]) + parseInt(time[1]);
console.log($(this).closest('.elementor-row').find('.frame-container')[0]);
YTFrame.build(id, seconds, $(this).closest('.elementor-row').find('.frame-container')[0]);
});
var YTFrame = {
build: function(id, seconds, container) {
var iframe = document.createElement('iframe');
iframe.src = 'https://www.youtube-nocookie.com/embed/' + id + '?autoplay=1&start=' + seconds;
$(container).html(iframe);
}
}
})(jQuery);
Также, данный скрипт позволил обойти некоторые ограничения.
Доделываем
Имея скрипт и накидав разметку, а также оболочку для аддона – создадим класс нашего виджета. Он является дочерним для базового абстрактного виджета элементор(который доступен только в ядре плагина); также, необходимы классы для контроля виджета в редакторе
<?php
use ElementorControls_Manager;
use ElementorWidget_Base;
use ElementorUtils;
use ElementorRepeater;
Теперь создадмим сам класс со следующими методами
<?php
class Elementor_Test_Widget extends ElementorWidget_Base {
public function get_name() {}
public function get_title() {}
public function get_icon() {}
public function get_categories() {}
protected function _register_controls() {}
protected function render() {}
protected function _content_template() {}
}
Первые 4 методы просто возвращают по строке. Метод render() строит разметку в редакторе(с приметкой) и на сайте
Здесь Widget_Base->get_settings_for_display() это получение настроек виджета, которые я создам в следующем методе.
Метод _register_controls() отвечает за создание контролей виджета в сайдбаре редактора – здесь немного поподробней. Все настройки растоложены в 3х табах – контент, стиль и дополнительные(их не трожь – они всегда дефолтные!). чтоб выбрать таб нужно в этом методе написать
и уже между этими тегами можно писать свою настройку.
Вся “слоенка” будет выглядеть так
// указание на таб
// если TAB_CONTENT поменять на TAB_STYLE
// будет запись в стиль-таб
$this->start_controls_section(
'content_section',
[
'label' => __('Video'),
'tab' => Controls_Manager::TAB_CONTENT,
]
);
// открытие новой секции
$this->add_control(
'title',
[
'label' => __('Section Title'),
'type' => Controls_Manager::TEXT,
'input_type' => 'text'
]
);
// настройка в секции, их может быть много
$this->add_control(
'video_id',
[
'label' => __('Video ID'),
'type' => Controls_Manager::TEXT,
'input_type' => 'text'
]
);
// закрытие текущей секции, но не таба!
// после этого можно открыть новую секцию
// она будет в том же табе
$this->end_controls_section();
Впринципе, этих методов достаточно для того, чтоб сделать рабочий виджет. Далее пойдет вещь, которую я не делал в этой работе, но хочу показать и рассказать.
Наверняка внимательный читатель не только статьи но и кода, заметил в рендеринг-методе эти строки
все они не имеют смысла без метода _content_template(). Поясню: виджет элементор – блок визуально редактируемый; это значит его можно редактировать в поле редактора, а не в сайдбаре. Кароч, 1ая строка это указатель на то, что эта настройка имееит возможность визуального редактирования; 2ой – получает набор классов для того чтоб javascript элементора взял под контроль этот тег; 3я – получает само значение настройки. В случаи с ‘advanced’ можно увидеть расширенную панель редактирования текста
Однако, чтоб все это сработало – необходим вышеуказанный метод с следующими строками
Заметь(!) эти строки должны выводиться, тк они будут вставлены в backbone-шаблоны(документация по ним). 2ая строка это т, что в окне редактора будет вставлено вместо редактируемой строки.
Заключение
Было разработанное расширение для Элементор. Код сможешь глянуть здесь, конечно он не полный(я удалил из него интеграции с другими плагинами, кастомными плагинами и сократил дерево настроек). Больше примеров ты увидишь в самом Элементор, ну а я попытался немного объяснить – что и как там работает.
Редактор Gutenberg блочный редактор wordpress для создания контентной части страницы. Он пример того, как плагин стал частью движка. В этой статье я буду не обозревать редактор, а выполнять разработку блока для расширения возможностей редактора.
Задача: разработать плагин, добавляющий блок последних записей в редактор.
В блоке можно редактировать:
Количество выводимых постов;
Категорию постов(из существующих, не пустых)
Статус отображения изображения записи
Я уверен, что подобные плагины существуют и, что подобный блок можно собрать используя другие плагины, на подобии ACF. Но данная задача – это реальное тестовое задание, выполнением которого я хотел бы поделится.
Теоретические выкладки
Начнем из далека – нужно же статье придать объёма!
Редактор Gutenberg – это новый(относительно, частью wp он стал в версии движка 5, до этого был плагином) визуальный редактор WordPress для записей и страниц. Проект назван именем Иогана Гутерберга, презентовавшего Европе печатный станок и начавшего печатную революцию. Его работа сделала знание и информацию доступнее и запустила социальную революцию. Аналогично этому, разработчики хотят сделать доступным создание продвинутый макетов страниц для всех пользователей WordPress(источник).
Основная особенность редактора Gutenberg – это преставление всего контента в виде блоков и определение макета записи прямо в редакторе.
Ну все, хватит копи-паста, мой внутренний борец с плагиатом не позволит более. Да, я знаю, что все мои статейки – это, всего лишь, пере озвучка документаций и других источников… но, иногда, пишу и свои наблюдения.
В общем, каждый блок редактора – скрипт написанный на javascript с использованием библиотеки react. Все, что нужно для создания блока – это зарегистрировать скрипт и стили(коль нужны); в скрипте определить зависимости и функцию(с методами и свойствами).
Опишу свои мысли о том, что я буду делать – дабы выполнить поставленную задачу. Первым делом, учитывая, что по правилам хорошего тона react общается с приложениями по средствам REST API, и нужного пути вордпресс по умолчанию не имеет – создам этот путь в WP REST API. Второе – я знаю, что в таблицу БД движок сохраняет запись о блоке; поскольку, наш блок во фронте должен быть динамичен – то запись должна хранить элемент, который вордпресс ассоциирует с функцией. Кто ранее знакомился с этим движком, понял о чем я – о шоркоде, который мне также предстоит создать. И последнее, несколько слов о javascript – внутри функции регистрации блока нужно будет определить 4 вещи: мета инфу о блоке, его атрибуты, сохраняемую строку и вид в редакторе.
Установка рабочей среды
Это действие нужно чисто для удобства и скорости разработки. Но, в принципе, код можно писать и в блокноте – но все используют специальные редакторы. Так-что, советую эти операции проделать тоже…
Первое, при любой разработке на PHP(а язык бэка wordpress именно php) понадобится локальный сервер. Я использую OpenServer: просто скачай с официального сайта и установи его.
Второе – это компилятор JSX в JS, SCSS в CSS. В качестве такого компилятора, я использую NodeJS с пакетами; далее, подробно об установке.
С официального сайта грузим и устанавливаем NodeJS. После успешной установки, зашел в командную строку, у нас появиться менеджер пакетов npm. Первое, что нужно сделать – это сформировать package.json запуском команды
npm init
и дальнейшими ответами на поставленные вопросы. После чего, в командной строке пишем
npm install --save-dev @babel/core @babel/cli
это установи транскриптор babel в зависимости для ноды. И, для полного понимания при компиляции, нужен пресет @wordpress/babel-preset-default. Запустим
Среда для компиляции JSX готова. Немного о нахождении компилируемого файла и самого запуска процесса компиляции. Пусть компилируемый файл называется script.js и скомпилированный script-compiled.js; как я понял наблюдением, файл script.js должен лежать на одном уровне с package.json и папкой node_modules, так как ругается на указанные пути(по крайней мере, так было у меня). Сам процесс компиляции запускается так
но, по условию, мне нужно выбрать категорию постов. Для этого передадим список категорий и путь, по которому блок будет брать информацию о постах – конечный PHP скрипт в этой части
<?php
function blocks_scripts()
{
$categories = get_categories();
if (!empty($categories)) {
foreach ($categories as $key => $value) {
$cats[$value->term_id] = $value->name;
}
}
// скрываем блок если нет категорий
if (!empty($cats)) {
// подключаем блок
wp_enqueue_script(
'gutenberg-latest-posts',
plugins_url('/block/js/gutenberg-latest-posts-block.js'),
['wp-blocks', 'wp-element', 'wp-editor', 'wp-i18n'],
true
);
// создаем обьект glp, содержащий
// список категорий и ссылку на WP REST API постов
wp_localize_script(
'gutenberg-latest-posts',
'glp',
[
'categories' => $cats,
'restURL' => esc_url(get_rest_url(null, 'block/v1/latest-posts'))
]
);
}
}
add_action('enqueue_block_editor_assets', 'blocks_scripts');
немного о зависимостях – скрипт wp-blocks содержит функции, необходимые для регистрации блока и образует js объект wp.blocks; wp-element содержит функции react с ворпрессовской “прокладкой”, объект wp.element; wp-i18n функции для создания translate ready, wp.18n. Зависимость wp-editor можно пропустить, но она содержит готовые функции для размещения элементов управления атрибутами блока.
Следующий шаг – создание пути ‘wp/v2/latest-posts‘ – ведь его еще не существует. Этот код его зарегистрирует в системе
Создадим js-файл по ранее указанному пути и, во избежание конфликтов с другими, сделаем замыкание. Замыкание выглядит как анонимная само исполняющаяся функция
В нижних скобках указаны те объекты, которые нужно “впустить”, сверху – под какими именами их использовать в порядке соответствия. Далее, для удобства(поймете после компиляции файла), нужно “вытянуть” некоторые методы с объектов
поля title и icon интуитивно понятные(есть еще и другие, необязательные), скажу только, что category это блок, где будет отображен блок(блоки по умолчанию ). Чтобы создать свою категорию добавь следующий PHP-код
Отдельно упомяну об атрибутах – из-за строгого отношения к типам данных, чтобы булевое значение не сохранить в БД; поэтому, приходится булевое ставить в зависимость от других типов, значение которых можно сохранить. Так у меня, выбор отображения изображения поста происходит чекбоксом(атрибут checked), состояние которого возвращается в типе bool. Чтобы сохранять это состояние, я ввел доп.атрибут в типе integer(thumb), значение которого в полной зависимостти от checked.
Осталось определить 2 метода, которые должны возвращать HTML-разметку имеющую по контейнеру. Первый(edit) отвечает за разметку в редакторе блоков, второй(save) – за то, что будет сохранено в БД. Начнем с первого(полный код, написанный мною)
немного о написанном: функции setCount, setCategory и setThumb – для изменения состояния атрибутов; Item и ее вызов кодом <Item /> – чисто реактовский прикол; setPosts получает список постов по нашему маршруту, и устанавливает его в атрибут posts. BlockControls и InspectorControls – методы объекта wp.editor: первый размещает внутреннюю разметку в верхней панели(toolbar) блока, второй – в боковой панели.
И, наконец-то, edit. Он очень прост, если бы разрабатываемый блок был стационарен – он возвращал бы такую же разметку, как и основное поле блока
но поскольку он динамичен – содержит мой шорткод. Обратите внимание, поскольку результатом должна быть HTML-разметка – я обернул шорткод в тег.
Заключение
После компиляции js был получен код регистрации нового блока. Забыл сказать в основном теле статьи – особенностью react есть то, что он рендерит по фреймам, а не по клику. Из этого следует, что любое действие следует “запирать” в функцию. Весь код выполненного тестового лежит здесь.
Все страницы сайта – это PHP шаблоны, содержащие HTML разметку с PHP тегами. Здесь будет рассмотрено создание шаблона страницы для отдельной записи – начиная от вордпресс цикла и до построения элементов навигации…
Каждая запись это отдельный представитель типа поста. Из коробки, есть 2 типа постов – страницы и записи; у каждой свои приколы по отображению – у страниц шаблоны для отображения, записей – форматы
Для создания шаблона страницы, в начало файла достаточно добавить комментарий
<?php
// Template Name: TemplateName
с форматами постов все немного сложнее…
Чтобы активировать возможность выбора формата, в functions.php на экшн after_setup_theme функцию со следующим содержимым
Данный код содержит все допустимые форматы для постов; чтобы получить формат текущего поста используйте функцию get_post_format без аргументов, если ты внутри цикла(о нем позже), или с указанием ID поста.
При создании шаблона для своего типа записей – движок позволяет использовать только схему с форматами. По наименованию: для страниц с заголовком требований нет(если стандартный шаблон, без заголовка – page.php). Для постов и нестандартных типов записей – схема такова single-{post_type}.php, ну или single.php для всех типов кроме страниц(page).
WordPress циклы
Будь-то страница или любой другой тип записи(с условием, что он публичен – смотрите “Создаем свой тип записи. Метадата, роли пользователей, таксономии“) его вывод будет начат с цикла. В принципе, тип записей может быть и непубличным, но тогда его нужно будет принудительно доставать, что, я считаю, нехорошо. Итак, цикл(иногда его называют loop, поскольку это перевод) будет выглядеть так
<?php
if (have_posts()) {
while (have_posts()) {
the_post();
// post content
}
}
Естественно, что в начале выводится шапка(get_header) и подвал(get_footer) в конце. Касательно наполнения цикла – принято подключать кусочек шаблона(отдельный файл) при помощи get_template_part($patth), содержащий разметку страницы.
Элементы страницы
Здесь я рассмотрю основные элементы сингл-поста, которые применимы как к записям, так и страницам и прочим типам.
Изображение записи
Для начала – нужно включить их поддержку – это делает следующий код
После этого, на странице редактирования записи появиться такой метабокс
Теперь о том, как сохраняется загруженное изображение. По умолчанию, при сохранении , сохраняется оригинал и три нарезанных картинки из оригинала. Перейдя по “Settongs > Media” размеры этих изображений можно менять. Чтобы отключить нарезку достаточно выставить размеры в 0. Также, можно задать свой размер для кадрирования
Теперь мы имеем 2 варианта по получению установленного изображения; первый – это получение url на оригинал изображения; второй – получение всего img-тега
Приведенный выше код работает только внутри вордпресс-цикла, иначе, в качестве первого аргумента, следует указать ID поста. Но функции вывода работают только в цикле. Размеры кадров я упомянул в предыдущем абзаце к тому, что в данных функциях(получение во втором, вывод первым) можно указывать в виде аргумента название размеров. Стандартно full(оригинал), large(1024px*1024px), medium(300px*300px) и thumbnail(150px*150px). Также, вместо строки с названием размера, можно указать массив с шириной и высотой; при этом, при первой загрузке страницы с новым размером, будет создан и сохранен новый кадр.
Касательно получения и выведения img-тега. Последним аргументом в функцию можно передать массив атрибутов тега. Поскольку, в данной функции задается дефолтное значение этого аргумента, то задать можно только такие атрибуты – src, class и alt.
Таксономии
Будь-то стандартные, либо же кастомные таксономии, древовидные они или нет – вывод их одинаков
здесь $object_ids это ID записи, а вот на $args, пожалуй, поподробнее:
orderby -поле, по которому сортировать порядок вывода:
поиск в полях терминов – term_id, name, slug, term_group;
по полям таксономии терминов – id(поле term_taxonomy_id в таблице БД), description, parent, count(количество терминов в таксономии);
среди полей таблицы term_relationships – term_order;
по полям меты – meta_value_num(количеству полей meta_value) и по значению meta_key
Можно и вообще отключить сортировку установив значение в ‘none’. Ключ order устанавливает порядок сортировки(ASK/DESC).
Также, в этом массиве можно задать множество других параметров поиска. К примеру, наличие/отсутствие/равенство метаполей, вложенность, древовидность и т.д. Получаем ссылку на архив термина
В случаи с выведением на страницу, указывать аргументы не обязательно, но в такой записи заголовок будет обрамлен H1-тегом. Не много о том, почему не стоит вытягивать post_title с обьекта WP_Post – потому, что запись может быть личной или защищенной паролем; при правильном выводе эти статусы являются частью заголовка, при неправильном – этот функционал придется дублировать… а это плохо!
поскольку одинаковых метаключей к одной записи может быть много, то данные извлекаются в виде массива; если поле одно – можно сразу его извлечь, указав true третьим аргументом – иначе, по умолчанию, будет false.
Навигация
Под контентом поста принято делать ссылки на предыдущий и следующий посты. Делается это следующим кодом
Через аргумент функции моно кастомизировать только атрибуты class у и aria-label тега nav, а также то, что в ссылке. Для этого в массиве $args есть ключи prev_text(может содержать html-теги), next_text(тоже), aria_label и class.
Если же такой уровень кастомизации не достаточен и нужна своя разметка – делаем следующее: получаем а-теги
В вордпресс принято поключать комментарии таким образом
<?php
if ( comments_open() || get_comments_number() ) :
comments_template();
endif;
Здесь проверяется открытые ли комментарии у поста и их наличие. При выполнении хотя б одного условия – будут загружены стандартные форма комментирования и список комментов, которые можно перезаписать в файле comments.php темы.
Рекомендую сделать также, это избавит от некоторых проблем. Дальше, создаем файл comments.php и пишем в него.
Форма комментариев
Здесь ситуация похожа на ситуацию с навигацией: форму комментария строит отдельная функция.
<?php
comment_form( $args, $post_id );
внутри цикла $post_id можно пропустить. При пустом $args получим следующий html
<div id="respond" class="comment-respond">
<h3 id="reply-title" class="comment-reply-title">Leave a Reply <small><a rel="nofollow" id="cancel-comment-reply-link" href="{cancelReplyLink}" style="display:none;">Cancel reply</a></small></h3>
<form action="{actionLink}" method="post" id="commentform" class="comment-form" novalidate="">
<!-- if current user logged in -->
<p class="logged-in-as">
<a href="{profileLink}" aria-label="Logged in as admin. Edit your profile.">Logged in as admin</a>. <a href="nonce=dc4f1aa958">Log out?</a>
</p>
<!-- endid -->
<p class="comment-form-comment">
<label for="comment">Comment</label>
<textarea id="comment" name="comment" cols="45" rows="8" maxlength="65525" required="required"></textarea>
</p>
<!-- if current user not logged in -->
<p class="comment-form-author">
<label for="author">Name <span class="required">*</span></label>
<input id="author" name="author" type="text" value="" size="30" maxlength="245" required="required">
</p>
<p class="comment-form-email">
<label for="email">Email <span class="required">*</span></label>
<input id="email" name="email" type="email" value="" size="30" maxlength="100" aria-describedby="email-notes" required="required">
</p>
<p class="comment-form-url">
<label for="url">Website</label>
<input id="url" name="url" type="url" value="" size="30" maxlength="200">
</p>
<p class="comment-form-cookies-consent">
<input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes"> <label for="wp-comment-cookies-consent">Save my name, email, and website in this browser for the next time I comment.</label>
</p>
<!-- endif -->
<p class="form-submit">
<input name="submit" type="submit" id="submit" class="submit" value="Post Comment">
<input type="hidden" name="comment_post_ID" value="10" id="comment_post_ID">
<input type="hidden" name="comment_parent" id="comment_parent" value="0">
</p>
<input type="hidden" id="_wp_unfiltered_html_comment_disabled" name="_wp_unfiltered_html_comment" value="f6a52e0808"><script>(function(){if(window===window.parent){document.getElementById('_wp_unfiltered_html_comment_disabled').name='_wp_unfiltered_html_comment';}})();</script>
</form>
</div>
Но, в отличие от навигации, здесь можно провести полную кастомизацию через функцию. Для этого заполним массив $args:
ключ fields – массив шаблонов(начиная с тега р), содержащий поля формы комментария за ключами author, email и url;
comment_field – содержит шаблон, непосредственно, поля ввода комментария;
must_log_in – текст для не залогиненных пользователей при обязательном логировании, чтоб оставить коммент;
logged_in_as – текст для залогиненного пользователя;
submit_field – шаблон поля отправки формы.
В принципе, для полной кастомизации этого достаточно, однако, в аргументе есть еще дополнительные поля для более тонкой настройки… и не только вида.
Немного о добавлении своих полей в форму комментирования: для добаления поля – фильтром comment_form_fields изменяй массив полей
после этого экшном wp_insert_comment нужно сохранить данное поле
<?php
add_action('wp_insert_comment', 'wp_insert_custom_comment_field', 10, 2);
function wp_insert_custom_comment_field($id, $comment)
{
if (isset($_POST['my_query'])) {
update_comment_meta($id, 'my_query', sanitize_text_field($_POST['my_query']));
}
}
Список комментариев
Следующая вещь, которая, по сути, есть продолжением формы комментариев – это список ранее оставленных комментариев. Он также выводится функцией
<?php
wp_list_comments($args, $comments);
При использовании стандартной схемы аргумент $comments указывать не нужно. Иначе, $comments = get_comments() в котором нужно формировать свой массив аргументов. При пустом $args wp_list_comments выведет следующую разметку для каждого коммента
Здесь самый широкий, даже, наверное, излишний, способ кастомизации – и все доступно через функцию:
max_depth – максимально разрешенная вложенность комментов;
style – стиль вывода дерева – ul, ol(по умолчанию) или div. Данный аргумент имеет значение для вложенных комментариев, поскольку сама функция должна быть при вызове обнесенная контейнером отдельно;
type – отображаемый тип комментариев;
per_page – в случаи необходимости пагинации – количество комментариев на странице;
page – страница пагинации коммента;
avatar_size – размер аватара комментатора
callback – функция-строитель каждого комментария, без закрывающего тега;
end-callback – строитель закрывающего тега;
walker экземпляр класса-строителя дерева комментариев.
Есть еще много ключей для “мелкой настройки” этой функции, но главное уже приведено. По поводу того, что функции открывающего и закрывающего тега разделены – тут, как и в меню, есть вложенность, и для ее реализации сделано так.
Сайдбар
Сайдбар – это место для вывода виджетов, виджет-зона. У нее, также как и у комментов, свой стандарт для выведения. В шаблоне страницы ставят такой код
<?php
get_sidebar($slug);
данный код подключит sidebar.php из темы; если $slug не пустая строка, то подключит sidebar-{$slug}.php
<?php
if (is_active_sidebar($slug)) {
dynamic_sidebar($slug);
}
$slug здесь это идентификатор виджет-зоны, который был указан при ее регистраци в функции register_sidebar().
Заключение
Рассмотрены все стандартные элементы шаблон страницы записи WordPress. О чем стоит упомянуть еще – так это о том, что в обертке каждого поста должны быть теги
это анкор для ссылки в шапке. Считаю, что рассказал вам обо всех элементах сих страниц, так как дополнительные – это виджеты, вставляемые в сайдбар. И кстати, некоторые из этих элементов встречаются и на страницах других типов.
Навигационное меню это один из 6-и типов навигации представленных в вордпресс. Поскольку 3 это часть “архитектурного ансамбля” других элементов – расскажу о них в будущих статьях; оставшиеся 2 типа – это виджеты со списками последних записей и различных архивов. О них, я считаю, и говорить не стоит…
Прежде, чем вызывать в шаблоне менюху, нужно зарегистрировать меню в системе. Да, в любом шаблоне это уже сделано, но ведь я рассматриваю случай с разработкой собственной! Итак, код регистрации(в functions.php темы)
Данным кодом я регнул локацию для вывода меню с идентификатором “primary” и именем “Primary Menu”. В случаи регистрации множества локаций для меню, код будет таким
массив аргументов необязателен, но тогда будет выведено первое непустое меню.
Теперь по аргументам… theme_location идентификатор зоны регистрации меню(если равно primary из примера реги, будет выведено меню, закреплённое за этой зоной в админке – выбран чекбокс). В menu будет слаг, название или id созданного меню.
Ключи container, container_class, container_id и container_aria_label – это тег обертки меню и ее атрибуты class, id и aria-label соответственно. В items_wrap, menu_class, menu_id хранятся разметка для обертки списка и ее атрибуты. Если ты используешь свой шаблон обертки – обязательно делай плейсхолдер %3$s, поскольку именно он будет заменен на пункты списка.
Каждый пункт меню – это ссылка, тег а; before и after – это содержимое перед и после ссылки. link_before и link_after – содержимое до и после текста ссылки(между открывающим и закрывающим тегами а).
Также, есть настройки, не связанные непосредственно с постройкой дерева – fallback_cb, depth и walker. Первая – это название функции, которая будет вызвана при отсутствии меню в БД; вторая – разрешенная глубина меню, третья – класс, который будет строить само меню(нужно указывать объект класса, а не строку). О нем то мы и поговорим более подробно.
Класс Walker. Кастомизация базового шаблона меню
По умолчанию, функция wp_nav_menu использует родной класс Walker_Nav_Menu расширяющий класс Walker. По сути, класс Walker_Nav_Menu содержит методы-шаблону постройки пунктов меню и оберток для них, а родительский класс содержит всю логику посстроения скелета по шаблонам ребенка. Для кастомизации шаблонов, в нем(классе), как и во всем вордпресс, предусмотрены фильтры и экшны; предлагая их кратко рассмотреть.
<?php
class Walker_Nav_Menu extends Walker {
/**
* What the class handles.
*
* @since 3.0.0
* @var string
*
* @see Walker::$tree_type
*/
public $tree_type = array( 'post_type', 'taxonomy', 'custom' );
/**
* Database fields to use.
*
* @since 3.0.0
* @todo Decouple this.
* @var array
*
* @see Walker::$db_fields
*/
public $db_fields = array(
'parent' => 'menu_item_parent',
'id' => 'db_id',
);
/**
* Starts the list before the elements are added.
*
* @since 3.0.0
*
* @see Walker::start_lvl()
*
* @param string $output Used to append additional content (passed by reference).
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
public function start_lvl( &$output, $depth = 0, $args = null ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "t";
$n = "n";
}
$indent = str_repeat( $t, $depth );
// Default class.
$classes = array( 'sub-menu' );
/**
* Filters the CSS class(es) applied to a menu list element.
*
* @since 4.8.0
*
* @param string[] $classes Array of the CSS classes that are applied to the menu `<ul>` element.
* @param stdClass $args An object of `wp_nav_menu()` arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$class_names = join( ' ', apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
$output .= "{$n}{$indent}<ul$class_names>{$n}";
}
/**
* Ends the list of after the elements are added.
*
* @since 3.0.0
*
* @see Walker::end_lvl()
*
* @param string $output Used to append additional content (passed by reference).
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
public function end_lvl( &$output, $depth = 0, $args = null ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "t";
$n = "n";
}
$indent = str_repeat( $t, $depth );
$output .= "$indent</ul>{$n}";
}
/**
* Starts the element output.
*
* @since 3.0.0
* @since 4.4.0 The {@see 'nav_menu_item_args'} filter was added.
*
* @see Walker::start_el()
*
* @param string $output Used to append additional content (passed by reference).
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $id Current item ID.
*/
public function start_el( &$output, $item, $depth = 0, $args = null, $id = 0 ) {
if ( isset( $args->item_spacing ) && 'discard' === $args->item_spacing ) {
$t = '';
$n = '';
} else {
$t = "t";
$n = "n";
}
$indent = ( $depth ) ? str_repeat( $t, $depth ) : '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'menu-item-' . $item->ID;
/**
* Filters the arguments for a single nav menu item.
*
* @since 4.4.0
*
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
*/
$args = apply_filters( 'nav_menu_item_args', $args, $item, $depth );
/**
* Filters the CSS classes applied to a menu item's list item element.
*
* @since 3.0.0
* @since 4.1.0 The `$depth` parameter was added.
*
* @param string[] $classes Array of the CSS classes that are applied to the menu item's `<li>` element.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
/**
* Filters the ID applied to a menu item's list item element.
*
* @since 3.0.1
* @since 4.1.0 The `$depth` parameter was added.
*
* @param string $menu_id The ID that is applied to the menu item's `<li>` element.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$id = apply_filters( 'nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args, $depth );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $class_names . '>';
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
if ( '_blank' === $item->target && empty( $item->xfn ) ) {
$atts['rel'] = 'noopener noreferrer';
} else {
$atts['rel'] = $item->xfn;
}
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
$atts['aria-current'] = $item->current ? 'page' : '';
/**
* Filters the HTML attributes applied to a menu item's anchor element.
*
* @since 3.6.0
* @since 4.1.0 The `$depth` parameter was added.
*
* @param array $atts {
* The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
*
* @type string $title Title attribute.
* @type string $target Target attribute.
* @type string $rel The rel attribute.
* @type string $href The href attribute.
* @type string $aria_current The aria-current attribute.
* }
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( is_scalar( $value ) && '' !== $value && false !== $value ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
/** This filter is documented in wp-includes/post-template.php */
$title = apply_filters( 'the_title', $item->title, $item->ID );
/**
* Filters a menu item's title.
*
* @since 4.4.0
*
* @param string $title The menu item's title.
* @param WP_Post $item The current menu item.
* @param stdClass $args An object of wp_nav_menu() arguments.
* @param int $depth Depth of menu item. Used for padding.
*/
$title = apply_filters( 'nav_menu_item_title', $title, $item, $args, $depth );
$item_output = $args->before;
$item_output .= '<a' . $attributes . '>';
$item_output .= $args->link_before . $title . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
/**
* Filters a menu item's starting output.
*
* The menu item's starting output only includes `$args->before`, the opening `<a>`,
* the menu item's title, the closing `</a>`, and `$args->after`. Currently, there is
* no filter for modifying the opening and closing `<li>` for a menu item.
*
* @since 3.0.0
*
* @param string $item_output The menu item's starting HTML output.
* @param WP_Post $item Menu item data object.
* @param int $depth Depth of menu item. Used for padding.
* @param stdClass $args An object of wp_nav_menu() arguments.
*/
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
И еще… мало не забыл… немного о методах. Для строительства скелета, есть 4 метода: start_lvl и end_lvl – строят открывающий и закрывающий теги обертки вложенных меню; start_el и end_el – открывающий и закрывающий теги пункта меню.
Глянем фильтры
Буду описывать их по порядку встречи в коде. Первым мы встречаем фильтр “nav_menu_submenu_css_class”. Он расположен в методе start_lvl и отвечает за фильтрацию классов оберток дочерних оберток списков. Функции-фильтру передаются 3 параметра – массив классов, вложенность строящейся обертки и массив, переданный при вызове wp_nav_menu. В этом методе хуков больше нет, перейду к следующему.
Метод end_lvl в принципе не может содержать ничего меняющегося, соответственно – хуков он не содержит. Глянем следующий метод – start_el – он то уж содержит много хуков.
Первый хук который мы здесь видим – фильтр nav_menu_item_args. Как можно понять с его кода, предназначен он для фильтрации массива с функции строителя(wp_nav_menu). Фильтр принимает 3 аргумента: массив для фильтрации, объект данных о строящемся пункте меню и его вложенность. Следующий, тоже фильтр, nav_menu_css_class фильтрует классы у тега li, принимает уже 4 аргумента – массив классов, объект пункта, массив из строителя и вложенность. Следующим, идет фильтр атрибута id тега li. Он принимает те же аргументы, что и предыдущий фильтр с той лишь разницей, что вместо массива классов – строка с текущим id(строка, поскольку class`ов может быть много, а id один).
Следующий фильтр nav_menu_link_attributes фильтрует атрибуты тега а. Принимает он те же аргументы, что и фильтр выше, с разницей в место строки id массив атрибутов тега а. Затем идет общий фильтр the_title, которым можно изменить текст ссылки, исходя из текущего текста и id пункта меню. После общего фильтра, идет местный nav_menu_item_title фильтр анкора ссылки. Если честно – не знаю и не понимаю, зачем прилепили общий, ведь этот получает данные для фильтрации, включающие предыдущие; а именно те же, что и с классами, только в место массива классов текст ссылки.
Ну и последним фильтром класса(метод end_el как и end_lvl не содержит никаких данных) является фильтр всего пункта меню. Он, вполне ожидаемо, принимает те же аргументы, что предыдущий, только вместо строки текста передана строка с HTML разметкой пункта.
Создание своего шаблона меню
В принципе, я не знаю что нужно поместить в меню, чего б не можно было сделать фильтрами в дефолтном классе; может, чтоб не наращивать код и с точки зрения оптимизации – выиграть какую-то долю процента…
Но ладно, раз уж это сделать можно – почему об этом не рассказать?… Первым делом – идем в файл wp-includes/class-walker-nav-menu.php, копируем к нам в файл соответствующий класс и переименовываем его. Именно копируем – я то знаю, где-то в буковке ошибся и все – ошибка, ничего не работает… хотя, впрочем, можете и с нуля набрать… только прочти до конца!
Итак, допустим, я решил создать walker-класс который будет строить только такую разметку
Первое, что можно заметить – наличие картинки, возможность выбора коей с админки пока не существует.
Безусловно, можно сделать кнопку у каждого пункта меню, которая бы открывала фрейм с медиатекой(как у миниатюры поста, к примеру); с целью укорочения этой статьи, я приделаю ввод id картинки с медиатеки
Этот код выведет под каждым пунктом меню следующее
но это еще не все. Для того, чтоб эту настройку сохранить – нужен дополнительный код. Я знаю, что каждый пункт меню хранится как запись с типом поста nav_menu_item; следовательно, настройка мета данное поста и и для его сохраниния нужно функцию повесить на экшн save_post_nav_menu_item(save_post_{post_type} в общем случаи)
<?php
add_action('save_post_nav_menu_item', 'save_nav_menu_item');
function save_nav_menu_item()
{
if (isset($_POST['menu-item-thumbnail'])) {
foreach ($_POST['menu-item-thumbnail'] as $key => $value) {
add_post_meta($key, '_item_thumbnail', $value);
}
}
}
здесь я убрал проверки на возможности пользователя, типы данных и прочее, чтоб не убивать читабельность кода; но ты в своих проектах о них не забывай… Еще одна вещь – все это(настройку, но не меню) можно было бы сделать используя плагины.
Теперь время непосредственно walker-класса. Я не буду делать возможности фильтрации данных и максимально захардкоджу код класса в целях экономии места
Ты наверное заметил, что я “убил” в своем классе часть аргументов функции строителя, для публичных проектов так не делай… никогда.
Заключение
Рассмотрел способ создания кастомного меню. Также был рассмотрен базовый класс, который создает и меню; для примера, наведу код, коим можно сделать такие же изменения в коде
Из “коробки” вордпресс имеет 2 типа записей: посты и страницы. Не всегда этого достаточно… Расскажу, как добавить кастомный тип постов, таксономии к нему и метаданные. А также, создадим свои роли пользователей для операций с постами этого типа.
Следуя традиции из кодекса вордпресс, зарегистрируем тип записи book и категории для нее book_cat. Для этого достаточно следующего кода
<?php
add_action('init', 'register_post_types');
function register_post_types()
{
// регистрируем тип записи
register_post_type('book');
// регистрируем таксономию для типа записи
register_taxonomy('book_cat', 'book');
}
По-поводу регистрации первой функции – все ясно-понятно – аргумент это слаг типа постов; по второй функции – первый аргумент это слаг таксономии, второй – к какому типу записей ее прикрепить, если таких типов несколько – укажи массив всех. В документации сказано, что второй аргумент при регистрации таксономии – это экраны, где выводятся данные таксономии, но, по-моему, мое определение ближе к сути.
Итак, данным выше кодом мы зарегистрировали тип постов, но смысл?.. все, что он(код) делает – создает запись о том, что такой тип существует; единственное, что ты можешь с ним сделать – получить его в вызове функции get_post_types()(ну и, соответственно, для таксономии get_taxonomies()).
Наполняем смыслами. Пункт админ-меню, форма создания/редактирования
Смыслы в процессе регистрации как типов так и таксономий, создают второй и третий аргументы функций, соответственно.
Для начала, проявим эти пункты в меню администратора – для этого допишем наш код до следующего состояния
В таком виде будет выведен пункт меню верхнего уровня в админке. Если же ключу show_in_menu присвоить слаг существующей страницы, то пункт появится как дочерний. Дальше,
<?php
add_action('init', 'register_post_types');
function register_post_types()
{
// массив для типа записей
$typeArray = [
'label' => 'Book',
'show_ui' => true,
'show_in_menu' => true
];
// массив для таксономий
$taxArray = [
'label' => 'Genres'
];
// регистрируем тип записи
register_post_type('book', $typeArray);
// регистрируем таксономию для типа записи
register_taxonomy('book_cat', 'book', $taxArray);
}
рассмотрим типы и таксономии отдельно…
Теперь о странице создания/редактировании поста. По умолчанию, будут доступны поля задания заголовка и поле редактора TinyMCE для контента. Чтобы добавить стандартные метабоксы, в массив $typeArray, в ключ supports впишите массив с идетификаторами всех необходимых меетабоксов: title, editor, author, thumbnail, excerpt, custom-fields, comments, revisions, page-attributes, post-formats. Для того, чтоб работал page-attributes нужно включить вложенность постов, т.е. hierarchical присвоить true.
С thumbnail и post-formats тоже есть своя особенность; их поддержку нужно включить следующим кодом
Еще, малость не забыл, все тексты в меню, форме постов и в фронте сайта задаются в массиве labels… ну, если дефолтные названия не нравятся.
Дальнейшие настройки типов
Следующее, что я предлагаю включить – так это отображение записей моего типа на странице создания меню. Чтоб это сделать, достаточно в массив $typeArray вписать ключ show_in_nav_menus со значением true.
Кстати, как ты видишь, наша таксономия Genres была туда добавлена сразу же, при ее регистрации.
Затем, предлагаю включить посты этого типа в WP REST API; для этого ключу show_in_rest также нужно присвоить значение true. Как ты заметишь, это действие, также, включит редактор Гутенберга. Сам слаг в АПИ будет равен слагу типа поста, однако, его можно изменить в параметре rest_base. По умолчанию, обслуживать АПИ-запросы к этим постам будет WP_REST_Posts_Controller, но параметром rest_controller_class можно изменить(прочти “WP REST API(дефолтное) — взаимодействие с WP сайтом“).
И, последнее, о настройках типов постов, связанное с админкой – установление прав на создание/редактирование постов. По умолчанию, права на сии действия ровны правам на эти действия с записями(post). Но также, при регистрации типа постов есть возможность задавать свои права на эти действия. Для установления своих прав в capability_type достаточно поставить массив из своих слагов(для единственного и множественного чисел – для примера – book books). При этом будут сгенерированы права: если map_meta_cap false(по умолчанию) то создадутся основные(publish_books, edit_books, edit_others_books и read_private_books), но если true – будут созданы и основные и метаправа(delete_published_books, edit_others_books и т.д.).
Также, массив прав можно задать иначе: capability_type поставить в false И в по ключу capabilities создать массив, в котором переназначить слаги существующим возможностям. И теперь, чтобы воспользоваться новыми правами, установим плагин редактирования ролей(к примеру, User Role Editor). С помощью этих плагинов обновим права администратора и создадим новую роль, которой присвоим права на этот тип записей(ну или дадим их существующей роли). Можно, конечно, сделать это кодом – но зачем, если это единоразовое редактирование настроек в таблице БД, после чего плагин следует выкинуть.
И последняя настройка в админке(если твой тип должен использовать дефолтные таксономии) – это ключу taxonomies передать массив со слагами таксономий.
Настройки типа записей для фронта сайта
Во фронте сайта настроек не так уж и много; прежде, чем что-то делать – нужно разрешить публичный доступ к этому типу: ключу publicly_queryable присвоить true. Второе – это конечно же – включение архива записей. Для этого ключу has_archive присваиваем true, после чего обычным циклом в шаблоне темы archive-book.php можно вывести все записи этого типа. Также, в этом параметре можно указать слаг для архива в место true, при этом шаблон отображения страницы тот же. Сами статьи можно просматривать уже после разрешения публичного доступа, который был дан ранее.
Третье – включим результаты поиска на сайте по статьям этого типа – присвоим exclude_from_search 0. Ну вот, пожалуй, все настройки типа записи.
Настройка кастомных таксономий
По аналогии с типом записей – рассмотрим админку, а потом фронт; в админке можно настроить иерархичность таксономии: ключ hierarchical в true(по умолчание) древовидность включена, false – выключена. Cледующее – настроить вид метабокса для отображения таксономии у ключа meta_box_cb назначить функцию отрисовки. Из коробки есть две такие post_categories_meta_box и post_tags_meta_box
После отображения в форме, будем настраивать отображение в админ-меню. Если тип записи в меню как отдельный пункт то таксономии отображаются как подпункты; их отображение можно скрыть поставив public false. Если же, тип записи, к которой принадлежит таксономия, отображается как подпункт в админ-меню – отобразить страницу таксономии настройкой массива не удастся.
Настройка WP REST API и настройка архивной страницы идентичны настройке в типах постов(вплоть до названия ключей).
Относительно прав для таксономий – их также можно настроить. По ключу capabilities задать массив из 4-ех значений:
после этого, все новые роли нужно добавить в пул ролей с помощью тех же плагинов.
Постмета
Постмета и метадата – одно и тоже – то, что ты увидишь в метабоксе custom fields. Собственно, мету можно создавать и через него, но удобнее это делать через свои метабоксы.
Метабоксы можно создавать используя как плагин ACF(забивая на программирование) так и кодом. Рассмотрим их созданиие написание скрипта…