Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EZP-31107: Built-in Query Types #972

Merged
merged 4 commits into from
Mar 26, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
277 changes: 194 additions & 83 deletions docs/guide/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,50 +172,139 @@ The Query controller makes it easy to retrieve content without writing custom PH
- List of Blog posts in a Blog
- List of Images in a Gallery

### Usage example
### Built-in Query Types

This example assumes a "Blog" container that contains a set of "Blog post" items. The goal is, when viewing a Blog, to list the Blog posts it contains.
You can make use of the following built-in Query Types for quick rendering:

Four items are required:
#### Children

- a `LocationChildren` QueryType - It will generate a Query retrieving the children of a given Location id
- a View template - It will render the Blog, and list the Blog posts it contains
- a `content_view` configuration - It will instruct Platform, when viewing a Content item of type Blog, to use the Query Controller, the view template, and the `LocationChildren` QueryType. It will also map the id of the viewed Blog to the QueryType parameters, and set which Twig variable the results will be assigned to.
- [service registration of the QueryType](#registering-the-querytype-into-the-service-container)
The `Children` Query Type retrieves children of the given Location.

#### The LocationChildren QueryType
It takes `location` or `content` as parameters.

QueryTypes are described in more detail in the [next section](#querytype-objects). In short, a QueryType can build a Query object, optionally based on a set of parameters. The following example will build a Query that retrieves the sub-items of a Location:
``` yaml
params:
query:
query_type: 'Children'
parameters:
content: '@=content'
assign_results_to: items
```

``` php
<?php
namespace App\QueryType;
#### Siblings

use eZ\Publish\API\Repository\Values\Content\LocationQuery;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId;
use eZ\Publish\Core\QueryType\QueryType;
The `Siblings` Query Type retrieves Locations that have the same parent as the provided Content item or Location.

class LocationChildrenQueryType implements QueryType
{
public function getQuery(array $parameters = [])
{
return new LocationQuery([
'filter' => new ParentLocationId($parameters['parentLocationId']),
]);
}
It takes `location` or `content` as parameters.

public function getSupportedParameters()
{
return ['parentLocationId'];
}
``` yaml
params:
query:
query_type: 'Siblings'
parameters:
content: '@=content'
assign_results_to: items
```

public static function getName()
{
return 'LocationChildren';
}
}
#### Ancestors

The `Ancestors` Query Type retrieves all ancestors (direct parents and their parents) of the provided Location.

It takes `location` or `content` as parameters.

``` yaml
params:
query:
query_type: 'Ancestors'
parameters:
content: '@=content'
assign_results_to: items
```

#### RelatedToContent

The `RelatedToContent` Query Type retrieves content that is a reverse relation to the provided Content item.

It takes `content` or `field` as obligatory parameters.
`field` indicates the Relation or RelationList Field that contains the relations.

``` yaml
params:
query:
query_type: 'RelatedToContent'
parameters:
content: '@=content'
field: 'relations'
assign_results_to: items
```

#### GeoLocation

The `GeoLocation` Query Type retrieves content by distance of the location provided in a MapLocation Field.

It takes the following obligatory parameters:

- `field` - MapLocation Field identifier
- `distance` - distance to check for
- `latitude` and `longitude` - coordinates of the location to check distance to
- (optional) `operator` - operator to check value against, by default `<=`

``` yaml
params:
query:
query_type: 'GeoLocation'
parameters:
field: 'location'
distance: 200
latitude: '@=content.getFieldValue("location").latitude'
longitude: '@=content.getFieldValue("location").longitude'
operator: '<'
assign_results_to: items
```

#### General Query Type parameters

All built-in Query Types take the following optional parameters:

- `limit` - limit for number of search hits to return
DominikaK marked this conversation as resolved.
Show resolved Hide resolved
- `offset` - offset for search hits, used for paging the results
- `sort` - sorting options
- `filter` - additional query filters:
- `content_type` - return only results of given content types
DominikaK marked this conversation as resolved.
Show resolved Hide resolved
- `visible_only` - return only visible results (default `true`)
- `siteaccess_aware` - return only results limited to current SiteAccess root (default `true`)

For example:

``` yaml
params:
query:
query_type: 'Children'
parameters:
content: '@=content'
filter:
content_type: ['blog_post']
visible_only: false
limit: 5
offset: 2
sort: 'content_name asc, date_published desc'
assign_results_to: items
```

!!! note "Sorting order"

To provide sorting order to the `sort` parameter, use names of the Sort Clauses in
`\eZ\Publish\API\Repository\Values\Content\Query\SortClause`.
DominikaK marked this conversation as resolved.
Show resolved Hide resolved

#### Using built-in Query Types

This example assumes a "Blog" container that contains a set of "Blog post" items. The goal is, when viewing a Blog, to list the Blog posts it contains.

Two items are required:

- a View template - It will render the Blog, and list the Blog posts it contains
- a `content_view` configuration - It will instruct Platform, when viewing a Content item of type Blog, to use the Query Controller, the view template, and the `Children` QueryType. It will also map the id of the viewed Blog to the QueryType parameters, and set which Twig variable the results will be assigned to.
DominikaK marked this conversation as resolved.
Show resolved Hide resolved

#### The `content_view` configuration

We now need a view configuration that matches Content items of type "Blog", and uses the QueryController to fetch the blog posts:
Expand All @@ -227,24 +316,24 @@ ezplatform:
content_view:
full:
blog:
controller: ez_query:locationQueryAction
controller: ez_query:contentQueryAction
template: content/view/full/blog.html.twig
match:
Identifier\ContentType: "blog"
params:
query:
query_type: LocationChildren
query_type: Children
parameters:
parentLocationId: '@=location.id'
location: '@=location'
assign_results_to: blog_posts
```

The view's controller action is set to the QueryController's `locationQuery` action (`ez_query:locationQueryAction`). Other actions are available that run a different type of search (contentInfo or content).
The view's controller action is set to the QueryController's `contentQuery` action (`ez_query:contentQueryAction`). Other actions are available that run a different type of search (contentInfo or location).

The QueryController is configured in the `query` array, inside the `params` of the `content_view` block:

- `query_type` specifies the QueryType to use, based on its name.
- `parameters` is a hash where parameters from the QueryType are set. Arbitrary values can be used, as well as properties from the currently viewed [Location](https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/Location.php) and [ContentInfo](https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/ContentInfo.php). In that case, the id of the currently viewed Location is mapped to the QueryType's `parentLocationId` parameter: `parentLocationId: "@=location.id"`
- `parameters` is a hash where parameters from the QueryType are set. Arbitrary values can be used, as well as properties from the currently viewed [Location](https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/Location.php) and [ContentInfo](https://github.com/ezsystems/ezpublish-kernel/blob/master/eZ/Publish/API/Repository/Values/Content/ContentInfo.php). In that case, the id of the currently viewed Location is mapped to the QueryType's `location` parameter: `location: '@=location'`
DominikaK marked this conversation as resolved.
Show resolved Hide resolved
- `assign_results_to` sets which Twig variable the search results will be assigned to.

#### The view template
Expand All @@ -259,6 +348,71 @@ Results from the search are assigned to the `blog_posts` variable as a `SearchRe
{% endfor %}
```

### Creating custom Query Types

Beyond the built-in Query Types, you can create your own.

For example, this Query Type returns a Query that searches for the 10 last published Content items, ordered by reverse publishing date.
DominikaK marked this conversation as resolved.
Show resolved Hide resolved
It accepts an optional `type` parameter that can be set to a Content Type identifier:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we need an example name and where to add it.


``` php
<?php
namespace App\QueryType;

use eZ\Publish\Core\QueryType\QueryType;
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
use eZ\Publish\API\Repository\Values\Content\Query;

class LatestContentQueryType implements QueryType
{
public function getQuery(array $parameters = [])
{
$criteria[] = new Query\Criterion\Visibility(Query\Criterion\Visibility::VISIBLE);
if (isset($parameters['type'])) {
$criteria[] = new Query\Criterion\ContentTypeIdentifier($parameters['type']);
}
// 10 is the default limit we set, but you can have one defined in the parameters
return new LocationQuery([
'filter' => new Query\Criterion\LogicalAnd($criteria),
'sortClauses' => [new Query\SortClause\DatePublished()],
'limit' => isset($parameters['limit']) ? $parameters['limit'] : 10,
]);
}
public static function getName()
{
return 'LatestContent';
}
/**
* Returns an array listing the parameters supported by the QueryType.
* @return array
*/
public function getSupportedParameters()
{
return ['type', 'limit'];
}
}
```

In the configuration, use the name of the Query Types as defined in `getName`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be different files (ezplatform.yaml by default, but not necessarily), so I clarified that it's content view config.


``` yaml
content_view:
full:
latest:
controller: ez_query:locationQueryAction
template: content/view/full/latest.html.twig
match:
Identifier\ContentType: "latest"
params:
query:
query_type: LatestContent
parameters:
parentLocationId: '@=location.id'
assign_results_to: latest
```

Your Query Type must be [registered as a service](#registering-the-querytype-into-the-service-container).

### Configuration details

#### `controller`
Expand All @@ -285,11 +439,11 @@ The Query is configured in a `query` hash in `params`, you could specify the Que
- This is the name of the Twig variable that will be assigned the results.
- Note that the results are the SearchResult object returned by the SearchService.

#### QueryType objects
### Query Type objects

QueryType is an object that builds a Query. It is different from [Public API queries](../api/public_php_api.md).
Query Type is an object that builds a Query. It is different from [Public API queries](../api/public_php_api.md).

To make a new QueryType available to the Query Controller, you need to create a PHP class that implements the QueryType interface, then register it as such in the Service Container.
To make a new Query Type available to the Query Controller, you need to create a PHP class that implements the QueryType interface, then register it as such in the Service Container.

For more information about the [Service Container on its documentation page](service_container.md).

Expand Down Expand Up @@ -338,49 +492,6 @@ A QueryType may accept parameters, including string, array and other types, depe

The implementations should use Symfony's `OptionsResolver` for parameter handling and resolution.

### QueryType example: latest content

This QueryType returns a Query that searches for **the 10 last published Content items, ordered by reverse publishing date**.
It accepts an optional `type` parameter that can be set to a Content Type identifier:

``` php
<?php
namespace App\QueryType;

use eZ\Publish\Core\QueryType\QueryType;
use eZ\Publish\API\Repository\Values\Content\LocationQuery;
use eZ\Publish\API\Repository\Values\Content\Query;

class LatestContentQueryType implements QueryType
{
public function getQuery(array $parameters = [])
{
$criteria[] = new Query\Criterion\Visibility(Query\Criterion\Visibility::VISIBLE);
if (isset($parameters['type'])) {
$criteria[] = new Query\Criterion\ContentTypeIdentifier($parameters['type']);
}
// 10 is the default limit we set, but you can have one defined in the parameters
return new LocationQuery([
'filter' => new Query\Criterion\LogicalAnd($criteria),
'sortClauses' => [new Query\SortClause\DatePublished()],
'limit' => isset($parameters['limit']) ? $parameters['limit'] : 10,
]);
}
public static function getName()
{
return 'LatestContent';
}
/**
* Returns an array listing the parameters supported by the QueryType.
* @return array
*/
public function getSupportedParameters()
{
return ['type', 'limit'];
}
}
```

### Naming of QueryTypes

Each QueryType is named after what is returned by `getName()`. **Names must be unique.** A warning will be thrown during compilation if there is a conflict, and the resulting behavior will be unpredictable.
Expand Down
Loading