> 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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://whoisandywhite.gitbook.io/porterwp/documentation/porter-query-filter-base.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
