# Itinerator Search Integration

This feature brings Itinerator content (listings, itineraries, regions, events, challenges, DMOs, content pages, tasks, and form blocks) into the default WordPress search experience without requiring custom templates per site.

## Request Flow

1. `pre_get_posts` (see `src/config/search.php`) captures the main frontend search query.
2. It calls `query_itin_api('page_search?search={term}')` once per request and builds synthetic `WP_Post` objects for every remote record.
3. Landing/template pages registered with `itinerator_page_type` are excluded from native results to prevent duplicate URLs.
4. `posts_results` merges native + synthetic posts, sorts by `post_date DESC`, slices the merged list manually, and updates `found_posts`, `max_num_pages`, and `post_count` so pagination stays accurate.
5. `post_link`/`page_link` overrides detect negative IDs (synthetic posts) and route permalinks back to the API record (`itinerator_url`).

## Page Mapping & Content Filters

- `get_itin_pages()` supplies all Itinerator Template Pages (pages that have `itinerator_page_type` but no `itinerator_page_slug`).
- Each page contributes metadata used to decide which remote items belong to it:
  - `itinerator_sitemap_listing_type`: comma-separated listing types; only enforced when the page type is `listing`.
  - `itinerator_sitemap_tag_filters`: JSON array of `{ category, id }` filters created in the page editor UI (`Content Filters` panel).
- **Important**: These same content filters are shared between sitemap generation and search integration. Configuring filters in the page editor affects both systems automatically.
- Remote items must satisfy **all** configured filter groups (listing type AND tag filters). Within a filter group, OR logic applies (match any selected type or tag).
- When multiple pages of the same `itinerator_page_type` match, the **first matching page wins** (based on the order returned by `get_itin_pages()`). If no filters match, the first page for that type becomes the fallback.

For more details on content filters, see the [Content Filters user documentation](../../user-guide/general/content-filters.md).

## Synthetic Post Metadata

Every synthetic search result exposes additional fields beyond the standard `WP_Post` structure:

| Property | Description |
| --- | --- |
| `itinerator_url` | Canonical URL built from the matched template page + remote slug |
| `itinerator_type` | Human-friendly badge label (listing main type if available, otherwise Title Case of the page type) |
| `hero_image` | Hero URL (falls back to empty string when not provided) |
| `itinerator_excerpt` | Trimmed description derived from `short_description`, `description`, or `subtitle` |
| `itinerator_remote_type` | Raw Itinerator entity type (`listing`, `itinerary`, etc.) |
| `itinerator_remote_id` | API ID for debugging |

Use the helper `itinerator_get_search_metadata( $post )` (or `Itinerator\Config\get_itinerator_search_metadata`) to retrieve this data safely inside themes or template overrides. The helper automatically reaches into the object cache so metadata stays consistent even after the loop mutates the post instance.

## Caching & Performance

- Synthetic posts are stored in the object cache (`wp_cache_set`) for seven days so repeated pagination requests reuse the same objects.
- `query_itin_api` already respects the global cache toggle (`Itinerator → Settings → Advanced`). When API caching is enabled, identical search queries will hit transients instead of the remote service.

## Theme Considerations

- Templates can continue to loop over `have_posts()` / `the_post()` without changes. Synthetic posts behave like normal WordPress posts except for their negative IDs.
- Display logic for badges, hero images, or excerpts should pull from `itinerator_get_search_metadata()` to stay type-agnostic.
- Because pagination is handled inside `posts_results`, themes do **not** need custom pagination logic; `paginate_links()` and `get_the_posts_pagination()` Just Work™.

## Troubleshooting Checklist

1. **No Itinerator results in search** – confirm template pages exist for each type and that API credentials are valid.
2. **Items show under the wrong template** – review the page's content filters in the editor; first matching filter wins, so overlapping filters will favor whichever page loads first.
3. **Permalinks going to WordPress 404** – ensure rewrite rules are flushed (visit Settings → Permalinks) and that each template page remains published.

