Message-ID: <1999954665.3076.1485851878705.JavaMail.confluence@ip-10-127-227-164> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_3075_1228312055.1485851878705" ------=_Part_3075_1228312055.1485851878705 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html 2. Browsing, finding, viewing

2. Browsing, finding, viewing

We will start by going through the various ways to find and retr= ieve content from eZ Publish using the API. While this will be covered in f= urther dedicated documentation, it is necessary to explain a few basic conc= epts 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.=

=20 =20

D= isplaying values from a Content item

In this recipe, we will see how to fetch a Content instance from the rep= ository, 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.

Viewing content
=20
$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.

=20
$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 services we will n= eed using getContentService() and getFiel= dTypeService().

=20
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 try/catch block. Since the Public API uses Exceptions for err= or handling, this is strongly encouraged, as it will allow you to condition= ally 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 item = using its ID, 66: $contentService->loadContent( 66 ). As you can see on the API doc page, this method exp= ects a Content ID, and returns a Content Value Object.

=20
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's (Content Object) fields using the Content= Type's (Content Class) FieldDefinitions (Content Class Attribute) ($c= ontentType->fieldDefinitions).

For each Field Definition (Content Class Attribute), we start by display= ing its identifier ($fieldDefinition->identifier). We then = get the FieldType (Datatype) instance using the FieldType Service ($f= ieldTypeService->getFieldType( $fieldDefinition->fieldTypeIdentifier = )). This method expects the requested FieldType's identifier, as a s= tring (ezstring, ezxmltext...), and returns an eZ\Publish\API\Re= pository\FieldType object.

The Field Value object (Content Object Attribute) is obtained using the&= nbsp;getFieldValue() method of the Content Value Object w= hich we obtained using ContentService::loadContent().

Using the FieldType object, we can convert the Field Value to a hash usi= ng the toHash() method, provided by every FieldType.= This method returns a primitive type (string, hash) out of a Field instanc= e.

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, FieldType, etc).

Loading Content in different languages

Since we didn't specify any language code, our Field object is returned = in the given Content item's main language. If you'd prefer it to fall back = to the SiteAccess language(s), then take advantage of TranslationHelpers. O= r if you want to use an altogether different language, you can specify a la= nguage code in the getField() call:

