From 7a8dbdd3c3d589529968193c052e424a29f744cf Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Wed, 17 Dec 2025 19:42:20 +0000 Subject: [PATCH 1/9] fix documentation --- README.md | 12 ++++++------ docs/Getting-started.md | 14 +++++++------- docs/post-types/Creating-columns.md | 5 ++--- docs/post-types/Defining-an-icon.md | 2 +- docs/post-types/Defining-hooks.md | 4 ++-- docs/post-types/Modifying-columns.md | 22 ++++++++++++---------- docs/taxonomies/Defining-hooks.md | 6 +++--- docs/taxonomies/Modifying-columns.md | 26 ++++++++++++++------------ 8 files changed, 47 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index b9443ba..194d2a7 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ class Book extends PostType { 'view_item' => __( 'View Book', 'text-domain' ), 'search_items' => __( 'Search Books', 'text-domain' ), 'not_found' => __( 'No Books found', 'text-domain' ), - 'not_found_in_trash' => __( 'No Books found in Trash', 'text-domain'), + 'not_found_in_trash' => __( 'No Books found in Trash', 'text-domain' ), 'parent_item_colon' => __( 'Parent Book', 'text-domain' ), ]; } @@ -98,7 +98,7 @@ class Book extends PostType { public function filters(): array { return [ 'genre', - 'category' + 'category', ]; } @@ -127,11 +127,11 @@ class Book extends PostType { Once the custom post type class is created it can be registered to WordPress by instantiating and call the register method. ```php -// Instantiate the Books PostType class. -$books = new Books; +// Instantiate the Book PostType class. +$book = new Book; -// Register the books PostType to WordPress. -$books->register(); +// Register the Book PostType to WordPress. +$book->register(); ``` ## Notes diff --git a/docs/Getting-started.md b/docs/Getting-started.md index 532b2f8..cd1b5bd 100644 --- a/docs/Getting-started.md +++ b/docs/Getting-started.md @@ -54,7 +54,7 @@ class Book extends PostType { 'view_item' => __( 'View Book', 'text-domain' ), 'search_items' => __( 'Search Books', 'text-domain' ), 'not_found' => __( 'No Books found', 'text-domain' ), - 'not_found_in_trash' => __( 'No Books found in Trash', 'text-domain'), + 'not_found_in_trash' => __( 'No Books found in Trash', 'text-domain' ), 'parent_item_colon' => __( 'Parent Book', 'text-domain' ), ]; } @@ -76,8 +76,8 @@ class Book extends PostType { */ public function taxonomies(): array { return [ - 'genre' - 'category', + 'genre', + 'category' ]; } @@ -123,9 +123,9 @@ class Book extends PostType { Once the custom post type class is created it can be registered to WordPress by instantiating and call the register method. ```php -// Instantiate the Books PostType class. -$books = new Books; +// Instantiate the Book PostType class. +$book = new Book; -// Register the books PostType to WordPress. -$books->register(); +// Register the Book PostType to WordPress. +$book->register(); ``` diff --git a/docs/post-types/Creating-columns.md b/docs/post-types/Creating-columns.md index 6561bba..2d95e0b 100644 --- a/docs/post-types/Creating-columns.md +++ b/docs/post-types/Creating-columns.md @@ -78,10 +78,9 @@ class Book extends PostType { //... - public function columns( $columns ): void + public function columns( Columns $columns ): Columns { - $columns->column( new PriceColumn ); - + $columns->add( new PriceColumn ); return $columns; } } diff --git a/docs/post-types/Defining-an-icon.md b/docs/post-types/Defining-an-icon.md index f309206..71463e9 100644 --- a/docs/post-types/Defining-an-icon.md +++ b/docs/post-types/Defining-an-icon.md @@ -14,7 +14,7 @@ class Books extends PostType /** * Returns the admin menu icon for the Books post type. * - * @return array + * @return string */ public function icon(): string { diff --git a/docs/post-types/Defining-hooks.md b/docs/post-types/Defining-hooks.md index 7f03b42..7b47b5e 100644 --- a/docs/post-types/Defining-hooks.md +++ b/docs/post-types/Defining-hooks.md @@ -15,9 +15,9 @@ class Books extends PostType /** * Adds additional hooks for the post type. * - * @return array + * @return void */ - public function hooks(): array + public function hooks(): void { add_action( 'save_post_book', [ $this, 'onSave' ], 10, 3 ); } diff --git a/docs/post-types/Modifying-columns.md b/docs/post-types/Modifying-columns.md index 362e300..2962ff4 100644 --- a/docs/post-types/Modifying-columns.md +++ b/docs/post-types/Modifying-columns.md @@ -19,7 +19,7 @@ class Books extends PostType * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { // Add a new price column. $columns->add( 'price', __( 'Price', 'my-text-domain' ) ); @@ -31,8 +31,8 @@ class Books extends PostType // Make the price column sortable. $columns->sortable( 'price', function( WP_Query $query ) { - $query->set( 'orderby', 'meta_value_num' ); $query->set( 'meta_key', 'price' ); + $query->set( 'orderby', 'meta_value_num' ); } ); return $columns; @@ -57,12 +57,11 @@ class Books extends PostType * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { $columns->populate( 'rating', function( $post_id ) { echo get_post_meta( $post_id, 'rating', true ) . '/10'; } ); - return $columns; } } @@ -85,14 +84,13 @@ class Books extends PostType * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { // Make the rating column sortable. $columns->sortable( 'rating', function( WP_Query $query ) { - $query->set( 'orderby', 'meta_value_num' ); $query->set( 'meta_key', 'rating' ); + $query->set( 'orderby', 'meta_value_num' ); } ); - return $columns; } } @@ -115,11 +113,10 @@ class Books extends PostType * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { // Hide the Author and Date columns $columns->hide( [ 'author', 'date' ] ); - return $columns; } } @@ -143,14 +140,19 @@ class Books extends PostType * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { +<<<<<<< Updated upstream // Order the new Rating and Genre columns. $columns->order( [ 'rating' => 2, 'genre' => 4, ] ); +======= + // Position the rating column after the title column. + $columns->position( 'rating', 'after', 'title' ); +>>>>>>> Stashed changes return $columns; } } diff --git a/docs/taxonomies/Defining-hooks.md b/docs/taxonomies/Defining-hooks.md index bdfe5ee..9a01ab1 100644 --- a/docs/taxonomies/Defining-hooks.md +++ b/docs/taxonomies/Defining-hooks.md @@ -12,11 +12,11 @@ class Genres extends Taxonomy //... /** - * Adds additional hooks for the post type. + * Adds additional hooks for the taxonomy. * - * @return array + * @return void */ - public function hooks(): array + public function hooks(): void { add_action( 'saved_term', [ $this, 'onSave' ], 10, 5 ); } diff --git a/docs/taxonomies/Modifying-columns.md b/docs/taxonomies/Modifying-columns.md index 0a5d1a3..f1f0e13 100644 --- a/docs/taxonomies/Modifying-columns.md +++ b/docs/taxonomies/Modifying-columns.md @@ -19,20 +19,20 @@ class Genres extends Taxonomy * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { // Add a new Popularity column. $columns->add( 'popularity', __( 'Popularity', 'my-text-domain' ) ); // Populate the popularity column with term meta. $columns->populate( 'popularity', function( $term_id ) { - echo '$' . get_term_meta( $term_id, '_popularity', true ); + echo get_term_meta( $term_id, '_popularity', true ); } ); // Make the popularity column sortable. $columns->sortable( 'popularity', function( WP_Term_Query $query ) { - $query->query_vars['orderby'] = 'meta_value_num'; $query->query_vars['meta_key'] = 'popularity'; + $query->query_vars['orderby'] = 'meta_value_num'; } ); return $columns; @@ -57,13 +57,12 @@ class Genres extends Taxonomy * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { $columns->populate( 'popularity', function( $term_id ) { - echo '$' . get_term_meta( $term_id, '_popularity', true ); + echo get_term_meta( $term_id, '_popularity', true ); } ); - return $columns; } } @@ -87,14 +86,13 @@ class Genres extends Taxonomy * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { // Make the popularity column sortable. $columns->sortable( 'popularity', function( WP_Term_Query $query ) { - $query->query_vars['orderby'] = 'meta_value_num'; $query->query_vars['meta_key'] = 'popularity'; + $query->query_vars['orderby'] = 'meta_value_num'; } ); - return $columns; } } @@ -117,11 +115,10 @@ class Genres extends Taxonomy * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { // Hide the Description column. $columns->hide( [ 'description' ] ); - return $columns; } } @@ -145,13 +142,18 @@ class Genres extends Taxonomy * * @return array */ - public function columns( Columns $column ): Columns + public function columns( Columns $columns ): Columns { +<<<<<<< Updated upstream // Order the new Popularity column. $columns->order( [ 'popularity' => 2, ] ); +======= + // Position the new Popularity column. + $columns->position( 'popularity', 'after', 'title' ); +>>>>>>> Stashed changes return $columns; } } From 75605607712f4927249ece8bb418384568a327ea Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Wed, 17 Dec 2025 19:42:30 +0000 Subject: [PATCH 2/9] add migration guide draft --- docs/migrating-from-v2-v3.md | 248 +++++++++++++++++++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 docs/migrating-from-v2-v3.md diff --git a/docs/migrating-from-v2-v3.md b/docs/migrating-from-v2-v3.md new file mode 100644 index 0000000..6b19b5d --- /dev/null +++ b/docs/migrating-from-v2-v3.md @@ -0,0 +1,248 @@ +# Migrating from PostTypes v2.x to v3.0 + +This guide highlights the key changes and migration steps for upgrading from PostTypes v2.x to v3.0. The v3.0 release introduces significant architectural improvements, stricter typing, and new extension points. 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.x 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 PSR-4 contracts in `src/Contracts/`. +- You must implement all required abstract methods (e.g., `name()`, `labels()`, `options()`, etc.) in your custom classes. +- The base classes no longer provide magic property population or dynamic label/option generation. + +#### Previous PostTypes API + +Previously, post types were instantiated and the object methods used to configure the post type programatically. + +```php +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. + +```php +remove( [ 'date', 'author' ] ); + + return $columns; + } + + public function icon(): string { + return 'dashicons-book-alt'; + } +} +``` + +Registration remains the same by instantiating class and calling the `register()` method. + +```php +// inside functions.php or plugin file. + +$books = new App\PostTypes\Books; +$books->register(); +``` + +--- + +### 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. + + +```php +class Books extends PostType { + public function name(): string { + return 'book'; + } + + public function slug(): string { + return 'books'; + } + + public function labels(): array { + return [ + 'name' => __( 'Book', 'post-types' ), + 'singular_name' => __( 'Book', 'post-types' ), + 'plural_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'; + } +} +``` + +--- + +### 3. **Columns API** +The columns system is now managed via the `Columns` class, passed as a parameter to the PostType `columns()` method. + + +```php +class Books extends PostType { + + //... + + public function columns( Columns $columns ): Columns { + + $columns->label( 'rating', __( 'Rating', 'text-domain' ) ); + + $columns->populate( 'rating', function( $post_id ) { + echo get_post_meta( $post_id, 'rating', true ); + } ); + + return $columns; + } +} +``` + +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()`. + +```php +class Books extends PostType { + + //... + + public function columns( Columns $columns ): Columns { + + // Column Builder usage. + $columns->add( 'rating' ) + ->after( 'title' ) + ->label(__( 'Rating', 'text-domain' ) ) + ->populate( function ( $post_id ) { + echo get_post_meta( $post_id, 'rating', true ) ); + } ); + + return $columns; + } +} +``` + +## Migration Steps + +1. **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). +2. **Update columns logic:** + - Use the new `Columns` API in your `columns()` method. +3. **Update registration:** + - Continue to call `register()` on your custom classes. +4. **Test thoroughly:** + - Run your test suite and verify admin UI behavior. + +--- + +## Example v2 vs v3 + +**v2.x:** +```php +// Import PostTypes. +use PostTypes\PostType; + +// 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' ); +``` + +**v3.0:** +```php +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'; + } +} +``` + +--- + +## 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](https://posttypes.jjgrainger.co.uk) From 0be0c5f0aa3a28b89ddbe9702c8b8ec746c345d6 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 22 Dec 2025 17:27:05 +0000 Subject: [PATCH 3/9] rename files and tweaks --- README.md | 4 +-- docs/SUMMARY.md | 35 ++++++++++--------- docs/post-types/README.md | 20 +++++------ ...{Creating-columns.md => create-columns.md} | 31 +++++++--------- ...supports.md => define-feature-supports.md} | 0 ...{Defining-filters.md => define-filters.md} | 2 +- .../{Defining-hooks.md => define-hooks.md} | 2 +- .../{Defining-an-icon.md => define-icon.md} | 0 .../{Defining-labels.md => define-labels.md} | 2 +- ...{Defining-options.md => define-options.md} | 2 +- ...ing-taxonomies.md => define-taxonomies.md} | 2 +- ...Modifying-columns.md => modify-columns.md} | 21 +++++------ docs/taxonomies/README.md | 12 +++---- .../{Defining-hooks.md => define-hooks.md} | 2 +- .../{Defining-labels.md => define-labels.md} | 0 ...{Defining-options.md => define-options.md} | 0 ...ing-post-types.md => define-post-types.md} | 0 ...Modifying-columns.md => modify-columns.md} | 0 18 files changed, 62 insertions(+), 73 deletions(-) rename docs/post-types/{Creating-columns.md => create-columns.md} (76%) rename docs/post-types/{Defining-feature-supports.md => define-feature-supports.md} (100%) rename docs/post-types/{Defining-filters.md => define-filters.md} (96%) rename docs/post-types/{Defining-hooks.md => define-hooks.md} (97%) rename docs/post-types/{Defining-an-icon.md => define-icon.md} (100%) rename docs/post-types/{Defining-labels.md => define-labels.md} (98%) rename docs/post-types/{Defining-options.md => define-options.md} (97%) rename docs/post-types/{Defining-taxonomies.md => define-taxonomies.md} (97%) rename docs/post-types/{Modifying-columns.md => modify-columns.md} (88%) rename docs/taxonomies/{Defining-hooks.md => define-hooks.md} (98%) rename docs/taxonomies/{Defining-labels.md => define-labels.md} (100%) rename docs/taxonomies/{Defining-options.md => define-options.md} (100%) rename docs/taxonomies/{Defining-post-types.md => define-post-types.md} (100%) rename docs/taxonomies/{Modifying-columns.md => modify-columns.md} (100%) diff --git a/README.md b/README.md index 194d2a7..6d623f9 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ class Book extends PostType { 'title', 'editor', 'thumbnail', - 'custom-fields' + 'custom-fields', ]; } @@ -80,7 +80,7 @@ class Book extends PostType { */ public function taxonomies(): array { return [ - 'genre' + 'genre', 'category', ]; } diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index ae46bff..f27d146 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -1,24 +1,25 @@ # Table of Contents * [PostTypes v3.0](../README.md) -* [Getting Started](Getting-started.md) +* [Migrating from v2 to v3](migrating-from-v2-v3.md) +* [Getting Started](getting-started.md) * [PostTypes](post-types/README.md) - * [Create a Post Type](post-types/Create-a-post-type.md) - * [Defining Labels](post-types/Defining-labels.md) - * [Defining Options](post-types/Defining-options.md) - * [Defining taxonomies](post-types/Defining-taxonomies.md) - * [Defining feature supports](post-types/Defining-feature-supports.md) - * [Defining an icon](post-types/Defining-an-icon.md) - * [Defining filters](post-types/Defining-filters.md) - * [Modifying columns](post-types/Modifying-columns.md) - * [Creating columns](post-types/Creating-columns.md) - * [Defining hooks](post-types/Defining-hooks.md) + * [Create a Post Type](post-types/create-a-post-type.md) + * [Define Labels](post-types/define-labels.md) + * [Define Options](post-types/define-options.md) + * [Define taxonomies](post-types/define-taxonomies.md) + * [Define feature supports](post-types/define-feature-supports.md) + * [Define an icon](post-types/define-icon.md) + * [Define filters](post-types/define-filters.md) + * [Modify columns](post-types/modify-columns.md) + * [Create columns](post-types/create-columns.md) + * [Define hooks](post-types/define-hooks.md) * [Taxonomies](taxonomies/README.md) - * [Create a Taxonomy](taxonomies/Create-a-taxonomy.md) - * [Defining Labels](taxonomies/Defining-labels.md) - * [Defining Options](taxonomies/Defining-options.md) - * [Defining Post Types](taxonomies/Defining-post-types.md) - * [Modifying Columns](taxonomies/Modifying-columns.md) - * [Defining Hooks](taxonomies/Defining-hooks.md) + * [Create a Taxonomy](taxonomies/create-a-taxonomy.md) + * [Define Labels](taxonomies/define-labels.md) + * [Define Options](taxonomies/define-options.md) + * [Define Post Types](taxonomies/define-post-types.md) + * [Modify Columns](taxonomies/modify-columns.md) + * [Define Hooks](taxonomies/define-hooks.md) * [Contributing](../CONTRIBUTING.md) * [Changelog](../Changelog.md) diff --git a/docs/post-types/README.md b/docs/post-types/README.md index 1b9c6f4..118be5b 100644 --- a/docs/post-types/README.md +++ b/docs/post-types/README.md @@ -2,13 +2,13 @@ The following section contains information on creating and working with post types. -* [Create a Post Type](Create-a-post-type.md) -* [Defining Labels](Defining-labels.md) -* [Defining Options](Defining-options.md) -* [Defining taxonomies](Defining-taxonomies.md) -* [Defining feature supports](Defining-feature-supports.md) -* [Defining an icon](Defining-an-icon.md) -* [Defining filters](Defining-filters.md) -* [Modifying columns](Modifying-columns.md) -* [Creating columns](Creating-columns.md) -* [Defining hooks](Defining-hooks.md) +* [Create a Post Type](create-a-post-type.md) +* [Define Labels](define-labels.md) +* [Define Options](define-options.md) +* [Define taxonomies](define-taxonomies.md) +* [Define feature supports](define-feature-supports.md) +* [Define an icon](define-an-icon.md) +* [Define filters](define-filters.md) +* [Modify columns](modify-columns.md) +* [Create columns](create-columns.md) +* [Define hooks](define-hooks.md) diff --git a/docs/post-types/Creating-columns.md b/docs/post-types/create-columns.md similarity index 76% rename from docs/post-types/Creating-columns.md rename to docs/post-types/create-columns.md index 2d95e0b..466ef27 100644 --- a/docs/post-types/Creating-columns.md +++ b/docs/post-types/create-columns.md @@ -1,4 +1,4 @@ -# Custom Columns +# Create Columns The `Column` class allows developers to create reusable, self-contained columns for the post listing table in the WordPress admin. These custom columns can display post meta, taxonomy values, or any custom data related to the post. @@ -36,33 +36,26 @@ class PriceColumn extends Column /** * Populate column callback. * - * @return void + * @return callable */ - public function populate( int $post_id ): void + public function populate(): callable { - echo '$' . get_post_meta( $post_id, '_price', true ); - } - - /** - * Set the column can be sorted. - * - * @return boolean - */ - public function isSortable(): bool - { - return true; + return function( int $post_id ) { + echo '$' . get_post_meta( $post_id, '_price', true ); + } } /** * Handle sorting the column by modifying the admin query. * - * @param $query \WP_Query - * @return void + * @return callable */ - public function sort(\WP_Query $query): void + public function sort(): callable { - $query->set( 'meta_key', '_price' ); - $query->set( 'orderby', 'meta_value_num' ); + return function( \WP_Query $query ) { + $query->set( 'meta_key', '_price' ); + $query->set( 'orderby', 'meta_value_num' ); + }; } } ``` diff --git a/docs/post-types/Defining-feature-supports.md b/docs/post-types/define-feature-supports.md similarity index 100% rename from docs/post-types/Defining-feature-supports.md rename to docs/post-types/define-feature-supports.md diff --git a/docs/post-types/Defining-filters.md b/docs/post-types/define-filters.md similarity index 96% rename from docs/post-types/Defining-filters.md rename to docs/post-types/define-filters.md index 0db583d..84c9b6d 100644 --- a/docs/post-types/Defining-filters.md +++ b/docs/post-types/define-filters.md @@ -1,4 +1,4 @@ -# Defining filters +# Define filters Filters that appear for the post type listing admin screen can be defined using the `filters()` method. diff --git a/docs/post-types/Defining-hooks.md b/docs/post-types/define-hooks.md similarity index 97% rename from docs/post-types/Defining-hooks.md rename to docs/post-types/define-hooks.md index 7b47b5e..e287c75 100644 --- a/docs/post-types/Defining-hooks.md +++ b/docs/post-types/define-hooks.md @@ -1,4 +1,4 @@ -# Defining hooks +# Define hooks Additional hooks are supported with the `hooks()` method. diff --git a/docs/post-types/Defining-an-icon.md b/docs/post-types/define-icon.md similarity index 100% rename from docs/post-types/Defining-an-icon.md rename to docs/post-types/define-icon.md diff --git a/docs/post-types/Defining-labels.md b/docs/post-types/define-labels.md similarity index 98% rename from docs/post-types/Defining-labels.md rename to docs/post-types/define-labels.md index 548135f..f36b866 100644 --- a/docs/post-types/Defining-labels.md +++ b/docs/post-types/define-labels.md @@ -1,4 +1,4 @@ -# Defining labels +# Define labels Labels for a PostType are defined in the `labels()` method and should return an array of labels. diff --git a/docs/post-types/Defining-options.md b/docs/post-types/define-options.md similarity index 97% rename from docs/post-types/Defining-options.md rename to docs/post-types/define-options.md index ab9a2ba..32bf2eb 100644 --- a/docs/post-types/Defining-options.md +++ b/docs/post-types/define-options.md @@ -1,4 +1,4 @@ -# Defining options +# Define options Options for a PostType are defined in the `options()` method and should return an array of valid [WordPress post type options](https://developer.wordpress.org/reference/functions/register_post_type/#parameters). diff --git a/docs/post-types/Defining-taxonomies.md b/docs/post-types/define-taxonomies.md similarity index 97% rename from docs/post-types/Defining-taxonomies.md rename to docs/post-types/define-taxonomies.md index fcf8da2..f4fd5f4 100644 --- a/docs/post-types/Defining-taxonomies.md +++ b/docs/post-types/define-taxonomies.md @@ -1,4 +1,4 @@ -# Defining Taxonomies +# Define Taxonomies Taxonomies for a PostType can be definied using the `taxonomies()` method. This method should return an array of taxonomy slugs to associate with the post type. diff --git a/docs/post-types/Modifying-columns.md b/docs/post-types/modify-columns.md similarity index 88% rename from docs/post-types/Modifying-columns.md rename to docs/post-types/modify-columns.md index 2962ff4..37353da 100644 --- a/docs/post-types/Modifying-columns.md +++ b/docs/post-types/modify-columns.md @@ -1,8 +1,8 @@ -# Modifying columns +# Modify columns To modify a post types admin columns use the `column()` method. This method accepts the `PostTypes\Columns` manager which has a variety of methods to help fine tune admin table columns. -## Adding Columns +## Add Columns To add columns to the admin edit screen pass an array of column slugs and labels to the `add()` method. @@ -62,6 +62,7 @@ class Books extends PostType $columns->populate( 'rating', function( $post_id ) { echo get_post_meta( $post_id, 'rating', true ) . '/10'; } ); + return $columns; } } @@ -91,6 +92,7 @@ class Books extends PostType $query->set( 'meta_key', 'rating' ); $query->set( 'orderby', 'meta_value_num' ); } ); + return $columns; } } @@ -117,14 +119,15 @@ class Books extends PostType { // Hide the Author and Date columns $columns->hide( [ 'author', 'date' ] ); + return $columns; } } ``` -## Column Order +## Position Columns -To rearrange columns pass an array of column slugs and position to the `order()` method. Only olumns you want to reorder need to be set, not all columns. +To rearrange columns use the `position` method to set a columns position before or after another. ```php @@ -142,17 +145,9 @@ class Books extends PostType */ public function columns( Columns $columns ): Columns { -<<<<<<< Updated upstream - // Order the new Rating and Genre columns. - $columns->order( [ - 'rating' => 2, - 'genre' => 4, - ] ); - -======= // Position the rating column after the title column. $columns->position( 'rating', 'after', 'title' ); ->>>>>>> Stashed changes + return $columns; } } diff --git a/docs/taxonomies/README.md b/docs/taxonomies/README.md index 042032a..570eafd 100644 --- a/docs/taxonomies/README.md +++ b/docs/taxonomies/README.md @@ -2,9 +2,9 @@ The following section contains information on creating and working with taxonomies. -* [Create a Taxonomy](Create-a-taxonomy.md) -* [Defining Labels](Defining-labels.md) -* [Defining Options](Defining-options.md) -* [Defining Post Types](Defining-post-types.md) -* [Modifying Columns](Modifying-columns.md) -* [Defining Hooks](Defining-hooks.md) +* [Create a Taxonomy](create-a-taxonomy.md) +* [Defune Labels](define-labels.md) +* [Defune Options](define-options.md) +* [Defune Post Types](define-post-types.md) +* [Modify Columns](modify-columns.md) +* [Defune Hooks](define-hooks.md) diff --git a/docs/taxonomies/Defining-hooks.md b/docs/taxonomies/define-hooks.md similarity index 98% rename from docs/taxonomies/Defining-hooks.md rename to docs/taxonomies/define-hooks.md index 9a01ab1..c13b4c6 100644 --- a/docs/taxonomies/Defining-hooks.md +++ b/docs/taxonomies/define-hooks.md @@ -1,4 +1,4 @@ -# Defining hooks +# Define hooks Additional hooks are supported with the `hooks()` method. diff --git a/docs/taxonomies/Defining-labels.md b/docs/taxonomies/define-labels.md similarity index 100% rename from docs/taxonomies/Defining-labels.md rename to docs/taxonomies/define-labels.md diff --git a/docs/taxonomies/Defining-options.md b/docs/taxonomies/define-options.md similarity index 100% rename from docs/taxonomies/Defining-options.md rename to docs/taxonomies/define-options.md diff --git a/docs/taxonomies/Defining-post-types.md b/docs/taxonomies/define-post-types.md similarity index 100% rename from docs/taxonomies/Defining-post-types.md rename to docs/taxonomies/define-post-types.md diff --git a/docs/taxonomies/Modifying-columns.md b/docs/taxonomies/modify-columns.md similarity index 100% rename from docs/taxonomies/Modifying-columns.md rename to docs/taxonomies/modify-columns.md From 98864efd2870d1ece63863e7db3526a9c797d9c4 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 22 Dec 2025 17:37:57 +0000 Subject: [PATCH 4/9] update migration guide --- docs/migrating-from-v2-v3.md | 64 ++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/docs/migrating-from-v2-v3.md b/docs/migrating-from-v2-v3.md index 6b19b5d..54dc29a 100644 --- a/docs/migrating-from-v2-v3.md +++ b/docs/migrating-from-v2-v3.md @@ -1,27 +1,26 @@ # Migrating from PostTypes v2.x to v3.0 -This guide highlights the key changes and migration steps for upgrading from PostTypes v2.x to v3.0. The v3.0 release introduces significant architectural improvements, stricter typing, and new extension points. Review and update your custom post types, taxonomies, and integrations as described below. +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.x post type and taxonomy definitions will not work without modification. +> **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 PSR-4 contracts in `src/Contracts/`. -- You must implement all required abstract methods (e.g., `name()`, `labels()`, `options()`, etc.) in your custom classes. -- The base classes no longer provide magic property population or dynamic label/option generation. +- `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. ```php -register(); PostType is an abstract class and methods are used to configure the post type declaratively. ```php -remove( [ 'date', 'author' ] ); return $columns; } + /** + * Set the post type menu icon. + */ public function icon(): string { return 'dashicons-book-alt'; } } ``` -Registration remains the same by instantiating class and calling the `register()` method. +Registration remains the same by instantiating class and calling the `register()` method inside your theme functions.php or plugin file. ```php // inside functions.php or plugin file. @@ -93,9 +100,8 @@ class Books extends PostType { public function labels(): array { return [ - 'name' => __( 'Book', 'post-types' ), + 'name' => __( 'Books', 'post-types' ), 'singular_name' => __( 'Book', 'post-types' ), - 'plural_name' => __( 'Book', 'post-types' ), ]; } @@ -156,7 +162,7 @@ class Books extends PostType { 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. +- `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. @@ -203,6 +209,7 @@ class Books extends PostType { ```php // Import PostTypes. use PostTypes\PostType; +use PostTypes\Taxonomy; // Create a book post type. $books = new PostType( 'book' ); @@ -215,6 +222,20 @@ $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(); ``` **v3.0:** @@ -238,6 +259,23 @@ class Book extends PostType { return 'dashicons-book-alt'; } } + + +class Genre extends PostType { + public function name(): string { + return 'genre'; + } + + public function options(): array { + return [ + 'hierarchical' => false, + ]; + } +} + +(new Book)->register(); +(new Genre)->register(); + ``` --- From 6e5455393a948e5b1ce38e86866e757aa3584967 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 22 Dec 2025 18:16:31 +0000 Subject: [PATCH 5/9] update post type documentation --- docs/migrating-from-v2-v3.md | 6 +- docs/post-types/create-columns.md | 7 +- docs/post-types/define-feature-supports.md | 2 +- docs/post-types/define-filters.md | 4 +- docs/post-types/define-icon.md | 2 +- docs/post-types/define-labels.md | 2 +- docs/post-types/define-options.md | 4 +- docs/post-types/define-taxonomies.md | 2 +- docs/post-types/modify-columns.md | 151 +++++++++++++++++---- 9 files changed, 141 insertions(+), 39 deletions(-) diff --git a/docs/migrating-from-v2-v3.md b/docs/migrating-from-v2-v3.md index 54dc29a..bc8f8b9 100644 --- a/docs/migrating-from-v2-v3.md +++ b/docs/migrating-from-v2-v3.md @@ -240,6 +240,9 @@ $genres->register(); **v3.0:** ```php +use PostTypes\PostType; +use PostTypes\Taxonomy; + class Book extends PostType { public function name(): string { return 'book'; @@ -261,7 +264,7 @@ class Book extends PostType { } -class Genre extends PostType { +class Genre extends Taxonomy { public function name(): string { return 'genre'; } @@ -275,7 +278,6 @@ class Genre extends PostType { (new Book)->register(); (new Genre)->register(); - ``` --- diff --git a/docs/post-types/create-columns.md b/docs/post-types/create-columns.md index 466ef27..3f1440a 100644 --- a/docs/post-types/create-columns.md +++ b/docs/post-types/create-columns.md @@ -1,6 +1,6 @@ # Create Columns -The `Column` class allows developers to create reusable, self-contained columns for the post listing table in the WordPress admin. These custom columns can display post meta, taxonomy values, or any custom data related to the post. +The `Column` class allows developers to create reusable, self-contained columns for the post listing table in the WordPress admin. These custom columns can display post meta, taxonomy values, or any custom data related to the post or taxonomy. Columns are defined by extending the abstract `PostTypes\Column` class and implementing the required `name()` method, along with any optional logic such as rendering, sorting, or changing the label. @@ -62,7 +62,7 @@ class PriceColumn extends Column ## Adding the Column to a Post Type -Once you’ve defined your custom column, you can add it to a PostType using the `$columns->column()` method inside your `PostType` class: +Once you’ve defined your custom column, you can add it to a PostType using the `$columns->column()` method inside your `PostType` or `Taxonomy` class: ```php use PostTypes\PostType; @@ -73,7 +73,8 @@ class Book extends PostType public function columns( Columns $columns ): Columns { - $columns->add( new PriceColumn ); + $columns->column( new PriceColumn ); + return $columns; } } diff --git a/docs/post-types/define-feature-supports.md b/docs/post-types/define-feature-supports.md index 7608660..ea9a662 100644 --- a/docs/post-types/define-feature-supports.md +++ b/docs/post-types/define-feature-supports.md @@ -1,6 +1,6 @@ ## Define feature supports -You can define the features your post types supports using the `supports` method. This works similarly to the [`post_type_supports`](https://developer.wordpress.org/reference/functions/post_type_supports/) function in WordPress and returns an array of 'features'. +Features supported by your post types can be defined using the `supports` method. This works similarly to the [`post_type_supports`](https://developer.wordpress.org/reference/functions/post_type_supports/) function in WordPress and returns an array of 'features'. The `title` and `editor` features are enabled by default, matching the WordPress defaults. A list of available features can be seen in the [WordPress documentation](https://developer.wordpress.org/reference/functions/post_type_supports/#more-information). diff --git a/docs/post-types/define-filters.md b/docs/post-types/define-filters.md index 84c9b6d..3ffd179 100644 --- a/docs/post-types/define-filters.md +++ b/docs/post-types/define-filters.md @@ -2,9 +2,9 @@ Filters that appear for the post type listing admin screen can be defined using the `filters()` method. -This should return an array of taxonomy slugs that are to be used as dropdown filters for the post type. +This must return an array of taxonomy slugs that are to be used as dropdown filters for the post type. -An empty array is returned by default. +By default, an empty array is returned. ```php use PostTypes\PostType; diff --git a/docs/post-types/define-icon.md b/docs/post-types/define-icon.md index 71463e9..9ca43bb 100644 --- a/docs/post-types/define-icon.md +++ b/docs/post-types/define-icon.md @@ -1,6 +1,6 @@ # Menu Icons -WordPress has [Dashicons](https://developer.wordpress.org/resource/dashicons/), an icon font you can use with your custom post types. +[Dashicons](https://developer.wordpress.org/resource/dashicons/) is an icon font you can use with your post types. To set the post type icon pass the dashicon icon slug in the `icon()` method. diff --git a/docs/post-types/define-labels.md b/docs/post-types/define-labels.md index f36b866..b86e07e 100644 --- a/docs/post-types/define-labels.md +++ b/docs/post-types/define-labels.md @@ -1,6 +1,6 @@ # Define labels -Labels for a PostType are defined in the `labels()` method and should return an array of labels. +Labels for a post type are defined in the `labels()` method and should return an array of labels. By default, an empty array is returned and the WordPress default labels are used. diff --git a/docs/post-types/define-options.md b/docs/post-types/define-options.md index 32bf2eb..528ecdb 100644 --- a/docs/post-types/define-options.md +++ b/docs/post-types/define-options.md @@ -1,8 +1,8 @@ # Define options -Options for a PostType are defined in the `options()` method and should return an array of valid [WordPress post type options](https://developer.wordpress.org/reference/functions/register_post_type/#parameters). +Options for a post type are defined in the `options()` method and must return an array of valid [WordPress post type options](https://developer.wordpress.org/reference/functions/register_post_type/#parameters). -By default, an empty array is returned but these options are merged with a generated options array in [PostTypes](#) and whatever options are defined here will overwrite those defaults. +By default, an empty array is returned but these options are merged with a generated options array in PostTypes and whatever options are defined here will overwrite those defaults. See [`register_post_type()`](https://developer.wordpress.org/reference/functions/register_post_type/#parameters) for a full list of supported options. diff --git a/docs/post-types/define-taxonomies.md b/docs/post-types/define-taxonomies.md index f4fd5f4..8ec10c8 100644 --- a/docs/post-types/define-taxonomies.md +++ b/docs/post-types/define-taxonomies.md @@ -26,6 +26,6 @@ class Books extends PostType } ``` -This method only attaches the taxonomy to the post type, to _create_ a taxonomy see the [documentation](../taxonomies/Create-a-taxonomy.md) on creating a new taxonomy. +This method only attaches the taxonomy to the post type, to _create_ a taxonomy see the [documentation](../taxonomies/create-a-taxonomy.md) on creating a new taxonomy. Taxonomies and post types can be created and registered in any order. diff --git a/docs/post-types/modify-columns.md b/docs/post-types/modify-columns.md index 37353da..2826638 100644 --- a/docs/post-types/modify-columns.md +++ b/docs/post-types/modify-columns.md @@ -1,10 +1,10 @@ # Modify columns -To modify a post types admin columns use the `column()` method. This method accepts the `PostTypes\Columns` manager which has a variety of methods to help fine tune admin table columns. +To modify a post types admin columns use the `column()` method. This method accepts the `PostTypes\Columns` manager that has a variety of methods to help fine tune admin table columns. ## Add Columns -To add columns to the admin edit screen pass an array of column slugs and labels to the `add()` method. +Use the `add` method to create a column and initiate the fluent column builder API. The column builder provides useful methods for defining a number of column attributes. ```php use PostTypes\PostType; @@ -22,27 +22,85 @@ class Books extends PostType public function columns( Columns $columns ): Columns { // Add a new price column. - $columns->add( 'price', __( 'Price', 'my-text-domain' ) ); + $columns->add( 'price' ) + // Set the label. + ->label( __( 'Price', 'my-text-domain' ) ) + // Position the column after the title column. + ->after( 'title' ) + // Set the populate callback. + ->populate( function( $post_id ) { + echo '$' . get_post_meta( $post_id, '_price', true ); + } ) + // Set the sort callback. + ->sort( function( WP_Query $query ) { + $query->set( 'meta_key', 'price' ); + $query->set( 'orderby', 'meta_value_num' ); + } ); - // Populate the price column with post meta. - $columns->populate( 'price', function( $post_id ) { - echo '$' . get_post_meta( $post_id, '_price', true ); - } ); + return $columns; + } +} +``` - // Make the price column sortable. - $columns->sortable( 'price', function( WP_Query $query ) { - $query->set( 'meta_key', 'price' ); - $query->set( 'orderby', 'meta_value_num' ); - } ); +## Modify a column + +Any column can be modified using the `modify` method. + +```php +use PostTypes\PostType; +use PostTypes\Columns; + +class Books extends PostType +{ + //... + + /** + * Set the PostTypes admin columns. + * + * @return array + */ + public function columns( Columns $columns ): Columns + { + // Update the WordPress author column label. + $columns->modify( 'author' )->label( __( 'Post Author', 'my-text-domain' ) ); + + return $columns; + } +} +``` + +## Position Columns + +To rearrange columns use either the `before` or `after` methods to set a columns position before or after another. + + +```php +use PostTypes\PostType; +use PostTypes\Columns; + +class Books extends PostType +{ + //... + + /** + * Set the PostTypes admin columns. + * + * @return array + */ + public function columns( Columns $columns ): Columns + { + // Position the price column after the title column. + $columns->add( 'price' )->after( 'title' ); return $columns; } } ``` + ## Populate Columns -To populate any column use the `populate()` method, by passing the column slug and a callback function. +To populate a column use the `populate()` method passing a callback function. ```php use PostTypes\PostType; @@ -59,8 +117,8 @@ class Books extends PostType */ public function columns( Columns $columns ): Columns { - $columns->populate( 'rating', function( $post_id ) { - echo get_post_meta( $post_id, 'rating', true ) . '/10'; + $columns->add( 'price' )->populate( function( $post_id ) { + echo '$' . get_post_meta( $post_id, '_price', true ); } ); return $columns; @@ -70,7 +128,7 @@ class Books extends PostType ## Sortable Columns -To define which custom columns are sortable use the `sortable()` method. +To make a column sortable use the `sort()` method and pass the sorting callback. ```php use PostTypes\PostType; @@ -88,8 +146,8 @@ class Books extends PostType public function columns( Columns $columns ): Columns { // Make the rating column sortable. - $columns->sortable( 'rating', function( WP_Query $query ) { - $query->set( 'meta_key', 'rating' ); + $columns->add( 'price' )->sort( function( WP_Query $query ) { + $query->set( 'meta_key', 'price' ); $query->set( 'orderby', 'meta_value_num' ); } ); @@ -98,9 +156,9 @@ class Books extends PostType } ``` -## Hide Columns +## Remove Columns -To hide columns pass the column slug to the `hide()` method. For multiple columns pass an array of column slugs. +To remove columns pass the column slug to the `remove()` method. For multiple columns pass an array of column slugs. ```php use PostTypes\PostType; @@ -118,17 +176,16 @@ class Books extends PostType public function columns( Columns $columns ): Columns { // Hide the Author and Date columns - $columns->hide( [ 'author', 'date' ] ); + $columns->remove( [ 'author', 'date' ] ); return $columns; } } ``` -## Position Columns - -To rearrange columns use the `position` method to set a columns position before or after another. +## Whitelist Columns +Use the `only()` method to define what columns should appear by passing an array of column slugs. ```php use PostTypes\PostType; @@ -145,12 +202,54 @@ class Books extends PostType */ public function columns( Columns $columns ): Columns { - // Position the rating column after the title column. - $columns->position( 'rating', 'after', 'title' ); + // Only show the checkbox, title and price columns. + $columns->only( [ 'cb', 'title', 'price' ] ); return $columns; } } ``` +## Low-level API + +The Columns class has a low-level API that can continue to be used to make and modify columns, however it is recommended to use the column builder API shown above. + +Below is an example of how to use the low-level API to create the price column. + +```php +use PostTypes\PostType; +use PostTypes\Columns; + +class Books extends PostType +{ + //... + + /** + * Set the PostTypes admin columns. + * + * @return array + */ + public function columns( Columns $columns ): Columns + { + // Add a new price column. + $columns->label( 'price', __( 'Price', 'my-text-domain' ) ); + + // Position the column after the title column. + $columns->position( 'price', 'after', 'title' ); + + // Set the populate callback. + $columns->populate( 'price', function( $post_id ) { + echo '$' . get_post_meta( $post_id, '_price', true ); + } ); + + // Set the sort callback. + $columns->sort( 'price', function( WP_Query $query ) { + $query->set( 'meta_key', 'price' ); + $query->set( 'orderby', 'meta_value_num' ); + } ); + + return $columns; + } +} +``` From fd8aee698e0fa384f525b97f3072225c8e4781b0 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 29 Dec 2025 18:02:08 +0000 Subject: [PATCH 6/9] update README and file names --- README.md | 8 +- docs/getting-started.md | 131 ++++++++++++++++++++++++++ docs/post-types/Create-a-post-type.md | 2 +- docs/post-types/create-a-post-type.md | 36 +++++++ docs/post-types/define-labels.md | 2 +- docs/taxonomies/create-a-taxonomy.md | 63 +++++++++++++ examples/Books.php | 99 +++++++++++++++++++ 7 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 docs/getting-started.md create mode 100644 docs/post-types/create-a-post-type.md create mode 100644 docs/taxonomies/create-a-taxonomy.md create mode 100644 examples/Books.php diff --git a/README.md b/README.md index 6d623f9..d026efc 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -# PostTypes v2.2.1 +# PostTypes v3.0 [![tests](https://github.com/jjgrainger/PostTypes/actions/workflows/tests.yml/badge.svg)](https://github.com/jjgrainger/PostTypes/actions/workflows/tests.yml) [![codecov](https://codecov.io/gh/jjgrainger/PostTypes/branch/master/graph/badge.svg?token=SGrK2xDF46)](https://codecov.io/gh/jjgrainger/PostTypes) [![Latest Stable Version](https://flat.badgen.net/github/release/jjgrainger/PostTypes/stable)](https://packagist.org/packages/jjgrainger/posttypes) [![Total Downloads](https://flat.badgen.net/packagist/dt/jjgrainger/PostTypes)](https://packagist.org/packages/jjgrainger/posttypes) [![License](https://flat.badgen.net/github/license/jjgrainger/PostTypes)](https://packagist.org/packages/jjgrainger/posttypes) -> Simple WordPress custom post types. +> Modern PHP abstractions for WordPress post types and taxonomies. + +## Migrating from v2 to v3 + +> Important: v3.0 is a breaking release. Existing v2 post type and taxonomy definitions will not work without modification. ## Requirements diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..cd1b5bd --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,131 @@ +# Getting Started + +## Requirements + +* PHP >=8.1 +* [Composer](https://getcomposer.org/) +* [WordPress](https://wordpress.org) >=6.3 + +## Installation + +#### Install with composer + +Run the following in your terminal to install PostTypes with [Composer](https://getcomposer.org/). + +``` +$ composer require jjgrainger/posttypes +``` + +PostTypes uses [PSR-4](https://www.php-fig.org/psr/psr-4/) autoloading and can be used with the Composer's autoloader. See Composer's [basic usage](https://getcomposer.org/doc/01-basic-usage.md#autoloading) guide for details on working with Composer and autoloading. + +## Basic Usage + +#### Create a custom post type + +Custom post types are defined as classes that extend the base `PostType` class. At a minimum, the `name` method must be implemented to define the post type slug. All other methods are optional and allow you to configure labels, options, taxonomies, admin columns, filters, and more as needed. + +```php + __( 'Book', 'text-domain' ), + 'singular_name' => __( 'Book', 'text-domain' ), + 'menu_name' => __( 'Books', 'text-domain' ), + 'all_items' => __( 'Books', 'text-domain' ), + 'add_new' => __( 'Add New', 'text-domain' ), + 'add_new_item' => __( 'Add New Book', 'text-domain' ), + 'edit_item' => __( 'Edit Book', 'text-domain' ), + 'new_item' => __( 'New Book', 'text-domain' ), + 'view_item' => __( 'View Book', 'text-domain' ), + 'search_items' => __( 'Search Books', 'text-domain' ), + 'not_found' => __( 'No Books found', 'text-domain' ), + 'not_found_in_trash' => __( 'No Books found in Trash', 'text-domain' ), + 'parent_item_colon' => __( 'Parent Book', 'text-domain' ), + ]; + } + + /** + * Define Post Type feature supports. + */ + public function supports(): array { + return [ + 'title', + 'editor', + 'thumbnail', + 'custom-fields' + ]; + } + + /** + * Define Taxonomies associated with the Post Type. + */ + public function taxonomies(): array { + return [ + 'genre', + 'category' + ]; + } + + /** + * Set the menu icon for the Post Type. + */ + public function icon(): string { + return 'dashicons-book'; + } + + /** + * Set the admin post table filters. + */ + public function filters(): array { + return [ + 'genre', + 'category' + ]; + } + + /** + * Define the columns for the admin post table. + */ + public function columns(Columns $columns): Columns { + // Remove the author and date column. + $columns->remove( [ 'author', 'date' ] ); + + // Add a Rating column. + $columns->add( 'rating', __( 'Rating', 'post-types' ) ); + + // Populate the rating column. + $columns->populate( 'rating', function( $post_id ) { + echo get_post_meta( $post_id, 'rating', true ); + } ); + + return $columns; + } +} +``` + +### Register a custom post type + +Once the custom post type class is created it can be registered to WordPress by instantiating and call the register method. + +```php +// Instantiate the Book PostType class. +$book = new Book; + +// Register the Book PostType to WordPress. +$book->register(); +``` diff --git a/docs/post-types/Create-a-post-type.md b/docs/post-types/Create-a-post-type.md index d843efe..183b014 100644 --- a/docs/post-types/Create-a-post-type.md +++ b/docs/post-types/Create-a-post-type.md @@ -21,7 +21,7 @@ class Books extends PostType ## Register PostType to WordPress -Once your PostType class is created the new post type can be registered to WordPress by instantiating the class and calling the `register()` method in your plugin or theme. +Once your PostType class is created it can be registered to WordPress by instantiating the class and calling the `register()` method in your plugin or theme. ```php // Instantiate the Books PostType class. diff --git a/docs/post-types/create-a-post-type.md b/docs/post-types/create-a-post-type.md new file mode 100644 index 0000000..183b014 --- /dev/null +++ b/docs/post-types/create-a-post-type.md @@ -0,0 +1,36 @@ +# Create a Post Type + +Post types can be made by creating a new class that extends the `PostType` abstract class. All PostType classes require you to implement the `name()` method. Below is an example of a simple Books PostType class to get started. + +```php +use PostTypes\PostType; + +class Books extends PostType +{ + /** + * Returns the post type name to register to WordPress. + * + * @return string + */ + public function name(): string + { + return 'book'; + } +} +``` + +## Register PostType to WordPress + +Once your PostType class is created it can be registered to WordPress by instantiating the class and calling the `register()` method in your plugin or theme. + +```php +// Instantiate the Books PostType class. +$books = new Books; + +// Register the books PostType to WordPress. +$books->register(); +``` + +{% hint style="info" %} +The `register()` method hooks into WordPress and sets all the actions and filters required to create your custom post type. You do not need to add any of your PostTypes code in actions/filters. Doing so may lead to unexpected results. +{% endhint %} diff --git a/docs/post-types/define-labels.md b/docs/post-types/define-labels.md index b86e07e..7b8f911 100644 --- a/docs/post-types/define-labels.md +++ b/docs/post-types/define-labels.md @@ -1,6 +1,6 @@ # Define labels -Labels for a post type are defined in the `labels()` method and should return an array of labels. +Labels for a post type are defined in the `labels()` method and must return an array of labels. By default, an empty array is returned and the WordPress default labels are used. diff --git a/docs/taxonomies/create-a-taxonomy.md b/docs/taxonomies/create-a-taxonomy.md new file mode 100644 index 0000000..107703a --- /dev/null +++ b/docs/taxonomies/create-a-taxonomy.md @@ -0,0 +1,63 @@ +# Taxonomies + +Taxonomies are created using the `Taxonomy` class. This works identically to the `PostType` class and holds similar methods. + +## Create a new taxonomy + +To create a new taxonomy pass the taxonomy name to the class constructor. Labels and the slug are generated from the taxonomy name. + +```php +use PostTypes\Taxonomy; + +class Genres extends Taxonomy +{ + /** + * Returns the taxonomy name to register to WordPress. + * + * @return string + */ + public function name(): string + { + return 'genre'; + } +} +``` + +## Set the slug for the Taxonomy + +By default, the Taxonomy name is used as the slug for the taxonomy too. To change this use the `slug()` method to return a slug string. + +```php +use PostTypes\Taxonomy; + +class Genres extends Taxonomy +{ + //... + + /** + * Returns the taxonomy slug. + * + * @return string + */ + public function slug(): string + { + return 'genres'; + } +} +``` + +## Register the Taxonomy to WordPress + +Once your Taxonomy class is created it can be registered to WordPress by instantiating the class and calling the `register()` method in your plugin or theme. + +```php +// Instantiate the Genres Taxonomy class. +$genres = new Genres; + +// Register the Genres Taxonomy to WordPress. +$genres->register(); +``` + +{% hint style="info" %} +The `register()` method hooks into WordPress and sets all the actions and filters required to create your taxonomy. You do not need to add any of your Taxonomy code in actions/filters. Doing so may lead to unexpected results. +{% endhint %} diff --git a/examples/Books.php b/examples/Books.php new file mode 100644 index 0000000..13d2b9a --- /dev/null +++ b/examples/Books.php @@ -0,0 +1,99 @@ + __( 'Book', 'post-types' ), + 'singular_name' => __( 'Book', 'post-types' ), + 'menu_name' => __( 'Books', 'post-types' ), + 'all_items' => __( 'Books', 'post-types' ), + 'add_new' => __( 'Add New', 'post-types' ), + 'add_new_item' => __( 'Add New Book', 'post-types' ), + 'edit_item' => __( 'Edit Book', 'post-types' ), + 'new_item' => __( 'New Book', 'post-types' ), + 'view_item' => __( 'View Book', 'post-types' ), + 'search_items' => __( 'Search Books', 'post-types' ), + 'not_found' => __( 'No Books found', 'post-types' ), + 'not_found_in_trash' => __( 'No Books found in Trash', 'post-types'), + 'parent_item_colon' => __( 'Parent Book', 'post-types' ), + ]; + } + + public function taxonomies(): array { + return [ + 'post_tag', + 'genre', + ]; + } + + public function supports(): array { + return [ + 'title', + 'editor', + 'author', + 'custom-fields', + ]; + } + + public function options(): array { + return [ + 'show_in_rest' => false, + ]; + } + + public function icon(): string { + return 'dashicons-book'; + } + + public function filters(): array { + return [ + 'genre', + 'post_tag', + ]; + } + + public function columns( Columns $columns ): Columns { + + $columns->remove( [ 'author', 'date' ] ); + + $columns->column( new Price ); + + $columns->label( 'rating', __( 'Rating', 'post-types' ) ); + + $columns->position( 'rating', 'after', 'price' ); + + $columns->populate( 'rating', function( $post_id ) { + echo get_post_meta( $post_id, 'rating', true ); + } ); + + $columns->sort( 'rating', function( $query ) { + $query->set('orderby', 'meta_value_num'); + $query->set('meta_key', 'rating'); + } ); + + $columns->add( 'rating' ) + ->after( 'price' ) + ->label( __( 'Rating', 'post-types' ) ) + ->populate( function( $post_id ) { + echo get_post_meta( $post_id, 'rating', true ); + } ) + ->sort( function( $query ) { + $query->set('orderby', 'meta_value_num'); + $query->set('meta_key', 'rating'); + } ); + + return $columns; + } +} From 3d672369d1738c77413d1127d48917adc2401ca0 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 29 Dec 2025 18:05:19 +0000 Subject: [PATCH 7/9] add migration guide to README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d026efc..07aba50 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Migrating from v2 to v3 -> Important: v3.0 is a breaking release. Existing v2 post type and taxonomy definitions will not work without modification. +> **Important**: v3.0 is a breaking release. Existing v2 post type and taxonomy definitions will not work without modification. Please review the migration guide in the [documentation](https://posttypes.jjgrainger.co.uk) on how to upgrade to version 3. ## Requirements From cf1d32d18c1e40f06b215ff8e461fc81447b50c6 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 29 Dec 2025 18:23:25 +0000 Subject: [PATCH 8/9] update Taxonomy documentation --- docs/taxonomies/Create-a-taxonomy.md | 2 +- docs/taxonomies/README.md | 9 +-- docs/taxonomies/create-a-taxonomy.md | 2 +- docs/taxonomies/create-columns.md | 91 ++++++++++++++++++++++++++++ docs/taxonomies/define-hooks.md | 2 +- docs/taxonomies/define-labels.md | 16 ++--- docs/taxonomies/define-options.md | 4 +- docs/taxonomies/define-post-types.md | 6 +- docs/taxonomies/modify-columns.md | 49 +++++++-------- 9 files changed, 137 insertions(+), 44 deletions(-) create mode 100644 docs/taxonomies/create-columns.md diff --git a/docs/taxonomies/Create-a-taxonomy.md b/docs/taxonomies/Create-a-taxonomy.md index 107703a..7507660 100644 --- a/docs/taxonomies/Create-a-taxonomy.md +++ b/docs/taxonomies/Create-a-taxonomy.md @@ -4,7 +4,7 @@ Taxonomies are created using the `Taxonomy` class. This works identically to the ## Create a new taxonomy -To create a new taxonomy pass the taxonomy name to the class constructor. Labels and the slug are generated from the taxonomy name. +Taxonomies are made by creating a new class that extends the `Taxonomy` abstract class. All Taxonomy classes require you to implement the `name()` method. ```php use PostTypes\Taxonomy; diff --git a/docs/taxonomies/README.md b/docs/taxonomies/README.md index 570eafd..af91b69 100644 --- a/docs/taxonomies/README.md +++ b/docs/taxonomies/README.md @@ -3,8 +3,9 @@ The following section contains information on creating and working with taxonomies. * [Create a Taxonomy](create-a-taxonomy.md) -* [Defune Labels](define-labels.md) -* [Defune Options](define-options.md) -* [Defune Post Types](define-post-types.md) +* [Define Labels](define-labels.md) +* [Define Options](define-options.md) +* [Define Post Types](define-post-types.md) * [Modify Columns](modify-columns.md) -* [Defune Hooks](define-hooks.md) +* [Create Columns](create-columns.md) +* [Define Hooks](define-hooks.md) diff --git a/docs/taxonomies/create-a-taxonomy.md b/docs/taxonomies/create-a-taxonomy.md index 107703a..7507660 100644 --- a/docs/taxonomies/create-a-taxonomy.md +++ b/docs/taxonomies/create-a-taxonomy.md @@ -4,7 +4,7 @@ Taxonomies are created using the `Taxonomy` class. This works identically to the ## Create a new taxonomy -To create a new taxonomy pass the taxonomy name to the class constructor. Labels and the slug are generated from the taxonomy name. +Taxonomies are made by creating a new class that extends the `Taxonomy` abstract class. All Taxonomy classes require you to implement the `name()` method. ```php use PostTypes\Taxonomy; diff --git a/docs/taxonomies/create-columns.md b/docs/taxonomies/create-columns.md new file mode 100644 index 0000000..552ebd8 --- /dev/null +++ b/docs/taxonomies/create-columns.md @@ -0,0 +1,91 @@ +# Create Columns + +The `Column` class allows developers to create reusable, self-contained columns for the taxonomy and post listing table in the WordPress admin. These custom columns can display any custom data related to the taxonomy. + +Columns are defined by extending the abstract `PostTypes\Column` class and implementing the required `name()` method, along with any optional logic such as rendering, sorting, or changing the label. + +## Creating a Custom Column + +To create a custom column, extend the base `Column` class and implement the methods you need. Here's an example of a `PopularityColumn` that pulls a `_popularity` meta field from the term and displays it in the admin table: + +```php +use PostTypes\Column; + +class PopularityColumn extends Column +{ + /** + * Defines the column key used internally. + * + * @return string. + */ + public function name(): string + { + return 'populariy'; + } + + /** + * Define the column label. + * + * @return string + */ + public function label(): string + { + return __( 'Popularity', 'my-text-domain' ); + } + + /** + * Position a column before/after another. + * + * @return array + */ + public function position(): array + { + return $this->after( 'title' ); + } + + /** + * Populate column callback. + * + * @return callable + */ + public function populate(): callable + { + return function( int $term_id ) { + echo get_term_meta( $term_id, '_popularity', true ); + }; + } + + /** + * Handle sorting the column by modifying the admin query. + * + * @return callable + */ + public function sort(): callable + { + return function( \WP_Term_Query $query ) { + $query->query_vars['meta_key'] = '_popularity'; + $query->query_vars['orderby'] = 'meta_value_num'; + }; + } +} +``` + +## Adding the Column to a Taxonomy + +Once you’ve defined your custom column, you can add it to a PostType using the `$columns->column()` method inside your `Taxonomy` class: + +```php +use PostTypes\Taxonomy; + +class Genres extends Taxonomy +{ + //... + + public function columns( Columns $columns ): Columns + { + $columns->column( new PopularityColumn ); + + return $columns; + } +} +``` diff --git a/docs/taxonomies/define-hooks.md b/docs/taxonomies/define-hooks.md index c13b4c6..8d9028d 100644 --- a/docs/taxonomies/define-hooks.md +++ b/docs/taxonomies/define-hooks.md @@ -2,7 +2,7 @@ Additional hooks are supported with the `hooks()` method. -Here you can register additional actions and filters to WordPress and allows you to keep logic associated with your post type in one class. +Here you can register additional actions and filters to WordPress and allows you to keep logic associated with your taxonomy in one class. ```php use PostTypes\Taxonomy; diff --git a/docs/taxonomies/define-labels.md b/docs/taxonomies/define-labels.md index e0f3bbc..98ad264 100644 --- a/docs/taxonomies/define-labels.md +++ b/docs/taxonomies/define-labels.md @@ -1,6 +1,6 @@ -# Defining labels +# Define labels -Labels for a Taxonomy are defined in the `labels()` method and should return an array of labels. +Labels for a Taxonomy are defined in the `labels()` method and must return an array of labels. By default, an empty array is returned and the WordPress default labels are used. @@ -21,12 +21,12 @@ class Genres extends Taxonomy public function labels(): array { return [ - 'name' => __( 'Genres', 'my-text-domain' ), - 'singular_name' => __( 'Genre', 'my-text-domain' ), - 'search_items' => __( 'Search Genres', 'my-text-domain' ), - 'all_items' => __( 'Genres', 'my-text-domain' ), - 'edit_item' => __( 'Edit Genre', 'my-text-domain' ), - 'view_item' => __( 'View Genre', 'my-text-domain' ), + 'name' => __( 'Genres', 'my-text-domain' ), + 'singular_name' => __( 'Genre', 'my-text-domain' ), + 'search_items' => __( 'Search Genres', 'my-text-domain' ), + 'all_items' => __( 'Genres', 'my-text-domain' ), + 'edit_item' => __( 'Edit Genre', 'my-text-domain' ), + 'view_item' => __( 'View Genre', 'my-text-domain' ), ]; } } diff --git a/docs/taxonomies/define-options.md b/docs/taxonomies/define-options.md index f8f6c2e..0dceafc 100644 --- a/docs/taxonomies/define-options.md +++ b/docs/taxonomies/define-options.md @@ -1,6 +1,6 @@ -# Defining options +# Define options -Options for a Taxonomy are defined in the `options()` method and should return an array of valid [WordPress taxonomy options](https://developer.wordpress.org/reference/functions/register_taxonomy/#parameters). +Options for a Taxonomy are defined in the `options()` method and must return an array of valid [WordPress taxonomy options](https://developer.wordpress.org/reference/functions/register_taxonomy/#parameters). By default, an empty array is returned. diff --git a/docs/taxonomies/define-post-types.md b/docs/taxonomies/define-post-types.md index 95eef00..46ea59b 100644 --- a/docs/taxonomies/define-post-types.md +++ b/docs/taxonomies/define-post-types.md @@ -1,6 +1,6 @@ -# Defining Post Types +# Define Post Types -Post types for a Taxonomy can be definied using the `posttypes()` method. This method should return an array of post type names to associate with the taxonomy. +Post types can be added to a Taxonomy using the `posttypes()` method. This method should return an array of post type names to associate with the taxonomy. An empty array is returned by default and no post types are attached to the Taxonomy. @@ -26,6 +26,6 @@ class Genres extends Taxonomy } ``` -This method only attaches the post type to the taxonomy, to _create_ a post type see the [documentation](../post-types/Create-a-post-type.md) on creating a new post type. +This method only attaches the post type to the taxonomy, to _create_ a post type see the [documentation](../post-types/create-a-post-type.md) on creating a new post type. Taxonomies and post types can be created and registered in any order. diff --git a/docs/taxonomies/modify-columns.md b/docs/taxonomies/modify-columns.md index 981460f..909e9c8 100644 --- a/docs/taxonomies/modify-columns.md +++ b/docs/taxonomies/modify-columns.md @@ -1,10 +1,10 @@ -# Modifying columns +# Modify columns To modify a taxonomies admin columns use the `column()` method. This method accepts the `PostTypes\Columns` manager which has a variety of methods to help fine tune admin table columns. -## Adding Columns +## Add Columns -To add columns to the admin list table pass an array of column slugs and labels to the `add()` method. +Use the `add` method to create a column and initiate the fluent column builder API. The column builder provides useful methods for defining a number of column attributes. ```php use PostTypes\Taxonomy; @@ -17,23 +17,25 @@ class Genres extends Taxonomy /** * Set the Taxonomy admin columns. * - * @return array + * @return Columns */ public function columns( Columns $columns ): Columns { // Add a new Popularity column. - $columns->label( 'popularity', __( 'Popularity', 'my-text-domain' ) ); - - // Populate the popularity column with term meta. - $columns->populate( 'popularity', function( $term_id ) { - echo get_term_meta( $term_id, '_popularity', true ); - } ); - - // Make the popularity column sortable. - $columns->sortable( 'popularity', function( WP_Term_Query $query ) { - $query->query_vars['meta_key'] = 'popularity'; - $query->query_vars['orderby'] = 'meta_value_num'; - } ); + $columns->add( 'popularity' ) + // Set the label. + ->label( __( 'Popularity', 'my-text-domain' ) ); + // Position the column after the title column. + ->after( 'title' ) + // Populate the popularity column with term meta. + >populate( 'popularity', function( $term_id ) { + echo get_term_meta( $term_id, '_popularity', true ); + } ); + // Make the popularity column sortable. + ->sortable( 'popularity', function( WP_Term_Query $query ) { + $query->query_vars['meta_key'] = '_popularity'; + $query->query_vars['orderby'] = 'meta_value_num'; + } ); return $columns; } @@ -42,7 +44,7 @@ class Genres extends Taxonomy ## Populate Columns -To populate any column use the `populate()` method, by passing the column slug and a callback function. +To populate any column use the `populate()` method and passing a callback function. ```php use PostTypes\Taxonomy; @@ -59,7 +61,7 @@ class Genres extends Taxonomy */ public function columns( Columns $columns ): Columns { - $columns->populate( 'popularity', function( $term_id ) { + $columns->add( 'popularity' )->populate( function( $term_id ) { echo get_term_meta( $term_id, '_popularity', true ); } ); @@ -70,7 +72,7 @@ class Genres extends Taxonomy ## Sortable Columns -To define which custom columns are sortable use the `sortable()` method. +To define which columns are sortable use the `sort()` method. ```php use PostTypes\Taxonomy; @@ -89,10 +91,11 @@ class Genres extends Taxonomy public function columns( Columns $columns ): Columns { // Make the popularity column sortable. - $columns->sortable( 'popularity', function( WP_Term_Query $query ) { - $query->query_vars['meta_key'] = 'popularity'; + $columns->add( 'popularity' )->sort( function( WP_Term_Query $query ) { + $query->query_vars['meta_key'] = '_popularity'; $query->query_vars['orderby'] = 'meta_value_num'; } ); + return $columns; } } @@ -125,7 +128,7 @@ class Genres extends Taxonomy } ``` -## Column Order +## Column Positioning To rearrange columns pass an array of column slugs and position to the `order()` method. Only olumns you want to reorder need to be set, not all columns. @@ -152,5 +155,3 @@ class Genres extends Taxonomy } } ``` - - From 06c43985c93c06452c03d556630e0709dec821f8 Mon Sep 17 00:00:00 2001 From: jjgrainger Date: Mon, 29 Dec 2025 18:27:36 +0000 Subject: [PATCH 9/9] update documentation examples --- docs/taxonomies/create-columns.md | 2 +- docs/taxonomies/modify-columns.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/taxonomies/create-columns.md b/docs/taxonomies/create-columns.md index 552ebd8..a8e4fd3 100644 --- a/docs/taxonomies/create-columns.md +++ b/docs/taxonomies/create-columns.md @@ -20,7 +20,7 @@ class PopularityColumn extends Column */ public function name(): string { - return 'populariy'; + return 'popularity'; } /** diff --git a/docs/taxonomies/modify-columns.md b/docs/taxonomies/modify-columns.md index 909e9c8..0de82fd 100644 --- a/docs/taxonomies/modify-columns.md +++ b/docs/taxonomies/modify-columns.md @@ -28,11 +28,11 @@ class Genres extends Taxonomy // Position the column after the title column. ->after( 'title' ) // Populate the popularity column with term meta. - >populate( 'popularity', function( $term_id ) { + >populate( function( $term_id ) { echo get_term_meta( $term_id, '_popularity', true ); } ); // Make the popularity column sortable. - ->sortable( 'popularity', function( WP_Term_Query $query ) { + ->sort( function( WP_Term_Query $query ) { $query->query_vars['meta_key'] = '_popularity'; $query->query_vars['orderby'] = 'meta_value_num'; } ); @@ -72,7 +72,7 @@ class Genres extends Taxonomy ## Sortable Columns -To define which columns are sortable use the `sort()` method. +To define a column as sortable use the `sort()` method by passing in the sort callback. ```php use PostTypes\Taxonomy;