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



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

    Начиная с версии 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.
    Метки:
    • +5
    • 27,9k
    • 7
    DataArt 77,46
    Технологический консалтинг и разработка ПО
    Поделиться публикацией
    Похожие публикации
    Комментарии 7
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        get_template_part() рекомендуется использовать в темах WordPress.
        require / include в плагинах.
        • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          get_template_part() — часть нативного WordPress API, используется для подключения повторяющихся частей темы. Также рекомендуется использовать его в разработке тем, т. к. с его помощью легче переподключить файлы в дочерних темах. Т. е. никто не запрещает использовать require, разработчики WordPress рекомендуют использовать get_template_part как лучшую практику. И, если вы будете писать темы для ThemeForest, можете не пройти проверку (там ко всему цепляются :)).
          Подробнее о подключении файлов:
          code.tutsplus.com/articles/how-to-include-and-require-files-and-templates-in-wordpress--wp-26419
        • 0
          О, спасибо! А что делать, когда надо
          [column_half_inner] [column_half_inner] Content [/column_half_inner] [column_half_inner] Content [/column_half_inner] [/column_half_inner]
          для созданных ШК? Возможно, остались секреты? :) Или делать _inner_inner?

          По скриптам, которые добавляются всегда, иногда так будет лучше: если все скрипты динамически объединяются в один файл и сжимаются.
          • 0
            Да, вы можете определить, сколько угодно внутренних шорткодов. Но, если приходится использовать такие структуры, возможно, стоит пересмотреть макет страницы? Обычно, если требуется столько вложений, я иду выше и пересматриваю разметку страницы, оптимизируя вложенности.
          • 0
            шорткоды и виджеты встроенные в тему WordPress как и виджеты крайне не рекомендованы т.к. при смене темы вся стилизация и весь функционал шорткодов и виджетов исчезает.
            их с недавних пор нужно выносить в отдельные плагины.

            это можно легко проверить установив плагин Theme Check (для проверки валидности вашей темы) который выдаст ошибку.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое