on Thursday 7 October 2021

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+)

 

//src/Provider/RelatedVariableProvider.php

<?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:

 

//config/package/template_rules.yaml

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:

 

//src/Provider/RelatedVariableProvider.php

<?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:

 

//config/package/template_rules.yaml

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:

 

//src/Provider/RelatedParameterProvider.php

<?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:

 

//config/packages/template_rules.yaml

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):

 

//config/services.yaml

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