1

Im trying to change the title of a post, but I want to include info from a meta field, the value of which isn't saved to the database until after the posts has already been saved. Other than this one thing, it's working fine as a wp_insert_post_data filter.

// add_filter('wp_insert_post_data', 'tr_change_show_title', 99, 2);

function tr_change_show_title($data, $postarr)
{
    if('post' != $data['post_type']) {
        // don't bother if its an auto-draft, as the post title may not have been completed yet, or in the trash
        if ( !in_array( $data['post_status'], array( 'auto-draft', 'trash' ) ))

        $child_ID = $postarr['ID']; // Child post id
        $parent_ID = get_post_meta($child_ID, "_wpcf_belongs_booking_id", true); // parent ID
        $post_type = $data['post_type'];

        if ($post_type == 'show' && get_post_type($parent_ID) == 'booking') {
            $parent_title = get_the_title($parent_ID);

            $data['post_title'] = $parent_title; // save the parent title first
        }

        if (function_exists('types_render_field')) {

            //this isn't working as the meta field isn't saved yet
            $show_time = types_render_field( "show-time", array("post_id"=>"$child_ID", "raw"=>"true") );
            if ($show_time){
                $data['post_title'] .= ' : ' . $show_time; // Append Show time to title
            }
        }
        $data['post_title'] .= ': show-' . $child_ID; // add the original post id
    }
    return $data;
}

I have read the codex, and this wpse post: https://wordpress.stackexchange.com/a/54713/13551 https://codex.wordpress.org/Function_Reference/wp_update_post

What I understand is that a infinite loop is a risk. To avoid this, we unhook and then rehook the save_post before and after the meat of our work. Ive come up with the following, but dont quite understand why Im still running into an infinite loop.

add_action( 'save_post', 'tr_save_post_show_title', 99 );

function tr_save_post_show_title($post_ID){
    if ( !wp_is_post_revision( $post_ID ) ) { // do nothing if a revision

        // Prevent infinite loop
        remove_action('save_post', 'tr_save_post_show_title');

        // Add our filter
        add_filter('wp_insert_post_data', 'tr_change_show_title', 99, 2);

        write_log(array( 'ID' => $post_ID));

        // Re-save the post this time with filter
        wp_update_post( array( 'ID' => $post_ID), true ); // true for error catching

        // Catch errors
        if (is_wp_error($post_id)) {
            $errors = $post_id->get_error_messages();
            foreach ($errors as $error) {
                write_log($error);
            }
        }
        // re-hook the save_post action
        add_action('save_post', 'tr_save_post_show_title');
    }
}
0

3 Answers 3

1

You hook the tr_change_show_title to a filter inside the function itself, that can make a infinite loop. All the stuff removing/adding actions and filters inside the functions should be deleted; instead check if the post data should be updated or not. In your case you should check if the title has the value you desire or not, if not run wp_update_post with the new value:

add_action( 'save_post', 'tr_save_post_show_title', 99, 2 );
function tr_save_post_show_title( $post_ID, $post ){

    if ( !wp_is_post_revision( $post_ID ) ) { // do nothing if a revision

       $meta = get_post_meta($post_ID, "your-meta-key", true );

        if( $meta != '' ) {

            $desired_title = "Whatever you want as title";

            // Check the title value to check if it should be modified
            if( $post->post_title != $desired_title ) {

                $post->post_title = $desired_title;
                wp_update_post( $post, true );

            }

        }

    }


}

You are worried about running that function after a meta field has been saved/updated. If the meta field is in the form where you are editing the post, you can access to the meta field value like any other meta field. Anyway, if you want to be sure that the meta field has been saved/updated, you can use the updated_{$meta_type}_meta action hook instead of save_post; as advantage, in this action hook you have direct access to current meta value (if any):

