Awwwards
TopDesignKing

Create Maintainable Custom Post Types in WordPress

WordPress provides a powerful framework for building websites, and one of its key features is the ability to create custom post types and taxonomies. Custom post types allow you to extend the default post and page functionality, while taxonomies enable you to categorize and organize your content in a structured manner. In this article, we will explore how to create custom post types and taxonomies using a factory approach, which allows for more modular and maintainable code.

Understanding Custom Post Types

Custom post types are a way to define different content types beyond the standard posts and pages in WordPress. They allow you to create specialized content structures tailored to your specific needs. For example, you might want to create a “Portfolio” post type to showcase your projects or a “Testimonial” post type to display customer reviews, or a “Slider” post type. Defining custom post types gives you greater control over the structure and behavior of your content.

Setting up the Custom Post Types Factory

To implement the factory approach, we’ll create a class called Weszty_Custom_Post_Types that encapsulates the logic for registering custom post types and taxonomies. This class will leverage the Singleton pattern to ensure that only one instance is created throughout the application. By using a factory approach, we can centralize the configuration and registration process, making it easier to manage and extend our custom post types and taxonomies.

Defining Post Types and Taxonomies

Inside the Weszty_Custom_Post_Types class, we’ll define an array of post types and their associated taxonomies. Each post type will have a corresponding method that returns the arguments for that specific post type. For example, the method get_slider_args() will return the arguments for the “slider” post type. Similarly, we’ll define methods for taxonomies associated with each post type.

Registering Post Types and Taxonomies

In the register_cpt() method, we iterate over the array of post types and call the register_post_type() and register_taxonomy() methods to register each post type and its associated taxonomy. These methods retrieve the arguments for each post type and taxonomy using the corresponding methods defined earlier.

Adding Additional Taxonomies

If you want to add an additional taxonomy to an existing post type, you can modify the get_taxonomy() method to include the taxonomy slug for the desired post type. Then, create a new method, such as get_additional_taxonomy_args(), to return the arguments for the new taxonomy. Finally, update the register_taxonomy() method to handle multiple taxonomies and iterate over them accordingly.

