Installation
composer require api-platform/elasticsearch
Ensure your elasticsearch PHP client is installed (ext-elasticsearch or elasticsearch/elasticsearch).
Configure Elasticsearch Connection
Add to .env:
ELASTICSEARCH_DSN=elasticsearch://localhost:9200
Or configure via config/services.php:
'elasticsearch' => [
'dsn' => env('ELASTICSEARCH_DSN', 'elasticsearch://localhost:9200'),
],
Enable Elasticsearch for a Resource
Annotate your entity with @ApiResource and add searchable: true:
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\GetCollection;
#[ApiResource(
operations: [new GetCollection()],
searchable: true,
)]
class Book {}
First Search Request Trigger a search via API:
curl -X GET "http://your-api/books?search=query"
The package automatically indexes and searches your data.
Automatic Indexing
?search=....Customizing Search Behavior
Override the default search behavior via a custom Search operation:
use ApiPlatform\Metadata\Operation;
use ApiPlatform\Metadata\GetCollection;
#[ApiResource(
operations: [
new GetCollection(
uriTemplate: '/books/search',
method: 'GET',
name: 'search_books',
// Customize Elasticsearch query via `processor`
processor: MyCustomSearchProcessor::class,
),
],
)]
class Book {}
Pagination and Sorting Leverage Elasticsearch’s capabilities:
# Pagination
curl "http://your-api/books?search=query&page=2&limit=10"
# Sorting
curl "http://your-api/books?search=query&[sort]=title,asc"
Filtering with Elasticsearch
Use Elasticsearch’s query DSL via ?search[query]=...:
curl "http://your-api/books?search[query]={\"bool\":{\"must\":[{\"match\":{\"title\":\"Laravel\"}}]}}"
Integration with API Platform Filters
Combine with API Platform’s built-in filters (e.g., SearchFilter, DateFilter):
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
#[ApiResource(
operations: [new GetCollection()],
searchable: true,
filters: [new SearchFilter()],
)]
class Book {}
Index Naming Conflicts
App\Entity\Book).curl -X DELETE "http://localhost:9200/App_Entity_Book"
Mapping Inconsistencies
curl -X POST "http://your-api/books/_reindex"
Elasticsearch\Client::putMapping().Performance with Large Datasets
curl -X GET "http://localhost:9200/_cluster/health"
CORS and Authentication
9200) is not secure. Use elasticsearch:9200 (TLS) in production.ELASTICSEARCH_DSN with the proxy URL (e.g., http://proxy:9200).Enable Elasticsearch Logging
Add to config/logging.php:
'channels' => [
'elasticsearch' => [
'driver' => 'monolog',
'handler' => 'stream',
'path' => storage_path('logs/elasticsearch.log'),
'level' => 'debug',
],
],
Then log queries in your SearchProcessor:
$this->logger->debug('Elasticsearch query:', ['query' => $query]);
Inspect Index Mappings Dump mappings for debugging:
curl -X GET "http://localhost:9200/App_Entity_Book/_mapping"
Test Locally with Docker
Use docker.elastic.co/elasticsearch/elasticsearch:8.5.0 for local development:
# docker-compose.yml
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.5.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
Custom Search Processors
Extend ApiPlatform\Elasticsearch\Processor\SearchProcessor to modify queries:
use ApiPlatform\Elasticsearch\Processor\SearchProcessorInterface;
class CustomSearchProcessor implements SearchProcessorInterface {
public function process($data, Operation $operation, array $uriVariables, array $context): array {
$query = $data['query'] ?? [];
$query['bool']['must'][] = ['term' => ['active' => true]]; // Add custom filter
return $data;
}
}
Async Indexing Use Laravel Queues to defer indexing:
use ApiPlatform\Elasticsearch\Indexer\IndexerInterface;
class AsyncIndexer implements IndexerInterface {
public function index($data, Operation $operation, array $context) {
IndexElasticsearchJob::dispatch($data, $operation, $context);
}
}
Multi-Tenancy Override index naming logic to include tenant IDs:
use ApiPlatform\Elasticsearch\Indexer\IndexerInterface;
class TenantAwareIndexer implements IndexerInterface {
public function getIndexName(string $resourceClass, array $context): string {
$tenantId = $context['tenant_id'] ?? 'default';
return sprintf('%s_%s', $tenantId, $resourceClass);
}
}
How can I help you explore Laravel packages today?