Creating custom query types¶
When installed on pure Symfony, Netgen Layouts comes with no built in query types which means you can only use manual collections in your blocks.
Tip
If you install Netgen Layouts on eZ Platform, you will get a single query type that will be automatically used when switching a collection from manual to dynamic in blocks.
When you implement your own query types, you will be able to use dynamic collections, and if you implement more than one query type, you will be able to select which query type to use when switching to dynamic collection in a block.
To implement a query type, you need a bit of configuration and a PHP class that will handle custom functionalities of a query type. In the following examples, we will show creating a custom query type that will use eZ search engine to search for items by text.
Configuring a new query type¶
To register a new query type in Netgen Layouts, you will need the following configuration:
netgen_layouts:
query_types:
my_search:
name: 'My search'
This configuration specifies a new query type with my_search
identifier and
My search
human readable name.
To make the system aware of the type of the items you will be returning from the query type and to enable proper conversion of those items to the internal Netgen Layouts format, you need to register a new value type with the following config and implement proper value loaders and converters as well as Content Browser support (not needed if you’re implementing a query type for value types where support already exists in Netgen Layouts, e.g. eZ Platform content):
netgen_layouts:
value_types:
my_value_type:
name: 'My value type'
Tip
It is possible to disable support for manually selecting items, if your backend that stores your data does not support it. An obvious example would be a query that fetches a list of articles from an RSS feed.
You can disable support for manual items with the following config:
netgen_layouts:
value_types:
my_value_type:
name: 'My value type'
manual_items: false
In this case, you only need to implement a value converter for your value type and can safely ignore value loader and Content Browser support.
Creating a PHP service for a query type¶
Every query type needs a single PHP class that specifies the entire behaviour of
a query type. This class needs to implement
Netgen\Layouts\Collection\QueryType\QueryTypeHandlerInterface
interface
which specifies a number of methods for you to implement.
Let’s create a basic query type handler class:
<?php
declare(strict_types=1);
namespace AppBundle\Collection\QueryType\Handler;
use Netgen\Layouts\API\Values\Collection\Query;
use Netgen\Layouts\Collection\QueryType\QueryTypeHandlerInterface;
use Netgen\Layouts\Parameters\ParameterBuilderInterface;
final class MySearchHandler implements QueryTypeHandlerInterface
{
public function buildParameters(ParameterBuilderInterface $builder): void
{
}
public function getValues(Query $query, int $offset = 0, ?int $limit = null): iterable
{
}
public function getCount(Query $query): int
{
}
public function isContextual(Query $query): bool
{
}
}
Specifying query type parameters¶
First method we will look at is buildParameters
method. By using an object
called parameter builder and adding parameter specifications to it, this method
will specify which parameters your custom query type will have. Details on how
the parameter builder works, what parameter types exist and how to implement
custom parameter type are explained in dedicated chapter.
Let’s add a custom parameter to our query type which will serve as an input for search text:
use Netgen\Layouts\Parameters\ParameterType;
public function buildParameters(ParameterBuilderInterface $builder): void
{
$builder->add('search_text', ParameterType\TextType::class);
}
Notice that we didn’t specify the human readable label for the parameter.
That’s because it is generated automatically via translation system. To
create the correct labels for your query type parameters, you need to add one
string to nglayouts
translation catalog for every parameter in your query type
with the format query.<query_type>.<parameter_name>
where query_type
and
parameter_name
are placeholders that need to be replaced with correct values.
So, for our custom search query type, the translation file would look something like this:
query.my_search.search_text: 'Search text'
Fetching the items¶
Second method in our handler example above is called getValues
. This method
is used for fetching the items from a query.
This method needs to return the array of domain objects that will be automatically converted to block items.
Warning
Query types are invisioned to always return only those items that can be safely rendered on the frontend. In other words, items returned from query types will always be presumed by the system to be visible and available. For example, in eZ Platform case, this means that query types need to return only visible items in correct language that the current user has access to.
Tip
In case of eZ Platform, query types can return the list of eZ ContentInfo
or Location
objects.
/**
* @var \eZ\Publish\API\Repository\SearchService
*/
private $searchService;
public function __construct(SearchService $searchService)
{
$this->searchService = $searchService;
}
public function getValues(Query $query, int $offset = 0, ?int $limit = null): iterable
{
$searchResult = $this->searchService->findLocations(
$this->buildQuery($query, false, $offset, $limit)
);
return array_map(
static function (SearchHit $searchHit) {
return $searchHit->valueObject;
},
$searchResult->searchHits
);
}
private function buildQuery(Query $query, bool $buildCountQuery = false, int $offset = 0, ?int $limit = null): LocationQuery
{
$locationQuery = new LocationQuery();
$criteria = [
new Criterion\FullText($query->getParameter('search_text')->getValue()),
new Criterion\Visibility(Criterion\Visibility::VISIBLE),
];
$locationQuery->filter = new Criterion\LogicalAnd($criteria);
$locationQuery->limit = 0;
if (!$buildCountQuery) {
$locationQuery->offset = $offset;
$locationQuery->limit = $limit;
}
return $locationQuery;
}
As you can see, getValues
method simply builds a location query for eZ
search engine and returns the list of found eZ locations. Conversion to block
items is handled automatically by Netgen Layouts.
Fetching the item count¶
To retrieve the item count from the query type, we use the getCount
method:
public function getCount(Query $query): int
{
$searchResult = $this->searchService->findLocations(
$this->buildQuery($query, true)
);
return $searchResult->totalCount;
}
Contextual queries¶
A contextual query is a query which needs the current context (i.e. current page) to run. Think of a situation where you have a layout with a block which shows top 5 items from the category it is applied to. Contextual query removes the need to create five different layouts for five different categories just so you can change the parent category from which to fetch the items. Instead, in a contextual query, you will take the currently displayed category and use it as the parent, making it possible to have only one layout for all five different categories.
In order for the system to work properly with contextual queries, one method is
used, isContextual
, which signals to the system if the query is contextual
or not. Most of the time, this method will return a value of a boolean parameter
specified inside of the query which decides if a query is contextual or not, for
example:
public function isContextual(Query $query): bool { return $query->getParameter('use_current_location')->getValue() === true; }
In our case, we will simply return false
from isContextual
method:
public function isContextual(Query $query): bool
{
return false;
}
Defining the Symfony service for our handler¶
To connect the created handler with query type configuration, we need to register the handler in Symfony DIC:
services:
app.collection.query_type.handler.my_search:
class: AppBundle\Collection\QueryType\Handler\MySearchHandler
arguments:
- "@ezpublish.api.service.search"
tags:
- { name: netgen_layouts.query_type_handler, type: my_search }
This configuration is a fairly regular specification of services in Symfony,
however, to correctly recognize our PHP class as a query type handler, we need
to tag it with netgen_layouts.query_type_handler
tag and attach to it a
type
key with a value which equals to the identifier of query type we
configured at the beginning (in this case my_search
).
After this, our query type is ready for usage.