=20
$content->getFieldValue( $fieldDefinition->identifier, 'fre-F=
R' )
=20
Exceptions handling
=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 may not be visible to our use= r. Both cases are covered with explicit error messages.

Travers= ing a Location subtree

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 recursi= vely prints this location's subtree.

In this code, we introduce the LocationService. This service is used to interac= t with locations (eZ Publish 4 nodes). We use two methods from this service= : loadLocation(), and load= LocationChildren().

Loading a location
=20
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  V= alue Object, here a Location. Errors are handled with excepti= ons: NotFoundException if the Location ID couldn't be f= ound, and UnauthorizedException if the current = repository user isn't allowed to view this location.

Iterating over a Location's children
=20
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 h= ere: the currently logged in user's permissions will be respected when load= ing children, and locations that can't be viewed won't be returned at all.<= /p>

Full code

Should you need more advanced children fetching methods, the = SearchService=  is what you are looking for.

Viewing Con= tent Meta Data

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. Th= is Value Object mostly provides primitive fields: contentTypeId, publishedDate or mainLocationId. But it is als= o used to request further Content related Value Objects from various servic= es.

The full example implements an ezpublish:cookbook:view_content_met= adata command that prints out all the available metadata, given a co= ntent ID.

We introduce here several new services: URLAliasService, UserService and <= code>SectionService. The concept should be familiar to you now.<= /p>

Services initialization
=20
/** @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

Setting t= he Repository User

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 UserSer= vice as follows:

=20
$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.

The Cont= entInfo Value Object

We will now load a ContentInfo object using the provided ID= and use it to get our Content's meta data

=20
$contentInfo =3D $contentService->loadContentInfo( $contentId );=
=20

Locations

Getting Content Locations
=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() t= o get the Locations for our Content= Info. This method returns an array of Location Value O= bjects. 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 <=3D 5.2)

When Content has multiple Locations, note that sorting on Location prope= rties cannot  work correctly. It's a limitation by design : SortClause= \LocationPriority will not be limited to the locations under Criterion\Pare= ntLocationId.

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.

 

Relations

We now want to list relations from and to our Content. Since relations a= re versioned, we need to feed the ContentService::loadRelati= ons() with a VersionInfo object. We can get the= current version's VersionInfo using 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.

Browsing a Content's relations
=20
// 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 loadRelation= s(), and use these Value Objects to get data about our relations. It has tw= o main properties: destinationContentInfo, and sourceContentInfo. They also= hold the relation type (embed, common...), and the optional Field this rel= ations is made with.

ContentInfo = properties

We can of course get our Content's metadata by using the Value Object's = properties.

Primitive object metadata
=20
// 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

Owning user

We can use UserService::loadUser() with content = ownerId property of our ContentInfo to load the C= ontent's owner as a User Value Object.

=20
$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.

Section

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.

=20
$section =3D $sectionService->loadSection( $contentInfo->sect=
ionId );
$output->writeln( "  <info>Section:</info> $section->name=
" );
=20

Versions

To conclude we can also iterate over the Content's version, as Ver= sionInfo Value Objects.

=20
$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() metho= d, and get an array of VersionInfo objects.

Search

In this section we will cover how the SearchService can be= used to search for Content, by using a Query and a combi= nations of Criteria you will get a = SearchResult object back containing list of Content and cou= nt 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).

Pe= rforming a simple full text search

In this recipe, we will run a simple full text search over every compati= ble attribute.

Query and= Criterion objects

We introduce here a new object: Query. It is used to b= uild up a Content query based on a set of Criterion objects.

version <=3D Ez Publish 5.2
=20
$query =3D new \eZ\Publish\API\Repository\Values\Content\Query();
$query->criterion =3D new Query\Criterion\FullText( $text );
=20
version >=3D Ez Publish 5.3
=20
$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 <= code>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.

NB: Be aware that the links point to code in the upcoming ve= rsion (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 othe= r by design and this can lead to a limitation in Content Search. See an example in the Criteri= on Independance example page.

Running the search query and using the results

The Query object is given as an argument to Sea= rchService::findContent(). This method returns a SearchRes= ult 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.

=20
$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 proper= ty of SearchHit, you will find the Content&n= bsp;object that match the given Query.

Tip: If you you are searching using a unique identifier, for in= stance using the content id or content remote id criterion, then = you can use SearchService::findSingle(), this ta= kes a Criterion and returns a single Content item, or throws NotFound excep= tion if none is found.

Perform= ing an advanced search

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.

=20
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 th= e subtree with pathString, which looks like: /1/2/. A ContentTypeId Criterion to limit the search= to Content of ContentType 1. Those two criteria are grouped with a L= ogicalAnd operator. The query is executed as before, with Sear= chService::findContent().

Perform= ing a fetch like search

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 eZ Publish 5.x versions= this will be powered by either Solr or ElasticSearch meaning it also repla= ces "ezfind" fetch functions.

Following the examples above we now change it a bit to combine several c= riteria with both a AND and a OR condition.

=20
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

ParentLocationId crite= rion limits the search to the children of location 2. An array of "Co= ntentTypeId" Criteria to limit the search to Content of Content= Type's with id 1 or 2 grouped in a LogicalOr operato= r. Those two criteria are grouped with a LogicalAnd = operator. As always the query is executed as before, with Search= Service::findContent().

Tip: Want to do a subtree filter ( in 4.x: fetch( 'content', 't= ree' ) )? Change the location filter to use the Subtree criterion filt= er as shown in the advanced search example above.

Using in() in= stead of OR

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 as single value (EQ operator).

If your on eZ Publish 5.1+ you can use the new ContentTypeIdentifier Criterion:

eZ Publish 5.1+ (2013.01+)
=20
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

Tip: All filter criteria are capable of doing an "IN" selection= , the ParentLocationId above could f.e. have been provided "array( 2, 43 )" to includ= e second level children in both your content tree (2) and your media tree (= 43). 

Perform= ing a pure search count

In many cases you= might need the number of Content items matching a search, but with no need= to do anything else with the results.
Thanks to the fact that the "searchHits" property of t= he SearchResult object always refer= s 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 n= umber of matching results is huge.

 

=20
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;=20

 


------=_Part_3075_1228312055.1485851878705--