Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Extended Cpts Laravel Package

johnbillion/extended-cpts

Extended CPTs is a WordPress developer library for registering custom post types and taxonomies with less code. It offers sensible defaults and extended arguments for admin columns, filters, labels, rewrite rules, and more.

Deep Wiki
Context7

Getting Started

johnbillion/extended-cpts is a drop-in replacement for register_post_type() and register_taxonomy(). You install it once, then replace those two calls with register_extended_post_type() / register_extended_taxonomy().

Install (choose one):

# As an mu-plugin (recommended for a site — always loaded, no activation)
composer require johnbillion/extended-cpts
# then symlink/copy vendor/johnbillion/extended-cpts into wp-content/mu-plugins/

# Or inside a theme/plugin that already loads Composer's autoloader
composer require johnbillion/extended-cpts

The library is not a normal plugin you activate in wp-admin — it's a library that requires its own functions. Load it before any code that calls register_extended_post_type().

First place to look: the repo's README.md and docs/ folder. README.md contains the full argument reference, which mirrors register_post_type() plus a few extended keys (admin_cols, admin_filters, dashboard_glance, featured_image, menu_icon, etc.). The source is a handful of classes: Extended_CPT, Extended_CPT_Admin, Extended_Taxonomy, Extended_Taxonomy_Admin.

First use case: register a book CPT with proper singular/plural labels and an admin column — the 3-second upgrade over stock WordPress:

add_action( 'init', function () {
    register_extended_post_type( 'book', [
        'menu_icon'     => 'dashicons-book',
        'supports'      => [ 'title', 'editor', 'thumbnail' ],
        'show_in_rest'  => true,
        'admin_cols'    => [
            'author' => [ 'title' => 'Author', 'meta_key' => 'book_author' ],
            'published' => [ 'title' => 'Published', 'meta_key' => 'book_published', 'date_format' => 'Y-m-d' ],
        ],
    ], [
        'singular' => 'Book',
        'plural'   => 'Books',
        'slug'     => 'books',
    ] );
} );

The third argument ([ 'singular' => ..., 'plural' => ... ]) is where you stop WordPress from guessing labels from the post type key — always pass it.

Implementation Patterns

Chain configuration off the returned object. register_extended_post_type() returns an Extended_CPT instance, so you can attach hooks and meta boxes fluently instead of scattering add_action calls across the codebase:

$books = register_extended_post_type( 'book', [], [ 'singular' => 'Book', 'plural' => 'Books' ] );

$books
    ->add_post_type_support( 'excerpt' )
    ->add_action( 'save_post_book', 'my_save_book_meta' )
    ->add_filter( 'the_content', 'my_append_book_meta' )
    ->add_meta_box(
        'book_details',
        'Book Details',
        'my_book_meta_box_render',
        'normal',
        'high'
    );

add_filter() / add_action() on the object are thin wrappers — they still register globally via WordPress, but they read nicely and keep CPT code co-located.

Register taxonomies attached to the post type explicitly. The second argument of register_extended_taxonomy() is the object type(s) the taxonomy belongs to; pass the CPT slug, not an array of WP_Post_Type objects:

$genres = register_extended_taxonomy( 'genre', 'book', [
    'show_in_rest' => true,
    'admin_cols'   => [
        'title'     => [ 'title' => 'Genre' ],
        'book_count'=> [ 'title' => 'Books', 'function' => 'count' ],
    ],
], [
    'singular' => 'Genre',
    'plural'   => 'Genres',
    'slug'     => 'genres',
] );

$genres->add_term_meta( 'genre_accent_color', 'Accent colour', [
    'type'        => 'string',
    'sanitize_callback' => 'sanitize_hex_color',
] );

Favor the declarative keys over hand-written manage_*_columns hooks. admin_cols, admin_filters, and dashboard_glance cover ~90% of the admin UI tweaks people write for CPTs. Each column spec supports title, meta_key (auto-renders the meta value), function (a callback receiving the post ID), date_format, and link_to_edit. Only fall back to add_filter( 'manage_book_posts_columns', ... ) when the column needs multi-value logic.

Term meta uses the taxonomy object, not the meta API directly. $taxonomy_obj->add_term_meta( $key, $label, $args ) registers the meta with WordPress and renders the field on the term add/edit screens. Reading it in templates is plain get_term_meta( $term_id, 'genre_accent_color', true ).

