How to set default taxonomy (category) for custom post types in WP

rainerbrunotte picture rainerbrunotte · Oct 31, 2013 · Viewed 7.8k times · Source

I have read through every blog post I could find, but nothing out there seems to work for me. All I would need to do is to have WP automatically assign a default taxonomy/category ("newest") to my custom post type "photos", so that when a user adds a new photo, the "newest" category is already selected and assigned (like for the "uncategorised" for a normal blog post).

declare ( encoding = 'UTF-8' );

! defined( 'ABSPATH' ) and exit;

add_action( 'init', array ( 'MCP_Photos', 'init' ) );

class MCP_Photos
{
    /**
     * Creates a new instance.
     * 
     * @wp-hook init
     * @see    __construct()
     * @return void
     */
    public static function init()
    {
        new self;
    }

    /**
     * Constructor
     */
    public function __construct()
    {
      $labels = array(
    'name' => 'Photography',
    'singular_name' => 'Photo',
    'add_new' => 'Add New',
    'add_new_item' => 'Add New Photo',
    'edit_item' => 'Edit Photo',
    'new_item' => 'New Photo',
    'all_items' => 'All Photos',
    'view_item' => 'View Photo',
    'search_items' => 'Search Photos',
    'not_found' =>  'No Photos found',
    'not_found_in_trash' => 'No Photos found in Trash', 
    'parent_item_colon' => '',
    'menu_name' => 'Photography'
  );

        $args = array(
    'labels' => $labels,
    'public' => true,
    'publicly_queryable' => true,
    'show_ui' => true, 
    'show_in_menu' => true, 
    'query_var' => true,
            'rewrite' => array(
                'with_front' => false,
                'slug' => "photo"
            ),
     'capability_type' => 'post',
    'has_archive' => true, 
    'hierarchical' => true,
    'menu_position' => null,
    'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
    'taxonomies' => array('post_tag')
        );
        register_post_type("photos", $args);

        // Prevent WordPress from sending a 404 for our new perma structure.
        add_rewrite_rule(
        '^photo/(\d+)/[^/]+/?$',
        'index.php?post_type=photos&p=$matches[1]',
        'top'
        );

        // Inject our custom structure.
        add_filter( 'post_type_link', array ( $this, 'fix_permalink' ), 1, 2 );
    }

    /**
     * Filter permalink construction.
     * 
     * @wp-hook post_type_link
     * @param  string $post_link default link.
     * @param  int    $id Post ID
     * @return string
     */
    public function fix_permalink( $post_link, $id = 0 )
    {
        $post = &get_post($id);
        if ( is_wp_error($post) || $post->post_type != 'photos' )
        {
            return $post_link;
        }
        // preview
        empty ( $post->slug )
            and $post->slug = sanitize_title_with_dashes( $post->post_title );

        return home_url(
            user_trailingslashit( "photo/$post->ID/$post->slug" )
        );
    }
}



// ----------------------------- add photography categories taxonomy ---------------------------------- 


function create_photo_categories() {
    register_taxonomy(
        'photography', // name of the taxonomy
        'photos', // for which post type it applies
        array(
            'labels' => array(
                'name' => 'Categories',
                'add_new_item' => 'Add New Category',
                'new_item_name' => "New Category"
            ),
            'show_ui' => true,
            'show_tagcloud' => false,
            'hierarchical' => true
        )
    );
}

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

Answer

Yashar picture Yashar · Jun 14, 2019

I just found this old question with no correct and complete answer. So I'm writing this to anyone interested in this topic.

I will explain in detail:

How to Register CPT & Custom Taxonomy & Add a Default Term (non-removable)

And how to register this default term to our custom post types, when no other term is selected via CPT metabox.

Steps to Proceed:

  1. You need to register your CPT using register_post_type function after init.
  2. Register custom taxonomy after using register_taxonomy function after registering CPT.
  3. Add terms to your custom taxonomy after registering it.
  4. Set the default term for your taxonomy.
  5. Add default term to CPT on post save.

Note: I've changed the structure of the code presented in the question and used singleton approach and removed parts specific to this question (e.g. rewrite rule etc.) I also changed the CPT name to photo, taxonomy name to gallery_cat and default term slug to default_gallery_cat.

IMPORTANT: default_{$taxonomy} option is used to set default category as non-removable, like default uncategorized WP category

Note: You can assign only one term as default taxonomy term.

I've written the other details as comments, in the code. It is a functional WP plugin as it is.

Final code:

<?php
/*
 Plugin Name: Sample CPT Default Taxonomy
 Plugin URI: http://www.yashar.site/
 Description: A sample plugin to register Photo CPT and gallery_cat custom taxonomy and add a default category.
 Version: 1.0
 Author: Yashar Hosseinpour
 Author URI: http://www.yashar.site/
 */

