EDIT: Justin Sternberg has now prepared an official code snippet which does the same thing … https://github.com/CMB2/CMB2-Snippet-Library/blob/master/filters-and-actions/cmb2-add-fields-dynamically.php

CMB2 is a super useful little metabox tool for use with WordPress. I tend to use it whenever I need to spin up complicated metaboxes quickly. It’s much cleaner than the Advanced Custom Fields plugin and results in easier to read/understand code and a less messy admin interface.

One problem I’ve been having for a while though, is with dynamically changing the metaboxes based on previous metabox selections. For the Undiecar Championship website, I needed a way to enter the number of races for each event, and display more metaboxes based on the number of races for the event. The problem I stumbled upon, is that to do this, I need to grab the existing post meta, which is fairly straightforward as you can simply get the post ID from the post query variable. However, when you try to save it, that query variable does not exist during the saving process and so you never know how many items to generate when saving.

The solution to this, is to do a check for the post query variable via $_GET[ 'post' ], and a check for the post_id POST variable via $_POST[ 'post_id' ].

Below is some cut down simple demo code, showing this in action. Hopefully it is of use to some of you đŸ˜€

<?php

add_action( 'cmb2_admin_init', 'cmb2_get_post_id' );
/**
 * Demo to show how to get the post ID from within a CMB2 metabox declaration.
 *
 * @copyright Copyright (c), Ryan Hellyer
 * @license http://www.gnu.org/licenses/gpl.html GPL
 * @author Ryan Hellyer 
 */
function cmb2_get_post_id() {
	$slug = 'something';

	// Get the post ID
	$post_id = null;
	if ( isset( $_GET[ 'post' ] ) ) {
		$post_id = $_GET[ 'post' ];
	} else if ( isset( $_POST[ 'post_ID' ] ) ) {
		$post_id = $_POST[ 'post_ID' ];
	}

	$cmb = new_cmb2_box( array(
		'id'           => $slug,
		'title'        => 'Some test metaboxes',
		'object_types' => array( 'post', ),
	) );

	$cmb->add_field( array(
		'name' => 'Set number of next item',
		'id'   => 'number_of_next_item',
		'type' => 'text',
		'default' => '1',
		'attributes' => array(
			'type' => 'number',
			'pattern' => '\d*',
		),
		'sanitization_cb' => 'absint',
		'escape_cb'       => 'absint',
	) );

	// Loop through however many items are selected in previous metabox
	$number_of_items = get_post_meta( $post_id, 'number_of_next_item', true );
	$number = 1;
	while ( $number <= $number_of_items ) {

		$cmb->add_field( array(
			'name' =>  'Item #' . $number,
			'desc'   => 'item_' . $number,
			'id'   => 'item_' . $number,
			'type' => 'text',
		) );

		$number++;
	}

}