Context: I am building an automated system which will grab record from a database and immediately publish them en masse as a CPT. When they are being published, I have a function in the functions.php file of a child theme that should automatically set two categories to the post, a "fixed" one called 'People', and another one based on a column (categ) which is part of the record sent with the post.

My issue is that this second category (which should change per the contents of the record) is being permanently set for all the posts as the first one being processed. Is there a way to modify the code below to better specify that it should return the category found in that specific record? Is it possibly an issue with using the 'save_post' hook for publishing multiple posts at the same time?

Content in functions.php

function post_auto_categ( $post_ID ) 
    global $wpdb;
    $user_ID = get_current_user_id();
    $post_type = 'people';

    $categname = $wpdb->get_var("SELECT categ FROM wp_people_posting");
    $categnum = $wpdb->get_var("SELECT term_id FROM wp_terms WHERE name ='" . $categname ."'");

    $peoplenum = $wpdb->get_var("SELECT term_id FROM wp_terms WHERE name ='People'");
    $post_categories=array($peoplenum, $categnum);

        wp_set_post_categories( $post_ID, $post_categories );
   return $post_ID;
add_action( 'save_post', 'post_auto_categ' );


enter image description here

Attribute with category content:

enter image description here

Any suggestions would be greatly appreciated. Thank you.

Edit for clearer context:

I have a table called 'wp_people_posting' in phpMyAdmin. enter image description here

The records in this table are uploaded through a separate plugin which grabs the records from Google Drive and inserts them into this table all at once.

Each record found in this table are contents posted onto the website, one post per record, as seen below.

enter image description here

All posts are then placed in two categories, one which is always named "Abstracts" (changed from previously mentioned People in a recent update) and another which should depend on the contents of the column 'categ'. The same category name is already inserted as a category on the website.

enter image description here

Is there a way in which I can call a function that would set the second category for every single post according to the contents of that particular post/record's 'categ' column?

Aka rather than all of them having the same "Data Science" (as seen in the 4th image), I want to set them all individually, as below.

Record in Database

which would mirror as the category listed with the post

Set category for post

Edit 2: Content in Publication Plugin

Below is the whole plugin that I am using to create the CPT, grabbing the details from the table and inserting them:

    echo 'ERROR: ABSPATH UNDEFINED. Access to this file is not allowed.';

function create_abstract_cpt() {

    $labels = array(
        'name' => _x( 'Abstracts', 'Post Type General Name', 'textdomain' ),
        'singular_name' => _x( 'Abstract', 'Post Type Singular Name', 'textdomain' ),
        'menu_name' => _x( 'Student Abstracts', 'Admin Menu text', 'textdomain' ),
        'name_admin_bar' => _x( 'Abstract', 'Add New on Toolbar', 'textdomain' ),
        'archives' => __( 'Abstract Archives', 'textdomain' ),
        'attributes' => __( 'Abstract Attributes', 'textdomain' ),
        'parent_item_colon' => __( 'Parent Abstract:', 'textdomain' ),
        'all_items' => __( 'All Abstracts', 'textdomain' ),
        'add_new_item' => __( 'Add New Abstract', 'textdomain' ),
        'add_new' => __( 'Add New', 'textdomain' ),
        'new_item' => __( 'New Abstract', 'textdomain' ),
        'edit_item' => __( 'Edit Abstract', 'textdomain' ),
        'update_item' => __( 'Update Abstract', 'textdomain' ),
        'view_item' => __( 'View Abstract', 'textdomain' ),
        'view_items' => __( 'View Abstracts', 'textdomain' ),
        'search_items' => __( 'Search Abstract', 'textdomain' ),
        'not_found' => __( 'Not found', 'textdomain' ),
        'not_found_in_trash' => __( 'Not found in Trash', 'textdomain' ),
        'featured_image' => __( 'Featured Image', 'textdomain' ),
        'set_featured_image' => __( 'Set featured image', 'textdomain' ),
        'remove_featured_image' => __( 'Remove featured image', 'textdomain' ),
        'use_featured_image' => __( 'Use as featured image', 'textdomain' ),
        'insert_into_item' => __( 'Insert into Abstract', 'textdomain' ),
        'uploaded_to_this_item' => __( 'Uploaded to this Abstract', 'textdomain' ),
        'items_list' => __( 'Abstracts list', 'textdomain' ),
        'items_list_navigation' => __( 'Abstracts list navigation', 'textdomain' ),
        'filter_items_list' => __( 'Filter Abstracts list', 'textdomain' ),
    $args = array(
        'label' => __( 'Abstract', 'textdomain' ),
        'description' => __( 'List of all the abstracts submitted by students from the Faculty of ICT', 'textdomain' ),
        'labels' => $labels,
        'menu_icon' => 'dashicons-text',
        'supports' => array('title', 'editor', 'excerpt', 'thumbnail', 'author', 'post-formats', 'custom-fields'),
        'public' => true,
        'show_ui' => true,
        'show_in_menu' => true,
        'menu_position' => 5,
        'show_in_admin_bar' => true,
        'show_in_nav_menus' => true,
        'can_export' => true,
        'has_archive' => true,
        'hierarchical' => false,
        'exclude_from_search' => false,
        'show_in_rest' => true,
        'publicly_queryable' => true,
        'capability_type' => 'post',
        'taxonomies' => array( 'category', 'post_tag' ),
    register_post_type( 'abstract', $args );

add_action( 'init', 'create_abstract_cpt', 0 );

add_action( 'wp', 'insert_into_cpt');

function verify_existing_abstract_in_cpt() 

    $id_arrays_in_cpt = [];
    $args = array(
        'post_type'      => 'abstract',
        'posts_per_page' => -1,

    $loop = new WP_Query($args);
    while ( $loop->have_posts() ) {
        $id_arrays_in_cpt[] = get_post_meta( get_the_ID(), 'abstract_id', true);
    return $id_arrays_in_cpt;

function query_abstract_post_table( $available_in_cpt ) 
    global $wpdb;
    $table_name = $wpdb->prefix . 'abstract_posts';
    if ( NULL === $available_in_cpt || empty($available_in_cpt) || 0 === $available_in_cpt) {
        $sql = "SELECT * FROM $table_name";
    } else {
        $ids = implode( ",", $available_in_cpt);
        $sql = "SELECT * FROM $table_name WHERE abstract_id NOT IN ($ids)";

    $results = $wpdb->get_results( $sql );
    return $results;

function insert_into_cpt()
    $available_in_cpt = verify_existing_abstract_in_cpt();
    $database_results = query_abstract_post_table( $available_in_cpt );

    if (NULL === $database_results || empty( $database_results ) || 0 === $database_results) 

    //Insert into CPT
    foreach($database_results as $result) 
        //Create post object
        $abstract_details = array(
            'post_title' => $result->title,
            'meta_input' => array(
                'abstract_id' => $result->abstract_id,
                'title' => $result->title,
                'author' => $result->author,
                'supervisor' => $result->supervisor,
                'cosupervisor' => $result->cosupervisor,
                'course' => $result->course,
                'categ' => $result->categ,
                'writeup' => $result->writeup,
                'firstabstractimage' => $result->firstabstractimage,
                'firstabstractimagecaption' => $result->firstabstractimagecaption,
                'secondabstractimage' => $result->secondabstractimage,
                'secondabstractimagecaption' => $result->secondabstractimagecaption,
                'ref' => $result->ref,
            'post_type'   => 'abstract',
            'post_status' => 'publish',
        wp_insert_post( $abstract_details );

These details are then taken by the content.php file and added into it directly with, for example, esc_html(get_post_meta(get_the_ID(), 'firstabstractimage', true)) (I know that changing content.php to CPT content is not ideal, will be modifying soon to taking the post template from an archive-abstracts.php file)

  • I've read this a few times and i don't understand what you're trying to do, or what the problem is. Can you edit your question and explain using different words?
    – Tom J Nowell
    May 18, 2021 at 0:09
  • @TomJNowell I have included an edit at the bottom. Hopefully this explains my situation further.
    – UgKnu
    May 18, 2021 at 0:30
  • ok so the SQL query you made is the same for all posts, so it will always return the same values. It's unclear what $categname is and how it relates to the post you're trying to create. How do you know which categ in the people posting table is the one you need? And where does the wp_people_posting table come from? Also, inserting variables directly into an SQL query can cause injection attacks, but you can fix that by running the query through $wpdb->prepare
    – Tom J Nowell
    May 18, 2021 at 10:19
  • @TomJNowell Is there a way to make it different for every post? That is my main concern. $categname is just selecting the column 'categ' from the table wp_people_posting (found in a linked phpMyAdmin db). I want $categname to change for every published post, according to what is found in the related attribute 'categ'.
    – UgKnu
    May 18, 2021 at 11:54
  • Also will add $wpdb->prepare thank you. I have never worked with databases and PHP so this is all quite new to me.
    – UgKnu
    May 18, 2021 at 11:55

1 Answer 1


You don't need a save_post hook.

When you call wp_insert_post, it will return either a post ID or an error object. We can use this to then assign the category by taking categ from $result.

I imagine it would looks similar to this:

$post_id = wp_insert_post( $abstract_details );
if ( ! is_wp_error( $post_id ) ) {
    wp_set_object_terms( $post_id, $result['categ'], 'category' );

You could also use the post_category parameter of wp_insert_post or the tax_input parameter.

  • Would this run within the foreach loop of the insert_into_cpt() function itself? And a final thing, what would 'category' in wp_set_object_terms actually be? Never used wp_set_object_terms before and quite confused from the WordPress developer resources.
    – UgKnu
    May 18, 2021 at 21:13
  • the same place your current wp_insert_post call goes. wp_set_object_terms sets the terms on an object. The object is the post you just created, the term is a category name, and the taxonomy is the post category taxonomy. The same way that a red car has an object ( the car ) that has a term ( red ) in a taxonomy ( colour ) ( wp_set_object_terms( $car_id, 'red', 'colours' ); ). Look at your CPT registration, it says it accepts 2 taxonomies, category and post_tag, the third parameter is the taxonomy name, the second the term, etc, refer to the official doc for wp_set_object_terms
    – Tom J Nowell
    May 18, 2021 at 21:32
  • Thank you for your answer as well as your patience, this works completely. In case someone has the exact same issue as myself, set the $result in the above answer as my $abstract_details, add ['meta_input'] before ['categ'] as well and place in same position as wp_insert_post call.
    – UgKnu
    May 18, 2021 at 23:37

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.