Skip to content

Recommendation

ezrecommendation-client adds a personalization solution to eZ Platform.

It enables you to track the way visitors use your website and recommends content based on their behavior. You can also use the personalized search (content suggestions) and personalized newsletter features (embedding personalized content in your newsletters).

More information

This page covers the ezrecommentation-client, which communicates with the Recommendation engine. To learn more about the engine itself, see eZ Personalizarion documentation.

Installation

ezrecommendation-client is provided in a separate package and is not included in eZ Platform by default.

To use it, you need to install the package:

1. Install and enable the bundle

Run composer require from your eZ Platform installation root:

1
2
composer require --no-update ezsystems/ezrecommendation-client
composer update --prefer-dist

Next, enable the bundle in app/AppKernel.php:

1
2
3
4
$bundles = [
    // existing bundles
    new EzSystems\EzRecommendationClientBundle\EzRecommendationClientBundle()
];

2. Import additional routing

Import additional routing by adding the following lines to your routing.yml file:

1
2
3
recommendationBundleRestRoutes:
    resource: '@EzRecommendationClientBundle/Resources/config/routing_rest.yml'
    prefix: %ezpublish_rest.path_prefix%

3. Register an eZ Personalization account

Register an account (customerID) with your eZ Sales manager.

Tip

If you want to use the Recommendation engine with the open source version of eZ Platform, send an email to support@yoochoose.com.

4. Allow public HTTP(S) access

