Skip to content

Conversation

@galatanovidiu
Copy link
Contributor

Summary

Proof of Concept: Alternative implementation to #115 using a Collection-based approach for filtering and sorting abilities.

This PR introduces WP_Abilities_Collection - a fluent, chainable collection class that provides Laravel-inspired filtering, sorting, and transformation methods for working with registered abilities. Instead of the query-based approach in #115, this implementation leverages in-memory collection operations for a more developer-friendly API.

Key Differences from #115

#115 Approach: Uses WP_Abilities_Query class with array-based filtering arguments (similar to WP_Query)

This PR Approach: Uses WP_Abilities_Collection class with fluent, chainable method calls (similar to Laravel Collections)

Example Comparison

Query Approach (#115):

$abilities = wp_get_abilities( array(
    'category' => 'content',
    'namespace' => 'my-plugin',
    'limit' => 10,
) );

Collection Approach (This PR):

$abilities = wp_get_abilities()
    ->where_category( 'content' )
    ->where_namespace( 'my-plugin' )
    ->sort_by( 'label' )
    ->all();

What's New

Core Collection Class

  • WP_Abilities_Collection - New collection class for filtering, sorting, and querying abilities
  • Implements IteratorAggregate and Countable for native PHP iteration
  • Fluent, chainable API for building complex queries
  • Supports dot notation for nested property access

Filtering Methods

  • where($key, $value) or where($key, $operator, $value) - Generic property filtering with operators (=, !=, !==, >, <, >=, <=)
  • where_in($key, $values) - Filter where property is in array of values
  • where_not_in($key, $values) - Filter where property is NOT in array of values
  • where_category($categories) - Filter by category (single string or array)
  • where_namespace($namespaces) - Filter by namespace (single string or array)
  • where_meta($filters) - Filter by metadata properties with dot notation support
  • filter($callback) - Custom callback filtering
  • search($term) - Full-text search across name, label, and description

Sorting Methods

  • sort_by($property, $descending) - Sort by property name or custom callback
  • sort_by_desc($property) - Shorthand for descending sort
  • reverse() - Reverse collection order

Retrieval & Utility Methods

  • all() / to_array() - Get all abilities as array
  • first($callback, $default) - Get first ability (optionally with filter)
  • last($callback, $default) - Get last ability (optionally with filter)
  • get($name, $default) - Get ability by name
  • pluck($value, $key) - Extract property values (supports dot notation: meta.annotations.readonly)
  • keys() - Get all ability names
  • values() - Re-index collection with sequential keys
  • count() - Count abilities in collection
  • is_empty() / is_not_empty() - Check if collection is empty

Changes by Component

PHP Core

  • New: includes/abilities-api/class-wp-abilities-collection.php (577 lines)
  • Modified: includes/abilities-api.php - wp_get_abilities() now returns WP_Abilities_Collection
  • Modified: REST API controllers updated to work with collection

Documentation

  • New: docs/8.advanced-filtering-and-sorting.md (441 lines) - Comprehensive guide
  • Updated: docs/4.using-abilities.md - Collection examples

JavaScript Client

  • Added category support to client package
  • Updated types to support category filtering
  • Enhanced store selectors and resolvers

Tests

  • New: tests/unit/abilities-api/wpAbilitiesCollection.php
  • Comprehensive test coverage for all collection methods

Documentation Highlights

The new docs/8.advanced-filtering-and-sorting.md includes:

  • Quick reference table of all methods
  • Practical examples for common use cases
  • Performance considerations
  • Method chaining patterns
  • Dot notation examples for nested properties

Backward Compatibility

Fully backward compatible

  • wp_get_abilities() without arguments still works
  • Collection implements IteratorAggregate - can be used in foreach loops
  • to_array() method converts collection back to array
  • Existing code continues to function unchanged

Performance Considerations

This is an in-memory collection approach, which means:

  • Pros: Extremely flexible, chainable, developer-friendly API
  • Pros: Excellent for <1000 abilities (typical use case)
  • Cons: May be less efficient than direct array filtering for very large datasets
  • Cons: Always loads all abilities into memory before filtering

Discussion Points

  1. API Style Preference: Query-based (Add Filtering Capabilities for Registered Abilities #115) vs Collection-based (this PR)?
  2. Performance Trade-offs: Is in-memory collection acceptable for expected scale?
  3. Developer Experience: Which API feels more natural for WordPress developers?
  4. Method Naming: I followed Laravel conventions

Related

galatanovidiu and others added 18 commits October 14, 2025 11:21
Public constants are converted to private static properties

Additionally, validation methods are updated to use guard clauses, and some boolean logic is simplified.
WP_Abilities_Query already returns all abilities when args is empty, making the backward compatibility check unnecessary.
Updates type hints to specify arrays of strings for 'category' and 'namespace' parameters.
Updates method names and related docblocks to clarify that query argument processing focuses on sanitization rather than validation.
Improves documentation to specify use of the query class for retrieving and filtering abilities.
Updates the `WP_Abilities_Collection::pluck()` method to support dot notation for accessing nested properties.

This allows for more flexible data extraction, such as retrieving values from `meta` properties like `meta.show_in_rest`. Both the value and key parameters now support this syntax.

The implementation is updated to handle nested data retrieval, and comprehensive unit tests and documentation are added to reflect this enhancement.

Also corrects a heading number in the advanced filtering documentation.
Update documentation for advanced filtering and sorting to improve clarity and prevent common mistakes.

- Clarify that the operator in the `where()` method is optional and defaults to an equality check.
- Document that `all()` is an alias for `to_array()`.
- Add a prominent note for `where_meta()` to specify that the 'meta.' prefix should be omitted from keys.
@codecov
Copy link

codecov bot commented Oct 15, 2025

Codecov Report

❌ Patch coverage is 78.68020% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.11%. Comparing base (c1b24b8) to head (b7e0f11).
⚠️ Report is 1 commits behind head on trunk.

Files with missing lines Patch % Lines
...es/abilities-api/class-wp-abilities-collection.php 78.72% 40 Missing ⚠️
includes/bootstrap.php 0.00% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##              trunk     #119      +/-   ##
============================================
- Coverage     86.65%   85.11%   -1.55%     
- Complexity      148      210      +62     
============================================
  Files            18       19       +1     
  Lines           982     1162     +180     
  Branches         92       90       -2     
============================================
+ Hits            851      989     +138     
- Misses          131      173      +42     
Flag Coverage Δ
javascript 93.04% <ø> (ø)
unit 83.15% <78.68%> (-1.56%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Enhancement New feature or request

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

Proposal: Add a convienient way to filter the list of all registered abilities

2 participants