Custom Select / Checkbox Tree

If you’ve used taxonomy field type, you will see it has an option to display options in a checkbox / select tree. When users select a parent item, it will show children item. It’s quite a nice feature for users. But how to do that with normal checkbox list or select field types?

The answer is simple: it’s already built-in the Meta Box plugin. And here is the sample code:

https://gist.github.com/rilwis/d6635c18cca81aaa4a593d4c178b456b

To enable select/checkbox tree, you need to:

  • Set the option in format of array( 'value' => 'Your value', 'label' => 'Your label' ), e.g. no more short type of 'value' => 'Label' anymore.
  • For child items, please add 'parent' => 'parent_value'
  • Set 'flatten' => false for the field
  • Set the field type checkbox_list if you want a checkbox tree or select_tree if you want a select tree.

How to save WYSIWYG content as post content

Sometimes you want to move the traditional editor for the post to another location, probably inside a meta box. It will keep everything more organized. For example, if you have an event post type, then you might have the following data:

  • Start date and time
  • End date and time
  • Venue
  • Description
  • Gallery

Then keeping the description (which can be the post content) along with other fields make users easier to fill in the data. While this is impossible to do that with the default WordPress editor, we can do that with the Meta Box plugin.

The steps to do that are describe below:

  1. Remove the default editor (we don’t want to display it at the top)
  2. Create a new wysiwyg field
  3. Set that field take the post content as the default value
  4. Save that field content into post content

The code is quite straight forward as below:

https://gist.github.com/rilwis/eacd78b507df951cb0450ebd38ee14ac

There are 2 things important in the code above:

  1. To make Meta Box save the wysiwyg content to post content, we have to set it 'id' => 'content'. This id is used to tell WordPress to get the submitted value and save it to post content.
  2. Because we use the WordPress’s id for the field, some WordPress style is applied and makes the field look weird. We have to use a custom_html field to output some CSS to fix it. Note that you might want to enqueue another stylesheet to do the same job. Here we use the custom_html for the quick fix, it just works.

The code above works for post. If you want to make it work for another post type, don’t forget to change the post type in the code above!

Using custom HTML field with callback to output anything

custom_html is a very useful field in Meta Box, which helps you to output anything in the post edit screen. It can be a warning box or an instruction for users to help them enter the correct data. But do you know that you can use PHP to render the HTML for the field? Using PHP, you can query the database, get WordPress information and show it. There’s no limit with PHP. This guide helps you understand the field using a callback function to query the WordPress database and output the result.

For the demo purpose: I will setup a connection from a post to many pages (1 to many). And when editing a page, I need to know which posts refer to the current page.

So, creating a connection from a page to many posts is very simple with a field post like this:

[
   'id'        => 'page',
   'name'      => 'Page',
   'type'      => 'post',
   'post_type' => 'page',
   'multiple'  => true,
],

To get the list of posts refer to the current page, I will use a field custom_html like this:

[
   'id'       => 'posts',
   'type'     => 'custom_html',
   'callback' => 'prefix_get_connected_posts',
],

Note the callback parameter: this callback refers to a custom PHP function (in this case prefix_get_connected_posts) which renders the content for the field.

Implementing this function is quite easy as below:

function prefix_get_connected_posts() {
   if ( ! $page_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT ) ) {
      return '';
   }
   $output = '';
   $posts  = get_posts( [
      'posts_per_page' => - 1,
      'meta_key'       => 'page',
      'meta_value'     => $page_id,
   ] );
   foreach ( $posts as $post ) {
      $output .= '<li><a href="' . get_permalink( $post ) . '">' . get_the_title( $post ) . '</a></li>';
   }

   return $output ? '<ul>' . $output . '</ul>' : '';
}

Here is the full code:

https://gist.github.com/rilwis/44058840b0bcddb4c3bd6e1f01fbb9e1

And here is the result:

When editing a post:

edit posts

When editing a page:

edit page

Using Meta Box with Composer

Composer is a package dependency manager for PHP. It’s kind of TGM Activation class for WordPress, but works for any PHP package. Composer allows us to include other PHP libraries in our projects without conflict between them. Without Composer, if a project requires 2 libraries A and B, and both of them requires a library C, then C might be included twice. The scenario can get worse if A and B includes different version of C which can cause a conflict when loading the library. Composer helps us to avoid this issue.

Besides, there are more than 100K packages (libraries) for us to explore if we use Composer. They can be a framework like Laravel, Symphony or a package to handle the payment with Stripe or authenticate with social networks. That means lots of possibility to do something with less code by standing on shoulders of giants!

