First draft preview in eZ Publish 5

The eZ Publish 5 series has been a successful step forward for eZ Publish with the adoption of the Symfony stack and the ability to run dual kernels with the new and legacy stacks. However, content preview functionality has gone through many fixes through the 5.x version. The final fix comes in eZ Publish 5.4.5, which is very important since eZ Publish 5.4 is a milestone release that will be used as a stepping stone for upgrades through to the 6.x series aka eZ Platform! Here's how to update your code to make your editors happy with proper content preview!

Let's start with an example full view controller. You are using a custom controller for a blog post because you need to fetch the contents of the author field, which is a single object relation.

publicfunction showBlogPostAction($locationId,$viewType,$layout=false,array$params=array()){$repository=$this->getRepository();$location=$repository->getLocationService()->loadLocation($locationId);$content=$repository->getContentService()->loadContentByContentInfo($location->getContentInfo());// For demo purposes we'll presume this field always has content and the relation is not in the trash$authorID=$content->getFieldValue('author')->destinationContentId;$author=$repository->getContentService()->loadContent($authorID);$params['author']=$author;// Forward the request to the original ViewController$response=$this->get('ez_content')->viewLocation($locationId,$viewType,$layout,$params);return$response;}

Unfortunately, if you preview the first draft of a blog post with this code (even on eZ Publish 5.4.5), you would get a complete break with the error: "Invalid or Empty Node passed to getItem constructor."

With the fix from this issue that is in eZ Publish 5.4.5, you can type hint the first parameter of your controller to be a location instead of a location ID -- the location ID being null for first drafts and causing the crash.

With this fix you have to make a couple more changes: add a "use" statement for the Location type hint so that the code knows which type of object to expect, and then use the $location object.

// ... namespace and other use statements here
use eZ\Publish\API\Repository\Values\Content\Location;// ... your actual class and other actions herepublicfunction showBlogPostAction( Location $location,$viewType,$layout=false,array$params=array()){$repository=$this->getRepository();// Note: don't need to load the location since it's already been passed to us$content=$repository->getContentService()->loadContentByContentInfo($location->getContentInfo());// This part is the same as before// For demo purposes we'll presume this field always has content and the relation is not in the trash$relatedAuthorID=$content->getFieldValue('single_relation')->destinationContentId;$relatedAuthor=$repository->getContentService()->loadContent($relatedAuthorID);$params['related_author']=$relatedAuthor;// Forward the request to the original ViewController// Pass the location ID if available$response=$this->get('ez_content')->viewLocation($location->id ? :$location,$viewType,$layout,$params);return$response;}

Lastly, in your Twig templates, do a quick check for a location ID whenever you need to use the location. For example, if you are outputting a canonical URL, you can omit it if the location ID is null:

{{ location.id ? url( location )}}

You can alternatively check location.isDraft in your templates. As a whole, it's quite a straightforward update to finally get content preview fully working!