In this blog, we’ll take a deep dive into the process of registering a custom post type (CPT) in WordPress and managing access restrictions. We’ll walk through the code to register the CPT "Custom Posts" (slug: rt-custom-posts) and restrict access to Gutenberg, with custom columns for post metadata. We will explore each function used to set up this feature, understand how metadata is managed for these posts, and discuss various aspects like filtering, metadata updates, and access management.
Key Points:
- Custom Post Type (CPT) Registration:
- Use
register_post_type()to create a CPT (rt-custom-posts) with custom labels, support for features like title, editor, author, and enabling REST API (Gutenberg support). - The post type is accessible publicly (
'public' => true) and has an archive ('has_archive' => true).
- Use
- Restricting Gutenberg to Custom Post Type:
- Use the
use_block_editor_for_post_typefilter to restrict Gutenberg editor usage to only the custom post type, allowing other post types to use the classic editor.
- Use the
- Managing Metadata for Embedded Posts:
- The metadata key
rt_embedded_in_postsstores embedded post IDs. Useget_post_meta()andupdate_post_meta()to retrieve and update this metadata. - When a custom post with embedded content is saved or updated, the metadata for both the custom post and the embedded posts is updated dynamically.
- The metadata key
- Custom Columns in Admin:
- Add a custom column (
Embedded In) to the admin posts table using themanage_posts_columnsandmanage_posts_custom_columnhooks to display where posts are embedded.
- Add a custom column (
- Handling Metadata and Block Parsing:
- Use
parse_blocks()to extract embedded post data from Gutenberg blocks and manage the relationships between posts. - Ensure that metadata updates are performed only when the post type and conditions are valid (e.g., not autosave, not a revision).
- Use
- Avoiding Duplicate IDs and Data Cleanup:
- Ensure that post IDs are unique when updating metadata and remove old IDs when blocks are removed or changed.
1. Setting Up the Custom Post Type (CPT)
The main feature of this class is registering the CPT with WordPress. Here’s how the Custom_Posts class sets up the CPT and manages its associated actions and metadata.
Code Structure:
namespace Lib_Plugin\PostTypes;
use Lib_Plugin\Helpers\Singleton;
class Custom_Posts {
use Singleton;
public const POST_TYPE = 'rt-custom-posts'; // Post type slug
public const EMBEDDED_COL_NAME = 'rt_embedded_in'; // Custom column name
public const EMBEDDED_IN_POSTS_KEY = 'rt_embedded_in_posts'; // Meta key to store embedded post data
public const LIB_BLOCK_NAME = 'lib-plugin/custom-post-block'; // Block name used in Gutenberg
public function __construct() {
$this->setup(); // Initialize actions and filters
}
}
The Custom_Posts class uses the Singleton trait to ensure only one instance of the class is loaded. Constants are defined for reuse in multiple functions to avoid hard-coding values like post type names and metadata keys.
2. Hooking into WordPress
The setup() function is where the CPT is registered, and additional hooks and filters are defined for custom functionality.
Key WordPress Hooks:
init: The CPT is registered during theinithook, which runs after WordPress has finished loading.use_block_editor_for_post_type: This filter controls the use of the block editor (Gutenberg) for specific post types.manage_posts_columns&manage_posts_custom_column: These hooks allow us to add custom columns to the posts table in the WordPress admin for metadata management.save_post: This action ensures that metadata is updated whenever a post is saved or updated.
3. Registering the Custom Post Type
The register_custom_post_type() function defines the custom post type and its features.
public function register_custom_post_type(): void {
register_post_type(
self::POST_TYPE,
array(
'labels' => array(
'name' => __( 'Custom Posts', 'lib-plugin' ),
'singular_name' => __( 'Custom Post', 'lib-plugin' ),
// More labels for the admin UI
),
'public' => true,
'has_archive' => true,
'hierarchical' => false,
'show_in_rest' => true, // Enable Gutenberg
'supports' => array( 'title', 'editor', 'thumbnail', 'author' ), // Define supported features
)
);
}
- Labels: Used to customize the text that appears in the WordPress admin interface.
public: Makes the post type accessible on the frontend.show_in_rest: Enables support for the Gutenberg block editor by allowing the CPT to be exposed via the REST API.supports: Defines features that the CPT will support, such as title, editor, and featured images.
4. Restricting Access to Gutenberg Editor
By default, all post types support the classic editor. We want to ensure that only our custom post type (rt-custom-posts) uses Gutenberg.
public function enable_gutenberg_for_only_custom_post_type( $can_edit, $post_type ): bool {
if ( self::POST_TYPE === $post_type ) {
return true;
}
return false;
}
This filter checks the post type and conditionally enables the Gutenberg editor only for the custom post type, ensuring other post types continue to use the classic editor if needed.
5. Adding Custom Columns in the Admin
One of the more advanced features of this code is the addition of a custom column, Embedded In, to the posts table. This column displays posts in which the current post is embedded.
public function add_column_in_posts( array $post_columns ): array {
$post_columns[ self::EMBEDDED_COL_NAME ] = __( 'Embedded In', 'lib-plugin' );
return $post_columns;
}
This code modifies the default post columns by adding a new one labeled Embedded In. This column will hold references to other posts where this post is embedded.
Updating Column Data:
public function update_embedded_in_custom_col_data( string $column_name, int $post_id ): void {
if ( self::EMBEDDED_COL_NAME !== $column_name ) {
return;
}
$embedded_in_posts_ids = get_post_meta( $post_id, self::EMBEDDED_IN_POSTS_KEY, true );
$embedded_in_posts_ids = $embedded_in_posts_ids ? json_decode( $embedded_in_posts_ids ) : array();
if ( empty( $embedded_in_posts_ids ) ) {
esc_html_e( 'Not embedded.', 'lib-plugin' );
return;
}
$embedded_in_posts_data = array();
foreach ( $embedded_in_posts_ids as $posts_id ) {
$title = get_the_title( $posts_id ) ?? $post_id;
$link = get_permalink( $posts_id ) ?? '#';
$embedded_in_posts_data[] = sprintf( '<a href="%1$s">%2$s</a>', esc_url( $link ), esc_html( $title ) );
}
echo implode( ', ', array_map( 'wp_kses_post', $embedded_in_posts_data ) );
}
Here, the post metadata is retrieved using get_post_meta(), and a list of embedded posts is generated. If no posts are found, the function returns “Not embedded.” Otherwise, it prints links to the embedded posts.
6. Updating Post Metadata
When a post is saved or updated, we need to ensure that the metadata is properly stored and reflects any embedded posts.
public function update_metadata_for_embedded_in_posts_id( int $post_id ): void {
if ( wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) || defined( 'DOING_AUTOSAVE' ) || ! current_user_can( 'edit_post', $post_id ) || ! has_block( self::LIB_BLOCK_NAME, $post_id ) ) {
return;
}
$custom_parsed_block = parse_blocks( get_post_field( 'post_content', $post_id ) );
if ( empty( $custom_parsed_block ) || ! is_array( $custom_parsed_block ) ) {
return;
}
$embedded_post_ids = array_unique(
array_filter(
array_map(
function ( $block ) {
if ( self::LIB_BLOCK_NAME === $block['blockName'] ) {
return $block['attrs']['embedPost']['id'] ?? null;
}
return null;
},
$custom_parsed_block
)
)
);
// More logic for updating post meta
}
This function ensures that only valid blocks (matching LIB_BLOCK_NAME) are parsed and that post metadata is updated accordingly. Each embedded post ID is extracted and stored in the custom post’s metadata.
Conclusion
This example demonstrates how to:
- Register a custom post type with custom columns and metadata.
- Manage access to the Gutenberg editor based on the post type.
- Update post metadata dynamically when posts are saved.
Thank you for reading through the post… More will be added.
by ~Leaveitblank (Mayank Tripathi)