Skip to content

Custom Post Types, Taxonomies & Metaboxes

Xe Plugin comes with structured classes for registering Custom Post Types (CPTs), Custom Taxonomies, and Custom Metaboxes with built-in rewrite flushing and security verification.


1. Custom Post Types (src/PostTypes.php)

CPTs are registered via src/PostTypes.php:

namespace Xe_Plugin;

class PostTypes {

  public function register(): void {
    add_action( 'init', [ $this, 'register_post_types' ] );
    register_activation_hook( _xe_plugin()->file(), [ $this, 'rewrite_flush' ] );
  }

  public function register_post_types() {
    $this->sample();
  }

  protected function sample() {
    $labels = [
      'name'          => esc_html__( 'Samples', 'xe-plugin' ),
      'singular_name' => esc_html__( 'Sample', 'xe-plugin' ),
      'add_new'       => esc_html__( 'Add New', 'xe-plugin' ),
      'edit_item'     => esc_html__( 'Edit Sample', 'xe-plugin' ),
      'all_items'     => esc_html__( 'All Samples', 'xe-plugin' ),
    ];

    $args = [
      'labels'          => $labels,
      'public'          => true,
      'show_ui'         => true,
      'show_in_menu'    => false, // Can be embedded under Plugin Menu
      'menu_icon'       => 'dashicons-portfolio',
      'query_var'       => true,
      'rewrite'         => [ 'slug' => 'xe-plugin-cpt' ],
      'capability_type' => 'post',
      'has_archive'     => true,
      'supports'        => [ 'title', 'editor', 'thumbnail', 'comments', 'revisions' ],
    ];

    return register_post_type( 'xe-plugin-cpt', $args );
  }

  public function rewrite_flush() {
    $this->register_post_types();
    flush_rewrite_rules();
  }

}

2. Custom Taxonomies (src/Taxonomies.php)

Taxonomies are registered on init (priority 0) in src/Taxonomies.php:

namespace Xe_Plugin;

class Taxonomies {

  public function register(): void {
    add_action( 'init', [ $this, 'register_taxonomies' ], 0 );
  }

  public function register_taxonomies(): void {
    // Hierarchical category
    register_taxonomy( 'xe-plugin-cat', [ 'xe-plugin-cpt' ], $this->sample_categories() );

    // Flat tag
    register_taxonomy( 'xe-plugin-tag', [ 'xe-plugin-cpt' ], $this->sample_tags() );
  }

}

3. Custom Metaboxes (src/Admin/Metaboxes/)

Metaboxes are organized as individual classes. See src/Admin/Metaboxes/Sample.php:

namespace Xe_Plugin\Admin\MetaBoxes;

use Xe_Plugin\Utils;

class Sample {

  public function register(): void {
    add_action( 'add_meta_boxes', [ $this, 'add' ] );
    add_action( 'save_post_' . $this->post_type(), [ $this, 'save' ] );
  }

  protected function post_type(): string {
    return 'xe-plugin-cpt';
  }

  public function add(): void {
    add_meta_box(
      'sample_meta_box',
      esc_html__( 'Sample Details', 'xe-plugin' ),
      [ $this, 'render' ],
      $this->post_type()
    );
  }

  public function render( $post ): void {
    wp_nonce_field( 'xep_cpt_meta_box', 'xep_cpt_meta_box_nonce' );
    $sample = get_post_meta( $post->ID, '_sample', true );
    ?>
    <div class="xe-plugin-field">
      <label for="sample"><?php echo esc_html__( 'Serial No:', 'xe-plugin' ); ?></label>
      <input type="text" name="sample" id="sample" value="<?php echo esc_attr( $sample ); ?>" required>
    </div>
    <?php
  }

  public function save( int $post_id ) {
    // 1. Verify nonce, capability, and autosave
    $is_valid = Utils::verify_save(
      'xep_cpt_meta_box',
      'xep_cpt_meta_box_nonce',
      $this->post_type(),
      $post_id
    );

    if ( ! $is_valid ) {
      return $post_id;
    }

    // 2. Sanitize and save post meta
    Utils::update_field( $post_id, 'sample', false, 'text', '_sample' );
  }

}