Global navigation

   Documentation Center
   eZ Studio & eZ Platform
     User Manual
     Technical Manual
     Glossary
   eZ Publish 4.x / legacy

 
eZ Publish (5.x)

eZ Publish 5.x | For eZ Platform & eZ Studio topics see Technical manual and User manual, for eZ Publish 4.x and Legacy topics see eZ Publish legacy

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

Table of Contents

Introduction

The public API will give you an easy access to the eZ Publish content repository. This repository is the core component that manages content, locations (nodes in eZ Publish 4.x), sections, content types (Content Classes in eZ Publish 4.x), users, user groups, and roles. It also provides a new, clear interface for plugging in custom field types (datatypes in eZ Publish 4.x).

 
The public API is built on top of a layered architecture, including a persistence API that abstracts storage. By using the public API, you are sure that your code will be forward compatible with future releases based on enhanced,   scalable and high-performance storage engines. Applications based on the public API are also fully backwards compatible by using the included storage engine based on the current kernel and database model.

Getting Started

With the introduction of Symfony 2 as the framework powering eZ Publish 5, the whole eZ Publish 4.x extensions system is changing. Pretty much everything needs to be done with entirely new concepts. In this chapter, we will see two ways of customizing eZ Publish 5: command line scripts (for import scripts, for instance), and custom controllers, the eZ Publish 5 equivalent of eZ Publish 4.x custom modules.

Symfony bundle

In order to test and use Public API code, you will need to build a custom bundle. Bundles are Symfony's extensions, and are therefore also used to extend eZ Publish. Symfony 2 provides code generation tools that will let you create your own bundle and get started in a few minutes.

In this chapter, we will show how to create a custom bundle, and implement both a command line script and a custom route with its own controller action and view. All shell commands assume that you use some linux shell, but those commands would of course also work on windows systems.

Generating a new bundle

First, change directory to your eZ Publish root.

Code Block
languagebash
$ cd /path/to/ezpublish5

Then use the app/console application, with the generate:bundle command to start the bundle generation wizard

Code Block
languagebash
$ php ezpublish/console generate:bundle

Let's follow the instructions provided by the ward. Our objective is to create a bundle named EzSystems/Bundles/CookBookBundle, located in the src directory.

The wizard will first ask about our bundle's namespace. Each bundle's namespace should feature a vendor name (in our own case: EzSystems), optionally followed by a sub-namespace (we could have chosen to use Bundle), and end with the actual bundle's name, suffixed with Bundle: CookbookBundle.

Code Block
languagebash
titleBundle namespace
Your application code must be written in bundles. This command helps you generate them easily.

Each bundle is hosted under a namespace (like Acme/Bundle/BlogBundle).

The namespace should begin with a "vendor" name like your company name, your project name, or your client name, followed by one or more optional category sub-namespaces, and it should end with the bundle name itself (which must have Bundle as a suffix).

See http://symfony.com/doc/current/cookbook/bundles/best_practices.html#index-1 for more details on bundle naming conventions.

Use / instead of \ for the namespace delimiter to avoid any problem.

Bundle namespace: EzSystems/CookbookBundle

You will then be asked about the Bundle's name, used to reference your bundle in your code. We can go with the default, EzSystemsCookbookBundle. Just hit enter to accept the default.

Code Block
languagebash
titleBundle name
In your code, a bundle is often referenced by its name. It can be the concatenation of all namespace parts but it's really up to you to come up with a unique name (a good practice is to start with the vendor name).

Based on the namespace, we suggest EzSystemsCookbookBundle.

Bundle name [EzSystemsCookbookBundle]:

The next question is your bundle's location. By default, the script offers to place it in the src folder. This is perfectly acceptable unless you have a good reason to place it somewhere else. Just hit enter to accept the default.

Code Block
languagenone
titleBundle directory
The bundle can be generated anywhere. The suggested default directory uses the standard conventions.


Target directory [/path/to/ezpublish5/src]:

Next, you need to choose the generated configuration's format, out of YAML, XML, PHP or annotations. We mostly use yaml in eZ Publish 5, and we will use it in this cookbook. Enter 'yml', and hit enter.

Code Block
languagenone
titleConfiguration format
Determine the format to use for the generated configuration.                                                                                                                        

Configuration format (yml, xml, php, or annotation) [annotation]: yml

The last choice is to generate code snippets demonstrating the Symfony directory structure. If you're learning Symfony, it is a good idea to accept, as it will pre-create a controller, yaml files...

Code Block
languagenone
titleGenerate snippets & directory structure
To help you get started faster, the command can generate some code snippets for you.

Do you want to generate the whole directory structure [no]? yes

The generator will then summarize the previous choices, and ask for confirmation. Hit enter to confirm.

Code Block
languagenone
titleSummary and confirmation
You are going to generate a "EzSystems\Bundle\CookbookBundle\EzSystemsCookbookBundle" bundle in "/path/to/ezpublish5/src/" using the "yml" format.

Do you confirm generation [yes]? yes

The wizard will generate the bundle, check autoloading, and ask about activation of your bundle. Hit enter to both questions to have your bundle automatically added to your Kernel (ezpublish/EzPublishKernel.php) and routes from your bundle added to the existing routes (ezpublish/config/routing.yml).

Code Block
titleActivation and generation
  Bundle generation

Generating the bundle code: OK
Checking that the bundle is autoloaded: OK
Confirm automatic update of your Kernel [yes]?
Enabling the bundle inside the Kernel: OK
Confirm automatic update of the Routing [yes]?
Importing the bundle routing resource: OK

  You can now start using the generated code!
 

Your bundle should be generated and activated. Let's now see how you can interact with the Public API by creating a command line script, and a custom controller route and action.

Creating a command line script in your bundle

Writing a command line script with Symfony 2 is very easy. The framework and its bundles ship with a few scripts. They are all started using php ezpublish/console <command> (app/console in a default symfony 2 application). You can get the complete list of existing command line scripts by executing php ezpublish/console list from the eZ Publish 5 root.

In this chapter, we will create a new command, identified as ezpublish:cookbook:hello, that takes an optionnal name argument, and greets that name. To do so, we need one thing: a class with a name ending with "Command" that extends Symfony\Component\Console\Command\Command. Note that in our case, we use ContainerAwareCommand instead of Command, since we need the dependency injection container to interact with the Public API). In your bundle's directory (src/EzSystems/CookbookBundle), create a new directory named Command, and in this directory, a new file named HelloCommand.php.

Add this code to the file:

Code Block
languagephp
titleHelloCommand.php
<?php
namespace EzSystems\CookBookBundle\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Input\InputArgument;

class HelloCommand extends \Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand
{
    /**
     * Configures the command
     */
    protected function configure()
    {
    }

    /**
     * Executes the command
     * @param InputInterface $input
     * @param OutputInterface $output
     */
    protected function execute( InputInterface $input, OutputInterface $output )
    {
    }
}

This is the skeleton for a command line script.

One class, with a name ending with "Command" (HelloCommand), that extends Symfony\Bundle\FrameworkBundle\Command\Command, and is part of our bundle's Command namespace. It has two methods: configure(), and execute(). We also import several classes & interfaces with the use keyword. The first two, InputInterface and OutputInterface are used to typehint the objects that will allow us to provide input & output management in our script.

Configure will be used to set your command's name, as well as its options and arguments. Execute will contain the actual implementation of your command. Let's start by creating the configure() method.

Code Block
titleTestCommand::configure()
protected function configure()
{
    $this->setName( 'ezpublish:cookbook:hello' );
    $this->setDefinition(
        array(
            new InputArgument( 'name', InputArgument::OPTIONAL, 'An argument' )
        )
    );
}

First, we use setName() to set our command's name to "ezpublish:cookbook:hello".
We then use setDefinition() to add an argument, named name, to our command.

You can read more about arguments definitions and further options in the Symfony 2 Console documentation. Once this is done, if you run php ezpublish/console list, you should see ezpublish:cookbook:hello listed in the available commands. If you run it, it should just do nothing.

Let's just add something very simple to our execute() method so that our command actually does something.

Code Block
titleTestCommand::execute()
protected function execute( InputInterface $input, OutputInterface $output )
{
    // fetch the input argument
    if ( !$name = $input->getArgument( 'name' ) )
    {
        $name = "World";
    }
    $output->writeln( "Hello $name" );
}

You can now run the command from the eZ Publish 5 root.

Code Block
languagebash
titleHello world
$ php ezpublish/console ezpublish:cookbook:hello world
Hello world

Creating a custom route with a controller action

In this short chapter, we will see how to create a new route that will catch a custom URL, and execute a controller action. We want to create a new route, /cookbook/test, that displays a simple Hello world message. This tutorial is a simplified version of the official one that can be found on http://symfony.com/doc/current/book/controller.html.

Info
titleeZ Publish 4 equivalent

This eZ Publish 5 extension would have been a custom module, with its own module.php file, in eZ Publish 4.