/**
 * Class for admin post types
 * 
 *  @package    weszty_web
 *  @author     Vecsei Szilveszter
 *  @copyright  Copyright (c) 2023, WesztyWeb
 *  @link       https://wesztyweb.com
 *  @since      1.0.0
 */

 class Weszty_Custom_Post_Types {

    use Singleton;

    protected static $instance;

    protected function init() {
        add_action( 'init', [ $this, 'register_cpt' ] );
    }
    
    public function register_cpt() {
        $post_types = $this->get_post_types();
        foreach ( $post_types as $post_type ) {
            $this->register_post_type( $post_type );
            $this->register_taxonomy( $post_type );
        }
    }

    private function register_post_type( $post_type ) {
        $args = $this->get_post_type_args( $post_type );
        register_post_type( $post_type, $args );
    }

    private function register_taxonomy( $post_type ) {
        $taxonomies = $this->get_taxonomy( $post_type );
        if ( $taxonomies ) {
            if ( is_array( $taxonomies ) ) {
                foreach ( $taxonomies as $taxonomy ) {
                    $args = $this->get_taxonomy_args( $taxonomy );
                    register_taxonomy( $taxonomy, $post_type, $args );
                }
            } else {
                $args = $this->get_taxonomy_args( $taxonomies );
                register_taxonomy( $taxonomies, $post_type, $args );
            }
        }
    }

    private function get_post_types() {
        return array(
            'slider',
            'testimonial',
            'project'
        );
    }

    private function get_post_type_args( $post_type ) {
        switch ( $post_type ) {
            case 'slider':
                return $this->get_slider_args();
            case 'testimonial':
                return $this->get_testimonial_args();
            case 'project':
                return $this->get_project_args();
        }
    }

    private function get_taxonomy( $post_type ) {
        switch ( $post_type ) {
            case 'slider':
                return 'slider_tag'; 
            case 'project':
                return 'project_category';
        }
        
        return false;
    }

    private function get_taxonomy_args( $post_type ) {
        switch ( $post_type ) {
            case 'slider':
                return $this->get_slider_tag_args();
            case 'project':
                return $this->get_project_category_args();
        }
    }

    private function get_slider_args() {
        $post_type = array(
            'label'       => esc_html__( 'Sliders', 'weszty_web' ),
            'plural_name' => 'Sliders',
            'singular_name' => 'Slider',
            'supports'    => array( 
                'title', 
                'thumbnail', 
                'editor'
            ),
            'menu_icon'   => 'dashicons-image-flip-horizontal',
        );

        $labels = $this->get_post_type_labels( $post_type );

        $args = array(
            'label'               => $post_type['label'],
            'labels'              => $labels,
            'public'              => false,
            'exclude_from_search' => true,
            'publicly_queryable'  => false,
            'show_ui'             => true,
            'show_in_nav_menus'   => false,
            'show_in_admin_bar'   => false,
            'show_in_rest'        => true,
            'capability_type'     => 'post',
            'hierarchical'        => false,
            'has_archive'         => false,
            'query_var'           => false,
            'can_export'          => true,
            'rewrite_no_front'    => false,
            'menu_icon'           => $post_type['menu_icon'],
            'supports'            => $post_type['supports']
        );

        return $args;
    }

    private function get_slider_tag_args() {
        $taxonomy = array(
            'label'       => esc_html__( 'Tags', 'weszty_web' ),
            'plural_name' => 'Tags',
            'singular_name' => 'Tag',
        );

        $labels = $this->get_taxonomy_labels( $taxonomy );

        $args = array(
            'hierarchical' => false,
            'labels' => $labels,
            'show_ui' => true,
            'public' => false,
            'update_count_callback' => '_update_post_term_count',
            'query_var' => true,
            'rewrite' => array( 'slug' => 'tag' ),
        );

        return $args;
    }

    private function get_testimonial_args() {
        // TODO : testimonial args here
    }

    private function get_project_args() {
        // TODO : project args here
    }

    private function get_project_category_args() {
        // TODO : project category args here
    }

    private function get_post_type_labels( $post_type ) {
        return array(
            'menu_name'          => esc_html__( $post_type['plural_name'], 'weszty_web' ),
            'name_admin_bar'     => esc_html__( $post_type['plural_name'], 'weszty_web' ),
            'add_new'            => esc_html__( 'Add ' . $post_type['singular_name'], 'weszty_web' ),
            'add_new_item'       => esc_html__( 'Add new ' . $post_type['singular_name'], 'weszty_web' ),
            'new_item'           => esc_html__( 'New ' . $post_type['singular_name'], 'weszty_web' ),
            'edit_item'          => esc_html__( 'Edit ' . $post_type['singular_name'], 'weszty_web' ),
            'view_item'          => esc_html__( 'View ' . $post_type['singular_name'], 'weszty_web' ),
            'update_item'        => esc_html__( 'View ' . $post_type['singular_name'], 'weszty_web' ),
            'all_items'          => esc_html__( 'All ' . $post_type['plural_name'], 'weszty_web' ),
            'search_items'       => esc_html__( 'Search ' . $post_type['singular_name'], 'weszty_web' ),
            'parent_item_colon'  => esc_html__( 'Parent ' . $post_type['singular_name'], 'weszty_web' ),
            'not_found'          => esc_html__( 'No ' . $post_type['plural_name'] . ' found', 'weszty_web' ),
            'not_found_in_trash' => esc_html__( 'No ' . $post_type['plural_name'] . ' found in Trash', 'weszty_web' ),
            'name'               => esc_html__( $post_type['singular_name'], 'weszty_web' ),
            'singular_name'      => esc_html__( $post_type['singular_name'], 'weszty_web' ),
        );
    }

    private function get_taxonomy_labels( $taxonomy ) {
        return array(
            'name'                       => esc_html__( $taxonomy['plural_name'], 'taxonomy general name' ),
            'singular_name'              => esc_html__( $taxonomy['singular_name'], 'taxonomy singular name' ),
            'search_items'               => esc_html__( 'Search '.$taxonomy['plural_name'] ),
            'popular_items'              => esc_html__( 'Popular '.$taxonomy['plural_name'] ),
            'all_items'                  => esc_html__( 'All '.$taxonomy['plural_name'] ),
            'parent_item'                => null,
            'parent_item_colon'          => null,
            'edit_item'                  => esc_html__( 'Edit '.$taxonomy['singular_name'] ),
            'update_item'                => esc_html__( 'Update '.$taxonomy['singular_name'] ),
            'add_new_item'               => esc_html__( 'Add New '.$taxonomy['singular_name'] ),
            'new_item_name'              => esc_html__( 'New '.$taxonomy['singular_name'].' Name' ),
            'separate_items_with_commas' => esc_html__( 'Separate '.$taxonomy['plural_name'].' with commas' ),
            'add_or_remove_items'        => esc_html__( 'Add or remove '.$taxonomy['plural_name'] ),
            'choose_from_most_used'      => esc_html__( 'Choose from the most used '.$taxonomy['plural_name'] ),
            'menu_name'                  => esc_html__( $taxonomy['plural_name'], 'weszty_web' ),
        );
    }
}