Register on init, but do not forget REST. If you want Gutenberg and the REST API to work, pass 'show_in_rest' => true to both the post type and every taxonomy. For taxonomies, 'show_in_rest' also enables the block editor taxonomy panel. There is no magic that turns this on for you.

Re-registering an existing CPT? Use register_extended_post_type() in the same init callback after unregister_post_type() (WP 4.7+) or hook later than the original. The library reads the current global $wp_post_types[ $post_type ] and layers extended args on top, so be explicit about every argument you care about — implicit inheritance from the original registration can be surprising.

Wiring admin filters for meta queries. admin_filters supports meta_key / meta_compare (and taxonomy for term-based filters). It generates the dropdown UI, the pre_get_posts query modification, and the "filter" reset link. Anything more complex (nested OR/AND meta queries) needs a custom filter — don't try to force meta_query through a simple meta_key.

Dashboard widget and menu. dashboard_glance adds an "at a glance" item (published count) and menu_icon accepts a Dashicon slug, an image URL, or base64 data URI. Both are one-liners in the registration args.

Gotchas and Tips

Load order is everything. If your theme calls register_extended_post_type() during after_setup_theme but the library is only required via a mu-plugin loaded after after_setup_theme, you get a fatal error. Register on init, and ensure the library is required from an mu-plugin or from functions.php before any init callback fires.

register_extended_post_type() does not replace register_post_type() — it wraps it. Do not call both for the same post type. Double registration throws _doing_it_wrong() notices and silently overwrites admin columns.

Labels are guessed if you omit them. When plural is passed, the library builds all 11 core labels. If you pass only singular, the plural is inferred (Book → Books, Person → Persons — not People). Always pass both for anything user-facing.

Permalinks must be flushed once. The library does not flush rewrite rules on its own (correctly). If your CPT slug changes, hook flush_rewrite_rules() on plugin activation or just visit Settings → Permalinks once. If /books/my-book/ 404s, this is almost always why.

add_meta_box() callbacks receive only $post. The wrapper is add_meta_box( $id, $title, $callback, $context, $priority ) — the $callback_args array from core is not exposed. If you need extra arguments, close over them in the callback. Also, for block-editor-first sites, prefer show_in_rest + custom meta registration over the classic meta box; the latter still works but is deprecated for block-based editing.

Admin columns expect specific column IDs. The admin_cols keys become the actual column IDs. If you name one author, it collides with the built-in Author column — same key, and yours wins. Prefix custom ones (book_author, book_isbn) to avoid collisions. There's also 'cb', 'title', and 'date' — those are WP-owned.

Term meta and multisite. add_term_meta() registers meta against the taxonomy — terms are per-site in multisite, so the same taxonomy on three sites has three independent sets of meta. There's no network-level term meta; use site options if you need shared values.

admin_filters + meta_key requires the meta to actually exist. If the meta is empty for some posts, those rows vanish from the filtered view. Use meta_compare => 'EXISTS' (or 'NOT EXISTS' for a "missing" filter) to keep or exclude them deliberately, not by accident.

Extended args are silently ignored if you call the stock function. A very common bug: pass 'admin_cols' to register_post_type() and nothing happens — WordPress ignores unknown args. The extended keys only work through register_extended_post_type().

Debugging: set WP_DEBUG to true. The library emits PHP notices for deprecated extended arguments (e.g., old admin_cols shapes). The tests in tests/ are the best reference for supported argument combinations — reading tests/Extended_CPT_Test.php is faster than trial and error in the admin UI.

Extension points: the returned Extended_CPT object exposes add_post_type_support(), add_action(), add_filter(), add_meta_box(), add_row_action(), and add_dashboard_glance_item(). Extended_Taxonomy exposes add_term_meta() and add_admin_col(). If those don't cover your need, the underlying hooks are standard WP admin hooks — nothing is namespaced away, so the library plays nicely with any other manage_* filter you already have.

Version note: recent releases require PHP 7.4+ and WordPress 5.x+. If you're on an older stack, pin to an earlier tag and check the changelog — the 2025 release dropped some legacy argument aliases.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager