Skip to content

Creating Custom Post Types with Wikit

Overview

Wikit provides a PostType base class that simplifies creating custom post types with built-in support for:

  • REST API integration
  • Meta field registration
  • Admin column customization
  • Gutenberg editor support
  • Automatic label generation

Location

Custom post types should be created in:

  • For wikit-app (shared): /wikit-app/src/PostType/YourType.php
  • For project-specific: /projects/{project-name}/repositories/{repo}/src/PostType/YourType.php

Basic Implementation

Step 1: Create the PostType Class

Create a new PHP file extending the Wikit PostType class:

php
<?php

namespace WDG\App\PostType;

use WDG\Core\PostType;
use WDG\Core\PostTypeTerm;
use WDG\Core\Taxonomy;

class Products extends PostType {

    /**
     * The post type slug
     * @var string
     */
    protected $post_type = 'product';

    /**
     * Associated taxonomy slug
     * @var string
     */
    protected $taxonomy = 'product_category';

    /**
     * Post type registration arguments
     * @var array
     */
    protected $args = [
        'menu_icon' => 'dashicons-cart',
        'supports'  => [
            'title',
            'editor',
            'thumbnail',
            'excerpt',
            'custom-fields',
            'revisions',
        ],
        'labels'    => [
            'name'          => 'Products',
            'singular_name' => 'Product',
            'add_new_item'  => 'Add New Product',
            'edit_item'     => 'Edit Product',
        ],
        'has_archive' => true,
        'rewrite'     => [
            'slug' => 'products',
            'with_front' => false,
        ],
    ];

    /**
     * Meta fields for the post type
     * @var array
     */
    protected $meta = [
        'price' => [
            'type'  => 'number',
            'label' => 'Price',
            'show_in_rest' => true,
        ],
        'sku' => [
            'type'  => 'string',
            'label' => 'SKU',
            'show_in_rest' => true,
        ],
        'in_stock' => [
            'type'  => 'boolean',
            'label' => 'In Stock',
            'show_in_rest' => true,
        ],
    ];

    /**
     * Admin list table columns
     * @var array
     */
    protected $list_table_columns = [
        'sku'            => 'SKU',
        'price'          => 'Price',
        'in_stock'       => 'Stock Status',
        'featured-image' => 'Image',
    ];

    /**
     * Initialize hooks and taxonomies
     */
    public function init() {
        parent::init();

        // Register associated taxonomy
        new Taxonomy( 'product_category', [ $this->post_type ], [
            'labels' => [
                'name'          => 'Product Categories',
                'singular_name' => 'Product Category',
            ],
            'hierarchical' => true,
        ]);

        // Optional: Add taxonomy as a term relationship
        $this->post_type_term = new PostTypeTerm(
            $this->post_type,
            $this->taxonomy,
            ['post', 'page'], // Where this CPT can be referenced
            [
                'labels' => [
                    'name'          => 'Related Products',
                    'singular_name' => 'Related Product',
                ],
                'hierarchical' => false,
            ]
        );
    }
}

Step 2: Instantiate the Post Type

In your theme's functions.php or a plugin file:

php
// Register the custom post type
new \WDG\App\PostType\Products();

Advanced Features

Custom Admin Columns

Override the manage_custom_column method to display custom data:

php
public function manage_custom_column( $column, $post_id ) {
    switch ( $column ) {
        case 'price':
            $price = get_post_meta( $post_id, 'price', true );
            echo $price ? '$' . number_format( $price, 2 ) : '—';
            break;
        case 'in_stock':
            $in_stock = get_post_meta( $post_id, 'in_stock', true );
            echo $in_stock ? '✓ In Stock' : '✗ Out of Stock';
            break;
        default:
            parent::manage_custom_column( $column, $post_id );
    }
}

Block Template

Define a default Gutenberg block template:

php
protected $args = [
    // ... other args
    'template' => [
        ['core/heading', ['placeholder' => 'Product Name']],
        ['core/paragraph', ['placeholder' => 'Product description...']],
        ['core/image', []],
        ['wdg/price-display', []],
    ],
];

External URL Support

For post types that link to external resources:

php
protected $args = [
    // ... other args
    'external_url' => true,
    'has_detail_page' => false, // No single post view
];

Real-World Examples

People Post Type (from wikit-app)

Located at /wikit-app/src/PostType/People.php:

  • Custom meta fields for contact information
  • Integration with WordPress users
  • Custom featured image labels
  • React-based user selection UI

Resources Post Type (from wikit-app)

Located at /wikit-app/src/PostType/Resources.php:

  • Simple implementation with taxonomy
  • PostTypeTerm for cross-referencing

Best Practices

  1. Namespace Appropriately: Use WDG\App\PostType for shared types, ProjectName\PostType for project-specific
  2. Use Meta Fields: Register meta through the $meta property for REST API support
  3. Leverage Parent Methods: Call parent::init() to ensure core functionality
  4. Follow Naming Conventions: Use singular class names (e.g., Product not Products)
  5. Add Taxonomies in init(): Register related taxonomies in the init() method

Common Patterns

Hierarchical Post Type

php
protected $args = [
    'hierarchical' => true,
    'supports' => ['title', 'editor', 'page-attributes'],
];

Archive Page Settings

php
protected $page_for = 'page_for_products'; // Option key
protected $page_for_ppp = 12; // Posts per page

public function init() {
    parent::init();
    // Automatically handles archive page selection in Settings
}

REST API Customization

php
public function rest_prepare_post_type( $response, $post_type, $request ) {
    // Add custom data to REST responses
    $response->data['custom_field'] = 'value';
    return $response;
}

Troubleshooting

  1. Post Type Not Appearing: Ensure the class is instantiated and flush permalinks (Settings → Permalinks → Save)
  2. Meta Not in REST: Add 'show_in_rest' => true to meta field definition
  3. 404 on Archive: Check has_archive is true and permalinks are flushed
  4. Admin Columns Empty: Verify manage_custom_column method implementation

Released under the MIT License.