eZ Community » Blogs » Jérôme Vieilledent » eZ Publish 5 from a technical point...

By

eZ Publish 5 from a technical point of view, Part 2

Monday 20 August 2012 10:13:12 am

  • Currently 5 out of 5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

After some weeks of extensive development at eZ engineering, it’s time to summarize a bit what has been done.

EDIT: The eZ Publish 5 story continues with  Real-life REST with eZ Publish 5

Full Composer usage

One of the first things we needed to do was to simplify a bit the installation process. The previous install document stated about a git submodule pointing to another repository (ezp-next, where most of the code is located). It has led some of you stuck because, for some obscure git reason, the submodule refused to install correctly.

So the decision was made to fully take advantage of Composer. All dependencies are now explicitely declared in composer.json, including ezp-next. In the long run, eZ Publish will probably get its entry in the Packagist database, but it’s still a bit early for now.

Upgrade

If you already installed eZ Publish 5 the old fashioned way and want to take advantage of all recent development, here’s the steps to follow :

  1. Update your git clone (git pull --rebase)
  2. Remove all dependencies by deleting the whole vendor/ directory
  3. Install the latest version of composer (php composer.phar selfupdate)
  4. Re-install the dependencies (php composer.phar install)

ViewController in eZ Publish 5

SiteAccess

As you probably know, the siteaccess system is central in eZ Publish and opens the doors to multi-sites installation. So it wouldn’t be a surprise for you to learn that we focused a lot on it lately.

In eZ Publish 5, a SiteAccess is an an object, exposed as a service (and thus easy to get, without having to manipulate some obscure globals). This SiteAccess object basically holds at least its name, the matching type used (as a string) and the matcher. It can also hold arbitrary attributes, in the form of a ParameterBag object from Symfony HttpFoundation component.

Matching

A significant difference with the eZ Publish 4.x system is that individual objects are used to match a SiteAccess. This is more powerful because it is now extensible thanks to dependency injection. All matchers must implement eZ\Publish\MVC\SiteAccess\Matcher and are processed in the SiteAccess router, which happens very early in the request lifecycle (before routing). 

In your configuration file, you’ll basically have to list the different matchers you want to use. By using a FQN (full qualified name) as a class name, you are able to point to a custom matcher, all relative class names starting from \eZ\Publish\MVC\SiteAccess\Matcher (so in parameters.yml.dist example, Map\URI will map to \eZ\Publish\MVC\SiteAccess\Matcher\Map\URI). For more information about siteaccess matching configuration, check the PHPDoc from the siteaccess router.

Hint : Inspecting the match() method from the siteaccess router, you might notice that it’s also possible to match a siteaccess by environment variable. This is interesting performance wise since no matcher is called this way.

 

In some cases it’s also necessary to clean up the URI after having matched a siteaccess (this is most likely the case when the siteaccess is part of the URI). In such cases, the matcher can implement the URILexer interface. Methods from this interface will be called after matching and before generating links. The fixed-up URI will then be part of the Request object as the semanticPathinfo attribute.

 

Injection in legacy

In order not to match the siteaccess more than once (that is once from eZ Publish 5 and once from legacy), the matched siteaccess is injected in the legacy kernel as soon as it is needed. Of course the array format is still being used here so that the legacy kernel perfectly understands it.

This is done thanks to an event triggered just before the build of the legacy kernel. The legacy bundle defines an event listener that does the job.

Limitation

Currently, almost every 4.x matching type is implemented except host_uri. It will be implemented as soon as possible through a new possibility to combine matchers together.

Session

Thanks to efforts from Damien, session is now fully handled by the Symfony stack through HttpFoundation, using its own mechanism and session handlers.

In a multi-siteaccess environment, it is still possible to use a different session name per siteaccess (see comments in provided config.yml). 

To keep session compatibility, a dedicated session handler has been developed in eZ Publish legacy (though it should probably better reside in the legacy bundle).

Please note that this part is not entirely stable yet. If you find any issue, please report it :-)

ContentView

Last but not least, content view controller is finally implemented ! It uses the brand new public API and brings several new interesting concepts.

HTTP cache

Formerly called view cache is now entirely HTTP based thanks to HttpFoundation and HttpKernel components. HTTP cache has always been very powerful and not using it would have been shameful ;-). The great advantage of it is that it can work with a reverse proxy like Varnish or out of the box thanks to the Symfony built-in reverse proxy.

