Ibexa 3 VariableProvider, successor of eZCoreExtraBundle ParameterProvider

on Thursday 7 October 2021

Before Ibexa 3, a great way to inject variables into content view templates was to use eZCoreExtraBundle ParameterProvider. Now, with Ibexa 3, there is a way to do the same without the need for a third party bundle.

First example

As an example, let's write a VariableProvider that loads all contents from one relation list field of our viewed content. (example code for PHP 7.4+)

 

   

<?php 
declare(strict_types=1); 
namespace App\Provider; 
use ArrayObject;
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\Core\FieldType\RelationList;
use eZ\Publish\Core\MVC\Symfony\View\ContentView;
use eZ\Publish\Core\MVC\Symfony\View\View;
use eZ\Publish\SPI\MVC\View\VariableProvider; 
class RelatedVariableProvider implements VariableProvider
{
    private ContentService $contentService; 
    public function __construct(ContentService $contentService)
    {
        $this->contentService = $contentService;
    } 
    public function getIdentifier(): string
    {
        // Will be used in config
        return 'related';
    } 
    public function getTwigVariables(View $view, array $options = []): object
    {
        /** @var ContentView $view */
        $content = $view->getContent(); 
        /** @var RelationList\Value $value */
        $value = $content->getFieldValue('media'); 
        // Return value is typed object, so we simple use ArrayObject
        return new ArrayObject(array_map(
            fn (int $contentId) => $this->contentService->loadContent($contentId),
            $value->destinationContentIds
        ));
    }
} 
   

Now, let's add it in our view configuration:

 

   

ezplatform:
  system:
    default:
      content_view:
        full:
          article:
            template: "@ezdesign/content/full/article.html.twig"
            match:
              Identifier\ContentType: article
            params:
              # Calling the VariableProvider is done by the ExpressionLanguage
              medias: '@=twig_variable_provider("related")' 
   

Going further

Ok, but what if I want to choose the field identifier? Sadly, unlike ParameterProvider, it's not possible to pass options directly (twig_variable_provider accepts only one argument: the provider identifier). But, since the VariableProvider is called through the ExpressionLanguage, we can call a method on the VariableProvider return value, with any argument we want.

Let's change our VariableProvider a bit:

 

   

<?php 
declare(strict_types=1); 
namespace App\Provider; 
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\Core\FieldType\RelationList;
use eZ\Publish\Core\MVC\Symfony\View\ContentView;
use eZ\Publish\Core\MVC\Symfony\View\View;
use eZ\Publish\SPI\MVC\View\VariableProvider; 
class RelatedVariableProvider implements VariableProvider
{
    private ContentService $contentService; 
    public function __construct(ContentService $contentService)
    {
        $this->contentService = $contentService;
    } 
    public function getIdentifier(): string
    {
        return 'related';
    } 
    public function getTwigVariables(View $view, array $options = []): object
    {
        return $this;
    } 
    public function load(View $view, string $fieldIdentifier): array
    {
        /** @var ContentView $view */
        $content = $view->getContent(); 
        /** @var RelationList\Value $value */
        $value = $content->getFieldValue($fieldIdentifier); 
        return array_map(
            fn(int $contentId) => $this->contentService->loadContent($contentId),
            $value->destinationContentIds
        );
    }
} 
   

The getTwigVariables method will simply return the provider itself, so we can call any method on it in the expression.

Let's change our view configuration accordingly:

 

   

ezplatform:
  system:
   default:
     content_view:
       full:
         article:
           template: "@ezdesign/content/full/article.html.twig"
           match: Identifier\ContentType: article
           params:
             # Calling the VariableProvider is done by the ExpressionLanguage
             medias: '@=twig_variable_provider("related").load(view, "media")' 
   

Migrating from eZCoreExtraBundle ParameterProvider

Let's see what our example would look like as a ParameterProvider, and list the differences:

 

   

<?php 
declare(strict_types=1); 
namespace App\Provider; 
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\Core\FieldType\RelationList;
use eZ\Publish\Core\MVC\Symfony\View\ContentView;
use Lolautruche\EzCoreExtraBundle\View\ConfigurableView;
use Lolautruche\EzCoreExtraBundle\View\ViewParameterProviderInterface; 
class RelatedParameterProvider implements ViewParameterProviderInterface
{
    private ContentService $contentService; 
    public function __construct(ContentService $contentService)
    {
        $this->contentService = $contentService;
    } 
    public function getViewParameters(ConfigurableView $view, array $options = [])
    {
        /** @var ContentView $view */
        $content = $view->getContent(); 
        /** @var RelationList\Value $value */
        $value = $content->getFieldValue($options['field_identifier']); 
        return array_map(
            fn(int $contentId) => $this->contentService->loadContent($contentId),
            $value->destinationContentIds
        );
    }
} 
   

The view configuration:

 

   

ezplatform:
  system:
    default:
      content_view:
        full:
          article:
            template: "@ezdesign/content/full/article.html.twig"
            match: Identifier\ContentType: article
            params:
              medias:
                provider: related
                options:
                  field_identifier: media 
   

And the service configuration (assuming autowiring and autoconfiguration are enabled):

 

   

services:
  App\Provider\RelatedParameterProvider:
    tags:
      - { name: "ez_core_extra.view_parameter_provider", alias: "related" } 
   

To migrate that ParameterProvider:

  • remove the service configuration, as the tag is handled by autoconfiguration
  • replace ViewParameterProviderInterface by VariableProvider
  • add getIdentifier method
  • add getTwigVariables method that simply return $this
  • optionnaly change getViewParameters method name or signature to better suit your needs
  • change the view configuration