• I am developing a job tracking database for my utility engineering company and am having an issue when running a save hook. I have a task_types db table that tasks are created off of and using a custom meta box on the “cover_page” (post edit screen) of the job I allow these tasks to be created for each job from user input. It is processing my validation code twice therefor creating 2 database entries and two identical tasks from its task type, which is not correct. Can anyone assist me with this problem. See the code snippets below.

    // Validate Data    for Custom Meta Boxes
    function rfd_argument_function() {
        global $post;
        global $rfd_meta_box_options; 
    
        foreach($rfd_meta_box_options as $rfd_meta_box_option){
                $meta_box_options[] = $rfd_meta_box_option['meta_box_form_fields'];
        } 
    
        foreach($meta_box_options as $options){
            foreach ($options as $option){
                if($option['name']){
                        $arguments = array(
                        'post_id' => $post->ID,
                        'name' => $option['name'],
                        );
                }
                do_action('save_hook', $arguments);
            }
        }
    }
    add_action( 'save_post', 'rfd_argument_function');
    
    // Save data for Custom Meta Boxes
    function rfd_save_meta_box_values( $args ) {
    $post_id = $args['post_id'];
    $name = $args['name'];
    $old_data = get_post_meta($post_id, $name);
    $new_data = $_POST[$name];
    
    // Autosave, do nothing
     if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
      return;
     }
     // Check permissions
     if ( 'page' === $_POST['post_type'] ) {
      if ( !current_user_can( 'edit_page', $post_id ) ) {
            return;
      }
     }else {
      if ( !current_user_can( 'edit_post', $post_id ) ) {
            return;
      }
     }
    if( $_POST['job-update-action']==='freeze'){ // Freeze associated items updated below in save_rfd_job_update_action.
            return;
    }
    // AJAX? Not used here
    if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
            return;
    }
    // Return if it's a post revision
    if ( false !== wp_is_post_revision( $post_id ) ){
            return;
    }
    
    // if new is same as old
    if($new_data === $old_data[0] ){
         return;
    }else{
        // add_post_meta($post_id, 'save-count', 0, true); // add "save-count" if none exists
        // $save_count = get_post_meta($post_id, 'save-count', true); // grab save count
        // if( $save_count == 0){
            // $save_count++;
             // update_post_meta( $post_id, 'save-count', $save_count );
            if($_POST['task-to-assign'] !== 'Select a Task Type to Assign'){
                global $wpdb;
                $table_name = $wpdb->prefix . 'task_types';
                $task_type = $wpdb->get_results("SELECT * FROM $table_name WHERE task_name = '" . $_POST['task-to-assign'] . "'", OBJECT);
                $table_name = $wpdb->prefix . 'tasks'; // Change table
                // check for duplicate task
                $task = $wpdb->get_results("SELECT * FROM $table_name WHERE task_name = '" . $_POST['task-to-assign'] . "' AND job_id = '" . $post_id . "'", OBJECT);
    
                // rfd_debugger( $task[0],1);
                // if( $task[0]->job_id === false){
                    $rows_affected = $wpdb->insert( $table_name, array( 'task_type_id' => $task_type[0]->id,
                                                                    'task_name' => $task_type[0]->task_name,
                                                                   'description' => $task_type[0]->description,
                                                                   'job_id' => $post_id,
                                                                   'assigned' => $_POST['person-to-assign'],
                                                                   'assigned_datetime' => current_time('mysql'),
                                                                )
                                            );
                // }
            }
    
        // }else{
            // delete_post_meta($post_id, 'save-count', 0);
        // }
        update_post_meta($post_id, $name, $new_data); // Add New Data
        update_post_meta($post_id, 'task-to-assign', 'Select a Task Type to Assign'); // Reset Field
        update_post_meta($post_id, 'person-to-assign', 'Select A Person'); // Reset Field
    }
    
    }
    add_action( 'save_hook', 'rfd_save_meta_box_values');
Viewing 2 replies - 1 through 2 (of 2 total)
  • Moderator bcworkz

    (@bcworkz)

    Others have had similar issues. Apparently the ‘save_post’ and similar actions can fire more than once for a particular post save, not to mention again anytime the post is updated. Something about the save draft mechanism I believe.

    It’s no problem with updating meta, as the same value gets overwritten, but when inserting rows, it really is a problem. You might try setting a transient and only insert rows if the transient does not exist.

    Yes, and I have faced similar issues with my plugins…

    Here a couple different solutions which have worked for me depending on the implementation, and whether high-performance is a factor:

    1) declare a static var, for example at the beginning of your function and conditionally skip portions of code:

    function rfd_argument_function() {
        global $post;
        global $rfd_meta_box_options;
        static $tc = 0; //include static var at head of function
    ...
    if( $tc < 1) {
    //run this code once only
    }
    ...
    
    //at the end of your code:
     $tc++;
    } //end rfd_argument_function

    this solution is the most performant I’ve used

    2) Otherwise, if there are significantly unique values being inserted, such as a unique job code identifier, you can test for the existence of the entry itself directly using a custom $wpdb query and nullify the insert on test conditions. This approach may not be possible depending on your data.

    3) Just delete the task-type and rewrite it again; this approach is also not as performant, but it’s the absolute dead simple approach: here’s how to keep multiple deletes from happening when there are loops involved-

    $turncz = -2;
       for($xv=0;$xv<$matches_cz;$xv++){
        $keycode = array_search($matches[2][$v] , $att_links);
    
        if ( !empty($keycode) ) {
          if($turncz != $keycode) $wpdb->delete( ...);
          $turncz = $keycode;
        ...// add relevant updating code
         } //close if
        } //close for

    this was code I developed to handle multiple shortcodes listed on a page. I needed to access and re-render these same shortcodes on a membership page. However, multiple shortcodes of the same type might be present. In order to make sure that only the latest shortcodes were entered in the db, I ended up deleting all shortcode db entries per page, and simply adding the db entries again.

    The extra db call- potentially even multiple calls depending on how many unique types of shortcodes were present – was acceptable versus testing whether each individual shortcode result was entered, a case which would then lead to managing updates as well as inserts… Some code you’re not seeing is where I threw all shortcodes types into an array ($att_links), and so I looped through them here deleting all relevant db entries ONCE and ONLY ONCE based on each unique type of shortcode, before collating and adding them with a single $wpdb query at the end of my function. My function happens to filter (‘content_save_pre’).

    Hopefully these ideas help you craft a solution that works best for your data.

Viewing 2 replies - 1 through 2 (of 2 total)
  • The topic ‘save post running twice and creating two database entries – how do I disable thi’ is closed to new replies.