Message-ID: <988276506.3022.1485851663278.JavaMail.confluence@ip-10-127-227-164> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_3021_1746430577.1485851663278" ------=_Part_3021_1746430577.1485851663278 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
We will start by going through the various ways to find and retr= ieve content from eZ Platform using the API. While this will be covered in = further dedicated documentation, it is necessary to explain a few basic con= cepts 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= .
In this recipe, we will see how to fetch a Content item from the reposit= ory, and obtain its Field's content.
Let's first see the full code. You can see the Command line version at&n= bsp;https:= //github.com/ezsystems/CookbookBundle/blob/master/Command/ViewContentComman= d.php.
$repository =3D $this->getContainer()->get( 'ezpublish.api.rep= ository' ); $contentService =3D $repository->getContentService(); $contentTypeService =3D $repository->getContentTypeService(); $fieldTypeService =3D $repository->getFieldTypeService(); try { $content =3D $contentService->loadContent( 66 ); $contentType =3D $contentTypeService->loadContentType( $content->= contentInfo->contentTypeId ); // iterate over the field definitions of the content type and print out= each field's identifier and value foreach( $contentType->fieldDefinitions as $fieldDefinition ) { $output->write( $fieldDefinition->identifier . ": " ); $fieldType =3D $fieldTypeService->getFieldType( $fieldDefinition= ->fieldTypeIdentifier ); $field =3D $content->getField( $fieldDefinition->identifier )= ; =09=09// We use the Field's toHash() method to get readable content out of = the Field $valueHash =3D $fieldType->toHash( $field->value ); $output->writeln( $valueHash ); } } catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e ) { // if the id is not found $output->writeln( "No content with id $contentId" ); } catch( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e ) { // not allowed to read this content $output->writeln( "Anonymous users are not allowed to read content w= ith id $contentId" ); }=20
Let's analyze this code block by block.
$repository =3D $this->getContainer()->get( 'ezpublish.api.rep= ository' ); $contentService =3D $repository->getContentService(); $contentTypeService =3D $repository->getContentTypeService(); $fieldTypeService =3D $repository->getFieldTypeService();=20
This is the initialization part. As explained above, everything in the P=
ublic API goes through the repository via dedicated services. We get the re=
pository from the service container, using the method get() of our container, obtained via
$this->getContainer()<=
/code>. Using our
$repository
variable, we fetch the two servi=
ces we will need using getContentService()
and =
getFieldTypeService()
.
try { // iterate over the field definitions of the content type and print out= each field's identifier and value $content =3D $contentService->loadContent( 66 );=20
Everything starting from line 5 is about getting our Content and iterati=
ng over its Fields. You can see that the whole logic is part of a
The first thing we do is use the Content Service to load a Content item =
using its ID, 66: $contentService->loadContent
( 66 )
. As you can see on the API doc page, this method e=
xpects a Content ID, and returns a Content Value Object.
foreach( $contentType->fieldDefinitions as $fieldDefinition ) { // ignore ezpage if( $fieldDefinition->fieldTypeIdentifier =3D=3D 'ezpage' ) continue; $output->write( $fieldDefinition->identifier . ": " ); $fieldType =3D $fieldTypeService->getFieldType( $fieldDefinition->= ;fieldTypeIdentifier ); $fieldValue =3D $content->getFieldValue( $fieldDefinition->identi= fier ); $valueHash =3D $fieldType->toHash( $fieldValue ); $output->writeln( $valueHash ); }=20
This block is the one that actually displays the value.
It iterates over the Content item's Fields using the Content Type's Fiel=
dDefinitions ($contentType->fieldDefinitions
).
For each Field Definition, we start by displaying its identifier (=
$fieldDefinition->identifier
). We then get the Field Type instanc=
e using the Field Type Service ($fieldTypeService->getFieldType( $=
fieldDefinition->fieldTypeIdentifier )
). This method expects the =
requested Field Type's identifier, as a string (ezstring, ezxmltext, etc.),=
and returns an eZ\Publish\API\Repository\FieldType
=
object.
The Field Value object is obtained using the getFieldValue()<=
/code> method of the Content Value Object which we obtained using =
;
ContentService::loadContent()
.
Using the Field Type object, we can convert the Field Value to a hash us=
ing the toHash()
method, provided by every Field Typ=
e. This method returns a primitive type (string, hash) out of a Field insta=
nce.
With this example, you should get a first idea on how you interact with = the API. Everything is done through services, each service being responsibl= e for a specific part of the repository (Content, Field Type, etc.).
Loading Content in different languages
Since we didn't specify any language code, our Field object is returned =
in the default language, depending on your language settings in ezpla=
tform.yml
. If you want to use a non-default language, you can specif=
y a language code in the getField()
call:
$content->getFieldValue( $fieldDefinition->identifier, 'fre-F= R' )=20
catch ( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e = ) { $output->writeln( "<error>No content with id $contentId found&= lt;/error>" ); } catch ( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e ) { $output->writeln( "<error>Permission denied on content with id= $contentId</error>" ); }=20
As said earlier, the Public API uses Exceptions to han=
dle errors. Each method of the API may throw different exceptions, dependin=
g on what it does. Which exceptions can be thrown is usually documented for=
each method. In our case, loadContent()
may throw t=
wo types of exceptions: NotFoundException
, if the request=
ed 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 t= his case, since our Command takes the Content ID as a parameter, this ID ma= y either not exist, or the referenced Content item may not be visible to ou= r user. Both cases are covered with explicit error messages.
This recipe will show how to traverse a Location's subtree. The full cod= e implements a command that takes a Location ID as an argument and recursiv= ely prints this location's subtree.
Full code
In this code, we introduce the LocationService. This service is used to interac=
t with Locations. We use two methods from this service: loa=
dLocation()
, and loadLocationChildren()=
.
try { // load the starting location and browse $location =3D $this->locationService->loadLocation( $locationId )= ; $this->browseLocation( $location, $output ); } catch ( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e ) { $output->writeln( "<error>No location found with id $locationI= d</error>" ); } catch( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e ) { $output->writeln( "<error>Current users are not allowed to rea= d location with id $locationId</error>" ); }=20
As for the ContentService, loadLocation()
returns a Value O=
bject, here a Location
. Errors are handled with exceptions:=
NotFoundException
if the Location ID couldn't be foun=
d, and UnauthorizedException
if the current r=
epository user isn't allowed to view this Location.
private function browseLocation( Location $location, OutputInterfac= e $output, $depth =3D 0 ) { $childLocationList =3D $this->locationService->loadLocationChildr= en( $location, $offset =3D 0, $limit =3D -1 ); // If offset and limit had been specified to something else then "all",= then $childLocationList->totalCount contains the total count for iterat= ion use foreach ( $childLocationList->locations as $childLocation ) { $this->browseLocation( $childLocation, $output, $depth + 1 ); } }=20
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 respec=
ted when loading children, and Locations that can't be viewed won't be retu=
rned at all.
Full code
Should you need more advanced children fetching methods, the =
SearchServic=
e
is what you are looking for.
Content is a central piece in the Public API. You will often need to sta=
rt from a Content item, and dig in from its metadata. Basic content metadat=
a is made available through ContentInfo
objects. T=
his Value Object mostly provides primitive fields: contentTypeId,
publishedDate
or mainLocationId
. But it is al=
so used to request further Content-related Value Objects from various servi=
ces.
The full example implements an ezpublish:cookbook:view_content_met=
adata
command that prints out all the available metadata, given a Co=
ntent ID.
Full code
We introduce here several new services: URLAliasService
, UserService
and SectionService
. The concept should be familiar to you n=
ow.
/** @var $repository \eZ\Publish\API\Repository\Repository */ $repository =3D $this->getContainer()->get( 'ezpublish.api.repository= ' ); $contentService =3D $repository->getContentService(); $locationService =3D $repository->getLocationService(); $urlAliasService =3D $repository->getURLAliasService(); $sectionService =3D $repository->getSectionService(); $userService =3D $repository->getUserService();=20
In a command line script, the repository runs as if executed by the anon=
ymous user. In order to identify it as a different user, you need to use th=
e UserSe=
rvice
as follows:
$administratorUser =3D $userService->loadUser( 14 ); $repository->setCurrentUser( $administratorUser );=20
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 automaticall= y set it in the repository.
We will now load a ContentInfo
object using the provided ID=
and use it to get our Content item's metadata
$contentInfo =3D $contentService->loadContentInfo( $contentId );==20
// show all locations of the content $locations =3D $locationService->loadLocations( $contentInfo ); $output->writeln( "<info>LOCATIONS</info>" ); foreach ( $locations as $location ) { $urlAlias =3D $urlAliasService->reverseLookup( $location ); $output->writeln( " $location->pathString ($urlAlias->path)"= ); }=20
We first use LocationService ::loadLocations()
=
to get the Locations for our Conte=
ntInfo
. This method returns an array of Location
Val=
ue 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.
We now want to list relations from and to our Content. Since relations a=
re versioned, we need to feed the ContentService::loadRelat=
ions()
with a VersionInfo
object. We can get=
the current version's VersionInfo
using ContentService::loadVersionInfo()
. If we had been l=
ooking for an archived version, we could have specified the version number =
as the second argument to this method.
// show all relations of the current version $versionInfo =3D $contentService->loadVersionInfo( $contentInfo ); $relations =3D $contentService->loadRelations( $versionInfo ); if ( count( $relations ) ) { $output->writeln( "<info>RELATIONS</info>" ); foreach ( $relations as $relation ) { $name =3D $relation->destinationContentInfo->name; $output->write( " Relation of type " . $this->outputRelation= Type( $relation->type ) . " to content $name" ); } }=20
We can iterate over the Relation objects array we got from loadRe=
lations()
, and use these Value Objects to get data about our relatio=
ns. It has two main properties: destinationContentInfo
, and
We can of course get our Content item's metadata by using the Value Obje= ct's properties.
// show meta data $output->writeln( "\n<info>METADATA</info>" ); $output->writeln( " <info>Name:</info> " . $contentInfo->= ;name ); $output->writeln( " <info>Type:</info> " . $contentType->= ;identifier ); $output->writeln( " <info>Last modified:</info> " . $conten= tInfo->modificationDate->format( 'Y-m-d' ) ); $output->writeln( " <info>Published:</info> ". $contentInfo= ->publishedDate->format( 'Y-m-d' ) ); $output->writeln( " <info>RemoteId:</info> $contentInfo->= ;remoteId" ); $output->writeln( " <info>Main Language:</info> $contentInf= o->mainLanguageCode" ); $output->writeln( " <info>Always available:</info> " . ( $c= ontentInfo->alwaysAvailable ? 'Yes' : 'No' ) );=20
We can use UserService::loadUser()
with Content ContentInfo
to load the Conte=
nt's owner as a User
Value Object.
$owner =3D $userService->loadUser( $contentInfo->ownerId ); $output->writeln( " <info>Owner:</info> " . $owner->cont= entInfo->name );=20
To get the current version's creator, and not the content's owner, you n=
eed to use the creatorId
property from the current version's <=
code>VersionInfo object.
The Section's ID can be found in the sectionId
property of =
the ContentInfo
object. To get the matching Section Value Obje=
ct, you need to use the SectionService::loadSection()
method.<=
/p>
$section =3D $sectionService->loadSection( $contentInfo->sect= ionId ); $output->writeln( " <info>Section:</info> $section->name= " );=20
To conclude we can also iterate over the Content's version, as Ve=
rsionInfo
Value Objects.
$versionInfoArray =3D $contentService->loadVersions( $contentInf= o ); if ( count( $versionInfoArray ) ) { $output->writeln( "\n<info>VERSIONS</info>" ); foreach ( $versionInfoArray as $versionInfo ) { $creator =3D $userService->loadUser( $versionInfo->creatorId = ); $output->write( " Version $versionInfo->versionNo " ); $output->write( " by " . $creator->contentInfo->name ); $output->writeln( " " . $this->outputStatus( $versionInfo->= ;status ) . " " . $versionInfo->initialLanguageCode ); } }=20
We use the ContentService::loadVersions()
met=
hod and get an array of VersionInfo
objects.
In this section we will cover how the SearchService
can =
be used to search for Content, by using a Query
and a com=
binations of Criteria
you will get a <=
code>SearchResult object back containing list of Content a=
nd 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 support=
s it (for instance Solr).
Full code
In this recipe, we will run a simple full text search over every compati= ble attribute.
We introduce here a new object: Query
. It is used to b=
uild up a Content query based on a set of Criterion
objects.=
p>
$query =3D new \eZ\Publish\API\Repository\Values\Content\Query(); $query->filter =3D new Query\Criterion\FullText( $text );=20
Multiple criteria can be grouped together using "logical criteria", such=
as LogicalAnd or LogicalOr. Since in =
this case we only want to run a text search, we simply use a =
FullText
criterion object.
The full list of criteria can be found on your installation in the follo= wing directory vendor/ezsystems/ezpublish-kernel/eZ/= Publish/API/Repository/Values/Content/Query/Criterion. Additionally you= may look at integration tests like vendor/ezsystems/ezpubli= sh-kernel/eZ/Publish/API/Repository/Tests/SearchServiceTest.php for mor= e details on how these are used.
The Query
object is given as an argument to Se=
archService::findContent()
. This method returns a SearchR=
esult
object. This object provides you with various information abou=
t the search operation (number of results, time taken, spelling suggestions=
, or facets, as well as, of course, the results themselves.
$result =3D $searchService->findContent( $query ); $output->writeln( 'Found ' . $result->totalCount . ' items' ); foreach ( $result->searchHits as $searchHit ) { $output->writeln( $searchHit->valueObject->contentInfo->nam= e ); }=20
The searchHits
properties of the SearchResult
=
object is an array of SearchHit
objects. In valueObject<=
/code> property of
SearchHit
, you will find the Con=
tent
object that matches the given Query
.
If you you are searching using a unique identifier, for instance u=
sing the Content ID or Content remote ID criterion, then you can use&n=
bsp; SearchService::findSingle()
, this takes a Crite=
rion and returns a single Content item, or throws a NotFound
e=
xception if none is found.
Full code
As explained in the previous chapter, Criterion objects are grouped toge=
ther using logical criteria. We will now see how multiple criteria objects =
can be combined into a fine grained search Query
.
use eZ\Publish\API\Repository\Values\Content\Query\Criterion; use eZ\Publish\API\Repository\Values\Content; // [...] $query =3D new Query(); $criterion1 =3D new Criterion\Subtree( $locationService->loadLocation( 2= )->pathString ); $criterion2 =3D new Criterion\ContentTypeIdentifier( 'folder' ); $query->criterion =3D new Criterion\LogicalAnd( array( $criterion1, $criterion2 ) ); $result =3D $searchService->findContent( $query );=20
A Subtree
criterion limits the search to =
the subtree with pathString, which looks like: /1/2/
. A <=
a href=3D"http://apidoc.ez.no/sami/trunk/NS/html/eZ/Publish/API/Repository/=
Values/Content/Query/Criterion/ContentTypeId.html" class=3D"external-link" =
rel=3D"nofollow"> ContentTypeId
Criterion to limit the se=
arch to Content of Content Type 1. Those two criteria are grouped with a SearchService::findContent()
.
Full code
A search isn't only meant for searching, it also provides the future int= erface for what you in eZ Publish 4.x would know as a content "fetch". And = as this is totally backend agnostic, in future versions this will be p= owered by either Solr or ElasticSearch meaning it also replaces "ezfin= d" fetch functions.
Following the examples above we now change it a bit to combine several c= riteria with both an AND and an OR condition.
use eZ\Publish\API\Repository\Values\Content\Query\Criterion; use eZ\Publish\API\Repository\Values\Content; // [...] $query =3D new Query(); $query->criterion =3D new Criterion\LogicalAnd( array( new Criterion\ParentLocationId( 2 ), new Criterion\LogicalOr( array( new Criterion\ContentTypeIdentifier( 'folder' ), new Criterion\ContentTypeId( 2 ) ) ) ) ); $result =3D $searchService->findContent( $query );=20
A ParentLocationId
cri=
terion limits the search to the children of location 2. An array of "=
ContentTypeId"
Criteria to limit the search to Content of Conte=
ntType's with id 1 or 2 grouped in a LogicalOr
opera=
tor. Those two criteria are grouped with a LogicalAnd
&nbs=
p;operator. As always the query is executed as before, with Sear=
chService::findContent()
.
Want to do a subtree filter? Change the location filter to use the = Subtree criterion filter as shown in the advanced search example above.
The above example is fine, but it can be optimized a bit by taking advan= tage of the fact that all filter criteria support being given an array of v= alues (IN operator) instead of a single value (EQ operator).
You can also use the ContentTypeIdentif=
ier
Criterion:
use eZ\Publish\API\Repository\Values\Content\Query\Criterion; use eZ\Publish\API\Repository\Values\Content; // [...] $query =3D new Query(); $query->criterion =3D new Criterion\LogicalAnd( array( new Criterion\ParentLocationId( 2 ), new Criterion\ContentTypeIdentifier( array( 'article', 'folder' ) ) ) ); $result =3D $searchService->findContent( $query );=20
All filter criteria are capable of doing an "IN" selection, the Par= entLocationId above could, for example, have been provided "arra= y( 2, 43 )" to include second level children in both your content tree (2) = and your media tree (43).
In many cases you might need the number of Content items matching a sear= ch, but with no need to do anything else with the results.
Thanks to the fact that the " searchHits " property of the
use eZ\Publish\API\Repository\Values\Content\Query; // [...] $query =3D new Query(); $query->limit =3D 0; // [...] ( Add criteria as shown above ) $resultCount =3D $searchService->findContent( $query )->totalCount;= pre>=20