The model used for content viewing is the validation model, based on Etag and Last-Modified headers. The combination of these 2 headers avoids having to clear the cache when a new content is published, which is really nice since according to Phil Karlton:

There are only two hard things in Computer Science: cache invalidation and naming things

The validation model seems to be sufficient, but we’re currently planning to also add some expiration stuff in there, potentially adding a huge performance boost.

ViewManager and ContentViewProvider

OK, we now have the content returned by the public API, but how will it match my template ? This is what the ViewManager is designed for.

The role of the ViewManager is to select the right template for displaying a given content or location. It aggregates objects called content view providers that implement eZ\Publish\MVC\View\ContentViewProvider interface.

Each time a content is to be displayed through the Content\ViewController, the ViewManager iterates over the registered ContentViewProvider objects and calls either getViewForContent() or getViewForLocation() depending on the context. 

Unfortunately, there is currently no configuration based content view provider yet. But for testing, it is fairly easy to develop a small custom content view provider.

Injecting variables in the view

Another exciting thing, it is possible to dynamically inject variables in content view templates. This is made possible by listening to the ezpublish.pre_content_view event. The event listener method receives an eZ\Publish\MVC\Event\PreContentViewEvent object.

See an example.

When to develop a custom ContentViewProvider

  • You want a custom template selection based on a very specific state of your application
  • You depend on external resources for view selection
  • You want to override the (upcoming) default one, based on configuration, for some reason

Render embedded content

I’m sure you use massively node_view_gui eZ 4.x template function to render an embedded node. Well, behind it you will find a lot of spaghetti code with some eZObjectForwarder magic inside (I emptied several aspirin boxes working on it). The main problem here is that node_view_gui (and other related template functions) doesn’t have the same behaviour than content/view module, particularly when it comes to view cache. 

It’s over in eZ Publish 5. Whenever you need to render a content, you’ll just have to run a subrequest against the ContentView controller ! 

{% render "ez_content:viewLocation" with {"locationId": 123, "viewMode": "line"} %}

Plus you’ll be able to take advantage of ESI and have a separated cache handling for each content (no more cache-block headaches !). 

{% render "ez_content:viewLocation" with {"locationId": 123, "viewMode": "line"}, {"standalone": true} %}

Or you can even use the new asynchronous rendering feature from Symfony 2.1 with the help of hinclude.js library !

{% render "ez_content:viewLocation" with {"locationId": 123, "viewMode": "line"}, {"standalone": "js"} %}

No more fetch !

Fetch functions are probably one of the most used feature in eZ Publish. With them you can grab some data (mainly content) and display it. The problem is that they make the MVC separation of concerns flawed because they add controller logic in the view layer. By extension, they make templates fat, with a lot of business logic, and sometimes (often?) un-readable.

In eZ Publish 5, you won’t have fetch functions any more. As a replacement, you will have to use subrequests calling other controllers, either yours or some from eZ Publish core, doing all the business logic to prepare the data to display.

Yes, we know how it sounds, this is an important shift. But the View layer is supposed to display things, not interact with them. And it brings a lot more of flexibility because you’ll not be in front of a monolith, wondering if your request will correctly work or not. You’ll be able to use your own logic, using all the power and granularity of the public API (don’t worry, examples will come later) and dependency injection.

Backwards compatibility

Once again, a lot of efforts were made for BC, especially regarding the ContentView controller.

Where is my template?

You might have wondered "Will my old template override defined in override.ini continue to work?". The answer is YES . This has been made possible by the ContentViewProvider system. A legacy content view provider has been introduced, with low priority, so that if no Twig template could be found to match this or that content, it will ask the legacy kernel to render the content. And to include the rendered content (aka the old $module_result.content), the decorator pattern is used so that you can specify which Twig template you want to use as a layout (see the implementation).

Of course, there are still missing parts such as persistent variable usage, css/js defined with ezjscore...

Where is my datatype?

What if your content uses a datatype that has not been migrated yet to the new public API ? Normally, the API will throw an exception saying that no converter could be found (that is to say : no object capable to convert the stored value in an appropriate object usable by the API). Well, in such case again a BC fallback as been set so that content handling is delegated to the legacy kernel (including template), so that your content will be correctly displayed, even with a non-supported datatype.

However this fallback is currently limited to the ContentView controller and thus not your own usage of the public API. As such, this will probably change in the future.

Conclusion

I hope this blog post will help you giving more insight to eZ Publish 5 ! More will come of course :-).

Proudly Developed with from