Skip to content

Instantly share code, notes, and snippets.

@bdunogier
Last active March 8, 2016 14:52
Show Gist options
  • Save bdunogier/a7c3aac71718d5466dc4 to your computer and use it in GitHub Desktop.
Save bdunogier/a7c3aac71718d5466dc4 to your computer and use it in GitHub Desktop.
Platform beginner tutorial

Tutorial

install clean

Project structure

eZ Platform follows the same conventions than Symfony Standard 2.8. This includes the same project structure, as described in the Symfony documentation "Creating the project" chapter.

There are a couple parts that are important to go through this tutorial.

The app folder

app contains most of eZ Platform / symfony application: configuration, cache, kernel, templates, translations...

** SCREENSOT OF THE ROOT STRUCTURE with app open**

app/config : the application's configuration

This is where the application is configured. It contains several files, each dedicated to specific parts. Most of them are similar to the ones that come up with symfony standard: config.yml, security.yml, routing.yml...

Read more: Configuration on symfony.com

parameters.yml contains infrastructure related settings: database credentials, solr uri... it is not versioned, since it is generated based on parameters.yml.dist.

ezplatform.yml contains the base configuration to get your ezplatform instance to run. It defines the default siteaccess requests are executed in. You can read more about Siteaccess in the documentation, but you don't need to right now.

Files that are suffixed with _dev, _prod and _behat are per environment files. They are loaded depending on the current environment, and include the file with the same name without the prefix: in dev environment, app/config/ezplatform_dev.yml will be loaded first, and will itself load ezplatform.yml. This allows for configuration of the application depending on the environment being executed.

app/Resources: templates and translations

The easiest way to add templates to your applications is to store them in the app/Resources/views folder. The hierarchy of that folder is completely up to you.

eZ Platform uses the Twig template language. If you're not familiar with it, we will explain the concepts used throughout this tutorial. You can also read the templating chapter of the Symfony doc to go more in depth.

app/AppKernel.php

This is the application's kernel. By default, it mostly loads the Bundles that make up the application. This is where you will enable newly installed or created bundles. AppBundle is already enabled.

When adding new bundles, we recommend that you add yours at the end of the bundles array. It will avoid conflicts when upgrading.

Symfony doc: the Kernel Class

src/AppBundle custom code

This is where custom application code will be stored. The first use-case in this tutorial will be custom controller.

If your application grows more complex, we recommend that you create other bundles for specific areas of the domain.

web/public: assets

This is where you can place your application's assets (css, javascript, images, ...).

You may also place assets in src/AppBundle/Resources/public. In that case, remember to add AppBundle to the assetic bundles list, and run app/console assets:install. As long as you don't need to organize things differently, we recommend that the application's assets are placed directly in web, as they can be referenced by their (shorter) relative path.

Customizing the general layout

We will begin by customizing global layout of our site, in order to end up with this rendering:

SCREENSHOT OF THE HOME

First, go to the root of your ezplatform site. You should view the root Folder of the clean install, without any kind of layout. You can go to /ez, edit this Content, and see that this page changes. When / is requested, eZ Platform renders the root Content using the ez_content:viewContent controller. We will customize this step, by instructing Platform to use a custom template to render this particular item.

eZ Platform organizes content as a tree. Each content item is referenced by a location, and each location as a parent. The root content location has the ID 2 by default.

Content rendering configuration

To use a custom template when rendering the root content, let's create a content_view configuration block for ezpublish.

We will use the default namespace, but we could have used any siteaccess instead. Edit app/config/ezplatform.yml. At the end, add the following block, right after the language configuration (pay attention to spacing: default should be at the same level than site_group):

		default:
		    content_view:
		        full:
			        root_folder:
			            template: "content/view/full/root_folder.html.twig"
			            match:
				            Id\Location: 2

This tells Platform to use the template when rendering any content referenced by the location with the id 2. There is a whole set of view matchers that can be used to customize rendering depending on any kind of circumstance.

Creating the template

Download index.html, and save inside app/Resources/views as as content/view/full/root_folder.html.twig.

Refresh the site's root, and you should see the site's structure, but without any styles or images. Let's fix this.

Edit the root_folder.html.twig template.

Fixing the assets

The first thing to do is fix the loading of the stylesheets, scripts and design images.

Download assets.zip, and unpack its contents to the web directory of your project. You will end up with web/assets/, containing css, js and images subfolders. In the template, in the<html> section, change the <style> tags about bootstrap and custom css lines with the following code:

{% stylesheets 'assets/css/*' filter='cssrewrite' %}
    <link rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}

