> For the complete documentation index, see [llms.txt](https://whoisandywhite.gitbook.io/porterwp/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://whoisandywhite.gitbook.io/porterwp/documentation/porter-query-filter-base.md).

# Porter\_QueryFilter\_Base

`Porter_QueryFilter_Base` is a theme-facing helper class bundled by PorterWP. Extend it when you need to customise a Query Loop or an inherited archive/search query without duplicating block-detection and query-scoping logic.

## Location

* `includes/helpers/class-Porter_QueryFilter_Base.php`
* Auto-loaded by `includes/porter-helper-functions.php`

## What it solves

* Targets a specific `core/query` block by CSS class.
* Applies changes safely to non-inherited Query Loops.
* Applies changes to the main query when the Query Loop inherits.
* Strips empty `s` values so a page does not accidentally become a search results page.
* Limits non-inherited changes to the matching Query Loop instance by `queryId`.

## Minimum implementation

```php
class Porter_Filter_UpcomingEvents extends Porter_QueryFilter_Base {
    protected string $className = 'upcoming-events';

    protected function should_apply_to_main( WP_Query $q ): bool {
        return $q->is_post_type_archive( 'event' ) || $q->is_tax( [ 'event-type', 'event-format' ] );
    }

    protected function filter_args( array $args, array $context ): array {
        $args['post_type'] = 'event';
        $args['post_status'] = [ 'publish' ];
        $args['meta_key'] = 'start_date';
        $args['orderby'] = 'meta_value_num';
        $args['order'] = 'ASC';

        if ( is_singular( 'event' ) ) {
            $args['post__not_in'] = [ get_the_ID() ];
        }

        return $args;
    }
}

new Porter_Filter_UpcomingEvents();
```

## Required and optional members

| Member                                              | Required                 | Purpose                                                                            |
| --------------------------------------------------- | ------------------------ | ---------------------------------------------------------------------------------- |
| `$className`                                        | Yes, in practice         | Matches the CSS class on the target `core/query` block.                            |
| `filter_args( array $args, array $context ): array` | Yes                      | Returns the amended query vars.                                                    |
| `should_apply_to_main( WP_Query $q ): bool`         | Only for inherited loops | Declares when the inherited main query should be modified.                         |
| `treat_post_type_change_as_real(): bool`            | No                       | Controls whether a post-type-only change counts as a meaningful main-query change. |
| `$blockName`                                        | No                       | Defaults to `core/query`. Override only if you have a strong reason.               |

## How it works

1. The constructor attaches to `pre_render_block`.
2. When a matching `core/query` block is found, the class checks whether the block `className` contains your `$className`.
3. For non-inherited loops, it captures the block `queryId` and filters `query_loop_block_query_vars`.
4. For inherited loops, it hooks `pre_get_posts` and only proceeds when `should_apply_to_main()` returns `true`.

## `filter_args()` context

`filter_args()` receives the current query args plus a context array:

| Key        | Value                                                |
| ---------- | ---------------------------------------------------- |
| `scope`    | `'block'` or `'main'`                                |
| `block`    | `WP_Block` for non-inherited loops, otherwise `null` |
| `wp_query` | `WP_Query` for inherited loops, otherwise `null`     |

This makes it easy to branch logic if the same class supports both block queries and inherited archive/search queries.

## Usage pattern

1. Add a custom class such as `upcoming-events` to the target Query Loop block.
2. Create a child class in your theme, usually inside `porter/inc/`.
3. Set `$className` to match the block class.
4. Implement `filter_args()` with the exact `WP_Query` vars you need.
5. If the Query Loop inherits from the current request, override `should_apply_to_main()`.
6. Instantiate the class once.

## Notes

* Class matching uses `strpos()`, so use a distinctive class name rather than something overly generic.
* If the Query Loop inherits the main query, nothing changes unless `should_apply_to_main()` explicitly returns `true`.
* `PORTER_QUERY_FILTER_DEBUG` enables diff logging for before/after query args. It does not turn on the internal verbose trace logger.
