logo

Netmaking

22 71 95 97

Getting started with eZ Publish 5

Since the introduction of the eZ Publish 5 in late 2012, there has been a lack of information for developers wanting to get started with this new, Symfony-based stack, but who have no previous knowledge of the eZ Publish legacy (<5) architecture. Let's fix that.

This tutorial covers the most basic use case for eZ Publish by the means of installation, basic concepts, and showing you how to fetch and format your content.

Disclaimer

This is a beginners guide and not a set of best practices. Several examples have been simplified compared to what you would expect in a production environment in order to reduce the complexity and get the message across.

Prerequisites

This article does not assume any prior knowledge with eZ Publish, but rather that you are a capable PHP developer, perhaps with experience from other MVC frameworks or Content Management Systems. Also, although not mandatory, you’ll probably grasp some of the concepts more easily if you have familiarized yourself with the Symfony framework, upon which eZ Publish is built.

Installing eZ Publish

Since the installation method is subject to change depending on your exact version, environment and so on, this tutorial does not provide a detailed installation guide. Please review the normal installation documentation for the exact steps involved in setting up eZ Publish in your environment.

Assuming that you are on a Linux-ish OS, the overall process might go something like this:

  1. Download and unpack eZ Publish
  2. Create a MySQL database using your method of choice
    1. NB. Make sure its charset is UTF-8
  3. Set proper file and directory permissions
  4. Create a Virtualhost specifying dev as your environment
  5. Complete the setup wizard accessible at yourhost.local/ezsetup
    1. NB. When reaching the package step, make sure to include the demo content

Directory structure

eZ Publish uses the Symfony full stack framework, and hence the directory structure is modelled upon the one provided by Symfony.

The main noticable change is the renaming of some files and directories from the generic app to the more specific ezpublish, and the inclusion of the legacy version of eZ Publish in the ezpublish_legacy directory (for the 5.x generation). Since this tutorial focuses on the new eZ Publish arcitecture, we will not be discussing the legacy code base.

Basic concepts

Developing with eZ Publish requires an understanding of the basic concepts. There’s a lot of information here, so you might want to run through this chapter a few times, and also follow along in the admin interface to get visual confirmation of the concepts.

eZ Publish consists of pieces of Content all created from the basis of Content Types. Content is to a Content Type what an Object is to a Class in object oriented programming.

The Content Type holds a list of Field Definitions which dictate which Fields the Content should contain. The Content Type Article might contain Fields Definitions for Title, Body and Author, while the Product Content Type might consist of a Product Name, a Product Number and a Price. eZ Publish comes with a set of standard Content Types, but you are expected to add to these by use of the admin interface (Setup > Classes) to fit your project needs.

Each Field Definition is based on a Field Type. The Field Type dicates how data for a Field is stored, gathered and presented. Field Type examples include Text line for simple one-line input fields, XML text which provides a WYSIWYG editor as its default input, and many more.

Content can consist of multiple Translations and each time Content is edited, a new Version for the given Translation is created. Supported Translation Languages are managed in the admin interface (Setup > Languages).

NB. When creating or editing Content, a new Version is automatically created. Not publishing or discarding this Version will result in a lingering, unpublished Draft which in turn will produce a warning upon the next edit of the Content.

Content structure

eZ Publish structures Content into multiple tree structures. Each piece of Content has one or more Locations in the content tree. Locations serve as pointers to the actual Content and its Fields which are the same for all Locations.

eZ Publish offers three content trees by default, accessible under separate navigation panes in the admin interface:

  • Content: The default content tree for your web site/application
  • Media: Used to store media content like images, videos and files
  • Users: Used to store users and user groups (which are just another set of Content Types as far as eZ Publish is concerned)

In addition to structuring Content by tree, you can also structure by Section and Relation. A Section is assigned to a given subtree of a Location tree, while a Relation is created between singular pieces of Content. Relations can be managed either on a Content level basis (by editing a piece of Content) or on a Field level, by making use of one of the Relation Field Types.

Content permission

Access to content is limited by Policies. Policies are grouped into Roles which in turn can be assigned to either a User or an entire User Group. The Policies in eZ Publish are very granular, and can be managed in full in the admin interface (Users > Roles).

Content access

Content stored in eZ Publish is accessed in the context of a Siteaccess. A Siteaccess is a combination of settings like which language and through which set of templates you want to access the Content. When you run the eZ Publish setup wizard, Siteaccesses are created for the admin interface and each language you choose to install, but you can easily add to these.

The names of some of these concepts are new since the 5th generation of eZ Publish, but you’ll still find references to the old names in the eZ Publish admin interface. Hence, you might want to familiarize yourself with their old name equivalents.

Create a place for your code

Let’s get started with some code so we can see how this all ties together.

Since this is our first stab at writing eZ Publish code, we need some place to put it. As pr Symfony conventions, project code is stored in “bundles”, which can be created using the following command from within your eZ Publish root directory.

NB. Replace the NM namespace with something that signifies you or your company:

php ezpublish/console generate:bundle --namespace="NM/ClientWebsiteBundle"

This command kicks off a wizard with a few questions. Accept all the defaults, except for the questions:

  • Determine the format to use for the generated configuration
    • Select yml
  • To help you get started faster, the command can generate some code snippets for you
    • Select yes

This will create a new subdirectory in your src/ folder with your bundle containing a few dummy-files.

If you are familiar with Symfony (or any other type of PHP framework) you know that the name of the MVC game consists of mapping routes to controllers which makes use of some business logic and then returns a response. And if you wanted to, you could do exactly that, with the entire power of the Symfony stack at your disposal. For generic purposes, however, eZ Publish ships with its own routing and content view controller, so let’s learn how to utilize those first.

Routing and the View Controller

When you access an URL containing a host name which points to your eZ Publish installation, the eZ Publish routing will try to determine which piece of Content or, more specifically, Location you’re looking for. It does this by first figuring out the siteaccess, and then looking up the Location.

How the siteaccess discovery is done depends on your settings. If you left the defaults when running the eZ Publish setup wizard, your siteaccess mapping method will be URI, which means that eZ Publish will use the first section of your URI to determine your siteaccess. If it’s unable to find a match, the default siteaccess will be selected. Once a siteaccess has been determined, eZ knows, among other things, in which database your content resides, and which Translation you’re looking for.

Read more: Siteaccess matching

Next, eZ will examine the rest of the URI in an attempt to match it to a specific Content Location.

Content, when published, automatically gets assigned an SEO-friendly URI corresponding to each of its Locations in the content tree (you’ll recognize these from browsing your content tree in the admin interface). Hence, when you attempt to access http://yoursite.com/eng/News/Some-Article, eZ Publish knows that you’re looking for the article named “Some Article” located in the folder named “News”, in the context of the “eng” (for English) siteaccess.

Given that eZ is able to match your URI to a Location (if not, it will fall back to standard Symfony routes), the routing will send you to the viewLocation action of the generic ViewController with parameters set for the Location ID and “full” as the default view type. This action will use the Location ID to fetch the Location using the eZ Publish Public API, and make it accessible in a full view template. The decision of which full view template to render is handled by the view provider.

Specifying a template

You can dicate which template you want eZ Publish to use by manipulating the view provider settings. Your bundle-specific settings files go in your bundle’s Resources/config directory, and should have the .yml extension. The file can be named anything, but for clearity’s sake, let’s call it view.yml.

Before editing the settings file, we should make our eZ Publish installation aware that it exists. There are several ways to do this, but we’ll go for the easy one for now which is to import it into the main eZ Publish settings file.

Open up ezpublish/config/ezpublish.yml and add the following to the top of the file (rememember to update the NM part to reflect your own namespace):

imports:
    - {resource: @NMClientWebsiteBundle/Resources/config/view.yml}

Now we’re ready to tell the view provider which template to use when we request a specific piece of Content. Most of the time you want to map a template to a specific Content Type, so let’s stick to that example for now.

Put the following in your newly created view.yml file:

ezpublish: 
    system:
         ezdemo_site_group:
             location_view:
                 full:
                     folder:
                         template: "NMClientWebsiteBundle:Folder:full.html.twig"
                         match:
                             Identifier\ContentType: [folder]

Let’s examine what we’re doing here:

  • Line 3 tells us for which siteaccess, or in this case which siteaccess group, we want our settings to take effect. Here we use a frontend siteaccess group available by default from the eZ Demo Bundle. Ensure that the siteaccess you’ll be using as your frontend is listed among the ones in theezdemo_site_group in ezpublish/config/ezpublish.yml.
  • Line 4 is a specific settings block for the view provider dictating how you want locations displayed.
  • Line 5 tells us that we want to manipulate the “full” view type, which is the view type sent to the ViewController by default.
  • Line 6 assignes a unique string identifier to our ruleset.
  • Lines 7 through 9 contain the specific rules. In this case we say that we want to use the templateNMClientWebsiteBundle:Folder:full.html.twig if the Content Type of the Location we are accessing is a Folder.

Read more: Configuring the view provider

Folder is the identifier of the Folder Content Type which comes with the eZ Publish demo content. In your project you’ll want to create your own Content Types and a Location tree from those types, but in our tutorial we’ll use the eZ Demo Bundle dummy content to save time.

Creating a template

The template name NMClientWebsiteBundle:Folder:full.html.twig translates into the file location src/NM/ClientWebsiteBundle/Resources/views/Folder/full.html.twig in the Symfony world, so let’s create that file, and put the following in it:

{% extends noLayout ? viewbaseLayout : "eZDemoBundle::pagelayout.html.twig" %}
{% block content %}
    <h2>{{ ez_render_field( content, "name" ) }}</h2>
    {{ ez_render_field( content, "description" ) }}
{% endblock %}

To make sure that your changes have taken effect, clear the eZ Publish cache by running

php ezpublish/console cache:clear

...and then browse to a folder in your frontend siteaccess where you should see the title and the description of the folder presented in the eZ Demo bundle design.

Let’s spend a few minutes going over the template and what it does.

Basically, the template follows the Twig template syntax, which is the default template engine for Symfony, but adds to that a specific ez_render_field function for rendering an eZ Content Field. This function takes 2 parameters, a variable containing the Content, and the identifier of the Field to render. The Content is automatically passed to the template from the eZ ViewController as the variable content.

ez_render_field will output your field data with some basic HTML formatting, so if you just want the data, you can use ez_field_value instead.

Read more: eZ Publish Twig functions

Fetching and presenting child locations

The next, most common use case is usually to fetch the children of a given Location. For instance, you might want to fetch products stored as Locations beneath a given folder Location, as is the case in the Shopping > Products folder of the demo content. To do this we need a controller to fetch the products under a given folder, as well as a template to render them.

Expanding upon our previous example, edit your folder full template to reflect the following:

{% extends noLayout ? viewbaseLayout : "eZDemoBundle::pagelayout.html.twig" %}
{% block content %}
    <h2>{{ ez_render_field( content, "name" ) }}</h2>
     {{ ez_render_field( content, "description" ) }}
     {{ render(controller(
         'NMClientWebsiteBundle:Folder:products’,
         { 'folderId': content.versionInfo.contentInfo.mainLocationId }
     ))
     }}
{% endblock %}

The render is standard Symfony syntax for including a controller and print out its result in a template. As you can see, we pass the location id of the blog post as a variable named folderId to the controller.

Read more: Creating and using templates (Symfony docs)

Next step is to create the controller we’re calling and make it fetch the products located beneath. As mentioned in the eZ Publish documentation, the API provides multiple ways of fetching content, ranging from the very simple (but limited), to the very flexible (but verbose). For our example controller, we’ve chosen something inbetween, making use of the search service.

Read more: Browsing, finding and viewing content

Create the file src/NM/Bundle/ClientWebsiteBundle/Controller/FolderController.php and insert the following code (remember to update the namespace reference):

<?php
namespace NM\Bundle\ClientWebsiteBundle\Controller;
use eZ\Bundle\EzPublishCoreBundle\Controller;
use eZ\Publish\API\Repository\Values\Content\Query;
use eZ\Publish\API\Repository\Values\Content\Query\Criterion;
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
class FolderController extends Controller
{
    public function productsAction($folderId)
     {
         $query = new Query();
         $query->criterion = new Criterion\LogicalAnd(
             array(
                 new Criterion\ParentLocationId( $folderId ),
                 new Criterion\ContentTypeIdentifier( 'product' )
             )
         );
         $result = $this->getRepository()->getSearchService()->findContent($query);
         $products = array();
         foreach($result->searchHits as $hit)
         {
                 $products[] = $hit->valueObject;
         }
        return $this->render(
                 'NMClientWebsiteBundle:Folder:products.html.twig',
                 array('products' => $products)
         );
     }
}

The code above does a search for locations matching two criteria:

1. The parent location id should be equal to the folder ID passed from our template.

2. The content type identifier should match that of the Product content type.

Since the findContent method of the search service contains both the search results and the number of hits, we do some massaging before passing the data off to the template.

Read more:

Finally, we create a template called src/NM/Bundle/ClientWebsiteBundle/Resources/views/Folder/products.html.twig to format the products, looking something like this:

{% for product in products %}
    <div>
         <h3><a href="{{ path( "ez_urlalias", {"locationId": product.versionInfo.contentInfo.mainLocationId} ) }}">{{ ez_render_field( product, "name" ) }}</a></h3>
         {{ ez_render_field( product, "image" ) }}
     </div>
{% endfor %}

This template displays the product image, and the product name as a link to the product itself.

Read more: Things you can do in content view templates

Summary

This tutorial barely scratches the surface of what’s possible with eZ Publish. Still, it should hopefully contain sufficient information (and references) to help you get started implementing your first eZ Publish web site. Good luck

Eirik Alfstad Johansen

Eirik har arbeidet med webutvikling siden 1997, og har bla. blitt publisert i internasjonale publikasjoner både på og av nett. Han har også drevet netmaking.com, en internasjonal nettside for nettutviklere, samt det prisbelønte nyhetsbrevet Absolute Webmaster. I dag er Eirik Alfstad Johansen daglig leder i Netmaking.