During our bundle's generation, we have chosen to generate the bundle with default code snippets. Fortunately, almost everything we need is part of those default snippets. We just need to do some editing, in particular in two locations: src/EzSystems/Resources/config/routing.yml and src/EzSystems/CookbookBundle/Controllers/DefaultController.php. The first one will be used to configure our route (/cookbook/test) as well as the controller action the route should execute, while the latter will contain the actual action's code.

routing.yml

This is the file where we define our action's URL matching. The generated file contains this YAML block

Code Block
titleGenerated routing.yml
ez_systems_cookbook_homepage:
    pattern:  /hello/{name}
    defaults: { _controller: EzSystemsCookbookBundle:Default:index }

We can safely remove this default code, and replace it with this

Code Block
titleEdited routing.yml
ezsystems_cookbook_hello:
    pattern:  /cookbook/{name}
    defaults: { _controller: EzSystemsCookbookBundle:Default:hello }

We define a route that matches the URI /cookbook/*, and executes the action hello in the Default controller of our bundle. The next step is to create this method in the controller.

DefaultController.php

This controller was generated by the bundle generator. It contains one method, helloAction(), that matched the YAML configuration we have changed in the previous part. Let's just rename the indexAction() method so that we end up with this code

Code Block
languagephp
titleDefaultController::helloAction()
public function helloAction( $name )
{
    $response = new \Symfony\Component\HttpFoundation\Response;
    $response->setContent( "Hello $name" );
    return $response;
}

We won't go into details about controllers in this cookbook, but let's walk through the code a bit. This method receives the parameter defined in routing.yml. It is named "name" in the route definition, and must be named $name in the matching action. Since the action is named "hello" in routing.yml, the expected method name is helloAction.

Controller actions must return a Response object, that will contain the response's content, the headers, and various optional properties that affect the action's behaviour. In our case, we simply set the content, using setContent(), to "Hello $name". Simple. Go to http://ezpublish5/cookbook/hello/YourName, and you should get "Hello YourName".

With both command line scripts and HTTP routes, you have the basics you need to start writing Public API code.

Browsing, Finding, Viewing

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.

Showing content of a Content's fields

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/docb22/ez-publish-cookbook/blob/master/EzSystems/CookBookBundle/Command/ViewContentCommand.php.

Code Block
languagephp
titleViewing content
linenumberstrue
$repository = $this->getContainer()->get( 'ezpublish.api.repository' );
$contentService = $repository->getContentService();
$fieldTypeService = $repository->getFieldTypeService();

try
{
    // iterate over the field definitions of the content type and print out each field's identifier and value
    $content = $contentService->loadContent( $contentId );
    $contentType = $content->contentType;
    foreach( $contentType->fieldDefinitions as $fieldDefinition )
    {
        if( $fieldDefinition->fieldTypeIdentifier == 'ezpage' ) continue; // ignore ezpage
        $output->write( $fieldDefinition->identifier . ": " );
        $fieldType = $fieldTypeService->getFieldType( $fieldDefinition->fieldTypeIdentifier );
        $field = $content->getField( $fieldDefinition->identifier );
        $valueHash = $fieldType->toHash( $field->value ); // use toHash for getting a readable output
        $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 with id $contentId" );
}

Let's analyze this code block by block.

Code Block
languagephp
linenumberstrue
$repository = $this->getContainer()->get( 'ezpublish.api.repository' );
$contentService = $repository->getContentService();
$fieldTypeService = $repository->getFieldTypeService();

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 getContentService() and getFieldTypeService().

 

Info
titleUsed classes

 

Other languages are accessible via the the language parameter in the method

$content->getField( $fieldDefinition->identifier, $otherlanguage)

 

Browsing Locations

 

This recipe shows how to browse a subtree starting from a given location. (Full code here)

 

Code Block
languagephp
titlebrowse locations
       try
        {
            // load the starting location and browse
            $location = $this->locationService->loadLocation( $locationId );
            $this->browseLocation( $location, 0, $output );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on location was not found
            $output->writeln( "No location with id $locationId" );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e )
        {
            // react on permission denied
            $output->writeln( "Anonymous users are not allowed to read location with id $locationId" );
        }

 

 

 

Code Block
languagephp
titleThe method browseLocation
    /**
     * this method prints out the location name and calls this method recursive for the locations children
     *
     * @param \eZ\Publish\API\Repository\Values\Content\Location $location
     * @param int $depth the current depth
     * @param OutputInterface $output
     */
    private function browseLocation( Location $location, $depth, OutputInterface $output) {
        // indent according to depth and write out the name of the content
        for( $k=0; $k<$depth; $k++)
        {
            $output->write( ' ' );
        }
        $output->writeln( $location->contentInfo->name );
        // get location children and browse
        $children = $this->locationService->loadLocationChildren( $location );
        foreach( $children as $childLocation )
        {
            $this->browseLocation( $childLocation, $depth +1, $output );
        }
    }
Info
titleUsed classes

Viewing Content Meta Data

This recipe shows how to read content meta data: Locations, UrlAliases, Relations, Versions, Contenttype, Section, Owner, RemoteId, Several Timestamps. (Full code here)

Code Block
languagephp
titleviewing content meta data
        // get the services from the repsitory
        $contentService = $repository->getContentService();
        $locationService = $repository->getLocationService();
        $urlAliasService = $repository->getURLAliasService();
        $userService = $repository->getUserService();
        $sectionService = $repository->getSectionService();

        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );

        try
        {
            $contentInfo = $contentService->loadContentInfo( $contentId );

            // show all locations of the content
            $locations = $locationService->loadLocations( $contentInfo );
            $output->writeln( "Locations:" );
            foreach( $locations as $location )
            {
                $output->write( "  Location: $location->pathString " ); // the path build from id's
                // printout the url alias
                $urlAlias = $urlAliasService->reverseLookup( $location );
                $output->writeln( " URLAlias: $urlAlias->path" );
            }

            // show all relations of the current version
            $versionInfo = $contentService->loadVersionInfo( $contentInfo );
            $relations = $contentService->loadRelations( $versionInfo );
            $output->writeln( "Relations:" );
            foreach( $relations as $relation )
            {
                $name = $relation->destinationContentInfo->name;
                $output->write( "  Relation of type ");
                $output->write( $this->outputRelationType( $relation->type ) );
                $output->writeln( " to content $name" );
            }

            // show meta data
            $output->writeln( "Name: $contentInfo->name" );
            $output->writeln( "Type: " .$contentInfo->contentType->identifier );
            $output->writeln( "Last modified: " . $contentInfo->modificationDate->format('Y-m-d') );
            $output->writeln( "Published: ". $contentInfo->publishedDate->format('Y-m-d') );
            $output->writeln( "RemoteId: $contentInfo->remoteId" );
            $output->writeln( "Main Language: $contentInfo->mainLanguageCode" );
            $output->writeln( "Always avaialble: " . ($contentInfo->alwaysAvailable==1?'Yes':'No' ) );
            $owner = $userService->loadUser( $contentInfo->ownerId );
            $output->writeln( "Owner: " . $owner->contentInfo->name );
            $section = $sectionService->loadSection( $contentInfo->sectionId );
            $output->writeln( "Section: $section->name" );

            // show versions
            $versionInfos = $contentService->loadVersions( $contentInfo );
            foreach ( $versionInfos as $versionInfo )
            {
                $creator = $userService->loadUser( $versionInfo->creatorId );
                $output->write( "Version $versionInfo->versionNo: '" );
                $output->write( $creator->contentInfo->name );
                $output->writeln( "' " . $this->outputStatus( $versionInfo->status ) . " " . $versionInfo->initialLanguageCode);
            }
        }
        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 with id $contentId" );
        }

This script produces e.g the follwing output:

Code Block
languagenone
Locations:
  Location: /1/2/103/  URLAlias: /testrootfolder
  Location: /1/2/94/106/  URLAlias: /Partner-Section/testrootfolder
Relations:
  Relation of type EMBED to content Multivariate Testing
  Relation of type EMBED to content test
Name: testrootfolder
Type: folder
Last modified: 2012-11-12
Published: 2012-11-06
RemoteId: 32d63a1493208f6d0e2d40ab25749af4
Main Language: eng-US
Always avaialble: Yes
Owner: Administrator User
Section: Standard
Version 1: 'Administrator User' ARCHIVED eng-US
Version 2: 'Administrator User' PUBLISHED eng-US


 

Performing a simple full text search

In this recipe a simple full text search is performed. (Full code here)

Code Block
languagephp
titlea simple full text search
        // get the repository from the di container
        $repository = $this->getContainer()->get( 'ezpublish.api.repository' );

        // get the services from repository
        $searchService = $repository->getSearchService();

        // create and execute the query and print out the result
        $query = new \eZ\Publish\API\Repository\Values\Content\Query();
        $query->criterion = new \eZ\Publish\API\Repository\Values\Content\Query\Criterion\FullText( $text );
        $result = $searchService->findContent( $query );
        $output->writeln( 'Found ' . $result->totalCount . ' items' );
        foreach( $result->searchHits as $searchHit )
        {
            $output->writeln( $searchHit->valueObject->contentInfo->name );
        }
        
Info
titleused classes

Performing an advanced search

In this recipe different criteria is combined using a logic 'AND' operation. The result is restricted additional (see recipe 9) to a given content type and subtree. (Full code here)

Code Block
languagephp
// create the query with three critions and print out the result
$query = new \eZ\Publish\API\Repository\Values\Content\Query();
$criterion1 = new Criterion\FullText( $text );
$location = $locationService->loadLocation( $locationId );
$criterion2 = new Criterion\Subtree( $location->pathString ); // restrict results to belong to the given subtree
$criterion3 = new Criterion\ContentTypeId( $contentTypeId ); // restrict to the given content type
$query->criterion = new Criterion\LogicalAND(
    array( $criterion1, $criterion2, $criterion3 )
);

Creating and editing Content

The following recipes show how to create simple content. As we don't want to rely on a specific installation with predefined content types we first show how to create a content type group and a simple content type within this group. Then we will create a content object of the newly created content type. The last two recipes show how to create content containing images and xml text.

Creating a content type group

This snippet creates a content type group for a given identifier (Full code here).

Code Block
languagephp
titlecreate content type group
linenumberstrue
        // get the services from the repsitory
        $contentTypeService = $repository->getContentTypeService();
        try
        {
            // instanciate a create struct and create the group
            $contentTypeGroupCreateStruct = $contentTypeService->newContentTypeGroupCreateStruct( $contentTypeGroupIdentifier );
            $contentTypeGroup =  $contentTypeService->createContentTypeGroup( $contentTypeGroupCreateStruct );
            print_r( $contentTypeGroup );
        }
        catch ( UnauthorizedException $e )
        {
            // react on permission denied
            $output->writeln( $e->getMessage() );
        }
        catch ( ForbiddenException $e )
        {
            // react on identifier already exists
            $output->writeln( $e->getMessage() );
        }
Info
titleused classes

 

If this snipped is run with the same init code from recipe 1 we will get an UnauthorizedException.

The solution is described in the next recipe.

Setting the user for authorizing actions

By default the repository assumes the anonymous user is acting. To change this the following code can be executed

Code Block
languagephp
titleauthentication
        // set specific user
		$userService = $repository->getUserService();
        $user = $userService->loadUserByCredentials( $user, $password );
        $repository->setCurrentUser( $user );

If the user is identified by other mechanisms the user also can be loaded by its id via the service method

$userService->loadUser($id)

Info
titleused classes

Creating a content type

With this snipped a content type with two fields of type 'ezstring' is created. (Full code here).

Code Block
languagephp
titlecreate content type
        // get the repository from the di container
        $repository = $this->getContainer()->get( 'ezpublish.api.repository' );

        // get the services from the repsitory
        $contentTypeService = $repository->getContentTypeService();
        $userService = $repository->getUserService();

        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );

        // load the content type group
        try
        {
            $contentTypeGroup = $contentTypeService->loadContentTypeGroupByIdentifier( $groupIdentifier );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            $output->writeln( "content type group with identifier $groupIdentifier not found" );
            return;
        }

        // instanciate a ContentTypeCreateStruct with the given content type identifier and set parameters
        $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct( $contentTypeIdentifier );
        $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; // the main language code for names and description
        $contentTypeCreateStruct->nameSchema = '<title>'; // the name schema for generating the content name by using the title attribute

        // set names for the content type
        $contentTypeCreateStruct->names = array(
            'eng-GB' => $contentTypeIdentifier . 'eng-GB',
            'ger-DE' => $contentTypeIdentifier . 'ger-DE',
        );

        // set description for the content type
        $contentTypeCreateStruct->descriptions = array(
            'eng-GB' => 'Description for ' . $contentTypeIdentifier . 'eng-GB',
            'ger-DE' => 'Description for ' . $contentTypeIdentifier . 'ger-DE',
        );

        /********************** add fields ***************************************/

        // add a title field
        $titleFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( 'title', 'ezstring' );
        $titleFieldCreateStruct->names = array( 'eng-GB' => 'Title', 'ger-DE' => 'Titel' ); // set names
        $titleFieldCreateStruct->descriptions = array( 'eng-GB' => 'The Title', 'ger-DE' => 'Der Titel' ); // set descriptions
        $titleFieldCreateStruct->fieldGroup = 'content'; // set an group for the field definition
        $titleFieldCreateStruct->position = 10; // set position inside the content type
        $titleFieldCreateStruct->isTranslatable = true; // enable translation
        $titleFieldCreateStruct->isRequired = true; // require this field to set on content creation
        $titleFieldCreateStruct->isSearchable = true; // enabled to find field via content search

        $contentTypeCreateStruct->addFieldDefinition( $titleFieldCreateStruct );

        // add a body field
        $bodyFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct( 'body', 'ezstring' );
        $bodyFieldCreate->names = array( 'eng-GB' => 'Body', 'ger-DE' => 'Text' );
        $bodyFieldCreate->descriptions = array( 'eng-GB' => 'Description for Body', 'ger-DE' => 'Beschreibung Text' );
        $bodyFieldCreate->fieldGroup = 'content';
        $bodyFieldCreate->position = 20;
        $bodyFieldCreate->isTranslatable = true;
        $bodyFieldCreate->isRequired = true;
        $bodyFieldCreate->isSearchable = true;

        $contentTypeCreateStruct->addFieldDefinition( $bodyFieldCreate );

        // set the content type group for the content type
        $groups = array( $contentTypeGroup );
        try
        {
            // create the content type draft and publish it
            $contentTypeDraft = $contentTypeService->createContentType( $contentTypeCreateStruct,$groups );
            $contentTypeService->publishContentTypeDraft( $contentTypeDraft );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e )
        {
            // react on permission denied
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\ForbiddenException $e )
        {
            // react on identifier already exists
            $output->writeln( $e->getMessage() );
        }
        catch( \Exception $e )
        {
            $output->writeln( $e->getMessage() );
        }
    }
