Pull to refresh
0
DataArt
Технологический консалтинг и разработка ПО

Создание шорткодов в WordPress CMS

Reading time 6 min
Views 47K


Что такое шорткоды

Начиная с версии 2.5, разработчики WordPress ввели понятие «Shortcodes API». Этот функционал позволяет создавать и использовать макрокоды в страницах сайта или в записях блога. Например, простая и короткая запись добавит на странице целую фотогалерею.

Более подробно прочитать о шорткодах и узнать, как создавать простые шорткоды, вы можете из документации WordPress.

В статье хочу показать, как правильно создавать более сложные шорткоды и решить наиболее распространенные проблемы при их создании:
  1. Подключение сторонних скриптов и запуск только при наличии шорткода на странице.
  2. Многоуровневый шорткод.
    • Составной шорткод.
    • Вложенность шорткодов.



Подготовка почвы

Прежде чем начать создавать что-либо, предлагаю свой вариант размещения файлов:

/
/Includes/
shortcodes.php

functions.php


Практически в каждом руководстве предлагают создавать шорткоды прямо в файле functions.php. Скажу сразу: я — противник такого подхода. Вместо этого настоятельно рекомендую вынести все шорткоды в отдельный файл (includes/shortcodes.php) и подключить его в functions.php одной строкой. Это значительно разгрузит functions.php и сделает код более читабельным.

Заметка: WordPress, конечно, поддерживает подключение файлов через require, но очень не рекомендует делать этого. Вместо этого предлагается использовать get_template_part().

Подключение скриптов

Многие начинающие разработчики очень часто совершают эту ошибку — подключают скрипты, необходимые для работы того или иного шорткода, сразу при объявлении шорткода. Т. е. скрипты загружаются всегда, даже если этого шорткода нет на странице.

Пример такой реализации:

function foobar_func( $atts ) {
    return "foo and bar";
}
add_shortcode( 'foobar', 'foobar_func' );
 
function foo_script () {
    wp_register_script( 'foo-js', get_template_directory_uri() . '/includes/js/foo.js');
    wp_enqueue_script( 'foo-js' );
}
add_action( 'wp_enqueue_scripts', 'foo_script');


Это полностью рабочий вариант, но скрипт будет загружаться на каждой странице, даже если он там не нужен (т. е. нет шорткода).

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

  1. Определить шорткод как отдельный класс.
  2. Добавить флаг, который определит есть ли данный шорткод на странице.
  3. Загружать скрипт только по флагу присутствия шорткода.


Вот и все…

Пример такой реализации:

class foobar_shortcode {
  static $add_script;
  static function init () {
      add_shortcode('foobar', array(__CLASS__, 'foobar_func'));
      add_action('init', array(__CLASS__, 'register_script'));
      add_action('wp_footer', array(__CLASS__, 'print_script'));
  }
  static function foobar_func( $atts ) {
      self::$add_script = true; 
      return "foo and bar";
  }
  static function register_script() {
      wp_register_script( 'foo-js', get_template_directory_uri() . '/includes/js/foo.js');
  }
 
  static function print_script () {
      if ( !self::$add_script ) return;
      wp_print_scripts('foo-js');
  }
}
foobar_shortcode::init();


В отличие от предыдущего варианта реализации, этот шорткод инициализируется, но все скрипты подгружаются только при наличии шорткода на странице.

Вложенные шорткоды

Есть еще пара проблем, с которыми могут столкнуться начинающие разработчики:

  • Создание многоуровневого шорткода (состоящий из нескольких).
  • Использование шорткода внутри такого же шорткода.


Теперь — более детально.

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

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

[price]
— [plan title=’Plan 1’ price=’99’]
— [option] Option 1 [/option]
— [option] Option 2 [/option]
— [option] … [/option]
— [/plan]
— [plan title=’Plan 2’ price=’499’]
— [option] Option 1 [/option]
— [option] Option 2 [/option]
— [option] … [/option]
— [/plan]

[/price]


В данном примере используется три шорткода: [price] [plan] [option].

add_shortcode( 'price', 'price_code' );
add_shortcode( 'plan', 'plan_code' );
add_shortcode( 'option', 'option_code' );


Для предотвращения использования внутренних шорткодов в качестве отдельных предлагается следующая схема:

Price -> вывод кода на страницу
Plan -> получение данных
Option -> получение данных

