arraypress / wp-register-list-filters
Lightweight library for registering custom list table filters in WordPress admin.
Installs: 11
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/arraypress/wp-register-list-filters
Requires
- php: >=7.4
This package is auto-updated.
Last update: 2025-12-23 21:09:56 UTC
README
A powerful, elegant library for adding custom filters to WordPress admin list tables (posts, pages, custom post types, and users).
Features
- 🎯 Simple API - Register filters with a single function call
- 📊 Multiple Filter Types - Taxonomy, meta field, and custom query filters
- 🔍 Auto-Detection - Automatically fetches taxonomy terms
- 🎨 Smart UI - Filters appear inline with WordPress's native interface
- 🔒 Capability Support - Restrict filters by user capabilities
- 🚀 Performance - Lazy-loads options and uses proper WordPress queries
- 🔗 AND Logic - Multiple filters drill down (Engineering + Inactive = both)
- ✨ Clean Code - Modern PHP 7.4+, fully typed, well-documented
Installation
Install via Composer:
composer require arraypress/wp-register-list-filters
Quick Start
Post Filters
use function ArrayPress\RegisterListFilters\register_post_list_filters; // Add filters to the Posts list table register_post_list_filters( 'post', [ // Taxonomy filter (auto-fetches terms) 'category' => [ 'label' => 'Category', 'taxonomy' => 'category', 'show_count' => true, 'hide_empty' => false ], // Meta field filter 'status' => [ 'label' => 'Status', 'options' => [ 'active' => 'Active', 'inactive' => 'Inactive', 'pending' => 'Pending' ] ], // Boolean meta filter '_featured' => [ 'label' => 'Featured Status', 'options' => [ '1' => 'Featured', '0' => 'Not Featured' ] ] ] );
User Filters
use function ArrayPress\RegisterListFilters\register_user_list_filters; // Add filters to the Users list table register_user_list_filters( [ // Meta field filter 'department' => [ 'label' => 'Department', 'options' => [ 'sales' => 'Sales', 'engineering' => 'Engineering', 'marketing' => 'Marketing' ] ], // Custom query callback 'registered' => [ 'label' => 'Registered', 'options' => [ 'today' => 'Today', 'last_7_days' => 'Last 7 Days', 'this_year' => 'This Year' ], 'query_callback' => function( $query, $value ) { $date_query = []; switch ( $value ) { case 'today': $date_query = [ 'after' => date( 'Y-m-d' ) . ' 00:00:00', 'before' => date( 'Y-m-d' ) . ' 23:59:59' ]; break; case 'last_7_days': $date_query = [ 'after' => '7 days ago' ]; break; case 'this_year': $date_query = [ 'after' => date( 'Y' ) . '-01-01' ]; break; } if ( ! empty( $date_query ) ) { $query->set( 'date_query', [ $date_query ] ); } } ] ] );
Custom Post Type Filters
// Register custom post type register_post_type( 'product', [ 'labels' => [ 'name' => 'Products', 'singular_name' => 'Product' ], 'public' => true, 'has_archive' => true ] ); // Add filters for the custom post type register_post_list_filters( 'product', [ // Taxonomy filter 'product_brand' => [ 'label' => 'Brand', 'taxonomy' => 'product_brand', 'show_count' => true ], // Meta filter '_stock_status' => [ 'label' => 'Stock', 'options' => [ 'in_stock' => 'In Stock', 'out_of_stock' => 'Out of Stock' ] ], // Custom callback for price range 'price_range' => [ 'label' => 'Price Range', 'options' => [ 'under_50' => 'Under $50', '50_to_100' => '$50 - $100', 'over_100' => 'Over $100' ], 'query_callback' => function( $query, $value ) { $meta_query = []; switch ( $value ) { case 'under_50': $meta_query = [ 'key' => '_price', 'value' => 50, 'compare' => '<', 'type' => 'NUMERIC' ]; break; case '50_to_100': $meta_query = [ 'key' => '_price', 'value' => [ 50, 100 ], 'compare' => 'BETWEEN', 'type' => 'NUMERIC' ]; break; case 'over_100': $meta_query = [ 'key' => '_price', 'value' => 100, 'compare' => '>', 'type' => 'NUMERIC' ]; break; } if ( ! empty( $meta_query ) ) { $query->set( 'meta_query', [ $meta_query ] ); } } ] ] );
Filter Configuration
Common Options
All filter types support these options:
| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Required | Label for the dropdown placeholder |
capability |
string |
manage_options |
Capability required to see filter |
options |
array |
[] |
Manual options (key => label) |
Taxonomy Filters
For taxonomy-based filters, add these options:
| Option | Type | Default | Description |
|---|---|---|---|
taxonomy |
string |
'' |
Taxonomy slug to fetch terms from |
show_count |
bool |
false |
Show post count next to term name |
hide_empty |
bool |
true |
Hide terms with no posts |
Example:
'category' => [ 'label' => 'Category', 'taxonomy' => 'category', 'show_count' => true, 'hide_empty' => false // Show all categories, even empty ones ]
Meta Field Filters
For meta field filters, the filter key is used as the meta_key:
// Automatically uses 'department' as meta_key 'department' => [ 'label' => 'Department', 'options' => [ 'sales' => 'Sales', 'engineering' => 'Engineering' ] ]
For meta keys with underscores, include them in the key:
'_featured' => [ 'label' => 'Featured', 'options' => [ '1' => 'Yes', '0' => 'No' ] ]
Custom Query Callbacks
For complex filtering logic, use a custom callback:
'custom_filter' => [ 'label' => 'Custom Filter', 'options' => [ 'option1' => 'Option 1', 'option2' => 'Option 2' ], 'query_callback' => function( $query, $value ) { // For posts: $query is WP_Query object // For users: $query is QueryArgsAdapter object // Modify the query based on selected value if ( $value === 'option1' ) { $query->set( 'meta_query', [ [ 'key' => '_custom_field', 'value' => 'custom_value', 'compare' => '=' ] ] ); } } ]
Multiple Post Types
Apply the same filters to multiple post types:
register_post_list_filters( [ 'post', 'page' ], [ '_visibility' => [ 'label' => 'Visibility', 'options' => [ 'public' => 'Public', 'private' => 'Private' ] ] ] );
Capability Restrictions
Restrict filters to specific user capabilities:
register_post_list_filters( 'post', [ '_admin_notes' => [ 'label' => 'Admin Notes', 'capability' => 'manage_options', // Only admins can see this 'options' => [ 'flagged' => 'Flagged', 'reviewed' => 'Reviewed' ] ] ] );
AND Logic (Drill-Down Filtering)
Multiple filters use AND logic for drill-down filtering:
- Select "Engineering" from Department → Shows 25 users
- Add "Inactive" filter → Shows only inactive engineers (fewer results)
- Add "This Year" registration filter → Shows inactive engineers who registered this year
This is standard WordPress behavior and matches user expectations.
How It Works
Posts
- Uses
restrict_manage_postsaction to render filters - Uses
parse_queryaction to modify WP_Query - Builds proper
meta_queryandtax_queryarrays with AND relations
Users
- Uses
restrict_manage_usersaction to render filters - Uses
users_list_table_query_argsfilter to modify user query - Prevents duplicate rendering (action fires twice per page)
Technical Details
Timing
The library automatically handles WordPress hook timing:
- Checks if
inithas fired (taxonomies are registered) - Loads hooks on
admin_init(proper admin context) - No manual hook management needed
Meta Queries
For multiple meta filters, the library builds proper meta_query arrays:
// Single filter [ 'meta_key' => 'department', 'meta_value' => 'engineering' ] // Multiple filters (AND logic) [ 'meta_query' => [ 'relation' => 'AND', [ 'key' => 'department', 'value' => 'engineering' ], [ 'key' => 'user_status', 'value' => 'active' ] ] ]
Taxonomy Queries
Similar approach for taxonomy filters:
[
'tax_query' => [
'relation' => 'AND',
[
'taxonomy' => 'category',
'field' => 'slug',
'terms' => 'technology'
],
[
'taxonomy' => 'post_tag',
'field' => 'slug',
'terms' => 'featured'
]
]
]
Requirements
- PHP 7.4 or higher
- WordPress 5.0 or higher
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
GPL-2.0-or-later
Credits
Developed by ArrayPress