namespace My_Plugin;

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class Photo
{
    /**
     * Instance of Photo
     * @access protected
     * @var object $instance The instance of Photo.
     */
    private static $instance = null;

    /**
     * Photo constructor.
     * This is a private constructor to be used in getInstance method to do things in a singleton way
     * It's a good idea to leave this constructor empty and make `init` method public to use it outside of the class, which is a good thing for Unit Testing
     * @access private
     */
    private function __construct()
    {
        $this->init();
    }

    /**
     * Initialize the plugin.
     * You can make it public and use it outside of the class
     * @access private
     * @return void
     */
    private function init()
    {
        // It's possible to use one method to cover these and hook it to `init`. I just like the way using single purpose OOP methods.
        // Note the priorities
        add_action('init', [$this, 'register_cpt'], 10);
        add_action('init', [$this, 'register_gallery_cat_tax'], 11);
        add_action('init', [$this, 'insert_default_gallery_cat_term' ], 12);

        // `save_post_{$post->post_type}` hook is used. Doc: https://developer.wordpress.org/reference/hooks/save_post_post-post_type/
        add_action( 'save_post_photo', [$this, 'set_default_gallery_cat'], 99, 2 );
    }

    /**
     * Register `Photo` CPT and `gallery_cat` taxonomy to it
     * This should be done after `init`
     * @access public
     * @wp-hook init
     * @return void
     */
    public function register_cpt()
    {
        $labels = array(
            'name' => 'Photos',
            'singular_name' => 'Photo',
            'add_new' => 'Add New',
            'add_new_item' => 'Add New Photo',
            'edit_item' => 'Edit Photo',
            'new_item' => 'New Photo',
            'all_items' => 'All Photos',
            'view_item' => 'View Photo',
            'search_items' => 'Search Photos',
            'not_found' =>  'No Photos found',
            'not_found_in_trash' => 'No Photos found in Trash',
            'parent_item_colon' => '',
            'menu_name' => 'Photography'
        );
        $args = array(
            'public' => true,
            'show_in_menu' => true,
            'labels' => $labels,
            'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' ),
            'taxonomies' => array('post_tag', 'gallery_cat')
        );
        register_post_type('photo', $args);
    }

    /**
     * Register `gallery_cat` taxonomy
     * This should be done after registering CPT
     * @access public
     * @wp-hook init
     * @return void
     */
    public function register_gallery_cat_tax() {
        $labels = [
            'name'                      =>  'Gallery Categories',
            'singular_name'             =>  'Gallery Category',
            'all_items'                 =>  'All Gallery Categories',
            'edit_item'                 =>  'Edit Category',
            'view_item'                 =>  'View Category',
            'update_item'               =>  'Update Category',
            'add_new_item'              =>  'Add New Category',
            'new_item_name'             =>  'Category Name',
            'parent_item'               =>  'Parent Category',
            'parent_item_colon'         =>  'Parent Category:',
            'search_items'              =>  'Search Gallery Categories',
            'popular_items'             =>  'Popular Categories',
        ];
        register_taxonomy(
            'gallery_cat',
            'photo',
            array(
                'labels' => $labels,
                'show_ui' => true,
                'show_tagcloud' => false,
                'hierarchical' => true
            )
        );
    }

    /**
     * Insert default gallery_cat
     * `default_{$taxonomy}` option is used to make this term as default `gallery_cat` term (non-removable)
     * @access public
     * @wp-hook init
     */
    public function insert_default_gallery_cat_term()
    {
        // check if category(term) exists
        $cat_exists = term_exists('default_gallery_cat', 'gallery_cat');

        if ( !$cat_exists ) {
            // if term is not exist, insert it
            $new_cat = wp_insert_term(
                'Default Gallery Name',
                'gallery_cat',
                array(
                    'description'   =>  'This is your default gallery category',
                    'slug'          =>  'default_gallery_cat',
                )
            );
            // wp_insert_term returns an array on success so we need to get the term_id from it
            $default_cat_id = ($new_cat && is_array($new_cat)) ? $new_cat['term_id'] : false;
        } else {
            //if default category is already inserted, term_exists will return it's term_id
            $default_cat_id = $cat_exists;
        }

        // Setting default_{$taxonomy} option value as our default term_id to make them default and non-removable (like default uncategorized WP category)
        $stored_default_cat = get_option( 'default_gallery_cat' );

        if ( empty( $stored_default_cat ) && $default_cat_id )
            update_option( 'default_gallery_cat', $default_cat_id );
    }

    /**
     * Add an default `gallery_cat` taxonomy term for `photo` CPT on save
     * If no `gallery_cat` is selected, default gallery_cat will be registered to the post
     * @access public
     * @wp-hook save_post_photo
     * @param integer $post_id
     * @param object $post
     */
    public function set_default_gallery_cat($post_id, $post)
    {
        if ( 'publish' === $post->post_status ) {
            $gallery_cats = wp_get_post_terms( $post_id, 'gallery_cat' );
            $default_gallery_cat = (int) get_option('default_gallery_cat');

            if ( empty($gallery_cats) ) {
                wp_set_object_terms( $post_id, $default_gallery_cat, 'gallery_cat' );
            }
        }
    }

    /**
     * Instance
     * Used to retrieve the instance of this class.
     * @access public
     * @return object $instance of the class
     */
    static public function getInstance() {
        if (self::$instance == NULL) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

// Run this
Photo::getInstance();

Completely tested and it's working on WP 5.1

Note: save_post_{$post->post_type} hook is used to add default category term when saving a photo CPT.

Note that I've used init hook for registering stuff, but if you are on a multi-site WP installation, you may consider using wp_loaded hook instead (& you may need some other modifications too).

P.S. I may complete and post this code on Github in the near future.