Т. е. вывод кода на страницу происходит только во внешнем шорткоде, внутренние же просто возвращают полученные данные. Пример такой реализации приведен ниже.
Описание функции внешнего шорткода:

    function price_code ($atts, $content) {
 // инициализация глобальных переменных для прайс планов
        $GLOBALS['plan-count'] = 0;
        $GLOBALS['plans'] = array();
 // чтение контента и выполнение внутренних шорткодов
        do_shortcode($content);
 // подготовка HTML кода
        $output = '<div class="price">';
        if(is_array($GLOBALS['plans'])) {
            foreach ($GLOBALS['plans'] as $plan) {
                $planContent  = '<div class="plan">';
                $planContent .=     $plan;
                $planContent .= '</div>';
                $output .= $planContent;
            }
        }
        $output .= '</div>';
 // вывод HTML кода
        return $output;
    }


Описание функций внутренних шорткодов:

function plan_code ($atts, $content) {
        // получаем параметры шорткода
        extract(shortcode_atts(array(
            'title'      => '',          // Plan title name
            'price'      => '0',         // Plan price
        ), $atts));
        // Подоготавливаем HTML: заголовок плана
        $plan_title  = '<div class="plan-title">'; 
        $plan_title .= '  <p>'.$title.'</p>';
        $plan_title .= '</div>';
        // Подоготавливаем HTML: стоимость
        $f_price = round(floatval($price), 2);
        $f_price = ($f_Price > 0) ? $f_Price : 0;
        $s_price = '$'.$f_Price;
        $price_plan  = '<div class="plan">';
        $price_plan .= ' <p class="price-sum">'.$s_price.'</p>';
        $price_plan .= ' <small class="price-text">'.$text.'</small>'; 
        $price_plan .= '</div>'; 
        // инициализация глобальных переменных для опций
        $GLOBALS['plan-options-count'] = 0;
        $GLOBALS['plan-options'] = array();
        // читаем контент и выполняем внутренние шорткоды
        do_shortcode($content);
        // Подоготавливаем HTML: опции
        $plan_options = '<div class="plan-options">';
        if (is_array($GLOBALS['plan-options'])) {
            foreach ($GLOBALS['plan-options'] as $option) {
                $plan_options .= $option;
            }
        }
        $s_OptionsDiv.= '</div>';
        // Подоготавливаем HTML: компонуем контент
        $plan_div  = $plan_title;
        $plan_div .= $price_plan;
        $plan_div .= $plan_options;
        // сохраняем полученные данные
        $i = $GLOBALS['plan-count'] + 1;
        $GLOBALS['plans'][$i] = $plan_div;
        $GLOBALS['plan-count'] = $i;
 // ничего не выводим
        return true;
    }
    function option_code ($atts, $content) {
        // Подоготавливаем HTML 
        $plan_option  = '<div class="price-option">';
        $plan_option .= '    <p class="price-option-text">'.do_shortcode($content).'</p>';
        $plan_option .= '</div>';
        // сохраняем полученные данные
        $i = $GLOBALS['plan-options-count'] + 1;
        $GLOBALS['plan-options'][$i] = $plan_option;
        $GLOBALS['plan-options-count'] = $i;
        // ничего не выводим
        return true;
    }


При этом подходе шорткод будет работать только в сборе, т. е. при правильном использовании, в других случаях ничего не будет выведено на экран (соответственно, ничего не сломается).

Конечно же, вы можете еще оптимизировать и усовершенствовать этот шорткод, но все же, думаю, основную идею продемонстрировать мне удалось.

Повторяющиеся шорткоды

Проблема заключается в следующем: нужно внутри шорткода использовать такой же шорткод. Наиболее частым примером в моей практике был шорткод для создания колонки. Т.е., к примеру, нужно реализовать разделение страницы на 2 части с помощью колонок и в первую колонку разделить еще на 2 колонки.

[column_half]
[column_half] Content [/column_half]
[column_half] Content [/column_half]
[/column_half]
[column_half] Content [/column_half]


К сожалению, для WordPress уже такая вложенность “не по зубам”. Верстка разлетится уже на втором контенте. Происходит это потому что при открытии шорткода WordPress сразу же ищет вторую (закрывающую) часть этого шорткода, т.е. в данном примере первая колонка будет закрыта на первом же вложенном шорткоде.

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

add_shortcode( 'column_half', 'column_half_code' );
add_shortcode( 'column_half_inner', 'column_half_code' );
function column_half_code ( $atts, $content ) {
    return "<div class=’col-lg-6’>".do_shortcode($content)."</div>";
}
В этом случае исходный синтаксис станет:
[column_half]
    [column_half_inner] Content [/column_half_inner]
    [column_half_inner] Content [/column_half_inner]
[/column_half]
[column_half] Content [/column_half]


Заключение

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

Автор: Дмитрий Кабаков, Senior Front-end Developer.
Tags:
Hubs:
+5
Comments 7
Comments Comments 7

Articles

Information

Website
www.dataart.com
Registered
Founded
Employees
1,001–5,000 employees