Allow public HTTP(S) access to the recommendation bundle API (<yourdomain>/api/ezp/v2/ez_recommendation/**)

IP whitelisting

The Recommendation engine servers need to access the API of an eZ Platform installation in order to continuously sync content. If it's not possible to allow public access, the following IP addresses can be used for whitelisting on, for example, a firewall.

54.229.102.177, 54.171.192.161, 54.77.201.13, 52.215.22.234, 52.18.150.96, 52.17.60.35, 52.17.36.104

If BASIC AUTH is required by policy

If your organization's policy is to use BASIC AUTH on the API interfaces, you need to add specific configuration.

You can define access restrictions on your site in app/config/security.yml.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
security:
    providers:
        ezpublish:
            id: ezpublish.security.user_provider
    firewalls:
        ezpublish_rest:
            pattern: ^/api/ezp/v2
            stateless: true
            ezpublish_http_basic:
                realm: eZ Publish REST API

Create a user with the name of the customerID and a password which is the license key in your local security provider. This user must have access granted on the URLs provided by the bundle API (see above). In order to tell the recommender to use this user and password to request resources on the eZ Platform instance, you can configure this as follows (an example file is available in the bundle under Resources/config/default_settings.yml):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ezrecommendation:
    system:
        <siteaccess>:
            export:
                authentication:
                    # Export folder authentication method:
                    # basic - password generated by script
                    # user - login and passwords set by user
                    # none - no authentication !Warning - allow users to download content without permission
                    method: basic
                    login: 12345
                    password: 1234-5678-9012-3456-7890
                document_root: '%kernel.root_dir%/../web/var/export/'

With the user authentication method, login must be the same as customer_id and password must be the same as license_key.

Place this in a settings file which won't be affected by an update to the bundle.

Configuration

Set up Content Type tracking

Visitor events (clicks, buys, etc.) on the site need to be sent to the Recommendation engine for the recommendations to be calculated. The Content Types that are tracked are also exported to the engine. Tracking Content Types is required for displaying recommendations.

You define Content Types to track in the local app/config/config.yml file. The content will then be initially exported by a script. After this, it will be kept in sync with the Personalization Solution every time a change occurs in the eZ Platform Back Office.

The bundle's configuration is SiteAccess-aware. This is an example of the settings (in config.yml):

1
2
3
4
5
6
7
8
9
ezrecommendation:
    system:
        <siteaccess>:
            authentication:
                customer_id: 12345
                license_key: 1234-5678-9012-3456-7890
            included_content_types: [blog, article]
            random_content_types: [blog]
            host_uri: http://example.com

Tip

It is recommended to provide your host_uri, customer_id and license_key through environment variables: '%env()%'.

Parameter Description
authentication.customer_id Your customer ID.
authentication.license_key Your license key.
host_uri The URI your site's REST API can be accessed from.
included_content_types Content Types on which the tracking script will be shown.
random_content_types Content Types which will be returned when the response from the Recommendation engine contains co content.

Advanced configuration

If the Content item's intro, author or image are stored in a different Field, you can specify its name in the parameters.yml file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
ezrecommendation:
    system:
        field:
            identifiers:
                intro:
                    blog_post: intro
                    article: lead
                author:
                    blog_post: author
                    article: authors
                image:
                    <content_type_name>: <field_name>

In case a content owner ID is missing, you can set up the default content author in the default_settings.yml file:

1
2
3
4
ezrecommendation:
    system:
        <siteaccess>:
            author_id: 14   # ID: 14 is default ID of admin user

You can edit advanced options for the Recommendation engine using the following settings:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ezrecommendation:
    system:
        api:
            admin:
                endpoint: 'https://admin.yoochoose.net'
            recommendation:
                endpoint: 'https://reco.yoochoose.net'
                consume_timeout: 20
            event_tracking:
                endpoint: 'https://event.yoochoose.net'
                script_url: 'cdn.yoochoose.net/yct.js'
            notifier:
                endpoint: 'https://admin.yoochoose.net'

Caution

Changing any of these parameters without a valid reason will break all calls to the Recommendation engine.

Enable tracking

The EzRecommendationClientBundle delivers a Twig extension which helps integrate the user tracking functionality into your site. Place the following code snippet in the <head> section of your header template:

1
2
3
{% if content is defined %}
    {{ ez_recommendation_track_user(content.id) }}
{% endif %}

How tracking works

In the eZ Personalization documentation you can find more information about tracking in general and about the generic asynchronous JavaScript tracker.

Checking if the bundle provides REST data

You can verify the import controller of the bundle by calling the local API. You should use the Accept header and may need to add an Authorization header if authentication is required.

To check if the content endpoint is working as expected, perform the following request:

1
2
3
GET http://<yourdomain>/api/ezp/v2/ez_recommendation/v1/content/{contentId}
Accept application/vnd.ez.api.Content+json
Authorization Basic xxxxxxxx

Additionally you should check if the contenttypes endpoint is working with the following request:

1
2
3
GET http://<yourdomain>/api/ezp/v2/ez_recommendation/v1/contenttypes/38?page=1&page_size=10
Accept application/vnd.ez.api.Content+json
Authorization Basic xxxxxxxx

The content endpoint returns one Content item and the contenttypes endpoint returns many.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
    "contentList": {
        "_media-type": "application/vnd.ez.api.contentList+json",
        "content": [
            {
                "_media-type": "application/vnd.ez.api.content+json",
                "contentId": 72,
                "contentTypeId": 38,
                "identifier": "place",
                "language": "eng-GB",
                "publishedDate": "2015-09-17T13:23:10+00:00",
                "author": "Sandip Patel",
                "uri": "/Places-Tastes/Places/Kochin-India",
                "categoryPath": "/1/2/95/71/73/",
                "mainLocation": {
                    "_media-type": "application/vnd.ez.api.mainLocation+json",
                    "_href": "/api/ezp/v2/content/locations/1/2/95/71/73/"
                },
                "locations": {
                    "_media-type": "application/vnd.ez.api.locations+json",
                    "_href": "/api/ezp/v2/content/objects/72/locations"
                },
                "name": "Kochin, India",
                "intro": "<![CDATA[<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5\"><p>We got the major port city on the south west coast of India.</p></section>\n]]>",
                "description": "<![CDATA[<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5\"><p><strong>Kochi </strong>(formerly Cochin) ... </p></section>\n]]>",
                "image": "/var/site/storage/images/places-tastes/places/kochin-india/282-5-eng-GB/Kochin-India.jpg",
                "caption": "<![CDATA[<section xmlns=\"http://ez.no/namespaces/ezpublish5/xhtml5\"><p>Chinese fishing nets ... </p></section>\n]]>",
                "location": "kochin, india",
                "authors_position": "Senior Editor",
                "tags": "India, Kochin",
                "rating": "0",
                "publication_date": "1442500260",
                "metas": ""
            }
        ],
        ....
    }
}

Exporting content information

To get recommendations you must first export the content information to the Recommendation engine.

After defining which Content Types should be tracked and recommended, start the full export. You can do it with the ezrecommendation:export:run command or by accessing the http://<yourdomain>/api/ezp/v2/ez_recommendation/ URL/endpoint.

1
2
3
4
5
php bin/console ezrecommendation:export:run
    --contentTypeIdList=<contentTypeId>,<contentTypeId>
    --webHook=https://admin.yoochoose.net/api/<your_customer_id>/items
    --hidden=1 --mandatorId=<your_customer_id>
    --host=<your_ezplatform_host_with_scheme>
1
http://<yourdomain>/api/ezp/v2/ez_recommendation/v1/runExport/<contentTypeIdList>?webHook=<webhook>&customerId=<customerId>&licenseKey=<licenseKey>&host=<host>

The bundle exporter collects all content related to the SiteAccesses of this customerID and stores it in files (1). After finishing, the systems sends a POST request to the webHook endpoint and informs the Recommendation engine to fetch new content (2). An internal workflow is then triggered (3) so that the generated files are downloaded (4) and imported in the Recommendation engine's content store (5).

The export process can take a few minutes.

Recommendation Full Content Export

Re-exporting modified Content Types

If the Content Types to be recommended are changed, you need to perform a new full export by running the php bin/console ezrecommendation:export:run command again.

Checking export results

There are three ways to check if content was transferred and stored successfully in the Recommendation engine:

REST request to the recommender's content store

To get the content of an imported item you can request the following REST resource:

GET https://admin.yoochoose.net/api/<your_customer_id>/item/<your_content_type_id>/<your_content_id>

This way requires BASIC Auth. BASIC Auth username is the customerID and the password is the license key.

Example response
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<items version="1">
    <item id="73" type="38">
        <imageurls>
            <imageurl type="preview">/var/site/storage/images/places-tastes/places/santo-domingo-dominican-republic/288-4-eng-GB/Santo-Domingo-Dominican-Republic.jpg</imageurl>
        </imageurls>
        <deeplinkurl>/Places-Tastes/Places/Santo-Domingo-Dominican-Republic</deeplinkurl>
        <validfrom>2015-09-17T13:24:25.000</validfrom>
        <categoryids/>
        <categorypaths>
            <categorypath>1/2/95/71/74</categorypath>
        </categorypaths>
        <content/>
        <attributes>
            <attribute value="/var/site/storage/images/places-tastes/places/santo-domingo-dominican-republic/288-4-eng-GB/Santo-Domingo-Dominican-Republic.jpg" key="image" type="TEXT"/>
            <attribute value="place" key="identifier" type="TEXT"/>
            <attribute value="fre-FR" key="language" type="TEXT"/>
            <attribute value="Senior Editor" key="authors_position" type="TEXT"/>
            <attribute value="Michael Wang" key="author" type="TEXT"/>
            <attribute value="/1/2/95/71/74/" key="categoryPath" type="TEXT"/>
            <attribute value="Michael Wang" key="author" type="NOMINAL"/>
            <attribute value="0" key="rating" type="TEXT"/>
            <attribute value="&lt;![CDATA[&lt;section xmlns=&quot;http://ez.no/namespaces/ezpublish5/xhtml5&quot;&gt;&lt;p&gt;Outstanding beaches of Dominican Republic, Samana is one of them.&lt;/p&gt;&lt;p&gt;&lt;em&gt;Photograph by Brian Henry - Anchorage north shore Samana, Dominican Republic&lt;/em&gt;&lt;/p&gt;&lt;/section&gt;&#xA;]]&gt;" key="caption" type="TEXT"/>
            <attribute value="/Places-Tastes/Places/Santo-Domingo-Dominican-Republic" key="uri" type="TEXT"/>
            <attribute value="38" key="contentTypeId" type="TEXT"/>
            <attribute value="Dominican Republic, Santo Domingo" key="tags" type="TEXT"/>
            <attribute value="&lt;![CDATA[&lt;section xmlns=&quot;http://ez.no/namespaces/ezpublish5/xhtml5&quot;&gt;&lt;p&gt;Santo Domingo meaning &quot;Saint Dominic&quot;, officially Santo Domingo de Guzm&amp;aacute;n, is the capital and largest city in the ... &lt;/p&gt;&lt;/section&gt;&#xA;]]&gt;" key="description" type="TEXT"/>
            <attribute value="73" key="contentId" type="TEXT"/>
            <attribute value="&lt;![CDATA[&lt;section xmlns=&quot;http://ez.no/namespaces/ezpublish5/xhtml5&quot;&gt;&lt;p&gt;The oldest European inhabited settlement in the Americas.&lt;/p&gt;&lt;/section&gt;&#xA;]]&gt;" key="intro" type="TEXT"/>
            <attribute value="1442500260" key="publication_date" type="TEXT"/>
            <attribute value="Santo Domingo, Dominican Republic" key="name" type="TEXT"/>
            <attribute value="Santo Domingo, Dominican Republic" key="location" type="TEXT"/>
            <attribute value="2015-09-17T13:24:25+00:00" key="publishedDate" type="TEXT"/>
        </attributes>
    </item>
</items>

Recommender Backend

You can log in to admin.yoochoose.net, switch to the Item Import tab and check if a FULL import was successful.

Item Import tab with full import results

Personalized Search Requests

Since the search functionality is included by default, you can also create and perform a search request to look for content that matches certain criteria. The easiest way to do this is to assign the content ID to the request parameter q. Make sure that the content ID is at least 2 characters long (for example, &q=73).

GET https://reco.yoochoose.net/api/v4/search/<your_customer_id>/get_suggestions.json?item=5&itemtype=<your_content_type>&q=<your_content_id>&attribute=name&attribute=author&attribute=uri&attribute=<your_custom_attribute>

Example response
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
    "ITEM": [
        {
            "yc_score": 2.209763288497925,
            "yc_id": "72",
            "yc_itemtype": "38",
            "name": "Kochin, India",
            "uri": "/Places-Tastes/Places/Kochin-India",
            "author": "Sandip Patel"
        },
        {
            "yc_score": 1.152622938156128,
            "yc_id": "73",
            "yc_itemtype": "38",
            "name": "Santo Domingo, Dominican Republic",
            "uri": "/Places-Tastes/Places/Santo-Domingo-Dominican-Republic",
            "author": "Michael Wang"
        }
    ],
    "details": [
        {
            "scope": "ITEM",
            "itemType": 38,
            "count": 2
        }
    ]
}

Subsequent content exports

The Recommendation engine is automatically kept in sync with the content in eZ Platform.

Every time an editor creates, updates or deletes content in the Back Office (1), a notification is sent to https://admin.yoochoose.net (2). The recommendation service also notifies other components of the Recommendation engine (3) and it eventually fetches the affected content (4) and updates it internally (5).

Subsequent Content exports

Displaying recommendations

Client-based recommendations

Recommendations are fetched and rendered asynchronously in the client, so there is no additional load on the server. Therefore it is crucial to check if the content export has been successful, as e.g. deeplinks and image references are included. If the export is NOT successful, the Recommendation engine will not have the full content information. This will break the recommendations. Even if the recommendations are displayed, there is a big chance they won't have images, titles or deeplinks.

In order to display recommendations on your site, you need to add the following JavaScript assets to your header template:

1
2
3
{% javascripts
    '@EzRecommendationClientBundle/Resources/public/js/EzRecommendationClient.js'
%}

This file is responsible for sending notifications to recommendation API after the user clicks on a tracking element.

To render recommended content, use a dedicated showRecommendationsAction from the RecommendationController.php:

1
2
3
4
5
6
7
8
render_esi(controller(‘EzRecommendationClientBundle:Recommendation:showRecommendations’, {
               ‘contextItems’: content.id,
               ‘scenario’: ‘front’,
               ‘outputTypeId’: ‘blog_post’,
               ‘limit’: 3,
               ‘template’: ‘EzRecommendationClientBundle::recommendations.html.twig’,
               ‘attributes’: [‘title’, ‘intro’, ‘image’, ‘uri’]
           }))

Tip

To check if tracking is enabled on the front end, use the ez_recommendation_enabled() Twig function. You can wrap the call to the RecommendationController with:

1
2
3
4
5
{% if ez_recommendation_enabled() %}
    <div class="container">
        {# ... #}
    </div>
{% endif %}

Parameters

Parameter Type Description
contextItems int ID of the content you want to get recommendations for.
scenario string Scenario used to display recommendations. You can create custom scenarios in the Recommendation engine's dashboard.
outputTypeId string Content Type you are expecting in response, e.g. blog_post.
limit int Number of recommendations to fetch.
template string Template name.
attributes array Fields which are required and will be requested from the Recommendation engine. These Field names are also used inside Handlebars templates.

You can also bypass named arguments using standard value passing as arguments.

Custom templates

If you want to use a custom template for displaying recommendations, it must include event_tracking.html.twig:

{% include 'EzRecommendationClientBundle::event_tracking.html.twig' %}.

Recommendation responses contain all content data which is requested as attribute in the recommendation call. This response data can be used in templates to render and style recommendations.

For example, the following GET request should deliver the response below if the content Fields were previously exported by the export script.

GET https://reco.yoochoose.net/api/v2/<your_customer_id>/someuser/popular.json?contextitems=71&numrecs=5&categorypath=/&outputtypeid=<your_content_type>&attribute=name,author,uri,image

Example response
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
{
    "contextItems": [
        {
            "itemId": 71,
            "itemType": 38,
            "sources": [
                "REQUEST"
            ],
            "viewers": 0
        }
    ],
    "recommendationItems": [
        {
            "itemId": 71,
            "itemType": 38,
            "relevance": 3,
            "links": {
                "clickRecommended": "//event.test.yoochoose.net/api/723/clickrecommended/someuser/38/71?scenario=popular&modelid=4199&categorypath=&requestuuid=d75e7cf0-e4ca-11e7-a94d-0a64dbbea736",
                "rendered": "//event.test.yoochoose.net/api/723/rendered/someuser/38/71?scenario=popular&modelid=4199&categorypath=&requestuuid=d75e7cf0-e4ca-11e7-a94d-0a64dbbea736"
            },
            "attributes": [
                {
                    "key": "image",
                    "values": [
                        "/var/site/storage/images/places-tastes/places/valencia-spain/276-4-eng-GB/Valencia-Spain.jpg"
                    ]
                },
                {
                    "key": "author",
                    "values": [
                        "Tara Fitzgerald"
                    ]
                },
                {
                    "key": "name",
                    "values": [
                        "Valencia, Spain"
                    ]
                },
                {
                    "key": "uri",
                    "values": [
                        "/Places-Tastes/Places/Valencia-Spain"
                    ]
                }
            ]
        },
        {
            "itemId": 75,
            "itemType": 38,
            "relevance": 1,
            "links": {
                "clickRecommended": "//event.test.yoochoose.net/api/723/clickrecommended/someuser/38/75?scenario=popular&modelid=4199&categorypath=&requestuuid=d75e7cf0-e4ca-11e7-a94d-0a64dbbea736",
                "rendered": "//event.test.yoochoose.net/api/723/rendered/someuser/38/75?scenario=popular&modelid=4199&categorypath=&requestuuid=d75e7cf0-e4ca-11e7-a94d-0a64dbbea736"
            },
            "attributes": [
                {
                    "key": "image",
                    "values": [
                        "/var/site/storage/images/places-tastes/places/brooklyn-new-york/300-4-eng-GB/Brooklyn-New-York.jpg"
                    ]
                },
                {
                    "key": "author",
                    "values": [
                        "Elizabeth Liu"
                    ]
                },
                {
                    "key": "name",
                    "values": [
                        "Brooklyn, New York"
                    ]
                },
                {
                    "key": "uri",
                    "values": [
                        "/Places-Tastes/Places/Brooklyn-New-York"
                    ]
                }
            ]
        }
    ]
}

Image variations

Displaying image variations is not currently supported out of the box.

You can work around this limitation by creating your own template (based on recommendations.html.twig).

If you want to access a specific image variation through API, you need to add the image parameter to the request URL with the name of the variation as its value. For example, to retrieve the rss variation of the image, use:

/api/ezp/v2/ez_recommendation/v1/contenttypes/16?lang=eng-GB&fields=title,description,image,intro,name&page=1&page_size=20&image=rss

Troubleshooting

Logging

Most operations are logged via the ez_recommendation Monolog channel. To log everything about Recommendation to dev.recommendation.log, add the following to your config.yml:

1
2
3
4
5
6
7
monolog:
    handlers:
        ez_recommendation:
            type: stream
            path: '%kernel.logs_dir%/%kernel.environment%.recommendation.log'
            channels: [ez_recommendation]
            level: info

You can replace info with debug for more verbosity.

Read the Docs