Creating Shortcodes in WordPress CMS



    What are shortcodes

    Starting with version 2.5, WordPress developers have introduced the concept of “Shortcodes API”. This functionality allows you to create and use macro codes in the pages of a site or in blog entries. For example, a simple and short entry will add an entire photo gallery to the page.

    You can read more about shortcodes and learn how to create simple shortcodes from the WordPress documentation .

    In the article I want to show how to create more complex shortcodes and solve the most common problems when creating them:
    1. Connecting third-party scripts and launching  only  if there is a shortcode on the page.
    2. Layered shortcode.
      • Composite shortcode.
      • Shortcode Nesting.



    Soil preparation

    Before you start creating anything, I suggest my option for placing files:

    //
    Includes /
    shortcodes.php
    ...
    functions.php


    Almost every manual suggests creating shortcodes directly in the functions.php file. I will say right away: I am an opponent of this approach. Instead, I highly recommend putting all the shortcodes in a separate file (includes / shortcodes.php) and connecting it to functions.php in one line. This will significantly offload functions.php and make the code more readable.

    Note : WordPress, of course, supports the inclusion of files through require, but really does not recommend doing this. Instead, it is suggested to use get_template_part () .

    Script Connection

    Many novice developers very often make this mistake - they include scripts necessary for the operation of a particular shortcode immediately when declaring a shortcode. That is, scripts are always loaded, even if this shortcode is not on the page.

    An example of such an implementation:

    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');
    


    This is a fully working option, but the script will be loaded on every page, even if it is not needed there (i.e. there is no shortcode).

    To avoid such situations, I propose using the following approach:

    1. Define a shortcode as a separate class.
    2. Add a flag that will determine if a given shortcode is on the page.
    3. Download the script only by the presence flag of the shortcode.


    That's all ...

    An example of such an implementation:

    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();
    


    Unlike the previous implementation, this shortcode is initialized, but all scripts are loaded only if there is a shortcode on the page.

    Nested Shortcodes

    There are a couple of problems that novice developers may encounter:

    • Creating a multi-level shortcode (consisting of several).
    • Using a shortcode inside the same shortcode.


    Now in more detail.

    Creating a multi-level shortcode
    The problem is that such a shortcode consists of several smaller shortcodes and it is necessary to prevent the possibility of using them as separate shortcodes (except when necessary).

    Take, for example, the shortcode that creates the pricing table. To do this, prepare three separate shortcodes:

    [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]


    This example uses three shortcodes: [price] [plan] [option].

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


    To prevent the use of internal shortcodes as separate, the following scheme is proposed:

    Price -> code output to the
    Plan page -> data acquisition
    Option -> data acquisition

    i.e., code is output to the page only in the external shortcode, while internal ones simply return the received data. An example of such an implementation is given below.
    Description of the function of the external shortcode:

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


    Description of the functions of internal shortcodes:

    function plan_code ($atts, $content) {
            // получаем параметры шорткода
            extract(shortcode_atts(array(
                'title'      => '',          // Plan title name
                'price'      => '0',         // Plan price
            ), $atts));
            // Подоготавливаем HTML: заголовок плана
            $plan_title  = '
    '; $plan_title .= '

    '.$title.'

    '; $plan_title .= '
    '; // Подоготавливаем HTML: стоимость $f_price = round(floatval($price), 2); $f_price = ($f_Price > 0) ? $f_Price : 0; $s_price = '$'.$f_Price; $price_plan = '
    '; $price_plan .= '

    '.$s_price.'

    '; $price_plan .= ' '.$text.''; $price_plan .= '
    '; // инициализация глобальных переменных для опций $GLOBALS['plan-options-count'] = 0; $GLOBALS['plan-options'] = array(); // читаем контент и выполняем внутренние шорткоды do_shortcode($content); // Подоготавливаем HTML: опции $plan_options = '
    '; if (is_array($GLOBALS['plan-options'])) { foreach ($GLOBALS['plan-options'] as $option) { $plan_options .= $option; } } $s_OptionsDiv.= '
    '; // Подоготавливаем 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 = '
    '; $plan_option .= '

    '.do_shortcode($content).'

    '; $plan_option .= '
    '; // сохраняем полученные данные $i = $GLOBALS['plan-options-count'] + 1; $GLOBALS['plan-options'][$i] = $plan_option; $GLOBALS['plan-options-count'] = $i; // ничего не выводим return true; }


    With this approach, the shortcode will work only in collection, that is, if used correctly, in other cases nothing will be displayed on the screen (accordingly, nothing will break).

    Of course, you can still optimize and improve this shortcode, but still, I think, I managed to demonstrate the main idea.

    Repeatable Shortcodes

    The problem is this: you need to use the same shortcode inside the shortcode. The most common example in my practice was the shortcode for creating a column. That is, for example, you need to implement the separation of the page into 2 parts using columns and in the first column split into 2 more columns.

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


    Unfortunately, for WordPress this nesting is already too tough. Layout will fly apart on the second content. This happens because when you open the shortcode, WordPress immediately looks for the second (closing) part of this shortcode, i.e. in this example, the first column will be closed on the first nested shortcode.

    Unfortunately, there are no other options for solving this problem than just adding new shortcodes. But, rewriting functions does not make sense, you can simply initialize the shortcode to existing functions:

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


    Conclusion

    In this article, I examined the most common problems that I myself have ever encountered. If you have something to add, fix, or propose your own solution to a particular problem, do not hesitate to write in the comments to this article.

    Posted by Dmitry Kabakov, Senior Front-end Developer.

    Also popular now: