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
- Namespace Appropriately: Use
WDG\App\PostTypefor shared types,ProjectName\PostTypefor project-specific - Use Meta Fields: Register meta through the
$metaproperty for REST API support - Leverage Parent Methods: Call
parent::init()to ensure core functionality - Follow Naming Conventions: Use singular class names (e.g.,
ProductnotProducts) - 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
- Post Type Not Appearing: Ensure the class is instantiated and flush permalinks (Settings → Permalinks → Save)
- Meta Not in REST: Add
'show_in_rest' => trueto meta field definition - 404 on Archive: Check
has_archiveis true and permalinks are flushed - Admin Columns Empty: Verify
manage_custom_columnmethod implementation