As explained on the Symfony assetic doc, this will iterate over the files in web/assets/css, and load them as stylesheets. Refresh the page, and you should now see the static design of the site. At the bottom of the template, you will find <script> tags that load jQuery and Bootstrap javascript. Replace them with an equivalent block for scripts:

{% javascripts 'assets/js/*' %}
    <script src="{{ asset_url }}"></script>
{% endjavascripts %}

Let's finish this by fixing the design images. Locate the the <img> tag with "images/128_bike_white_avenir.png". Change the src to {{ asset('assets/images/128_bike_white_avenir.png') }}:

<img alt="Brand" src="{{ asset('assets/images/128_bike_white_avenir.png') }}">

Do the same for "images/logo_just_letters.png", and refresh the page. The design should now be in order, with the logo, fonts and colors.

Rendering the content

At this point, the root_folder.html.twig template is static. It doesn't render any dynamic data from the repository.

The root is rendered by the ez_content:viewAction controller action. This action assigns the currently viewed content as the content twig variable. We will use that variable to display some of the fields from the root content. Replace the central section of the template, around line 90, with the following block:

<section class="buttons">
    <div class="container">
        <div class="row regular-content-size">
            <div class="col-xs-10 col-xs-offset-1 box-style">
                <h3 class="center bottom-plus new-header">{{ ez_content_name(content) }}</h3>
                <div class="col-xs-10 text-justified">{{ ez_render_field(content, 'description') }}</div>
            </div>
        </div>
    </div>
</section>

The page will now show the values of title and description fields of the root Platform Content.

Extracting the layout

The general layout of the site, with the navigation, footer, scripts... is written down in the template we use to render the root. Let's extract the part that is common to all the pages so that we can re-use it.

Twig supports a powerful template inheritance api. Templates may declare named blocks. Any template mayextend another templates, and modify the blocks defined by its parents.

Create a new app/Resources/views/pagelayout.html.twig template, and copy the contents of the current root_folder.html.twig into it. Change the central section from the previous chapter as follows:

<section class="buttons">
    <div class="container">
        <div class="row regular-content-size">
            <div class="col-xs-10 col-xs-offset-1 box-style">
                {% block content %}
                {% endblock %}
            </div>
        </div>
    </div>
</section>

This defines a block named content. Other templates can add content to it, so that the result of the execution of the controller is contained within the site's general layout.

Edit root_folder.html.twig, and remove everything. Replace it with the following code:

{% extends "pagelayout.html.twig" %}

{% block content %}
<h3 class="center bottom-plus new-header">{{ ez_content_name(content) }}</h3>
<div class="col-xs-10 text-justified">{{ ez_render_field(content, 'description') }}</div>
{% endblock %}

This will re-use pagelayout.html.twig, and replace the content block with the title and description from the root folder. We could easily create more blocks in the pagelayout so that templates can modify other parts of the page (footer, head, navigation), and we will over the course of this tutorial. We can now create more templates that inherit from pagelayout.html.twig, and customize how content is rendered.

Let's do it for the Ride content type.

Rendering a ride

The first thing we need to do is access one of the ride content items. Since we don't have any navigation yet, we need to cheat a bit. Go to the backoffice (/ez), and navigate to a Ride content in the tree. Go to the details tab, and find the content id.

Edit full/root_folder.html.twig, and add the following line at the beginning of the content block, replacing 1234 with your content id:

<a href="{{ path( ez_urlalias( {"contentId", 1234} ) }}">Ride content item</a>

What we did here is generate a path() to the url alias of the content item with the id 1234. An url alias is a user friendly virtual URL mapped to a location. They are automatically generated by the Repository. When an URL alias is used to access the site, it is dynamically routed to ez_content:viewAction() to render the mapped content.

Refresh the homepage, and click on the "Ride content item" link. You should now see the default template, without the pagelayout. Let's first create the template, and enable it for content items of type ride.

Edit app/config/ezplatform.yml, and add a new block at the same level than root_folder:

full_ride:
    template: "content/view/full/ride.html.twig"
    match:
        Identifier\ContentType: "ride"

The root_folder rule matched on the content's location id. This one matches on the "ride" content type identifier. Create the template, and override the content block using the HTML from the static pages:

{% extends "pagelayout.html.twig" %}

