feat(theme): rewrite WP_Query meta_query to JOIN pod table for table-based fields - #7574
Conversation
…based storage
Adds two callbacks on the existing WP_Query_Integration singleton:
- pre_get_posts (priority 20) walks the meta_query, strips Pods' optional
_pods_/pods_ alias prefix on each key, and resolves the key against the
fields of every table-based Pod attached to the queried post_type.
Matching keys are stashed on the query object and removed from
meta_query so WP_Meta_Query never builds postmeta joins/where for them.
- posts_clauses (priority 20) splices LEFT JOIN against {prefix}pods_{pod}
for each spec, plus a LEFT JOIN against {prefix}podsrel for relationship
fields. WHERE clauses mirror WP_Meta_Query's compare allow-list and
sanitize values via ->prepare.
Behavior: WP_Query with meta_query that names a table-based pod field now
returns the same IDs as the equivalent pods() helper call. Meta-based Pods
and non-pod queries are untouched (early-return in pre_get_posts).
Fixes pods-framework#7280
PR Summary
|
|
AI disclosure: this PR was written with Claude Opus 5 assistance, reviewed and tested by me. |
build_meta_query_where() took $meta_query['type'] and interpolated it straight
into CAST( ... AS {$type} ) with no validation. $compare was already restricted
to a known list, but $type was not, so arbitrary SQL could be injected through
the meta_query 'type' key.
The type now goes through get_cast_for_type(), which mirrors
WP_Meta_Query::get_cast_for_type(): anything outside the supported set falls
back to CHAR. That also fixes NUMERIC, which is not valid MySQL CAST syntax --
core maps it to SIGNED and this now does the same.
Refs pods-framework#7280
WP_Meta_Query wraps LIKE values as '%' . esc_like( $value ) . '%' before binding them. The table-storage WP_Query integration bound the raw value, so a LIKE on a table field was an exact match and any % or _ in the value acted as a SQL wildcard. The same meta_query could return different rows depending on the storage type. REGEXP / NOT REGEXP are unchanged; core binds those with a plain %s with no esc_like or wildcards, which already matches this code. An array value has no single literal meaning for LIKE, so the clause is skipped instead of emitting malformed SQL. Fixes pods-framework#7611
Description
AI disclosure: this PR was written with Claude assistance, reviewed and tested by me.
Adds WP_Query integration so that meta_query clauses naming a table-based pod field are rewritten into LEFT JOINs against
{prefix}pods_{pod}(plus{prefix}podsrelfor relationship fields) instead of going throughwp_postmeta.Two new callbacks on the existing
Pods\Theme\WP_Query_Integrationsingleton:pre_get_posts(priority 20) walks the meta_query, strips Pods' optional_pods_/pods_alias prefix on each key, and resolves the key against the fields of every table-based Pod attached to the queried post_type. Matching keys are stashed on the query object and removed from meta_query so WP_Meta_Query never builds postmeta joins/where for them.posts_clauses(priority 20) splices LEFT JOIN against{prefix}pods_{pod}for each spec, plus a LEFT JOIN against{prefix}podsrelfor relationship fields. WHERE clauses mirror WP_Meta_Query's compare allow-list and sanitize values via$wpdb->prepare().Behavior: WP_Query with meta_query that names a table-based pod field returns the same IDs as the equivalent
pods()helper call. Meta-based Pods and non-Pod queries are untouched (early-return inpre_get_posts).Commit
fix: escape LIKE values and add wildcards for table field queries(Fixes #7611)WP_Meta_Querywraps LIKE values as'%' . esc_like( $value ) . '%'before binding. The integration bound the raw value, so a LIKE on a table field was an exact match and any%or_in the value acted as a SQL wildcard. The same meta_query could return different rows depending on storage type. This commit mirrors the core esc_like + wildcard wrapping forLIKE/NOT LIKE. REGEXP/NOT REGEXP are unchanged (core uses plain%s). An array LIKE value has no single literal meaning, so the clause is skipped. Backward compatible.Related GitHub issue(s)
Testing instructions
Requires the slic WordPress harness (per
CLAUDE.md).Activate the plugin from this branch.
Create a Post Type Pod (
book) with Table Based storage.Add two fields:
subtitle(Text) andrelated_book(Pick, single, self-referencing).Create three posts with
subtitlevalues Alpha, Beta, Gamma.Run a smoke query:
Expected: array containing exactly the post ID whose
subtitleisBeta. Without the fix the array is empty.Additional manual checks for the LIKE fix (
#7611): substring LIKEsubtitle LIKE 'pecan'matchesButter Pecan; literal%/_in the value are escaped (e.g. value50%does not act as a wildcard).Automated regression coverage in the new wpunit group:
Test cases:
test_meta_query_by_table_field_returns_same_ids_as_pods_helpertest_pods_prefixed_meta_key_resolves_to_table_fieldtest_mixed_query_pod_field_and_foreign_metatest_relationship_field_meta_query_uses_podsrel_jointest_suppress_filters_short_circuits_rewritetest_no_table_meta_keys_means_no_join_addedtest_like_value_gets_wildcards_and_esc_like(WP_Query_Integration: LIKE comparisons skip esc_like() and add no wildcards #7611: plain,%,_, NOT LIKE)test_regexp_value_has_no_added_wildcards(WP_Query_Integration: LIKE comparisons skip esc_like() and add no wildcards #7611: REGEXP stays plain)test_like_with_array_value_bails_closed(WP_Query_Integration: LIKE comparisons skip esc_like() and add no wildcards #7611: array value skipped)Testing: verified by the existing
pods-wp-query-table-metagroup. For #7611 the wpunit harness cannot run in this env (no WordPress/DB), so LIKE/regexp coverage was verified by lint + phpstan on both files (clean) and by parallel inspection sub-agents (A: issue + core parity, B: call sites + deprecated APIs); no findings remain to fix on the #7611 lines.Tested manually: confirmed this resolves the reported issue.
Backward compatible: yes, no breaking changes.
Screenshots / screencast
Not applicable (no UI changes).
Changelog text for these changes
Feature: WP_Query meta_query now resolves table-based pod field keys via JOIN against the pod table and podsrel, so standard
$query->get_posts()calls return the same IDs as thepods()helper. Backward compatible with meta-based Pods. #7280 (@faisalahammad)Fix: LIKE / NOT LIKE on table-based pod fields now uses
esc_like()and%wildcards, so the same meta_query returns the same rows regardless of storage type and values containing%/_are treated literally. Fixes #7611 (@faisalahammad)PR checklist