Skip to content

Commit 7fee57b

Browse files
authored
Merge pull request #151 from ryanwelcher/fix/order-by-post-id
Fix: Post ID ordering not working on frontend
2 parents 7096bf4 + 3da1eb4 commit 7fee57b

File tree

6 files changed

+706
-1
lines changed

6 files changed

+706
-1
lines changed

CLAUDE.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,14 @@ npm run format # Format code using WordPress standards
6767
Each trait in the Traits directory handles a specific query modification:
6868
- `Date_Query.php` - Before/after/relative date filtering
6969
- `Disable_Pagination.php` - Performance optimization by disabling pagination
70+
- `Enable_Caching.php` - Transient caching for query results
7071
- `Exclude_Current.php` - Remove current post from results
72+
- `Exclude_Posts.php` - Exclude specific posts by ID
7173
- `Exclude_Taxonomies.php` - Exclude posts by taxonomy terms
7274
- `Include_Posts.php` - Manually select specific posts
7375
- `Meta_Query.php` - Post meta filtering with multiple conditions
7476
- `Multiple_Posts.php` - Multiple post type selection
77+
- `OrderBy.php` - Post ordering with WP_Query compatibility normalization (e.g., 'id' → 'ID')
7578
- `Post_Parent.php` - Child post filtering
7679
- `Tax_Query.php` - Advanced taxonomy queries with AND/OR logic
7780

@@ -150,6 +153,32 @@ Developers can extend AQL in two ways:
150153

151154
See `extending-aql.md` for detailed examples.
152155

156+
## Important Implementation Notes
157+
158+
### WP_Query Parameter Normalization
159+
160+
WordPress REST API and WP_Query handle parameter values differently:
161+
162+
- **REST API**: More lenient, automatically normalizes values (e.g., `orderby=id``orderby=ID`)
163+
- **WP_Query**: Case-sensitive, requires exact values (e.g., must use `orderby=ID` not `orderby=id`)
164+
165+
This can cause discrepancies where queries work in the block editor (REST API) but fail on the frontend (WP_Query). The `OrderBy` trait handles this by normalizing `'id'``'ID'` to ensure consistent behavior across both contexts.
166+
167+
**When adding new orderby options:**
168+
1. Check WordPress WP_Query documentation for the correct case/format
169+
2. Add normalization in the `OrderBy` trait if the REST API and WP_Query values differ
170+
3. Test both in the block editor (REST API) and on the frontend (WP_Query)
171+
172+
### Adding New Query Parameter Traits
173+
174+
To add a new query parameter type:
175+
176+
1. Create a new trait in `includes/Traits/` with a `process_{param_name}()` method
177+
2. Add the trait to `Query_Params_Generator.php`
178+
3. Add a mapping in `Query_Params_Generator::ALLOWED_CONTROLS` array
179+
4. Create corresponding UI controls in `src/components/`
180+
5. The trait's process method should populate `$this->custom_args` with WP_Query-compatible parameters
181+
153182
## Testing
154183

155184
PHPUnit tests are located in `tests/unit/`. Configuration in `phpunit.xml` uses PHPUnit 8.5 with Yoast polyfills for PHP 7.4+ compatibility.

_blueprints/e2e-blueprint.json

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,54 @@
77
{
88
"step": "activatePlugin",
99
"pluginPath": "/wordpress/wp-content/plugins/advanced-query-loop/index.php"
10+
},
11+
{
12+
"step": "runPHP",
13+
"code": "<?php\nrequire_once '/wordpress/wp-load.php';\n$posts = get_posts(array('post_type' => 'post', 'posts_per_page' => -1, 'post_status' => 'any'));\nforeach ($posts as $post) {\n\twp_delete_post($post->ID, true);\n}\necho 'Deleted ' . count($posts) . ' existing posts';\n?>"
14+
},
15+
{
16+
"step": "wp-cli",
17+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-06-15 14:30:00' --porcelain"
18+
},
19+
{
20+
"step": "wp-cli",
21+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-02-10 09:15:00' --porcelain"
22+
},
23+
{
24+
"step": "wp-cli",
25+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-09-22 16:45:00' --porcelain"
26+
},
27+
{
28+
"step": "wp-cli",
29+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-01-05 08:00:00' --porcelain"
30+
},
31+
{
32+
"step": "wp-cli",
33+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-11-30 11:20:00' --porcelain"
34+
},
35+
{
36+
"step": "wp-cli",
37+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-03-18 13:00:00' --porcelain"
38+
},
39+
{
40+
"step": "wp-cli",
41+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-07-07 15:30:00' --porcelain"
42+
},
43+
{
44+
"step": "wp-cli",
45+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-04-25 12:15:00' --porcelain"
46+
},
47+
{
48+
"step": "wp-cli",
49+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-12-12 10:45:00' --porcelain"
50+
},
51+
{
52+
"step": "wp-cli",
53+
"command": "wp post create --post_title='Test Post - ID: will be updated' --post_content='Test content for verifying Post ID ordering functionality.' --post_status=publish --post_date='2024-05-08 17:00:00' --porcelain"
54+
},
55+
{
56+
"step": "runPHP",
57+
"code": "<?php\nrequire_once '/wordpress/wp-load.php';\n\n// Update post titles to include their actual IDs\n$posts = get_posts(array(\n\t'post_type' => 'post',\n\t'posts_per_page' => -1,\n\t'post_status' => 'publish',\n\t'orderby' => 'ID',\n\t'order' => 'ASC'\n));\n\nforeach ($posts as $post) {\n\tif (strpos($post->post_title, 'will be updated') !== false) {\n\t\twp_update_post(array(\n\t\t\t'ID' => $post->ID,\n\t\t\t'post_title' => 'Test Post - ID: ' . $post->ID\n\t\t));\n\t}\n}\n\necho 'Cleaned and created ' . count($posts) . ' test posts';\n?>"
1058
}
1159
]
1260
}