{% block content %}
    <!-- Content -->
    <section class="below-navbar">
        <div class="container">
            <div class="row regular-content-size">
                <div class="col-xs-10 col-xs-offset-1 row-padding">
                    {#% include "ride_search.html.twig" %#}
                    <div class="col-xs-12">
                        <div class="col-xs-1 text-left">
                            <h2>{{ "Ride:"|trans }}</h2>
                        </div>
                        <div class="col-xs-11 text-left">
                            <h2 class="extra-padding-left">{{ ez_content_name(content) }}</h2>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <section class="first-below-navbar">
        <div class="container">
            <div class="row regular-content-size">
                <div class="col-xs-10 col-xs-offset-1">
                    <h4 class="underscore-green"> </h4>
                </div>
                <div class="col-xs-10 col-xs-offset-1 padding-box">
                    <div class="col-xs-2">
                        <div class="box-ride">
                            <h3 class="special-number">{{ ez_field_value(content, "length").value }} Km</h3>
                            <p class="pre-small special-number special-number-margin"><strong>{{ "Level"|trans }}</strong></p>
                            <p class="text-center">
                                {% set difficulty = ez_field_value(content, "difficulty").selection|first %}
                                {% for i in 0..difficulty %}<i class="fa fa-star star-color"></i>{% endfor %}
                            </p>
                        </div>
                    </div>
                    <div class="col-xs-10 bottom-extra">
                        <div class="col-xs-2 text-right">
                            <p>{{ "Name:"|trans }}</p>
                        </div>
                        <div class="col-xs-4">
                            <p class="strong">
                                {% for rider in ez_field_value(content, "riders").authors %}
                                    {{ rider.name }}
                                    {% if not loop.last %}<br>{% endif %}
                                {% endfor %}
                            </p>
                        </div>
                        <div class="col-xs-1 text-right">
                            <p>{{ "Start:"|trans }}</p>
                            <p>{{ "End:"|trans }}</p>
                        </div>
                        <div class="col-xs-5">
                            <p class="strong">{{ ez_field_value(content, "start_point") }}</p>
                            <p class="strong">{{ ez_field_value(content, "end_point") }}</p>
                        </div>
                    </div>
                    <div class="col-xs-10">
                        <div class="col-xs-2 text-right">
                            <p>{{ "Description:"|trans }}</p>
                        </div>
                        <div class="col-xs-10 text-justify">
                            {{ ez_render_field(content, "description") }}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <!-- BEGIN points of interest -->
    <section class="photos">
        <div class="container">
            <div class="row regular-content-size">
                <div class="col-xs-10 col-xs-offset-1" id="wrapper">
                    <div class="col-xs-12">
                        <h4 class="underscore-green">Points of interest:</h4>
                        {{ ez_render_field(content, 'points_of_interest', { "template": _self }) }}
                    </div>
                </div>
            </div>
        </div>
    </section>
    <!-- END points of interest -->

    <section class="buttons">
        <div class="container">
            <div class="row regular-content-size">
                <div class="col-xs-5 button-padding">
                    <a href="#" class="button pull-right"><i class="fa fa-print fa-lg"></i> {{ "PRINT RIDE"|trans }}</a>
                </div>
                <div class="col-xs-5 col-xs-offset-2 button-padding">
                    <a href="#" class="button pull-left"><i class="fa fa-download fa-lg"></i> {{ "DOWNLOAD"|trans }}</a>
                </div>
            </div>
        </div>
    </section>
{% endblock %}

{% block ezobjectrelationlist_field %}
    {% if not ez_is_field_empty( content, 'points_of_interest' ) %}
        {% for contentId in ez_field_value(content, 'points_of_interest').destinationContentIds %}
            {% if loop.index > 0 and loop.index is divisible by(3) %}
                <div class="col-xs-12 padding-box"></div>
            {% endif %}

            {{ render( controller( "ez_content:viewAction", {'contentId': contentId, 'viewType': 'embed'} ) ) }}
        {% endfor %}
    {% endif %}
{% endblock %}

Next

  • Ride list template ?
@bdunogier
Copy link
Author

So far, that version covers:

  • a presentation of the application's structure, with links to both sf and ez docs
  • steps and explanations of involved concepts to create the pagelayout, add the assets, link to a ride and display (partial) a ride.

The dev part would I guess happen after a presentation of the backoffice, and after creating at least the ride content type and a content of that type. I'm not a big fan of the content type creation part. The process is imho a bit tedious.

I'm considering writing a small console script that creates the content type, and walk the user through the result. An alternative would be to begin small, with only a couple field definitions. Then add new ones, and update the template after each addition. This could work. For instance, the map could be added as a second or third step, and the templating code introduced at the same time.

@mere-teresa
Copy link

I'm not a big fan of the content type creation part. The process is imho a bit tedious.

I understand but imho :
1/ It is a good introduction to content types and fieldtypes so a good intro to eZ P features.
2/ It's easy (not too technical)
3/ Can we provide a Content Type ready-to-import ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment