How do I run a get_posts query on an individual options page for my plugin and send those results to another function to populate the select fields?

I'm building a custom plugin that builds a vcard from custom fields on various post types.

On my options page, generated by CMB2, loaded on cmb2_admin_init, I have various select fields that show meta_keys I've found from running a query against all post types. I am seeing a huge number in queries naturally as the entries grow inside each post type. Therefore, I only want to run that query when on the specific admin options page. That is the only time I need that query.

I have been trying to use the 'load-{options-page.php}' action hook which works fine, but I don't understand how to run that query and then get the results inside the function firing on cmb2_admin_init. They are both inside a class so maybe I can pass a variable? But ti always seems to re-run the query because the variable gets assigned inside the function firing on cmb2_admin_init.

I can run this method on 'load-[name-of-optins-page.php}'

 * Convert to array for CMB2 field type
 * @since  0.1.0
public function get_meta_keys_array() {
    $options = array();
    // Get keys from a WP_Query
    $fields = $this->get_meta_keys();
    if( $fields ) {
        // Builds simple array to populate my select fields
        foreach( $fields as $field => $key ) {
            $options[$key] = $key;
    return apply_filters( 'vcard_get_meta_keys_array_output', $options );

and then this function for the field display on 'cmb2_admin_init'

 * Registers options page menu item and form.
 * @since    1.9.0
public function register_options_metabox() {

    // This is where I am running into trouble.
    $meta_keys = $this->get_meta_keys_array();  

    $cmb_options = new_cmb2_box( array(
        'id'           => 'vcard_generator_metabox',
        'title'        => esc_html__( 'vCard Generator Settings', $this->plugin_name ),
        'object_types' => array( 'options-page' )

        $cmb_options->add_field( array(
            'name'             => esc_html__($person['name'], $this->plugin_name),
            'id'               => 'vcard_generator_metabox' . $person['id'],
            'type'             => 'select',
            'show_option_none' => true,
            'default'          => 'custom',
            'options'          => $meta_keys // This is where the meta_keys are populated
        ) );


Note that these functions are truncated and will not work with copy/paste

2 Answers 2


If I’ve understood this correctly, the answer is a simple one.

If these are both in the same class, set a property on your class for storing the output of the first function that runs. From there, any subsequent functions can access the data stored on that property.

  • This is the correct approach. The next problem that is the cmb_admin_init fires before load-{admin.php} is fired and also before get_current_screen() or $pagenow are both available. So now the problem is firing the query and storing the value before the options page select fields are loaded. Nov 10, 2017 at 15:51
  • I hooked my query into the cmb2_admin_init hook before the options page loads and then used the global variable $pagenow to get it down to at least the admin.php page. It's close enough for right now. Still not the exactoptions page slug but better than every single admin page load. Thanks for the help @phil Nov 10, 2017 at 16:11
  • I’m not familiar with how the cmb2 plugin works, but have you tried generating the meta box on a later hook? E.g; what happens if you hook all of this onto the ‘load-xxx’ hook?
    – Phil Kurth
    Nov 10, 2017 at 19:17
  • This works in theory and when you are already sitting on the admin page and hit refresh. But after you leave that options page, the menu items are not created since the hook is too late. CMB2 just doesn't work that way. I did find a repo that might be merged to CMB2 that allows the use of the load- hook. For right now, use $pagenow global variable and narrowing it down to admin.php works great. Nov 13, 2017 at 15:27
  • Fair enough. If you needed to go finer on your context control you could always resort to checking the same $_GET variables that WP uses.
    – Phil Kurth
    Nov 13, 2017 at 20:33
  1. Instead of iterating over every post, you could use query on meta table with distinct.

    SELECT DISTINCT meta_key FROM wp_postmeta;

This will be faster and more efficient, DISTINCT is expensive though. https://codex.wordpress.org/Custom_Queries

  1. You can not make this query always. Save the query results in a transient variable. https://codex.wordpress.org/Transients_API If the variable is not set, make the query store the result in transient.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.