Fortunately, Meta Box is totally compatible with Composer and you can use it as a library for your projects. As Meta Box is a WordPress-specific package, the “project” term here means a WordPress website, plugin or theme. This tutorial will show you how to use it for a plugin. I will name the plugin MB Composer Example and its code is available on Github.

Installing Composer

Please follow the very detailed guide on the Composer homepage to know how to install Composer on your system.

Project setup

Composer requires a config file named composer.json where we defines all the dependencies. So, create it in the plugin folder and add the following content:

{
  "require": {
    "rilwis/meta-box": "4.*"
  }
}

The first (and often only) thing you specify in composer.json is the require key. You’re simply telling Composer which packages your project depends on. The package name has the format author/package-name. In this case, it’s rilwis/meta-box.

The value is the version you want to use in your project. 4.* means the latest version 4. You can set a specific version 4.8.7 or using wildcard. For more info about the syntax of versioning, please follow the Composer documentation.

You can add more packages by adding more key-value pairs if your project uses other packages than Meta Box.

Installing dependencies

The 2nd step is telling Composer to pull the packages that your project depends on to the local folder. Simply cd to your project folder and run:

composer install

Make sure you install Composer globally. If you don’t know how to do that, please refer to the documentation in the Installing Composer section above.

After a minute, Composer will download Meta Box and put it in a local folder. Your folder structure after that will look like this:

meta box composer

Autoloading

To make Meta Box works, we need to include the plugin’s file in our plugin. Although we can hardcode that, Composer has an intelligent autoloading mechanism to load packages. Simply put the following line in the plugin’s main file:

require plugin_dir_path( __FILE__) . 'vendor/autoload.php';

I use plugin_dir_path function because this is a WordPress environment. But you can ignore it and use a simpler version like this:

require __DIR__ . 'vendor/autoload.php'; // PHP 5.3

// or

require 'vendor/autoload.php'; // Relative path inclusion

Now Meta Box is available in your plugin and we can start using it.

Register meta boxes and custom fields

As Meta Box is loaded, we can register meta boxes and custom fields as usual. In this example, I register a demo meta box with 2 fields of Name and Bio:

add_filter( 'rwmb_meta_boxes', 'mb_composer_example_register_meta_boxes' );
function mb_composer_example_register_meta_boxes( $meta_boxes ) {
    $meta_boxes[] = array(
        'title'  => __( 'A sample meta box', 'mb-composer-example' ),
        'fields' => array(
            array(
                'name' => __( 'Name', 'mb-composer-example' ),
                'id'   => 'name',
                'type' => 'text',
            ),
            array(
                'name' => __( 'Bio', 'mb-composer-example' ),
                'id'   => 'bio',
                'type' => 'wysiwyg',
            ),
        ),
    );

    return $meta_boxes;
}

Now go to Dashboard > Posts > Add New and we’ll see:

meta box

Bravo! We made it.

Conclusion

This tutorial is a very simple example of using Meta Box with Composer. You can learn more about Composer by reading its documentation and browser packages at packagist.org. Although this is not a traditional way to develop plugins for WordPress, using a modern PHP tool like Composer benefits us a lot because of 2 reasons:

  • Dependency management: it’s always a headache for managing dependencies in WordPress. There are some works around that like TGM Plugin Activation class and Plugin Dependency. But it’s still far from perfect. Not all plugins are be able to be included inside another plugin like Meta Box.
  • Vast amount of libraries: this is a huge advantage of using Composer. With these well-coded libraries, your development process will be easier, faster and thus, it saves you tons of time.

So, enjoy Composer and Meta Box! And share with us how you use them in your projects via the Contact form. We’d love to know that!

Conditional Logic and Taxonomies

As you know, Meta Box Conditional Logic can works with default taxonomies like tags or categories and even custom taxonomy. For example:

Visible a field or meta box when selected post category is either 4, or 5, or 6

'visible' => ['post_category', 'in', [4,5,6]]

Since 1.3, you can define the condition’s value using slug. Just append slug: before the selector. Like so:

'visible' => ['slug:post_category', 'in', ['fashion', 'gaming', 'technology']]

Cool right?

You can also set conditional logic depends on selected custom taxonomy by using tax_query. Like so:
Hide a field or meta box when selected custom taxonomy’s id is greater than 5

'hidden' => ['tax_query[product]', '>', 5]

Of course, it works with slug also:

