Question

I have seen this question asked a bunch but nothing seems to work. I have a simple metabox within a CPT that needs to save two values. It needs to save a dropdown of pages and a url text field. I can get the text field to save and display but not the drop down. My class is being called on initialization. I think it has something to do with the $dropdown_args variable but I'm not too sure. I can see it saving in my database but it just doesn't display the selection after saving. The description metabox saves and displays fine, it's the landing page one that needs help. I have posted all my code and any help will be greatly appreciated.

    class description_meta {

    public $dropdown_args = array();

    /**
     * Constructor.
     */
    public function __construct() {
        if ( is_admin() ) {
            add_action( 'load-post.php',     array( $this, 'init_metabox' ) );
            add_action( 'load-post-new.php', array( $this, 'init_metabox' ) );            
        }

        $this->dropdown_args = [
            'show_option_none' => '- select a page -',
            'option_none_value' => 'no page selected',
            'id'               => 'landing_page',
            'name'             => 'landing_page',
            'value_field'      => 'landing_page',
            'selected'         => $landing_page
        ];

    }

    /**
     * Meta box initialization.
     */
    public function init_metabox() {
        add_action( 'add_meta_boxes', array( $this, 'add_metabox'  )        );
        add_action( 'save_post',      array( $this, 'save_metabox' ), 10, 2 );
    }

    public function register() {
        add_action( 'save_post',      array( $this, 'save_metabox' ), 10, 2 );
    }

    /**
     * Adds the meta box.
     */
    public function add_metabox() {
        add_meta_box(
            'description',
            'Description',
            array( $this, 'render_description' ),
            'funnels',
            'advanced',
            'default'
            );

         add_meta_box(
            'landing_page',
            'Landing Page',
            array( $this, 'render_landing' ),
            'funnels',
            'advanced',
            'default'
            );
    }

    /**
     * Renders the meta box.
     */
    public function render_description( $post ) {
        // Add nonce for security and authentication.
        wp_nonce_field( 'update_post', 'funnel_nonce' );

        // Use get_post_meta to retrieve an existing value from the database.
        $description = get_post_meta( $post->ID, 'description', true );

        // Display the form, using the current value.
       ?>
       <input type="text" class="widefat" placeholder="Enter description..." id="description" name="description" value="<?php echo esc_attr( $description ); ?>">
       <?php 
   }

   public function render_landing( $post ) {
        wp_nonce_field( 'update_post', 'funnel_nonce' );

        $landing_page = get_post_meta( $post->ID, 'landing_page', true );
        $landing_link = get_post_meta( $post->ID, 'landing_link', true );


        ?>
        <h4 style="display: inline-block;">Select a page:</h4>
        <?php wp_dropdown_pages($this->dropdown_args); ?>
        <br>
        <h4 style="display: inline-block;">Enter the success link:</h4>
        <input style="width: 600px;" type="url" value="<?php echo esc_attr( $landing_link ); ?>" name="landing_link" id="landing_link" placeholder="Enter success link...">
        <br>
        <br>

        <div class="meta_links" id="meta_links">
            <a href="#">Meta link 1</a>
            <a href="#">Meta link 2</a>
            <a href="#">Meta link 3</a>
        </div> 
        <?
        
   }

    /**
     * Handles saving the meta box.
     *
     * @param int     $post_id Post ID.
     * @param WP_Post $post    Post object.
     * @return null
     */
    public function save_metabox( $post_id, $post ) {

        // Add nonce for security and authentication.
        $nonce_name   = isset( $_POST['funnel_nonce'] ) ? $_POST['funnel_nonce'] : '';
        $nonce_action = 'update_post';


        // Check if nonce is valid.
        if ( ! wp_verify_nonce( $nonce_name, $nonce_action ) ) {
            return;
        }

        // Check if user has permissions to save data.
        if ( ! current_user_can( 'edit_post', $post_id ) ) {
            return;
        }

        // Check if not an autosave.
        if ( wp_is_post_autosave( $post_id ) ) {
            return;
        }

        // Check if not a revision.
        if ( wp_is_post_revision( $post_id ) ) {
            return;
        }

        $fields = [
            'landing_page' => 'landing_page',
            'landing_link' => 'landing_link',
            'description' => 'description'
        ];

        foreach ( $fields as $posted_key => $post_meta_key ) {
            $posted_value = '';

            if ( isset( $_POST[ $posted_key ] ) ) {
                $posted_value = sanitize_text_field( wp_unslash( $_POST[ $posted_key ] ) );
            }

            if ( ! empty( $posted_value ) ) {
                update_post_meta( $post_id, $post_meta_key, $posted_value );
            } 

            else {
                delete_post_meta( $post_id, $posted_key );
            }
        }
    }
}

new description_meta();
Was it helpful?

Solution

It looks like the variable you're using to set the selected item is not defined in that scope.

In your constructor:

    public function __construct() {
        if ( is_admin() ) {
            add_action( 'load-post.php',     array( $this, 'init_metabox' ) );
            add_action( 'load-post-new.php', array( $this, 'init_metabox' ) );            
        }

        $this->dropdown_args = [
            'show_option_none' => '- select a page -',
            'option_none_value' => 'no page selected',
            'id'               => 'landing_page',
            'name'             => 'landing_page',
            'value_field'      => 'landing_page',
            'selected'         => $landing_page
        ];
    }

$landing_page is not defined here.

I'm not completely familiar with all your code, but for the other field you use get_post_meta( $post->ID, 'description', true ) to get the current value for that field.

As $post isn't available in your constructor you need to correctly set the selected argument in render_landing(). Perhaps something like:

Replace:

    <?php wp_dropdown_pages($this->dropdown_args); ?>

With:

    <?php 
    $args = $this->dropdown_args; 
    $args['selected'] = get_post_meta( $post->ID, 'landing_page', true );
    wp_dropdown_pages($args);
    ?>

As your code is nice and clean I guess you'd want to tidy up the constructor so it isn't setting the selected field to an empty value, and you could make the string landing_page a class-level variable or constant so that it doesn't get repeated in different places.

Let me know if that works

Licensed under: CC-BY-SA with attribution
Not affiliated with wordpress.stackexchange
scroll top