📜 ⬆️ ⬇️

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 macrocodes in website pages or blog entries. For example, a simple and short recording will add a whole photo gallery on the page.

You can read more about shortcodes and learn how to create simple shortcodes from the WordPress documentation .
')
In this article I want to show how to create more complex shortcodes correctly and solve the most common problems when creating them:
  1. Connect third-party scripts and run only if there is a shortcode on the page.
  2. Layered Shortcode.
    • Composite shortcode.
    • Nesting shortcodes.



Soil preparation

Before you begin to create something, I suggest my own version of the file placement:

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


Virtually every guide suggests creating shortcodes right in the functions.php file. I will say right away: I am an opponent of such an approach. Instead, I strongly recommend putting all the shortcodes in a separate file (includes / shortcodes.php) and plugging it into functions.php in one line. This will significantly unload functions.php and make the code more readable.

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

Connecting scripts

Many novice developers very often make this mistake - they include the scripts necessary for the work of one or another shortcode, immediately upon the announcement of the shortcode. That is, the 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 version, but the script will be loaded on each page, even if it is not needed there (i.e. there is no shortcode).

In order to avoid such situations, I suggest using the following approach:

  1. Define the shortcode as a separate class.
  2. Add a flag that determines whether this shortcode is on the page.
  3. Download the script only on the shortcode presence flag.


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:



Now - in more detail.

Creating a layered shortcode
The problem is that such a shortcode consists of several smaller shortcodes and it is necessary to prevent them from being used as separate shortcodes (except when necessary).

Take, for example, the shortcode that creates the pricing table. To do this, it is necessary to prepare three separate schools

[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 -> output code to page
Plan -> Data Acquisition
Option -> data retrieval

That is, the output of the code to the page occurs only in the external shortcode, while the internal one simply returns the received data. An example of such an implementation is given below.
Description of the external shortcode function:

  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; } 


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 = '<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; } 


With this approach, the shortcode will only work as an assembly, i.e., 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.

Duplicate shortcodes

The problem is this: you need to use the same shortcode inside the shortcode. The most frequent example in my practice was the shortcode for creating a column. Ie, for example, you need to implement the division of the page into 2 parts with the help of columns and in the first column to divide by 2 columns.

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


Unfortunately, WordPress already has this kind of nesting power. Layout scatter on the second content. This happens because when you open a shortcode, WordPress immediately looks for the second (closing) part of this shortcode, i.e. in this example, the first column will be closed at the first nested shortcode.

To solve this problem, unfortunately, there are no other options than simply adding new shortcodes. But, it makes no sense to rewrite the functions, you can simply initialize the shortcode to the existing functions:

 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] 


Conclusion

In this article, I looked at the most frequent problems that I myself have ever encountered. If you have something to add, correct, or suggest your own solution to a particular problem, do not hesitate to write in the comments to this article.

Author: Dmitry Kabakov, Senior Front-end Developer.

Source: https://habr.com/ru/post/265245/


All Articles