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

=20 =20

D= isplaying values from a Content item

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.

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 servi= ces we will need using getContentService() and = getFieldTypeService().

=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 later 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 e= xpects 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 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:

=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 item may not be visible to ou= r user. 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 recursiv= ely prints this location's subtree.

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()= .

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 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.

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 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.

Viewing Con= tent Metadata

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.

We introduce here several new services: URLAliasService , UserService and SectionService . The concept should be familiar to you n= ow.

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 UserSe= rvice 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 item's metadata

=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()= 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.

 

Relations

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.

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 loadRe= lations(), and use these Value Objects to get data about our relatio= ns. It has two main properties: destinationContentInfo, and sourceContentInfo. They also hold the relation type (embed, comm= on, etc.), and the optional Field this relations is made with.

ContentInfo = properties

We can of course get our Content item's metadata by using the Value Obje= ct'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 Conte= nt'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.<= /p>

=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 Ve= rsionInfo 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() met= hod 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 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).

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.

 

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

 

Running the search query and using the results

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.

=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<= /code> property of SearchHit, you will find the Con= tent object that matches the given Query.

Tip

 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.

 

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 = 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 LogicalAnd operator. The query is executed as before, with SearchService::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 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.

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

 

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

You can also use the ContentTypeIdentif= ier  Criterion:

=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 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).

 

Perform= ing a pure search count

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 SearchResult object always refers to the total amount, i= t is enough to run a standard search and set $limit to 0. This way no resul= ts will be retrieved, and the search will not be slowed down, even when the= number 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_3021_1746430577.1485851663278--