---
title: "Public Search API"
manual: "ke_search"
version: "main"
source: "PublicSearchApi/Index.rst"
rendered: "2026-09-25T08:10:48+00:00"
---

# Public Search API {#publicsearchapi}

Since ke_search 7.1, third-party extensions can execute searches
programmatically via a public API. The same search logic as the frontend
plugins is used (search phrase, filters, hooks), so results stay consistent.

## Overview {#overview}

-   **SearchService** – Entry point. Inject
    `\Tpwd\KeSearch\Service\SearchService`.
    Use `search(SearchRequest $request): SearchResult` for paginated full
    results, or `searchUids(SearchRequest $request): array` for all matching
    UIDs only (no pagination).
-   **SearchRequest** – Value object for search parameters (search word,
    starting points, filters, sort, limit, etc.).
-   **SearchResult** – Value object with `getResults()` (raw index rows)
    and `getTotalCount()`.

## Basic usage {#basic-usage}

Inject the service and run a search with custom parameters:

```php
use Tpwd\KeSearch\Domain\Search\SearchRequest;
use Tpwd\KeSearch\Service\SearchService;

class MyController
{
    public function __construct(
        private readonly SearchService $searchService
    ) {}

    public function myAction(): void
    {
        $request = SearchRequest::fromArray([
            'searchWord' => 'my search term',
            'startingPoints' => '1,2,3',   // comma-separated page UIDs
            'limit' => 20,
            'filter' => [123 => ['tag1', 'tag2']],
            // optional: filter UID => selected option tags
        ]);
        $result = $this->searchService->search($request);

        $rows = $result->getResults();
        // array of tx_kesearch_index rows
        $total = $result->getTotalCount();
    }
}
```

## Advanced usage (same search, empty phrase) {#advanced-usage-same-search-empty-phrase}

This feature can be useful e.g. to run the **same** search as the current
plugin (same filters, starting points, configuration) but **without** a
search phrase, e.g. to use the result set for further processing.

Use this in a hook (e.g. [modifyResultList](../Hooks/Index.html#hooks)) where you have
access to the plugin instance (`$pObj` or `$this` in a hook class
that receives the plugin):

```php
use Tpwd\KeSearch\Domain\Search\SearchRequest;
use Tpwd\KeSearch\Service\SearchService;

// Inside a hook (e.g. modifyResultList) with access to the plugin $pObj:
$customRequest = SearchRequest::fromContext($pObj)->withEmptySearchPhrase();
$customResult = $this->searchService->search($customRequest);

$indexRows = $customResult->getResults();
// Use the rows for further processing
$total = $customResult->getTotalCount();
```

`SearchRequest::fromContext($context)` copies the current plugin's
configuration (FlexForm/conf), starting points, selected filters, sort order,
and results per page. `withEmptySearchPhrase()` returns a new request
with the same parameters but an empty search word.

## All result UIDs only (efficient, no pagination) {#all-result-uids-only-efficient-no-pagination}

You may need **all** matching index records (not just the first page) but only
the **UIDs** to load full records or content elsewhere. Use
`searchUids()` to run the same search without pagination and get a list
of UIDs (no full row data, efficient):

```php
// Inside a hook with access to the plugin $pObj:
$customRequest = SearchRequest::fromContext($pObj)->withEmptySearchPhrase();
$uids = $this->searchService->searchUids($customRequest);
// list<int> of tx_kesearch_index UIDs

// Use $uids to load full records, build custom lists, etc.
foreach ($uids as $uid) {
    // ...
}
```

`searchUids(SearchRequest $request): array` runs the same WHERE/ORDER as
the normal search but returns only UIDs and applies no limit, so you get every
matching index record's UID.

## SearchRequest methods {#searchrequest-methods}

-   `SearchRequest::fromArray(array $data): self` – Build from an array
    (e.g. `searchWord`, `startingPoints`, `filter`, `page`,
    `sortByField`, `sortByDir`, `limit`, `conf`).
-   `SearchRequest::fromContext(SearchContextInterface $context): self` –
    Build from the current plugin/context (e.g. in a hook). Copies conf,
    filters, sort, limit.
-   `withEmptySearchPhrase(): self` – Same parameters as the current
    request but with `searchWord` set to empty.

## SearchResult methods {#searchresult-methods}

-   `getResults(): array` – Raw index rows (same structure as
    `tx_kesearch_index`).
-   `getTotalCount(): int` – Total number of hits.
-   `getTagsFromSearchResult(): ?array` – Tag counts for faceting, if
    requested (optional).

## SearchService::searchUids() {#searchservice-searchuids}

-   `searchUids(SearchRequest $request): array` – Runs the search with the
    same filters/context but **no pagination** and returns only UIDs:
    `list<int>` of `tx_kesearch_index` UIDs. Use when you need all
    matching IDs without full row data.

## Notes {#notes}

-   The API uses the same Db, Filters, and Searchphrase logic as the frontend.
    Hooks such as [getQueryParts](../Hooks/Index.html#hooks) and
    [modifySearchWords](../Hooks/Index.html#hooks) are applied when running a search via the
    API.
-   When building a request with `fromContext()`, the current
    `ServerRequestInterface` is copied so that language overlay and similar
    request-dependent behaviour work as in the frontend.
