This guide highlights the key changes and migration steps for upgrading from PostTypes v2 to v3. The v3 release introduces significant changes. Review and update your custom post types, taxonomies, and integrations as described below.
v3.0 shifts PostTypes to a declarative, class-based architecture to improve readability, testability, and long-term extensibility.
Important: v3.0 is a breaking release. Existing v2 post type and taxonomy definitions will not work without modification.
Major Changes
1. Abstract Base Classes & Contracts
PostType and Taxonomy are now abstract classes and implement new contracts in src/Contracts/.
You must implement the required name() abstract method in your custom classes.
All other methods (e.g labels(), options(), taxonomies(), columns() etc.) must be used to pass the correct definitions for your post types and taxonomies.
The base classes no longer provide magic property population or dynamic label/option generation. Post types and taxonomy properties must be explicitly defined.
Previous PostTypes API
Previously, post types were instantiated and the object methods used to configure the post type programatically.
// Import PostTypes.use PostTypes\PostType;// Create a book post type.$books=newPostType('book');// Hide the date and author columns.$books->columns()->hide(['date','author']);// Set the Books menu icon.$books->icon('dashicons-book-alt');// Register the post type to WordPress.$books->register();
New PostType API
PostType is an abstract class and methods are used to configure the post type declaratively.
Registration remains the same by instantiating class and calling the register() method inside your theme functions.php or plugin file.
2. Options, Labels, and Taxonomies
All configuration (labels, options, taxonomies, supports, filters, columns, icon) must be provided via explicit methods. Only name() is strictly required; all other methods are optional and return sensible defaults.
3. Columns API
The columns system is now managed via the Columns class, passed as a parameter to the PostType columns() method.
Some methods on the Columns have changed or been replaced.
add has been replaced with label.
add() and modify() now return a Column Builder instance for fluent column configuration.
order has been removed and replaced with a position API.
A new column method allows passing Column classes for creating complex columns.
The low-level Columns API is still available to use. For simple changes, you can call methods directly on the Columns instance. For more complex or fluent definitions, use the Column Builder via add() or modify().
Migration Steps
Update all custom PostType and Taxonomy classes:
Extend the new abstract base classes.
Implement required methods (at minimum name()).
Move configuration into explicit methods (labels, options, supports, etc).
Update columns logic:
Use the new Columns API in your columns() method.
Update registration:
Continue to call register() on your custom classes.
Test thoroughly:
Run your test suite and verify admin UI behavior.
Example v2 vs v3
v2.x:
v3.0:
Additional Notes
See the updated README and docs for more examples and details.
Review the new src/Contracts/ interfaces for extension points.
If you encounter issues, check the test suite and consult the documentation
namespace App\PostTypes;
use PostTypes\PostType;
use PostTypes\Columns;
class Books extends PostType {
/**
* Set the post type name.
*/
public function name(): string {
return 'book';
}
/**
* Define post type columns.
*/
public function columns( Columns $columns ): Columns {
$columns->remove( [ 'date', 'author' ] );
return $columns;
}
/**
* Set the post type menu icon.
*/
public function icon(): string {
return 'dashicons-book-alt';
}
}
// inside functions.php or plugin file.
$books = new App\PostTypes\Books;
$books->register();
class Books extends PostType {
public function name(): string {
return 'book';
}
public function slug(): string {
return 'books';
}
public function labels(): array {
return [
'name' => __( 'Books', 'post-types' ),
'singular_name' => __( 'Book', 'post-types' ),
];
}
public function options(): array {
return [
'public' => true,
];
}
public function taxonomies(): array {
return [ 'genres' ];
}
public function supports(): array {
return [ 'title', 'editor' ];
}
public function filters(): array {
return [ 'genres' ];
}
public function columns( Columns $columns ): Columns {
$columns->remove( [ 'date', 'author' ] );
return $columns;
}
public function icon(): string {
return 'dashicons-book-alt';
}
}
// Import PostTypes.
use PostTypes\PostType;
use PostTypes\Taxonomy;
// Create a book post type.
$books = new PostType( 'book' );
// Attach the genre taxonomy (which is created below).
$books->taxonomy( 'genre' );
// Hide the date and author columns.
$books->columns()->hide( [ 'date', 'author' ] );
// Set the Books menu icon.
$books->icon( 'dashicons-book-alt' );
// Register the post type to WordPress.
$books->register();
// Create a genre taxonomy.
$genres = new Taxonomy( 'genre' );
// Set options for the taxonomy.
$genres->options( [
'hierarchical' => false,
] );
// Register the taxonomy to WordPress.
$genres->register();
use PostTypes\PostType;
use PostTypes\Taxonomy;
class Book extends PostType {
public function name(): string {
return 'book';
}
public function taxonomies(): array {
return [ 'genre' ];
}
public function columns(Columns $columns): Columns {
$columns->remove( [ 'date', 'author' ] );
return $columns;
}
public function icon(): string {
return 'dashicons-book-alt';
}
}
class Genre extends Taxonomy {
public function name(): string {
return 'genre';
}
public function options(): array {
return [
'hierarchical' => false,
];
}
}
(new Book)->register();
(new Genre)->register();