This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Feb 5, 2026. It is now read-only.

Add Filtering Capabilities for Registered Abilities using collections (POC) - #119

Draft
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2
Draft

Add Filtering Capabilities for Registered Abilities using collections (POC)#119
galatanovidiu wants to merge 18 commits into
trunkfrom
feature/filter-registered-abilities-v2

Conversation

@galatanovidiu

Copy link
Copy Markdown
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

galatanovidiuand 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

codecovBot commented Oct 15, 2025

Copy link
Copy Markdown

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 linesPatch %Lines
...es/abilities-api/class-wp-abilities-collection.php78.72%40 Missing ⚠️
includes/bootstrap.php0.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 
FlagCoverage Δ
javascript93.04% <ø> (ø)
unit83.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.

@jorgefilipecostajorgefilipecosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see the appeal of chainable method calls for the developer experience of filtering abilities with code.

WordPress already manages numerous collections (like Blocks, Patterns, etc.), and developers may justifiably wonder why we aren't using a similar filtering approach for this new "abilities" registry. Or if implementing a new approach why not use it in all other collections too?

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

I guess most filtering will not be done by code; in part, maybe LLMs will filter abilities to retrieve the relevant ones for the context, and in that case, an LLM could easily work with a filtering array query.

Then we have filtering in the REST API. Filtering in the REST API is query-style ("namespace" => "core", "category" => "user"), so in the end, we would need something that converts query-style filters to the chainable method calls.

Given all the history of WordPress relying on query-style filters, and given that for the REST API we will need them anyway, I think at least initially it may be better to follow a simple query based approach.

cc: @gziolo, @swissspidy, in case you have some thoughts here or a different opinion.

@gziolo

Copy link
Copy Markdown
Member

We need to decide between this chainable method calls approach and something simple like wp_get_abilities( [ "namespace" => "core", "category" => "user", "meta" => [ "show_in_rest" => true ] ] ), similar to what is proposed in #115.

It would be interesting to explore how to support a simple interface using array syntax where all conditions are combined with the AND operator, while supporting WP filter called per item when looping through all items to enable more complex filtering conditions.

While the first would fit nicely into WordPress core when performance and extensibility are the most essential aspects, for plugin and theme usage, the alternative syntax based on the collections pattern would be a nice addition.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

[Type] EnhancementNew 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

3 participants

@galatanovidiu@gziolo@jorgefilipecosta