arraypress/wp-register-tables
Composer 安装命令:
composer require arraypress/wp-register-tables
包简介
A declarative WordPress admin table registration system for BerlinDB-powered plugins
关键字:
README 文档
README
A declarative system for registering WordPress admin tables with BerlinDB integration. Eliminates hundreds of lines of boilerplate code for list tables.
Installation
composer require arraypress/wp-register-tables
Quick Start
register_admin_table( 'my_orders', [ // Menu registration 'page_title' => __( 'Orders', 'myplugin' ), 'menu_title' => __( 'Orders', 'myplugin' ), 'menu_slug' => 'my-orders', 'capability' => 'manage_options', 'icon' => 'dashicons-cart', 'position' => 30, // Labels 'labels' => [ 'singular' => __( 'order', 'myplugin' ), 'plural' => __( 'orders', 'myplugin' ), ], // Data 'callbacks' => [ 'get_items' => '\\MyPlugin\\get_orders', 'get_counts' => '\\MyPlugin\\get_order_counts', 'delete' => '\\MyPlugin\\delete_order', ], // Columns 'columns' => [ 'order_number' => __( 'Order', 'myplugin' ), 'customer' => __( 'Customer', 'myplugin' ), 'total' => __( 'Total', 'myplugin' ), 'status' => __( 'Status', 'myplugin' ), 'created_at' => __( 'Date', 'myplugin' ), ], 'sortable' => [ 'order_number', 'total', 'created_at' ], ] );
That's it — the menu page, rendering, screen options, and all admin hooks are handled automatically.
Configuration Reference
register_admin_table( 'table_id', [ // Menu Registration 'page_title' => 'Orders', // Page title tag text (auto-generated from labels.title) 'menu_title' => 'Orders', // Menu item text (falls back to page_title) 'menu_slug' => 'my-orders', // Admin page slug (falls back to table ID) 'parent_slug' => '', // Parent menu slug (empty = top-level page) 'capability' => 'manage_options', // Capability required to view page 'icon' => 'dashicons-admin-generic', // Dashicon or URL (top-level only) 'position' => null, // Menu position (top-level only) // Labels 'labels' => [ 'singular' => 'order', // Used in nonces, notices, no-items text 'plural' => 'orders', // Used in bulk nonces, search, views 'title' => 'Orders', // Page/header title (auto-generated from plural) 'add_new' => 'Add New Order', // Add button text (auto-generated from singular) 'search' => 'Search Orders', // Search box label (auto-generated from plural) 'not_found' => 'No orders yet.', // Empty state message 'not_found_search' => 'No orders found for your search.', ], // Data callbacks 'callbacks' => [ 'get_items' => callable, // Required: Returns array of items 'get_counts' => callable, // Required: Returns status counts array 'delete' => callable, // Optional: Enables auto delete row action 'update' => callable, // Optional: Update handler 'search_callback' => callable, // Optional: Custom search term resolution ], // Display 'per_page' => 30, // Default items per page 'searchable' => true, // Show search box 'show_count' => false, // Show total count in header title // Header 'logo' => '', // URL to logo image for EDD-style header 'header_title' => '', // Override title in header (falls back to labels title) // Columns 'columns' => [], // Column definitions (see Columns section) 'sortable' => [], // Sortable column keys 'primary_column' => '', // Column for row actions (auto-detected) 'hidden_columns' => [], // Columns hidden by default in Screen Options // Actions 'row_actions' => [], // Row action definitions (see Row Actions section) 'bulk_actions' => [], // Bulk action definitions (see Bulk Actions section) // Filtering 'views' => [], // Status view tabs (see Views section) 'filters' => [], // Dropdown filters (see Filters section) 'status_styles' => [], // Status => badge type mappings for auto-formatting // Flyout Integration 'flyouts' => [ 'edit' => '', // Flyout ID for edit actions 'view' => '', // Flyout ID for view actions ], 'add_button' => '', // Add button: flyout ID, URL string, or callable // Permissions 'capabilities' => [ // Per-action overrides (capability used as default) 'view' => '', 'edit' => '', 'delete' => '', 'bulk' => '', ], // Help Tabs 'help' => [], // Help tab definitions (see Help Tabs section) // Styling 'body_class' => '', // Additional CSS class added to admin body ] );
Menu Registration
The library automatically registers admin menu pages. No manual add_menu_page() or add_submenu_page() calls are
needed.
Top-Level Menu Page
register_admin_table( 'my_orders', [ 'page_title' => __( 'Orders', 'myplugin' ), 'menu_title' => __( 'Orders', 'myplugin' ), 'menu_slug' => 'my-orders', 'capability' => 'manage_options', 'icon' => 'dashicons-cart', 'position' => 30, // ... ] );
Submenu Page
register_admin_table( 'my_orders', [ 'page_title' => __( 'Orders', 'myplugin' ), 'menu_title' => __( 'Orders', 'myplugin' ), 'menu_slug' => 'my-orders', 'parent_slug' => 'my-plugin', 'capability' => 'manage_options', // ... ] );
Auto-Generated Defaults
Many menu fields are auto-generated if not provided:
menu_slugdefaults to the table IDpage_titledefaults tolabels.title(which itself defaults fromlabels.plural)menu_titledefaults topage_titlecapabilitydefaults tomanage_options
So the minimal registration for a submenu page is:
register_admin_table( 'my_orders', [ 'parent_slug' => 'my-plugin', 'labels' => [ 'singular' => __( 'order', 'myplugin' ), 'plural' => __( 'orders', 'myplugin' ), ], 'callbacks' => [ 'get_items' => '\\MyPlugin\\get_orders', 'get_counts' => '\\MyPlugin\\get_order_counts', ], 'columns' => [ 'name' => __( 'Name', 'myplugin' ), 'status' => __( 'Status', 'myplugin' ), ], ] );
Modern Header
The library includes a modern EDD-style header with logo support. The header renders outside the WordPress .wrap div
for proper full-width styling.
register_admin_table( 'my_orders', [ 'logo' => plugin_dir_url( __FILE__ ) . 'assets/logo.png', 'header_title' => 'Order Management', 'show_count' => true, // ... ] );
When show_count is enabled, the total item count displays next to the title.
Columns
Simple Format
'columns' => [ 'name' => __( 'Name', 'myplugin' ), 'email' => __( 'Email', 'myplugin' ), 'status' => __( 'Status', 'myplugin' ), ],
Advanced Format
'columns' => [ 'customer' => [ 'label' => __( 'Customer', 'myplugin' ), 'primary' => true, // Row actions appear on this column 'align' => 'left', // left, center, right 'width' => '200px', // CSS width 'callback' => function( $item ) { $avatar = get_avatar( $item->get_email(), 32 ); return $avatar . ' ' . esc_html( $item->get_display_name() ); }, ], 'total' => [ 'label' => __( 'Total', 'myplugin' ), 'align' => 'right', ], ],
Structured Format
For more complex column layouts, use the structured format with before, title, after, and link:
'columns' => [ 'customer' => [ 'label' => __( 'Customer', 'myplugin' ), 'primary' => true, 'before' => function( $item ) { return get_avatar( $item->get_email(), 32 ); }, 'title' => function( $item ) { return $item->get_display_name(); }, 'after' => function( $item ) { return '<br><small>' . esc_html( $item->get_email() ) . '</small>'; }, 'link' => 'edit_flyout', // or 'view_flyout', callable, or URL string ], ],
The link option controls how the title is linked:
| Value | Behavior |
|---|---|
'edit_flyout' |
Opens the edit flyout (requires flyouts.edit config) |
'view_flyout' |
Opens the view flyout (requires flyouts.view config) |
callable |
Called with $item, should return a URL |
string |
Used directly as URL |
Auto-Formatting
Columns are automatically formatted based on naming patterns. The library detects column types by matching against exact names, prefixes, suffixes, and substrings:
| Type | Matching Patterns | Formatting |
|---|---|---|
email |
Contains email |
Mailto link |
phone |
phone, mobile, cell, fax, contains phone |
Clickable tel: link |
country |
country, country_code, suffix _country |
Flag + country name |
date |
created, updated, modified, contains _at or date |
Human time diff |
price |
Contains price, total, amount, _spent, cost, revenue, balance |
Formatted currency |
rate |
rate, discount, commission, suffix _rate |
Rate format |
percentage |
Contains percent, suffix _pct |
Percentage format |
status |
status, contains _status |
Status badge |
count |
count, limit, quantity, contains _count |
Number (∞ for -1) |
items |
items, order_items, suffix _items |
Summary with "and X others" |
user |
user, author, customer, suffix _user |
Avatar + linked name |
taxonomy |
terms, tags, categories, suffix _terms |
Linked term badges |
image |
image, avatar, thumbnail, contains _image |
Thumbnail (supports attachment IDs and URLs) |
color |
color, colour, suffix _color |
Color swatch + code |
url |
url, website, link |
Linked hostname |
boolean |
active, enabled, verified, prefix is_, has_, can_ |
Yes/No icon |
code |
code, sku, uuid, hash, suffix _code, _id, _key |
Monospace code |
duration |
elapsed, runtime, contains duration, suffix _seconds |
Human duration |
file_size |
size, bytes, contains filesize, suffix _size |
Human file size |
Column Auto-Format Configuration
Some auto-formatted types accept additional configuration via the column config array:
'columns' => [ 'status' => [ 'label' => __( 'Status', 'myplugin' ), 'styles' => [ // Custom status => badge type mappings 'active' => 'success', 'inactive' => 'default', 'pending' => 'warning', ], ], 'avatar' => [ 'label' => __( 'Avatar', 'myplugin' ), 'size' => [ 64, 64 ], // Image size as [width, height] or size name ], 'author' => [ 'label' => __( 'Author', 'myplugin' ), 'avatar' => 24, // Avatar size in pixels for user type ], 'line_items' => [ 'label' => __( 'Products', 'myplugin' ), 'singular' => 'product', // Singular label for items type 'plural' => 'products', // Plural label for items type ], 'tags' => [ 'label' => __( 'Tags', 'myplugin' ), 'taxonomy' => 'post_tag', // Taxonomy slug for linked term admin pages ], 'attachment_size' => [ 'label' => __( 'Size', 'myplugin' ), 'decimals' => 2, // Decimal places for file_size type ], ],
Row Actions
Row actions appear on hover below the primary column.
URL-Based Actions
'row_actions' => [ 'view' => [ 'label' => __( 'View', 'myplugin' ), 'url' => fn( $item ) => get_permalink( $item->get_id() ), ], 'archive' => [ 'label' => __( 'Archive', 'myplugin' ), 'url' => fn( $item ) => admin_url( '...' ), 'confirm' => __( 'Archive this item?', 'myplugin' ), 'class' => 'archive-link', ], ],
Flyout Actions
'row_actions' => [ 'edit' => [ 'label' => __( 'Edit', 'myplugin' ), 'flyout' => true, // Opens the flyout defined in flyouts.edit ], ],
Handler-Based Actions
Define a handler callback and the action is automatically processed with nonce verification, capability checks, and
clean redirects:
'row_actions' => [ 'toggle_status' => [ 'label' => fn( $item ) => $item->get_status() === 'active' ? __( 'Deactivate', 'myplugin' ) : __( 'Activate', 'myplugin' ), 'confirm' => fn( $item ) => $item->get_status() === 'active' ? __( 'Deactivate this customer?', 'myplugin' ) : __( 'Activate this customer?', 'myplugin' ), 'handler' => function( $item_id, $config ) { $customer = get_customer( $item_id ); if ( $customer ) { $new_status = $customer->get_status() === 'active' ? 'inactive' : 'active'; update_customer( $item_id, [ 'status' => $new_status ] ); } return true; }, // Optional: custom nonce action (default: {action_key}_{singular}_{item_id}) 'nonce_action' => 'toggle_customer_{id}', // Optional: custom success/error notices 'notice' => [ 'success' => __( 'Customer status updated.', 'myplugin' ), 'error' => __( 'Failed to update status.', 'myplugin' ), ], ], ],
Handler return values control the redirect:
| Return | Behavior |
|---|---|
true |
Redirects with updated=1 |
false |
Redirects with error=action_failed |
array |
Array keys become URL parameters (e.g., ['activated' => 1]) |
Callback Actions
For full control over the action HTML:
'row_actions' => [ 'custom' => [ 'callback' => function( $item ) { return sprintf( '<a href="%s">%s</a>', esc_url( '...' ), 'Custom' ); }, ], ],
Conditional Actions
Actions can be conditionally shown based on the item:
'row_actions' => [ 'refund' => [ 'label' => __( 'Refund', 'myplugin' ), 'condition' => fn( $item ) => $item->get_status() === 'completed', 'handler' => function( $item_id ) { /* ... */ }, ], ],
Action Capabilities
Individual row actions can require specific capabilities:
'row_actions' => [ 'delete_permanently' => [ 'label' => __( 'Delete Permanently', 'myplugin' ), 'capability' => 'delete_others_posts', 'handler' => function( $item_id ) { /* ... */ }, ], ],
Auto Delete Action
When you provide a delete callback in callbacks, the library automatically adds a delete row action with nonce
verification and confirmation dialog. To disable:
'callbacks' => [ 'delete' => '\\MyPlugin\\delete_order', ], // The delete row action is added automatically. // To prevent it, simply omit the delete callback.
Row Actions as Callable
For complete control, pass a callable instead of an array:
'row_actions' => function( $item, $item_id ) { $actions = []; $actions['edit'] = sprintf( '<a href="%s">Edit</a>', esc_url( '...' ) ); return $actions; },
Bulk Actions
'bulk_actions' => [ 'delete' => [ 'label' => __( 'Delete', 'myplugin' ), 'callback' => function( $ids ) { $deleted = 0; foreach ( $ids as $id ) { if ( delete_item( $id ) ) { $deleted++; } } return [ 'deleted' => $deleted ]; }, ], 'activate' => [ 'label' => __( 'Set Active', 'myplugin' ), 'capability' => 'manage_options', 'callback' => function( $ids ) { $updated = 0; foreach ( $ids as $id ) { if ( update_item( $id, [ 'status' => 'active' ] ) ) { $updated++; } } return [ 'updated' => $updated ]; }, 'notice' => [ 'success' => __( '%d customers activated.', 'myplugin' ), 'error' => __( 'Failed to activate customers.', 'myplugin' ), ], ], ],
Callback return values control the redirect:
| Return | Behavior |
|---|---|
array |
Keys become URL parameters |
int |
Redirects with updated={value} |
bool |
Redirects with updated={count} or updated=0 |
Bulk Action Notices
The notice config supports both array and callable formats:
// Array format (with %d placeholder for count) 'notice' => [ 'success' => __( '%d customers activated.', 'myplugin' ), 'error' => __( 'Failed to activate customers.', 'myplugin' ), ], // Callable format (receives $_GET for full control) 'notice' => function( $params ) { $count = absint( $params['updated'] ?? 0 ); return [ 'type' => 'success', 'message' => sprintf( '%d items processed.', $count ), ]; },
Views (Status Tabs)
Views display as clickable tabs above the table. Counts are automatically fetched from the get_counts callback.
Simple Format
Keys are auto-labeled by replacing underscores/hyphens with spaces and capitalizing:
'views' => [ 'active', 'pending', 'not_active' ], // Renders as: All | Active | Pending | Not Active
Explicit Format
'views' => [ 'active' => __( 'Active', 'myplugin' ), 'pending' => __( 'Awaiting Review', 'myplugin' ), 'completed' => __( 'Completed', 'myplugin' ), ],
Mixed Format
'views' => [ 'active', // Auto-labeled "Active" 'pending' => __( 'Awaiting Review', 'myplugin' ), // Custom label 'inactive', // Auto-labeled "Inactive" ],
Views with zero items are automatically hidden. The "All" tab is always shown with the total count.
Filters
Dropdown filters appear above the table with a "Filter" button. A "Clear" button appears when any filter is active.
'filters' => [ 'country' => [ 'label' => __( 'All Countries', 'myplugin' ), 'options_callback' => fn() => get_country_options(), ], 'type' => [ 'label' => __( 'All Types', 'myplugin' ), 'options' => [ 'physical' => __( 'Physical', 'myplugin' ), 'digital' => __( 'Digital', 'myplugin' ), ], ], 'date_range' => [ 'label' => __( 'All Dates', 'myplugin' ), 'options' => [ 'today' => __( 'Today', 'myplugin' ), 'this_week' => __( 'This Week', 'myplugin' ), 'this_month' => __( 'This Month', 'myplugin' ), ], 'apply_callback' => function( &$args, $value ) { if ( $value === 'today' ) { $args['date_query'] = [ 'after' => 'today' ]; } }, ], ],
Without an apply_callback, the filter value is passed directly as a query argument using the filter key (e.g.,
$args['country'] = 'us').
Search
Default Search
When searchable is enabled (default), a search box appears above the table. The search term is passed as
$args['search'] to the get_items callback.
Custom Search Callback
For tables where the search term needs to be resolved against related data (e.g., searching orders by customer email when the email lives in a separate customers table):
'callbacks' => [ 'get_items' => '\\MyPlugin\\get_orders', 'get_counts' => '\\MyPlugin\\get_order_counts', 'search_callback' => function( string $search ) { // Look up customer by email $customer = get_customer_by_email( $search ); if ( $customer ) { return [ 'customer_id' => $customer->get_id() ]; } // Search customers by name, return matching IDs $customers = get_customers( [ 'search' => $search, 'fields' => 'ids' ] ); if ( ! empty( $customers ) ) { return [ 'customer_id__in' => $customers ]; } // Return empty array to fall back to default search behavior return []; }, ],
The callback receives the search string and returns an array of query args to merge into the query. When the callback
returns a non-empty array, the raw search term is not passed to get_items. When it returns an empty array, the default
$args['search'] behavior is used as a fallback.
Search Results Banner
When a search is active, a banner displays showing the search term with a "Clear search" link. This is automatic and requires no configuration.
Status Styles
Map status values to badge types for automatic status column formatting:
'status_styles' => [ 'active' => 'success', 'pending' => 'warning', 'inactive' => 'default', 'cancelled' => 'danger', ],
These styles are passed to the auto-formatter when rendering status columns.
Capabilities
Single Capability
Apply one capability to all actions (also used for menu page access):
'capability' => 'manage_options',
Per-Action Capabilities
Override capabilities for specific actions. The single capability value is used as the default for any action not
explicitly defined:
'capability' => 'edit_posts', // Default for all actions + menu access 'capabilities' => [ 'view' => 'edit_posts', // View the table 'edit' => 'edit_posts', // Edit row actions 'delete' => 'delete_others_posts', // Delete row action 'bulk' => 'manage_options', // Bulk action dropdown ],
Add Button
The add button appears in the header area. Three formats are supported:
// Flyout ID — opens a flyout panel 'add_button' => 'customers_add', // URL — renders as a link button 'add_button' => admin_url( 'admin.php?page=add-customer' ), // Callable — full control over output 'add_button' => function() { return '<a href="#" class="page-title-action">Add New</a>'; },
The button text comes from labels.add_new. If add_new is empty, no button is rendered.
Flyout Integration
Integrates with wp-register-flyouts for inline editing panels:
register_admin_table( 'my_orders', [ 'flyouts' => [ 'edit' => 'orders_edit', // Flyout ID for editing 'view' => 'orders_view', // Flyout ID for viewing ], 'add_button' => 'orders_add', // Flyout ID for adding 'row_actions' => [ 'edit' => [ 'label' => __( 'Edit', 'myplugin' ), 'flyout' => true, // Uses flyouts.edit ], ], // Structured columns can also link to flyouts 'columns' => [ 'name' => [ 'label' => __( 'Name', 'myplugin' ), 'title' => fn( $item ) => $item->get_name(), 'link' => 'edit_flyout', // Uses flyouts.edit ], ], ] );
Help Tabs
Add help tabs to the Screen Options area:
'help' => [ 'overview' => [ 'title' => __( 'Overview', 'myplugin' ), 'content' => '<p>This screen shows all customers.</p>', ], 'filters' => [ 'title' => __( 'Filtering', 'myplugin' ), 'callback' => function() { return '<p>Use the dropdowns to filter by country or status.</p>'; }, ], 'sidebar' => '<p><strong>For more info:</strong></p><p><a href="#">Documentation</a></p>', ],
The special sidebar key sets the help sidebar content. All other keys create individual help tabs.
Screen Options
The library automatically registers a "Number of items per page" screen option. Users can also show/hide columns via Screen Options. Both settings persist per-user.
Clean URLs
The library maintains clean URLs throughout:
- Filter submissions redirect to clean URLs (no
_wpnonce,_wp_http_referer,actionin URL) - Single actions redirect to clean URLs after processing
- Bulk actions redirect to clean URLs after processing
Body Classes
Admin table pages automatically receive CSS body classes for targeted styling:
admin-table— added to all table pagesadmin-table-{id}— table-specific class (e.g.,admin-table-my_customers)- Custom class from the
body_classconfig option
Hooks
Filters
// Modify column definitions add_filter( 'arraypress_table_columns', fn( $columns, $id, $config ) => $columns, 10, 3 ); // Modify hidden columns add_filter( 'arraypress_table_hidden_columns', fn( $hidden, $id, $config ) => $hidden, 10, 3 ); // Modify sortable columns add_filter( 'arraypress_table_sortable_columns', fn( $sortable, $id, $config ) => $sortable, 10, 3 ); // Modify query args before fetching items add_filter( 'arraypress_table_query_args', fn( $args, $id, $config ) => $args, 10, 3 ); add_filter( 'arraypress_table_query_args_{table_id}', fn( $args, $config ) => $args, 10, 2 ); // Modify row actions add_filter( 'arraypress_table_row_actions', fn( $actions, $item, $id ) => $actions, 10, 3 ); add_filter( 'arraypress_table_row_actions_{table_id}', fn( $actions, $item ) => $actions, 10, 2 ); // Modify bulk actions add_filter( 'arraypress_table_bulk_actions', fn( $actions, $id ) => $actions, 10, 2 ); // Modify status views add_filter( 'arraypress_table_views', fn( $views, $id, $status ) => $views, 10, 3 ); // Custom admin notices add_filter( 'arraypress_table_admin_notices', fn( $notices, $id, $config ) => $notices, 10, 3 ); add_filter( 'arraypress_table_admin_notices_{table_id}', fn( $notices, $config ) => $notices, 10, 2 );
Actions
// Before/after table renders add_action( 'arraypress_before_render_table', fn( $id, $config ) => null, 10, 2 ); add_action( 'arraypress_before_render_table_{table_id}', fn( $config ) => null, 10, 1 ); add_action( 'arraypress_after_render_table', fn( $id, $config ) => null, 10, 2 ); add_action( 'arraypress_after_render_table_{table_id}', fn( $config ) => null, 10, 1 ); // Item deleted add_action( 'arraypress_table_item_deleted', fn( $item_id, $result, $id, $config ) => null, 10, 4 ); add_action( 'arraypress_table_item_deleted_{table_id}', fn( $item_id, $result, $config ) => null, 10, 3 ); // Bulk action processed add_action( 'arraypress_table_bulk_action', fn( $items, $action, $id ) => null, 10, 3 ); add_action( 'arraypress_table_bulk_action_{table_id}', fn( $items, $action ) => null, 10, 2 ); add_action( 'arraypress_table_bulk_action_{table_id}_{action}', fn( $items ) => null, 10, 1 ); // Custom single action (only needed if NOT using handler in row_actions config) add_action( 'arraypress_table_single_action_{table_id}', fn( $action, $item_id, $config ) => null, 10, 3 );
Complete Example
register_admin_table( 'my_customers', [ // Menu registration 'page_title' => __( 'Customers', 'myplugin' ), 'menu_title' => __( 'Customers', 'myplugin' ), 'menu_slug' => 'my-customers', 'capability' => 'manage_options', 'icon' => 'dashicons-groups', 'position' => 30, // Labels 'labels' => [ 'singular' => __( 'customer', 'myplugin' ), 'plural' => __( 'customers', 'myplugin' ), 'title' => __( 'Customers', 'myplugin' ), ], // Data 'callbacks' => [ 'get_items' => '\\MyPlugin\\get_customers', 'get_counts' => '\\MyPlugin\\get_customer_counts', 'delete' => '\\MyPlugin\\delete_customer', 'update' => '\\MyPlugin\\update_customer', 'search_callback' => function( string $search ) { $order_customer_ids = get_order_customer_ids_by_search( $search ); if ( ! empty( $order_customer_ids ) ) { return [ 'id__in' => $order_customer_ids ]; } return []; }, ], // Display 'logo' => plugin_dir_url( __FILE__ ) . 'logo.png', 'per_page' => 25, 'show_count' => true, 'body_class' => 'customers-page', // Flyouts 'flyouts' => [ 'edit' => 'customers_edit', ], 'add_button' => 'customers_add', // Columns 'columns' => [ 'name' => [ 'label' => __( 'Customer', 'myplugin' ), 'primary' => true, 'before' => function( $item ) { return get_avatar( $item->get_email(), 32 ); }, 'title' => function( $item ) { return $item->get_name(); }, 'link' => 'edit_flyout', ], 'email' => __( 'Email', 'myplugin' ), 'total_spent' => [ 'label' => __( 'Total Spent', 'myplugin' ), 'align' => 'right', ], 'status' => [ 'label' => __( 'Status', 'myplugin' ), 'width' => '100px', ], 'country' => __( 'Country', 'myplugin' ), 'date_created' => __( 'Joined', 'myplugin' ), ], 'sortable' => [ 'name', 'total_spent', 'date_created' ], // Row Actions 'row_actions' => [ 'edit' => [ 'label' => __( 'Edit', 'myplugin' ), 'flyout' => true, ], 'toggle_status' => [ 'label' => fn( $item ) => $item->get_status() === 'active' ? __( 'Deactivate', 'myplugin' ) : __( 'Activate', 'myplugin' ), 'confirm' => fn( $item ) => $item->get_status() === 'active' ? __( 'Deactivate this customer?', 'myplugin' ) : __( 'Activate this customer?', 'myplugin' ), 'handler' => function( $item_id ) { $customer = get_customer( $item_id ); $new_status = $customer->get_status() === 'active' ? 'inactive' : 'active'; return update_customer( $item_id, [ 'status' => $new_status ] ); }, 'notice' => function( $params ) { $action = isset( $params['activated'] ) ? 'activated' : 'deactivated'; return [ 'type' => 'success', 'message' => sprintf( 'Customer %s successfully.', $action ), ]; }, ], ], // Bulk Actions 'bulk_actions' => [ 'delete' => [ 'label' => __( 'Delete', 'myplugin' ), 'callback' => function( $ids ) { $deleted = 0; foreach ( $ids as $id ) { if ( delete_customer( $id ) ) { $deleted++; } } return [ 'deleted' => $deleted ]; }, ], 'activate' => [ 'label' => __( 'Set Active', 'myplugin' ), 'capability' => 'manage_options', 'callback' => function( $ids ) { $updated = 0; foreach ( $ids as $id ) { if ( update_customer( $id, [ 'status' => 'active' ] ) ) { $updated++; } } return [ 'updated' => $updated ]; }, 'notice' => [ 'success' => __( '%d customers activated.', 'myplugin' ), 'error' => __( 'Failed to activate customers.', 'myplugin' ), ], ], ], // Views & Filters 'views' => [ 'active', 'inactive', 'pending' ], 'filters' => [ 'country' => [ 'label' => __( 'All Countries', 'myplugin' ), 'options_callback' => '\\MyPlugin\\get_country_options', ], ], 'status_styles' => [ 'active' => 'success', 'inactive' => 'default', 'pending' => 'warning', ], 'capabilities' => [ 'delete' => 'manage_options', ], 'help' => [ 'overview' => [ 'title' => __( 'Overview', 'myplugin' ), 'content' => '<p>Manage your customers from this screen.</p>', ], ], ] );
Migration from v1
If you were previously using get_table_renderer() with manual menu registration, you can now remove that boilerplate:
Before (v1):
register_admin_table( 'my_customers', [ 'page' => 'my-customers', // ... ] ); add_action( 'admin_menu', function() { add_menu_page( 'Customers', 'Customers', 'manage_options', 'my-customers', get_table_renderer( 'my_customers' ), 'dashicons-groups', 30 ); } );
After (v2):
register_admin_table( 'my_customers', [ 'menu_slug' => 'my-customers', 'capability' => 'manage_options', 'icon' => 'dashicons-groups', 'position' => 30, 'labels' => [ 'singular' => 'customer', 'plural' => 'customers', ], // ... ] );
The legacy page config key is still supported and maps to menu_slug automatically. The get_table_renderer()
function is still available for edge cases where you need to embed a table in an existing page.
Requirements
- PHP 7.4+
- WordPress 5.0+
- BerlinDB-based custom tables
- arraypress/wp-composer-assets
Dependencies
The following ArrayPress libraries are used for column auto-formatting:
- wp-date-utils — Date and duration formatting
- wp-countries — Country flag and name rendering
- wp-currencies — Currency formatting
- wp-status-badge — Status badge rendering
- wp-rate-format — Rate and percentage formatting
License
GPL-2.0-or-later
Credits
Created by David Sherlock at ArrayPress.
arraypress/wp-register-tables 适用场景与选型建议
arraypress/wp-register-tables 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 13 次下载、GitHub Stars 达 2, 最近一次更新时间为 2025 年 11 月 10 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「wordpress」 「crud」 「wp-admin」 「data-tables」 「wp-list-table」 「berlindb」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 arraypress/wp-register-tables 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 arraypress/wp-register-tables 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 arraypress/wp-register-tables 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Gii Generator for double model generation
A whitelabeled and modernized wp-login.php
A PSR-7 compatible library for making CRUD API endpoints
Admin panel generator for Laravel 8 and based on Vuetify Admin, a separate SPA admin framework running on top of REST APIs.
Easily create custom meta boxes in WordPress.
Helper class that lets you create options settings page securely and swiftly without dealing with WordPress Settings API.
统计信息
- 总下载量: 13
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 2
- 点击次数: 13
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: GPL-2.0-or-later
- 更新时间: 2025-11-10