includes/Query_Params_Generator.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class Query_Params_Generator {
2222
use Traits\Tax_Query;
2323
use Traits\Post_Parent;
2424
use Traits\Enable_Caching;
25+
use Traits\OrderBy;
2526

2627
/**
2728
* The list of allowed controls and their associated params in the query.
@@ -30,7 +31,7 @@ class Query_Params_Generator {
3031
'additional_post_types' => 'multiple_posts',
3132
'taxonomy_query_builder' => 'tax_query',
3233
'post_meta_query' => 'meta_query',
33-
'post_order' => 'post_order',
34+
'post_order' => 'orderBy',
3435
'exclude_current_post' => 'exclude_current',
3536
'include_posts' => 'include_posts',
3637
'child_items_only' => 'post_parent',

includes/Traits/OrderBy.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
* OrderBy Trait - Handles post ordering parameter normalization
4+
*
5+
* This trait processes the 'orderBy' parameter from the Query Loop block
6+
* and normalizes it for WP_Query compatibility. This is necessary because
7+
* the WordPress REST API and WP_Query handle certain parameter values
8+
* differently.
9+
*
10+
* Key Issue Solved:
11+
* - REST API (block editor): Accepts 'id' (lowercase) and normalizes internally
12+
* - WP_Query (frontend): Requires 'ID' (uppercase) - case-sensitive
13+
*
14+
* Without this normalization, queries can work in the editor but fail on the
15+
* frontend, causing posts to fall back to default date ordering instead of
16+
* the selected ordering method.
17+
*
18+
* @package AdvancedQueryLoop\Traits
19+
*/
20+
21+
namespace AdvancedQueryLoop\Traits;
22+
23+
/**
24+
* Trait OrderBy
25+
*
26+
* Processes and normalizes the orderBy parameter from block attributes
27+
* to ensure compatibility with WP_Query's case-sensitive requirements.
28+
*
29+
* @since 4.4.0
30+
*/
31+
trait OrderBy {
32+
/**
33+
* Process the orderBy parameter from the block query.
34+
*
35+
* This method retrieves the orderBy value from the block's custom parameters
36+
* and normalizes it for WP_Query. Specifically, it handles the case where
37+
* lowercase 'id' needs to be converted to uppercase 'ID'.
38+
*
39+
* WordPress WP_Query expects 'ID' (uppercase) for ordering by post ID, but
40+
* the REST API accepts 'id' (lowercase). This normalization ensures consistent
41+
* behavior between the block editor (which uses REST API) and the frontend
42+
* (which uses WP_Query directly).
43+
*
44+
* Valid orderBy values (after normalization):
45+
* - 'ID' - Order by post ID (note: uppercase required)
46+
* - 'author' - Order by post author
47+
* - 'title' - Order by post title
48+
* - 'name' - Order by post name (slug)
49+
* - 'date' - Order by post date
50+
* - 'modified' - Order by last modified date
51+
* - 'rand' - Random order
52+
* - 'comment_count' - Order by number of comments
53+
* - 'menu_order' - Order by menu order
54+
* - 'post__in' - Order by post ID inclusion order
55+
* - 'meta_value' - Order by meta value (requires meta_key)
56+
* - 'meta_value_num' - Order by numeric meta value (requires meta_key)
57+
*
58+
* @since 4.4.0
59+
*
60+
* @return void
61+
*/
62+
// phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
63+
public function process_orderBy(): void {
64+
// Retrieve the orderBy parameter from the block's custom parameters.
65+
$order_by = $this->custom_params['orderBy'] ?? null;
66+
67+
// Normalize lowercase 'id' to uppercase 'ID' for WP_Query compatibility.
68+
// WP_Query is case-sensitive and only recognizes 'ID' (uppercase).
69+
$this->custom_args['orderby'] = ( 'id' === $order_by ) ? 'ID' : $order_by;
70+
}
71+
}

0 commit comments

Comments
 (0)