add_action( 'updated_post_meta', 'tr_save_post_show_title', 99, 4 );
function tr_save_post_show_title( $meta_id, $object_id, $meta_key, $meta_value ) {

    if ( $meta_key == 'the_meta_key_here' && $meta_value != '' && $object_id && ! wp_is_post_revision( $object_id ) ) {

        //write_log(array( 'ID' => $post_id ));

        // Get post data
        $post = get_post( $object_id );

        if( $post ) {
            $desired_title = 'Combine here the $post->post_title with $meta_value as you desire.';

            // Check the title value to check if it should be modified
            if( $post->post_title != $desired_title ) {

                $post->post_title = $desired_title;
                wp_update_post( $post );

            }

        }

    }

}
11
  • I think you've missed my point. Im after the value of $show_time = types_render_field( "show-time", array("post_id"=>"$child_ID", "raw"=>"true") ); At the point of save_post, this value hasn't been set in the database, yet I want to add it to the post title. Take a read of wordpress.stackexchange.com/a/54713/13551. He's asking the same kind of question. Im just having trouble implementing it.
    – orionrush
    Commented Apr 21, 2015 at 1:02
  • I'm sorry but I don't know what types_render_field(); it is not a function from WordPress. Your problem seems to be related with custom meta fields. If those fields are in the form, I don't see why you can not get the value in save_post action as you with any other custom field.
    – cybmeta
    Commented Apr 21, 2015 at 5:29
  • See updated answer.
    – cybmeta
    Commented Apr 21, 2015 at 5:36
  • Erm, this produces a bunch of notices - first missing semicolon wp_update_post( $post, true )
    – orionrush
    Commented Apr 21, 2015 at 21:20
  • Sorry for the typo. Fixed.
    – cybmeta
    Commented Apr 22, 2015 at 5:20
0

With respect to @cybmeta's contribution, I think that the Types plugin may be throwing us a curveball.

He is correct in that you cannot (at least in this context) add a filter at the point of save_post without creating an infinite loop.

As I was trying to access the value of a meta field, his suggestion for updated_post_meta may be appropriate, but didn't seem to work as the the meta key wasn't available when the hook ran. This again could be an issue with the way Types sets up custom fields.

In the end I have corrected my original approach. Regardless of any influence from Types the following is a working example of changing a post title, at the point of save_post, incorporating the value of a meta field, and preventing an infinite loop. As save_post is the last action in the chain, it should run after all other database changes, and therefore you should feel fairly confident that the WP_Post Object will include any meta field values, and all changes made by any previous filters or actions. If I hadn't wanted current information from my meta fields, a simple filtering via wp_insert_post_data would have sufficed.

add_action('save_post', 'tr_save_post_show_title', 99, 2);

function tr_save_post_show_title ($post_ID, $post) {
    if($post->post_type == 'show' && !in_array($post -> post_status, array('auto-draft', 'revision', 'trash'))) {
        // don't bother if our CPT post is an auto-draft, a revision or in the trash

        $parent_ID = get_post_meta($post_ID, "_wpcf_belongs_booking_id", true); // Types relationship
        $current_title = $post -> post_title; // Get current post title saved in DB

        $post_showtime = get_post_meta($post_ID, "wpcf-show-time", true ); // our custom meta field
        if ($post_showtime == ''){
            $post_showtime = 'To be confirmed';
        }

        if ($parent_ID && get_post_type($parent_ID) == 'booking') { // Check to see if Types a parent relationship has been assigned.

            $show_post_title = get_the_title($parent_ID); // get the id of the parent post
            if ($show_post_title != ''){
                $show_post_title .= ': '  . $post_showtime;
            }
        } else if (!$parent_ID){ // when a parent is not assigned
            $show_title = "Show not yet assigned to venue: " . $post_showtime;
        }
        if ($current_title != $show_post_title ){ // The current title does not match what we would like it to be

            // Prevent infinite loop
            remove_action('save_post', 'tr_save_post_show_title');

            // Re-save the post with our new title
            wp_update_post( array( 'ID' => $post_ID, 'post_title' => $show_post_title ), false ); // true for error catching

            // re-hook the save_post action
            add_action('save_post', 'tr_save_post_show_title', 99, 2);

            // Catch any errors with our custom logging function http://goo.gl/P9HbcK
            if (is_wp_error($post_ID)) {
                $errors = $post_id->get_error_messages();
                foreach ($errors as $error) {
                    write_log($error);
                }
            }
        }
    }
}

Interestingly save_post_<post_type> which runs just before save_post wasn't getting the most recent values from the meta field either.

0

My approach to this problem is to access "raw" metadata directly from $_POST[metabox_id] in wp_insert_post_data filter hook. I do admit that this approach is suitable more for my project where posts are generated entirely out of metadata and need to be re-rendered everytime. Tough someone might find it useful.

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.