'hidden' => ['slug:tax_query[product]', '!=', 'drones']

The difference between Include Exclude, Show Hide and Conditional Logic extensions

We have 3 extensions that can help you control the visibility of a meta box for a certain post/page:

That might be confused as you don’t know which extension is the right choice for you. So the main difference between them is the following:

Meta Box Include Exclude hides meta boxes by PHP, meaning:

  • Meta boxes are removed completely from the editing screen, no HTML markup is outputted at all
  • Since meta boxes are removed completely, there are no inputs at all and thus, there are no meta values are saved at all
  • There is no way to show the meta boxes again unless reloading the page

Both Meta Box Conditional Logic and Show Hide hide meta boxes by Javascript, meaning:

  • The HTML of meta boxes are outputted, inputs are only hidden and the meta values are submitted when saving post and are saved in the database
  • You can show them without reloading the page

The Meta Box Conditional Logic differs from Show Hide: it has more advanced options which allows you to show/hide not only meta boxes but also fields and specific HTML elements. The Show/Hide extension can show/hide only meta boxes. Besides, the number of conditions in Conditional Logic are more than Show/Hide extension (greater, less than, not equal, etc.).

Hide Group with Conditional Logic

Since 1.2, you can hide Group without using this ugly hack! Group just works as other fields

Currently, Meta Box Group doesn’t output html ID attribute so Conditional Logic can’t touch Group. To hide Group, we’ll need an extra hack

Imagine we have a Group like this:

'fields' => array(
    array(
        'id'     => 'standard',
        'type'   => 'group',
        'clone'  => true,
        'sort_clone' => true,


        'hidden' => array('post_format', 'aside'), // Doesn't work

        ...
    )
)

So we’ll wrap this field by a ID which same as this group’s ID.

'fields' => array(
    array(
        'id'     => 'standard',
        'type'   => 'group',
        'clone'  => true,
        'sort_clone' => true,


        'hidden' => array('post_format', 'aside'), // This will works
        'before' => '<div id="standard">', // ID should same as this field id
        'after'  => '</div>',
        ...
    )
)

Hide Tabs with Conditional Logic

Conditional Logic works with any DOM elements. That means you can hide your tabs with Meta Box Conditional Logic. This guide show you how to do that.

Because Tabs isn’t regular field or meta box so you have to use rwmb_outside_conditions filter to tell Conditional Logic add this element. Imagine you have create a meta box like this:

add_filter( 'rwmb_meta_boxes', function($meta_boxes)
{
    $meta_boxes[] = array(
        'title'     => __( 'Meta Box Tabs 2', 'rwmb' ),
        'tabs'      => array(
            'bio'      => __( 'Biography', 'rwmb' ),
            'interest' => __( 'Interest', 'rwmb' ),
        ),
        'tab_style' => 'box',
        'fields'    => array(
            array(
                'name' => __( 'Bio', 'rwmb' ),
                'id'   => 'bio',
                'type' => 'textarea',
                'tab'  => 'bio',
            ),
            array(
                'name' => __( 'Interest', 'rwmb' ),
                'id'   => 'interest',
                'type' => 'textarea',
                'tab'  => 'interest',
            ),
        ),
    );

    return $meta_boxes;
});

This will generate a meta box with two tabs Biography and Interest. And here is the html output:

<ul class="rwmb-tab-nav">
    <li class="rwmb-tab-bio rwmb-tab-active" data-panel="bio"><a href="#">Biography</a></li>
    <li class="rwmb-tab-interest" data-panel="interest"><a href="#">Interest</a></li>
</ul>

If you want to hide a tab, just add its class to rwmb_outside_conditions filter. For example, if I want to hide Interest tab when post format is aside. Simply add these lines:

add_filter( 'rwmb_outside_conditions', function( $conditions )
{
    $conditions['.rwmb-tab-interest'] = array(
        'hidden' => array('post_format', 'aside')
    );

    return $conditions;
} );

This will tell Conditional Logic hide .rwmb-tab-interest selector when post_format is aside

Hide the first tab

In case you want to hide the first tab, you have to hide its tab pane also and show the next tab pane, this example shows you how to do that:

add_filter( 'rwmb_outside_conditions', function( $conditions )
{
    $conditions['.rwmb-tab-bio, .rwmb-tab-panel-bio'] = array(
        'hidden' => array('post_format', 'aside')
    );

    $conditions['.rwmb-tab-panel-interest'] = array(
        'visible' => array('post_format', 'aside')
    );

    return $conditions;
} );