Directorist version
8.9.3
Environment
- WordPress 7.0.4
- PHP 8.2.29
- LocalWP
- Directorist Core with Featured Listings enabled
- Reproduced without a pricing-plan query provider overriding Core featured ordering
Summary
A published listing without an _featured post-meta record can be opened through its direct URL but is excluded from normal All Listings/archive queries.
A missing _featured value should mean “not featured.” Instead, the current Core query requires the meta key to exist when featured ordering is enabled.
Adding _featured = 0 to the same listing makes it appear immediately.
User impact
- Published listing counts can disagree with the listings visible in the frontend grid.
- A newly created, imported, migrated, or programmatically inserted listing may have a working direct URL but remain undiscoverable in archives and search.
- Sorting differently does not reliably restore the listing because featured ordering is applied across multiple order modes.
- The failure is silent; there is no validation or admin warning about the missing meta row.
Reproduction setup
Enable Featured Listings and create two otherwise identical published listings:
| Listing |
_featured state |
| A |
0 |
| B |
Meta row does not exist |
Both listings must have a valid directory type and any required non-expired state.
Steps to reproduce
- Open both listings through their direct URLs.
- Confirm both are publicly accessible.
- Load the All Listings page.
- Search for Listing B by its exact title.
- Add
_featured = 0 to Listing B.
- Reload the same archive/search request.
Actual result
| State |
Direct URL |
All Listings/archive |
_featured = 0 |
Visible |
Visible |
Missing _featured |
Visible |
Missing |
Missing meta changed to 0 |
Visible |
Visible |
Expected result
Listings with missing _featured metadata must be treated as non-featured and remain in all normal archive/search result sets.
Only an explicit featured-only request should exclude them.
Root cause
There are two Core query mechanisms that can independently require the meta row.
1. Normal archive meta query
Directorist\Directorist_Listings::execute_meta_query_args() adds this clause whenever Featured Listings are enabled and the request is not explicitly filtering to featured listings:
$meta_queries['_featured'] = [
'key' => '_featured',
'type' => 'NUMERIC',
'compare' => 'EXISTS',
];
This turns featured ordering into a filtering requirement.
2. Featured sorting through meta_key
Several order modes assign:
$args['meta_key'] = '_featured';
and then order by meta_value_num.
This appears in title, date, random, and search ordering paths. WordPress joins the postmeta table for that key, so posts with no matching row can be excluded even if the explicit EXISTS meta query is removed.
Therefore, removing only the EXISTS clause may not fully fix the bug.
Known affected query paths
Review at minimum:
Directorist_Listings::parse_query_args()
Directorist_Listings::parse_search_query_args()
Directorist_Listings::execute_meta_query_args()
- Title ascending/descending
- Date ascending/descending
- Random
- Price
- Popular/views/rating ordering
- Exact-title search
- Pagination/count queries
Why the metadata can be missing
The current admin metabox normally writes _featured = false, but missing rows can still exist because of:
- Legacy listings created before all save paths initialized the meta.
- CSV/import or migration paths.
- REST/API or custom integrations.
- Direct
wp_insert_post() usage.
- Interrupted/incomplete listing creation.
- Third-party extensions that create listings without the standard Core form handler.
The read query should tolerate these records even if all current write paths are hardened.
Extension interaction
Directorist Pricing Plans 4.0.1 currently filters directorist_query_arg_has_featured to false on frontend requests and provides its own featured/priority sorting through a LEFT JOIN and COALESCE(..., 0).
That integration can mask this Core bug while it is active. The issue reproduces in the Core query path when that provider is absent, inactive, older, or does not intercept the request.
A Core fix must avoid double joins or ordering conflicts when Pricing Plans supplies its own query strategy.
Suggested implementation direction
A complete fix should include both query hardening and data hardening.
A. Query hardening
For normal archives, do not require _featured to exist.
Treat missing values as zero during ordering. A robust strategy is:
- Use a scoped
LEFT JOIN for _featured.
- Order by a numeric expression equivalent to
COALESCE( featured_value, 0 ) DESC.
- Preserve the selected secondary order, such as title/date/price.
- Prevent duplicates if historical data contains more than one
_featured row.
- Ensure found rows and pagination totals remain correct.
The Pricing Plans provider already demonstrates a compatible left-join/COALESCE approach, but Core should implement its own narrowly scoped strategy or reusable query helper without coupling to the extension.
A meta-query EXISTS OR NOT EXISTS solution may keep both record types in the result, but it must be validated carefully across named-clause ordering and all supported sort modes.
B. Featured-only behavior
Explicit featured-only requests should remain strict:
Missing or zero values must not be returned by featured-only queries.
C. Write-path hardening
Audit all Core listing creation/update paths and initialize:
when the listing is not featured.
Include admin, frontend submission, REST, import, migration, renewal, duplicate/clone, and any background creation paths.
D. Optional migration
Consider a versioned, batch-safe migration that backfills _featured = 0 for existing listings missing the key.
The read-query fix is still required because custom integrations can create incomplete metadata after a migration. Any migration must avoid one unbounded update on large directories.
Compatibility and regression risks
- Do not break Pricing Plans display-priority ordering.
- Preserve featured-first ordering for
_featured = 1.
- Preserve the user-selected secondary sort.
- Avoid duplicate posts caused by postmeta joins.
- Keep pagination totals and
found_posts accurate.
- Keep multi-directory filtering intact.
- Do not make featured-only queries include missing values.
- Verify search and normal archive queries separately.
- Ensure admin/non-frontend queries using
directorist_query_arg_has_featured remain correct.
Test matrix
Use at least three listings in the same directory:
| Listing |
Featured meta |
| Featured |
1 |
| Normal |
0 |
| Legacy/missing |
No row |
For every normal archive/search query:
- All three listings are returned.
- Featured listing is first when featured ordering applies.
- Normal and missing-meta listings follow the selected secondary order.
Test the following sorts:
- Title ascending
- Title descending
- Date ascending
- Date descending
- Random
- Price ascending/descending
- Views/popular
- Rating
- Exact-title search
Also verify:
- Featured-only returns only the
1 listing.
- Pagination totals count all normal eligible listings.
- No duplicates are returned.
- Results are correct with and without Pricing Plans active.
- Different directory types do not leak into each other.
- A newly imported/API-created listing without the meta remains visible.
Acceptance criteria
- A published listing with no
_featured row appears in normal archives and search.
- Missing featured metadata behaves as numeric zero for ordering.
- Featured listings retain featured-first ordering.
- Featured-only queries remain strict.
- All supported order modes and pagination remain correct.
- Current extensions can override/enhance ordering without conflict.
- Core write paths initialize the default meta.
- Regression coverage is added for both archive and search queries.
Isolating the problem
Directorist version
8.9.3
Environment
Summary
A published listing without an
_featuredpost-meta record can be opened through its direct URL but is excluded from normal All Listings/archive queries.A missing
_featuredvalue should mean “not featured.” Instead, the current Core query requires the meta key to exist when featured ordering is enabled.Adding
_featured = 0to the same listing makes it appear immediately.User impact
Reproduction setup
Enable Featured Listings and create two otherwise identical published listings:
_featuredstate0Both listings must have a valid directory type and any required non-expired state.
Steps to reproduce
_featured = 0to Listing B.Actual result
_featured = 0_featured0Expected result
Listings with missing
_featuredmetadata must be treated as non-featured and remain in all normal archive/search result sets.Only an explicit featured-only request should exclude them.
Root cause
There are two Core query mechanisms that can independently require the meta row.
1. Normal archive meta query
Directorist\Directorist_Listings::execute_meta_query_args()adds this clause whenever Featured Listings are enabled and the request is not explicitly filtering to featured listings:This turns featured ordering into a filtering requirement.
2. Featured sorting through
meta_keySeveral order modes assign:
and then order by
meta_value_num.This appears in title, date, random, and search ordering paths. WordPress joins the postmeta table for that key, so posts with no matching row can be excluded even if the explicit
EXISTSmeta query is removed.Therefore, removing only the
EXISTSclause may not fully fix the bug.Known affected query paths
Review at minimum:
Directorist_Listings::parse_query_args()Directorist_Listings::parse_search_query_args()Directorist_Listings::execute_meta_query_args()Why the metadata can be missing
The current admin metabox normally writes
_featured = false, but missing rows can still exist because of:wp_insert_post()usage.The read query should tolerate these records even if all current write paths are hardened.
Extension interaction
Directorist Pricing Plans 4.0.1 currently filters
directorist_query_arg_has_featuredto false on frontend requests and provides its own featured/priority sorting through aLEFT JOINandCOALESCE(..., 0).That integration can mask this Core bug while it is active. The issue reproduces in the Core query path when that provider is absent, inactive, older, or does not intercept the request.
A Core fix must avoid double joins or ordering conflicts when Pricing Plans supplies its own query strategy.
Suggested implementation direction
A complete fix should include both query hardening and data hardening.
A. Query hardening
For normal archives, do not require
_featuredto exist.Treat missing values as zero during ordering. A robust strategy is:
LEFT JOINfor_featured.COALESCE( featured_value, 0 ) DESC._featuredrow.The Pricing Plans provider already demonstrates a compatible left-join/COALESCE approach, but Core should implement its own narrowly scoped strategy or reusable query helper without coupling to the extension.
A meta-query
EXISTS OR NOT EXISTSsolution may keep both record types in the result, but it must be validated carefully across named-clause ordering and all supported sort modes.B. Featured-only behavior
Explicit featured-only requests should remain strict:
Missing or zero values must not be returned by featured-only queries.
C. Write-path hardening
Audit all Core listing creation/update paths and initialize:
when the listing is not featured.
Include admin, frontend submission, REST, import, migration, renewal, duplicate/clone, and any background creation paths.
D. Optional migration
Consider a versioned, batch-safe migration that backfills
_featured = 0for existing listings missing the key.The read-query fix is still required because custom integrations can create incomplete metadata after a migration. Any migration must avoid one unbounded update on large directories.
Compatibility and regression risks
_featured = 1.found_postsaccurate.directorist_query_arg_has_featuredremain correct.Test matrix
Use at least three listings in the same directory:
10For every normal archive/search query:
Test the following sorts:
Also verify:
1listing.Acceptance criteria
_featuredrow appears in normal archives and search.Isolating the problem