Info
titleused classes

 

Creating content

In this recipe content is created under a given parent location. It is assumed that the loaded content type is the one created in recipe 4. (Full code here).

Code Block
languagephp
titlecreate content
        // get the repository from the di container
        $repository = $this->getContainer()->get( 'ezpublish.api.repository' );

        // get the services from the repsitory
        $contentService = $repository->getContentService();
        $locationService = $repository->getLocationService();
        $contentTypeService = $repository->getContentTypeService();
        $userService = $repository->getUserService();

        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );
        try
        {
            // load the content type and instanciate a content create struct and set title and body fields
            $contentType = $contentTypeService->loadContentTypeByIdentifier( $contentTypeIdentifier );
            $contentCreateStruct = $contentService->newContentCreateStruct( $contentType, 'eng-GB' );
            $contentCreateStruct->setField( 'title', $title );
            $contentCreateStruct->setField( 'body', $body );

            // instanciate a location create struct from the parent location
            $locationCreateStruct = $locationService->newLocationCreateStruct( $parentLocationId );

            // create a draft using the content and location create structs and publish it
            $draft = $contentService->createContent( $contentCreateStruct, array( $locationCreateStruct ) );
            $content = $contentService->publishVersion( $draft->versionInfo );
            print_r( $content );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on content type or location not found
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $e )
        {
            // react on a field is not valid
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\ContentValidationException $e )
        {
            // react on a required field is missing or empty
            $output->writeln( $e->getMessage() );
        }
    }
Info
titleused classes

 

Updating Content

In this recipe the previously created content is updated with a new title and body in the same language. (Full code here)

Code Block
languagephp
titleupdate content
         try
        {
            // create a content draft from the current published version
            $contentInfo = $contentService->loadContentInfo( $contentId );
            $contentDraft = $contentService->createContentDraft( $contentInfo );

            // instanciate a content update struct and set the new fields
            $contentUpdateStruct = $contentService->newContentUpdateStruct();
            $contentUpdateStruct->initialLanguageCode = 'eng-GB'; // set language for new version
            $contentUpdateStruct->setField( 'title', $newtitle );
            $contentUpdateStruct->setField( 'body', $newbody );

            // update and publish draft
            $contentDraft = $contentService->updateContent( $contentDraft->versionInfo, $contentUpdateStruct );
            $content = $contentService->publishVersion( $contentDraft->versionInfo );
            print_r( $content );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on content not found
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $e )
        {
            // react on a field is not valid
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\ContentValidationException $e )
        {
            // react on a required field is missing or empty
            $output->writeln( $e->getMessage() );
        }
    }
Info
titleused classes

Translating content

It is the same code as for updating (see recipe 6). The initial language should be set to the translation language.

Code Block
languagephp
titletranslating
            // set language for new version
            $contentUpdateStruct->initialLanguageCode = $newLanguage;
            // set fields
            $contentUpdateStruct->setField( 'title', $newtitle );
            $contentUpdateStruct->setField( 'body', $newbody );

Multiple translations at once

It is possible to to make an update in content  or create content with more than one language. But there is a restriction - only one language can be assigned to the newly created version (which is displayed in the 4.x admin GUI in the translations column).

Code Block
languagephp
titleupdate multiple languages
            // set one language for new version
            $contentUpdateStruct->initialLanguageCode = 'fra-FR';
			// set fields for german - here the language has to be passed in third argument
            $contentUpdateStruct->setField( 'title', $newgermantitle, 'ger-DE' );
            $contentUpdateStruct->setField( 'body', $newgermanbody, 'ger-DE' );
            // set fields for french
            $contentUpdateStruct->setField( 'title', $newfrenchtitle );
            $contentUpdateStruct->setField( 'body', $newfrenchbody );


Creating Content containing an image

This recipe shows how to create an  content object containing an image. (Full code here)

Code Block
languagephp
titlecreating an image
        // get the repository from the di container
        $repository = $this->getContainer()->get( 'ezpublish.api.repository' );

        // get the services from the repsitory
        $contentService = $repository->getContentService();
        $locationService = $repository->getLocationService();
        $userService = $repository->getUserService();
        $contentTypeService = $repository->getContentTypeService();

        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );
        try
        {
            // load the image content type and instanciate a content create struct
            $contentType = $contentTypeService->loadContentTypeByIdentifier( "image" );
            $contentCreateStruct = $contentService->newContentCreateStruct( $contentType, 'eng-GB' );
            $contentCreateStruct->setField('name',$name); // set name field

            // set image file field
            $value = new \eZ\Publish\Core\FieldType\Image\Value(
                array(
                    'path' => $file,
                    'fileSize' => filesize($file),
                    'fileName' => basename($file),
                    'alternativeText' => $name
                )
            );
            $contentCreateStruct->setField( 'image' , $value );

            // instanciate a location create struct and create and publish the content
            $locationCreateStruct = $locationService->newLocationCreateStruct( $parentLocationId );
            $draft = $contentService->createContent( $contentCreateStruct,array( $locationCreateStruct ) );
            $content = $contentService->publishVersion( $draft->versionInfo );
            print_r( $content );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on content type or location not found
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException $e )
        {
            // react on remote id exists already
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $e )
        {
            // react on a field is not valid
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\ContentValidationException $e )
        {
            // react on a required field is missing or empty
            $output->writeln( $e->getMessage() );
        }
    }

 

Create Content with XML Text

This recipe shows how to create content with xml text. As content type the folder is used where the description is filled with xml. It is also shown how to embed the previously created image in the description. The image Id is given by a command line argument.

Code Block
languagephp
titleworking with xml text
             // load a folder content type and instanciate a content creation struct
            $contentType = $contentTypeService->loadContentTypeByIdentifier( "folder" );
            $contentCreateStruct = $contentService->newContentCreateStruct( $contentType, "eng-GB" );
            $contentCreateStruct->setField( "name", $name ); // set name of the folder
            $xmltext = "<?xml version='1.0' encoding='utf-8'?><section><paragraph>This is a <strong>image test</strong></paragraph>
                        <paragraph><embed view='embed' size='medium' object_id='$imageId'/></paragraph></section>";
            $contentCreateStruct->setField( "description", $xmltext ); // set description of the folder

            // instanciate a location create struct and create and publsidh the content
            $locationCreateStruct = $locationService->newLocationCreateStruct( $parentLocationId );
            $draft = $contentService->createContent( $contentCreateStruct, array( $locationCreateStruct ) );
            $content = $contentService->publishVersion( $draft->versionInfo );
            print_r( $content );

Working with locations

Adding a new location to content

This recipe shows how to add a new location to a given content object. (Full code here)

Code Block
languagephp
titleadd location
        // get needed services from the repsitory
        $contentService = $repository->getContentService();
        $locationService = $repository->getLocationService();
        $userService = $repository->getUserService();
        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );
        try
        {
            // add a location to the content by instaciating a location create struct
            // from the parent location id and pass it
            // to the createLocation method along with the content info
            $locationCreateStruct = $locationService->newLocationCreateStruct( $parentLocationId );
            $contentInfo = $contentService->loadContentInfo( $contentId );
            $newLocation = $locationService->createLocation( $contentInfo, $locationCreateStruct );
            print_r($newLocation); // prints out the new location
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on content or location not found
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e )
        {
            // react on permission denied
            $output->writeln( $e->getMessage() );
        }
    }
