Use Elasticsearch with Symfony Framework and elasticsearch-php library

Elastic provides elasticsearch-php to connect to Elasticsearch cluster. This Library is an API client with all possible options. It's light weight and lets developer chose implementation to use it. It's a very good alternative to Elastica and his abstraction layer. On this blog I'm using elasticseach-php with Twig to manage my indices and query Elasticsearch.

Setup

$ composer require elasticsearch/elasticsearch

There is a lot of option to configure the lib, official documentation is very well done. For this blog, I only have one node in my cluster so my setup is very simple.

<?php 
use Elasticsearch\ClientBuilder;
 
$client = EClientBuilder::create()
    ->setHosts(['127.0.0.1:9200'])
    ->build();

M6Web provides symfony bundle to allow you write the configuration in YAML and retrieves Client directly from the Symfony container. The bundle also comes with datacollector so you will get all your Elasticsearch queries in the Symfony debug Toolbar.

Using Twig

All queries need index name, sometimes type name and a query body. In the documentation, all exemples use PHP array in query body.

$response = $client->search([
    'index' => 'my_index',
    'type' => 'my_type',
    'body' => [
        'query' => [
            'match' => [
                'testField' => 'abc'
            ]
        ]
    ]
];

We can also use raw json on query body instead of PHP array.

$response = $client->search([
    'index' => 'my_index',
    'type' => 'my_type',
    'body' => '{"query": {"match": [{"testField": "abc"}]}}
];

I prefer the second option, I found json simpler to read and I can copy/paste it to debug my query in Kibana. It's also an effective way to learn Elasticsearch query DSL.

But getting dynamic json is not simple with PHP. Thanks to Twig, it's very easy to deal with complex and parameterized json.

{
    "_source": {
        "excludes": ["body", "cat_tag"]
    },
    "from": {{ from }},
    "size": {{ size }},
    "query": {
{% if catId != 0 or tagId != 0 %}
        "nested": {
            "path": "{% if catId != 0 %}category{% else %}tags{% endif %}",
            "query": {
                "term": {
                    "{% if catId != 0 %}category{% else %}tags{% endif %}.id": {% if catId != 0 %}{{ catId }}{% else %}{{ tagId }}{% endif %}
                }
            }
        }
{% else %}
        "term": {
            "locale": {
                "value": "{{ locale}}"
            }
        }
{% endif %}
    },
    "sort": [
        {
            "published_at": {
                "order": "desc"
            }
        }
    ]
}

To use this template, I just need to inject Twig in my class.

<?php
declare(strict_types=1);
 
namespace Blog\Search\Request;
 
use Elasticsearch\Client
use Twig\Environment;
 
class Search
{
    /**
     * @var Environment
     */
    private $templating;
 
    /**
     * @var Client
     */
    private $client;
 
    public function __construct(Environment $templating, Client $client)
    {
        $this->templating = $templating;
        $this->client = $client;
    }
 
    public function search(array $tplParams = []): array
    {
        return $this->client->search(
            [
                'index' => 'blog',
                'type' => 'post',
                'body' => $this->templating->render('es/search.json.twig', $tplParams)
           ]
        );
    }
}

I'm using the same way to manage my indices. I'm using PHP array only when indexing new post because I got some trouble with double quote escaping in HTML document. For Indices and indexing I wrote some Symfony commands.

Add a comment