We will start by going through the various ways to find and retrieve content from eZ Publish using the API. While this will be covered in further dedicated documentation, it is necessary to explain a few basic concepts of the Public API. In the following recipes, you will learn about the general principles of the API as they are introduced in individual recipes.
Displaying values from a Content
In this recipe, we will see how to fetch a Content instance from the repository, and obtain its Field's content.
Let's first see the full code. You can see the Command line version at https://github.com/ezsystems/CookbookBundle/blob/master/Command/ViewContentCommand.php.
Let's analyze this code block by block.
This is the initialization part. As explained above, everything in the Public API goes through the repository via dedicated services. We get the repository from the service container, using the method
get() of our container, obtained via
$this->getContainer(). Using our $repository variable, we fetch the two services we will need using
Everything starting from line 5 is about getting our Content and iterating over its Fields. You can see that the whole logic is part of a
try/catch block. Since the Public API uses Exceptions for error handling, this is strongly encouraged, as it will allow you to conditionally catch the various errors that may happen. We will cover the exceptions we expect in a next paragraph.
The first thing we do is use the Content Service to load a Content using its ID, 66:
( 66 ). As you can see on the API doc page, this method expects a Content ID, and returns a Content Value Object.
This block is the one that actually displays the value.
It iterates over the Content's (Content Object) fields using the ContentType's (Content Class) FieldDefinitions (Content Class Attribute) (
For each Field Definition (Content Class Attribute), we start by displaying its identifier (
$fieldDefinition->identifier). We then get the FieldType (Datatype) instance using the FieldType Service (
$fieldTypeService->getFieldType( $fieldDefinition->fieldTypeIdentifier )). This method expects the requested FieldType's identifier, as a string (ezstring, ezxmltext...), and returns an
The Field Value object (Content Object Attribute) is obtained using the
getFieldValue() method of the Content Value Object which we obtained using
Using the FieldType object, we can convert the Field Value to a hash using the
toHash() method, provided by every FieldType. This method returns a primitive type (string, hash) out of a Field instance.
With this example, you should get a first idea on how you interact with the API. Everything is done through services, each service being responsible for a specific part of the repository (Content, FieldType, etc).
Loading Content in different languages
Since we didn't specify any language code, our Field objects is returned in the default language, depending on your language settings in
ezpublish.yml. If you want to use a non-default language, you can specify a language code in the
As said earlier, the Public API uses Exceptions to handle errors. Each method of the API may throw different exceptions, depending on what it does. Which exceptions can be thrown is usually documented for each method. In our case,
loadContent() may throw two types of exceptions:
NotFoundException, if the requested ID isn't found, and
UnauthorizedException if the currently logged in user isn't allowed to view the requested content.
It is a good practice to cover each exception you expect to happen. In this case, since our Command takes the content ID as a parameter, this ID may either not exist, or the referenced content may not be visible to our user. Both cases are covered with explicit error messages.
Traversing a Location subtree
This recipe will show how to traverse a Location's subtree. The full code implements a command that takes a Location ID as an argument, and recursively prints this location's subtree.
As for the ContentService,
loadLocation() returns a Value Object, here a
Location. Errors are handled with exceptions:
NotFoundException if the Location ID couldn't be found, and
UnauthorizedException if the current repository user isn't allowed to view this location.
LocationService::loadLocationChildren() returns a LocationList Value Objects that we can iterate over.
Note that unlike loadLocation(), we don't need to care for permissions here: the currently logged in user's permissions will be respected when loading children, and locations that can't be viewed won't be returned at all.
Should you need more advanced children fetching methods, the
SearchService is what you are looking for.
Viewing Content Meta Data
Content is a central piece in the Public API. You will often need to start from a Content, and dig in from its metadata. Basic content metadata is made available through
ContentInfo objects. This Value Object mostly provides primitive fields:
mainLocationId. But it is also used to request further Content related Value Objects from various services.
The full example implements an
ezpublish:cookbook:view_content_metadata command that prints out all the available metadata, given a content ID.
Setting the Repository User
In a command line script, the repository runs as if executed by the anonymous user. In order to identify it as a different user, you need to use the
UserService as follows:
This may be crucial when writing maintenance or synchronization scripts.
This is of course not required in template functions or controller code, as the HTTP layer will take care of identifying the user, and automatically set it in the repository.
The ContentInfo Value Object
We will now load a
ContentInfo object using the provided ID and use it to get our Content's meta data
We first use
LocationService::loadLocations() to get the Locations for our
ContentInfo. This method returns an array of
Location Value Objects. In this example, we print out the Location's path string (/path/to/content). We also use URLAliasService::reverseLookup() to get the location's main URLAlias.
Limitations with multi-location
(version <= 5.2)
When Content has multiple Locations, note that sorting on Location properties cannot work correctly. It's a limitation by design : SortClause\LocationPriority will not be limited to the locations under Criterion\ParentLocationId.
Location Search in 5.3
This limitation does not exist in Location Search. Migrating to version 5.3 adds the Location Search feature, and removes this limitation.
With Location Search, usage of Location Criterions and Sort Clauses with Content Search is deprecated. If search by Location properties is needed Location Search should be used.
We now want to list relations from and to our Content. Since relations are versioned, we need to feed the
ContentService::loadRelations() with a
VersionInfo object. We can get the current version's
ContentService::loadVersionInfo(). If we had been looking for an archived version, we could have specified the version number as the second argument to this method.
We can iterate over the Relation objects array we got from loadRelations(), and use these Value Objects to get data about our relations. It has two main properties: destinationContentInfo, and sourceContentInfo. They also hold the relation type (embed, common...), and the optional Field this relations is made with.
We can of course get our Content's metadata by using the Value Object's properties.
To get the current version's creator, and not the content's owner, you need to use the
creatorId property from the current version's
The section's ID can be found in the
sectionId property of the
ContentInfo object. To get the matching Section Value Object, you need to use the SectionService::loadSection() method.
To conclude we can also iterate over the Content's version, as
VersionInfo Value Objects.
We use the
ContentService::loadVersions() method, and get an array of
In this section we will cover how the
SearchService can be used to search for Contentent, by using a
Query and a combinations of
Criteria you will get a
SearchResult object back containing list of Content and count of total hits. In the future this object will also include facets, spell checking and "more like this" when running on a backend that supports it (for instance Solr).
Performing a simple full text search
In this recipe, we will run a simple full text search over every compatible attribute.
Query and Criterion objects
We introduce here a new object:
Query. It is used to build up a Content query based on a set of
The full list of criteria can be found on your installation in the following directory vendor/ezsystems/ezpublish-kernel/eZ/Publish/API/Repository/Values/Content/Query/Criterion. Additionally you may look at integration tests like vendor/ezsystems/ezpublish-kernel/eZ/Publish/API/Repository/Tests/SearchServiceTest.php for more details on how these are used.
NB: Be aware that the links point to code in the upcoming version (master) and might not represent the criteria in your eZ Publish version.
Criterion independence before 5.3
Note the criterion independance : Criterion are not related to each other by design and this can lead to a limitation in Content Search. See an example in the Criterion Independance example page.
Running the search query and using the results
Query object is given as an argument to
SearchService::findContent(). This method returns a
SearchResult object. This object provides you with various information about the search operation (number of results, time taken, spelling suggestions, or facets, as well as of course, the results themselves.
searchHits properties of the
SearchResult object is an array of SearchHit objects. In
valueObject property of
SearchHit, you will find the
Content object that match the given
Tip: If you you are searching using a unique identifier, for instance using the content id or content remote id criterion, then you can use
SearchService::findSingle(), this takes a Criterion and returns a single Content, or throws NotFound exception if none is found.
Performing an advanced search
As explained in the previous chapter, Criterion objects are grouped together using Logical criteria. We will now see how multiple criteria objects can be combined into a fine grained search
Subtree criterion limits the search to the subtree with pathString, which looks like:
ContentTypeId Criterion to limit the search to Content of ContentType 1. Those two criteria are grouped with a
LogicalAnd operator. The query is executed as before, with
Performing a fetch like search
A search isn't only meant for searching, it also provides the future interface for what you in eZ Publish 4.x would know as a content "fetch". And as this is totally backend agnostic, in future eZ Publish 5.x versions this will be powered by either Solr or ElasticSearch meaning it also replaces "ezfind" fetch functions.
Following the examples above we now change it a bit to combine several criteria with both a AND and a OR condition.
ParentLocationId criterion limits the search to the children of location 2. An array of "
ContentTypeId" Criteria to limit the search to Content of ContentType's with id 1 or 2 grouped in a
LogicalOr operator. Those two criteria are grouped with a
LogicalAnd operator. As always the query is executed as before, with
Tip: Want to do a subtree filter ( in 4.x: fetch( 'content', 'tree' ) )? Change the location filter to use the Subtree criterion filter as shown in the advanced search example above.
Using in() instead of OR
The above example is fine, but it can be optimized a bit by taking advantage of the fact that all filter criteria support being given an array of values (IN operator) instead of as single value (EQ operator).
If your on eZ Publish 5.1+ you can use the new
Tip: All filter criteria are capable of doing an "IN" selection, the ParentLocationId above could f.e. have been provided "array( 2, 43 )" to include second level children in both your content tree (2) and your media tree (43).
Performing a pure search count
In many cases you might need the number of contents matching a search, but with no need to do anything else with the results.
Thanks to the fact that the "searchHits" property of the
SearchResult object always refers to the total amount, it is enough to run a standard search and set $limit to 0. This way no results will be retrieved, and the search will not be slowed down, even when the number of matching results is huge.