What if I need more taxonomies for a CPT ?

In case a post type has multiple taxonomies we can pass an array to the get_taxonomy() method:

private function get_taxonomy( $post_type ) {
    switch ( $post_type ) {
        case 'slider':
            return array( 'slider_tag', 'additional_taxonomy' ); 
        case 'project':
            return 'project_category';
        }

    return false;
}
private function get_taxonomy_args( $post_type ) {
    switch ( $post_type ) {
        case 'slider':
            return $this->get_slider_tag_args();
        case 'additional_taxonomy':
            return $this->get_additional_taxonomy_args();
        case 'project':
            return $this->get_project_category_args();
    }
}

Depending on how similar your CPT arguments are, you might want to use a new method where you can modify and add specific values to the $defaults array based on your requirements. This is a comprehensive list of the default arguments that can be used when registering a post type.

private function get_default_args( $overrides ) {
    $defaults = array(
        'label'               => '', // The post type label
        'labels'              => array(), // An array of labels for the post type
        'public'              => true, // Whether the post type is publicly accessible
        'exclude_from_search' => false, // Whether to exclude the post type from search results
        'publicly_queryable'  => true, // Whether the post type can be queried publicly
        'show_ui'             => true, // Whether to display the post type user interface
        'show_in_nav_menus'   => true, // Whether to show the post type in navigation menus
        'show_in_admin_bar'   => true, // Whether to show the post type in the admin bar
        'show_in_rest'        => false, // Whether to enable REST API for the post type
        'capability_type'     => 'post', // The capability type for the post type
        'hierarchical'        => false, // Whether the post type is hierarchical
        'has_archive'         => false, // Whether to enable post type archives
        'rewrite'             => true, // Whether to enable URL rewriting for the post type
        'rewrite_no_front'    => false, // Whether to prevent front base in the rewrite rules
        'query_var'           => true, // Whether to enable custom query variable for the post type
        'can_export'          => true, // Whether the post type can be exported
        'menu_position'       => null, // The position in the menu order where the post type should appear
        'menu_icon'           => null, // The URL or dashicon name for the menu icon
        'supports'            => array(), // An array of supported features for the post type
        );

    $args = array_merge( $defaults, $overrides );
    return $args;
}

Conclusion

Using a factory approach to create custom post types and taxonomies in WordPress offers several benefits, including modularity, maintainability, and centralized configuration. By encapsulating the registration logic within a single class, you can easily manage and extend your custom content structures. The code becomes more organized, and future modifications and additions are straightforward. With this approach, you have the flexibility to adapt your WordPress site to your specific content requirements.

Remember to test your custom post types and taxonomies thoroughly to ensure they function as expected. Happy coding!

Don't be weird.

Would you like more information or do you have a question?

scroll
10%
Drag View Close play