vendor/sonata-project/admin-bundle/src/Twig/RenderElementRuntime.php line 43

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of the Sonata Project package.
  5.  *
  6.  * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  7.  *
  8.  * For the full copyright and license information, please view the LICENSE
  9.  * file that was distributed with this source code.
  10.  */
  11. namespace Sonata\AdminBundle\Twig;
  12. use Sonata\AdminBundle\FieldDescription\FieldDescriptionInterface;
  13. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  14. use Twig\Environment;
  15. use Twig\Extension\RuntimeExtensionInterface;
  16. use Twig\TemplateWrapper;
  17. final class RenderElementRuntime implements RuntimeExtensionInterface
  18. {
  19.     /**
  20.      * @var PropertyAccessorInterface
  21.      */
  22.     private $propertyAccessor;
  23.     /**
  24.      * @internal This class should only be used through Twig
  25.      */
  26.     public function __construct(PropertyAccessorInterface $propertyAccessor)
  27.     {
  28.         $this->propertyAccessor $propertyAccessor;
  29.     }
  30.     /**
  31.      * render a list element from the FieldDescription.
  32.      *
  33.      * @param object|mixed[]       $listElement
  34.      * @param array<string, mixed> $params
  35.      */
  36.     public function renderListElement(
  37.         Environment $environment,
  38.         $listElement,
  39.         FieldDescriptionInterface $fieldDescription,
  40.         array $params = []
  41.     ): string {
  42.         $template $this->getTemplate(
  43.             $fieldDescription,
  44.             $fieldDescription->getAdmin()->getTemplateRegistry()->getTemplate('base_list_field'),
  45.             $environment
  46.         );
  47.         [$object$value] = $this->getObjectAndValueFromListElement($listElement$fieldDescription);
  48.         return $this->render($fieldDescription$templatearray_merge($params, [
  49.             'admin' => $fieldDescription->getAdmin(),
  50.             'object' => $object,
  51.             'value' => $value,
  52.             'field_description' => $fieldDescription,
  53.         ]), $environment);
  54.     }
  55.     public function renderViewElement(
  56.         Environment $environment,
  57.         FieldDescriptionInterface $fieldDescription,
  58.         object $object
  59.     ): string {
  60.         $template $this->getTemplate(
  61.             $fieldDescription,
  62.             '@SonataAdmin/CRUD/base_show_field.html.twig',
  63.             $environment
  64.         );
  65.         return $this->render($fieldDescription$template, [
  66.             'field_description' => $fieldDescription,
  67.             'object' => $object,
  68.             'value' => $fieldDescription->getValue($object),
  69.             'admin' => $fieldDescription->getAdmin(),
  70.         ], $environment);
  71.     }
  72.     /**
  73.      * render a compared view element.
  74.      *
  75.      * @param mixed $baseObject
  76.      * @param mixed $compareObject
  77.      */
  78.     public function renderViewElementCompare(
  79.         Environment $environment,
  80.         FieldDescriptionInterface $fieldDescription,
  81.         $baseObject,
  82.         $compareObject
  83.     ): string {
  84.         $template $this->getTemplate(
  85.             $fieldDescription,
  86.             '@SonataAdmin/CRUD/base_show_field.html.twig',
  87.             $environment
  88.         );
  89.         $baseValue $fieldDescription->getValue($baseObject);
  90.         $compareValue $fieldDescription->getValue($compareObject);
  91.         $baseValueOutput $template->render([
  92.             'admin' => $fieldDescription->getAdmin(),
  93.             'field_description' => $fieldDescription,
  94.             'value' => $baseValue,
  95.             'object' => $baseObject,
  96.         ]);
  97.         $compareValueOutput $template->render([
  98.             'field_description' => $fieldDescription,
  99.             'admin' => $fieldDescription->getAdmin(),
  100.             'value' => $compareValue,
  101.             'object' => $compareObject,
  102.         ]);
  103.         // Compare the rendered output of both objects by using the (possibly) overridden field block
  104.         $isDiff $baseValueOutput !== $compareValueOutput;
  105.         return $this->render($fieldDescription$template, [
  106.             'field_description' => $fieldDescription,
  107.             'value' => $baseValue,
  108.             'value_compare' => $compareValue,
  109.             'is_diff' => $isDiff,
  110.             'admin' => $fieldDescription->getAdmin(),
  111.             'object' => $baseObject,
  112.             'object_compare' => $compareObject,
  113.         ], $environment);
  114.     }
  115.     /**
  116.      * @param mixed $element
  117.      *
  118.      * @throws \RuntimeException
  119.      *
  120.      * @return mixed
  121.      */
  122.     public function renderRelationElement($elementFieldDescriptionInterface $fieldDescription)
  123.     {
  124.         if (!\is_object($element)) {
  125.             return $element;
  126.         }
  127.         $propertyPath $fieldDescription->getOption('associated_property');
  128.         if (null === $propertyPath) {
  129.             if (!method_exists($element'__toString')) {
  130.                 throw new \RuntimeException(sprintf(
  131.                     'You must define an `associated_property` option or create a `%s::__toString` method'
  132.                     .' to the field option %s from service %s is ',
  133.                     \get_class($element),
  134.                     $fieldDescription->getName(),
  135.                     $fieldDescription->getAdmin()->getCode()
  136.                 ));
  137.             }
  138.             return $element->__toString();
  139.         }
  140.         if (\is_callable($propertyPath)) {
  141.             return $propertyPath($element);
  142.         }
  143.         return $this->propertyAccessor->getValue($element$propertyPath);
  144.     }
  145.     /**
  146.      * Extracts the object and requested value from the $listElement.
  147.      *
  148.      * @param object|mixed[] $listElement
  149.      *
  150.      * @throws \TypeError when $listElement is not an object or an array with an object on offset 0
  151.      *
  152.      * @return mixed[] An array containing object and value
  153.      *
  154.      * @phpstan-return array{0: object, 1: mixed}
  155.      */
  156.     private function getObjectAndValueFromListElement(
  157.         $listElement,
  158.         FieldDescriptionInterface $fieldDescription
  159.     ): array {
  160.         if (\is_object($listElement)) {
  161.             $object $listElement;
  162.         } elseif (\is_array($listElement)) {
  163.             if (!isset($listElement[0]) || !\is_object($listElement[0])) {
  164.                 throw new \TypeError(sprintf('If argument 1 passed to %s() is an array it must contain an object at offset 0.'__METHOD__));
  165.             }
  166.             $object $listElement[0];
  167.         } else {
  168.             throw new \TypeError(sprintf('Argument 1 passed to %s() must be an object or an array, %s given.'__METHOD__, \gettype($listElement)));
  169.         }
  170.         if (\is_array($listElement) && \array_key_exists($fieldDescription->getName(), $listElement)) {
  171.             $value $listElement[$fieldDescription->getName()];
  172.         } else {
  173.             $value $fieldDescription->getValue($object);
  174.         }
  175.         return [$object$value];
  176.     }
  177.     /**
  178.      * @param array<string, mixed> $parameters
  179.      */
  180.     private function render(
  181.         FieldDescriptionInterface $fieldDescription,
  182.         TemplateWrapper $template,
  183.         array $parameters,
  184.         Environment $environment
  185.     ): string {
  186.         $content $template->render($parameters);
  187.         if ($environment->isDebug()) {
  188.             $commentTemplate = <<<'EOT'
  189. <!-- START
  190.     fieldName: %s
  191.     template: %s
  192.     compiled template: %s
  193.     -->
  194.     %s
  195. <!-- END - fieldName: %s -->
  196. EOT;
  197.             return sprintf(
  198.                 $commentTemplate,
  199.                 $fieldDescription->getFieldName(),
  200.                 $fieldDescription->getTemplate() ?? '',
  201.                 $template->getSourceContext()->getName(),
  202.                 $content,
  203.                 $fieldDescription->getFieldName()
  204.             );
  205.         }
  206.         return $content;
  207.     }
  208.     private function getTemplate(
  209.         FieldDescriptionInterface $fieldDescription,
  210.         string $defaultTemplate,
  211.         Environment $environment
  212.     ): TemplateWrapper {
  213.         $templateName $fieldDescription->getTemplate() ?? $defaultTemplate;
  214.         return $environment->load($templateName);
  215.     }
  216. }