Info
titleUsed classes

Move or Copy Subtree

This recipe shows how to move or copy a subtree to a given parent location (Full code here)

Code Block
languagephp
titlecopy/move location
        // get the services from the repsitory
        $contentService = $repository->getContentService();
        $locationService = $repository->getLocationService();
        $userService = $repository->getUserService();

        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );

        try
        {
            // copy or move the src location to the destination location
            $srcLocation = $locationService->loadLocation( $srcLocationId );
            $destinationParentLocation = $locationService->loadLocation( $destinationParentLocationId );
            if( $operation == 'copy' )
            {
                $newLocation = $locationService->copySubtree( $srcLocation, $destinationParentLocation );
            }
            else if( $operation == 'move' )
            {
                $newLocation = $locationService->moveSubtree( $srcLocation, $destinationParentLocation );
            }
            else
            {
                $output->writeln( "operation must be copy or move" );
                return;
            }
            print_r( $newLocation );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on location not found
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e )
        {
            // react on permission denied
            $output->writeln( $e->getMessage() );
        }
    }
Info
titleUsed classes

Hide/Unhide Location

This recipe shows how to hide/unhide a location. (Full code here)

Code Block
languagephp
titlehide/unhide location
        // get the services from the repsitory
        $locationService = $repository->getLocationService();
        $userService = $repository->getUserService();
        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );
        try
        {
            // hide the location and print out
            $location = $contentService->loadContentInfo( $contentId );
            $hiddenLocation = $locationService->hideLocation( $location );
            print_r( $hiddenLocation );
            // unhide the now hidden location and print out
            $unhiddenLocation = $locationService->unhideLocation( $hiddenLocation );
            print_r( $unhiddenLocation );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on location not found
            $output->writeln( "No location with id $locationId" );
        }
    }
Info
titleUsed classes

Deleting locations

If a content has more than one location the method

LocationService::delete(Location $location) 

removes the location and if exists all descendants of the location. However the content itself is untouched as it has still other locations.

If the deleted location is the last one of the content the content itself is deleted. This applies also to all descendants of the location.

Alternatively a location can also moved to the trash via the method:

TrashService::trash(Location $location)

Other Recipes

Deleting Content

The result of deleting content permanently  is equivalent to deleting all locations of content (see recipe 15).

It is done via the method:

ContentService::delete(ContentInfo $contentInfo)

Assigning section to content

On creation of content the section of the parent location's content is assigned by default to the new content. However it is possible to assign a specific section on creation by setting it in the ContentCreateStruct:

$contentCreateStruct->section = $sectionId

Later on sections can be assigned with the following code (Full code here):

Code Block
languagephp
titleassign section to content
        // get the services from the repsitory
        $contentService = $repository->getContentService();
        $sectionService = $repository->getSectionService();
        $userService = $repository->getUserService();
        // load the admin user and set it has current user in the repository
        $user = $userService->loadUser( 14 );
        $repository->setCurrentUser( $user );
        try
        {
            // load the value objects and assign the content to the section
            $contentInfo = $contentService->loadContentInfo( $contentId );
            $section = $sectionService->loadSection( $sectionId );
            $sectionService->assignSection( $contentInfo, $section );
            // reaload the content info and print out
            $contentInfo =  $contentService->loadContentInfo( $contentId );
            $output->writeln( $contentInfo->sectionId );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\NotFoundException $e )
        {
            // react on content or section not found
            $output->writeln( $e->getMessage() );
        }
        catch( \eZ\Publish\API\Repository\Exceptions\UnauthorizedException $e )
        {
            // react on permission denied
            $output->writeln( $e->getMessage() );
        }
    }
Info
titleUsed classes

 

 

About this Cookbook

The objective of this Public API Cookbook is to progressively guide you through useful, everyday business logic, using the API in concrete recipes: obtaining a Location from a Content, fetching a set of Content, creating a User, and so on.

For each recipe, newly introduced elements will be explained in detail, including the required API components (services, value objects...). 

Info

Did we forget an important recipe ?

Let us know in the comments.

Suggested tools

In addition to this cookbook, we strongly recommend that you use a full featured PHP IDE, such as Eclipse or PhpStorm. It will provide you information on every piece of code you use, including objects and classes documentation. We have paid very careful attention to PHPDoc throughout this API, and such a tool is a very valuable help when using this API.

On top of this, generated public API documentation can be found online, in various formats:

Children Display