Paragraphs Editor
ParagraphsEditorRenderer.php
Go to the documentation of this file.
1 <?php
2 
4 
15 
16 /**
17  * A DOM processor plugin for rendering embedded paragraphs.
18  *
19  * @DomProcessorDataProcessor(
20  * id = "paragraphs_editor_renderer",
21  * label = "Paragraphs Editor Renderer"
22  * )
23  */
24 class ParagraphsEditorRenderer implements DataProcessorInterface, ContainerFactoryPluginInterface {
26 
27  /**
28  * The paragraph entity view builder.
29  *
30  * @var \Drupal\Core\Entity\EntityViewBuilderInterface
31  */
32  protected $viewBuilder;
33 
34  /**
35  * The renderer service for rendering paragraph views.
36  *
37  * @var \Drupal\Core\Render\RendererInterface
38  */
39  protected $renderer;
40 
41  /**
42  * Creates a paragraph renderer plugin.
43  *
44  * @param \Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface $field_value_manager
45  * The field value manager service to initialize the element trait.
46  * @param \Drupal\Core\Entity\EntityViewBuilderInterface $view_builder
47  * The view builder that will create render arrays for paragraphs that need
48  * to be rendered.
49  * @param \Drupal\Core\Render\RendererInterface $renderer
50  * The renderer service for rendering pargraph entities.
51  */
52  public function __construct(FieldValueManagerInterface $field_value_manager, EntityViewBuilderInterface $view_builder, RendererInterface $renderer) {
53  $this->initializeParagraphsEditorElementTrait($field_value_manager);
54  $this->viewBuilder = $view_builder;
55  $this->renderer = $renderer;
56  }
57 
58  /**
59  * {@inheritdoc}
60  */
61  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
62  return new static(
63  $container->get('paragraphs_editor.field_value.manager'),
64  $container->get('entity_type.manager')->getViewBuilder('paragraph'),
65  $container->get('renderer')
66  );
67  }
68 
69  /**
70  * {@inheritdoc}
71  */
72  public function process(SemanticDataInterface $data, DomProcessorResultInterface $result) {
73  if ($data->is($this->getSelector('widget'))) {
74  $entity = $data->get('paragraph.entity');
75  if ($entity) {
76  $markup = $this->render($data, $entity);
77  $result->replaceWithHtml($data, $markup);
78  return $result;
79  }
80  }
81  return $result;
82  }
83 
84  /**
85  * An internal utility function for rendering paragraphs.
86  *
87  * Since paragraphs editor fields are saved with the contents of their
88  * embedded paragraphs completely removed, calling render on the first entity
89  * process triggers a render on each child entity, and this behavior recurses
90  * down the tree.
91  *
92  * This render wrapper function eliminates these recursive renders by
93  * rendering the leaves of the tree first, caching the result, then working
94  * its way up the tree to render each level until it hits the top, thus
95  * avoiding deeply recursive rendering.
96  *
97  * @param \Drupal\dom_processor\DomProcessor\SemanticDataInterface $data
98  * The semantic data containing information about the paragraph to be
99  * rendered.
100  * @param \Drupal\paragraphs\ParagraphInterface $entity
101  * The paragraph to be rendered.
102  *
103  * @return string
104  * The rendered markup for the entity.
105  */
106  protected function render(SemanticDataInterface $data, ParagraphInterface $entity) {
107  $render_cache = &drupal_static(__CLASS__ . '::' . __FUNCTION__, []);
108  $langcode = $data->get('langcode');
109  $cache_key = $this->getCacheKey($entity, $langcode);
110 
111  if (empty($render_cache[$cache_key])) {
112  $to_process = [];
113  $to_render = [];
114 
115  array_push($to_process, $entity);
116  array_push($to_render, $entity);
117  while ($entity = array_shift($to_process)) {
118 
119  foreach ($entity->getFields() as $items) {
120  $field_definition = $items->getFieldDefinition();
121 
122  if ($this->fieldValueManager->isParagraphsEditorField($field_definition)) {
123  $wrapper = $this->fieldValueManager->wrapItems($items);
124  foreach ($wrapper->getReferencedEntities() as $child_entity) {
125  $to_render[] = $child_entity;
126  $to_process[] = $child_entity;
127  }
128  }
129  elseif ($this->fieldValueManager->isParagraphsField($field_definition)) {
130  foreach ($this->fieldValueManager->getReferencedEntities($items) as $child_entity) {
131  $to_process[] = $child_entity;
132  }
133  }
134  }
135  }
136 
137  $view_mode = $data->get('settings.view_mode');
138  while ($entity = array_pop($to_render)) {
139  $view = $this->viewBuilder->view($entity, $view_mode, $langcode);
140  $render_cache[$this->getCacheKey($entity, $langcode)] = $this->renderer->render($view);
141  }
142  }
143 
144  return $render_cache[$cache_key];
145  }
146 
147  /**
148  * Gets a static cache key for en entity.
149  *
150  * @param \Drupal\paragraphs\ParagraphInterface $entity
151  * The entity to get the static cache key for.
152  * @param string $langcode
153  * The language code for additional cache context.
154  *
155  * @return string
156  * The static cache key.
157  */
158  protected function getCacheKey(ParagraphInterface $entity, $langcode) {
159  $keys = [$entity->uuid(), $entity->getRevisionId()];
160  if ($langcode) {
161  $keys[] = $langcode;
162  }
163  return implode(':', $keys);
164  }
165 
166 }
process(SemanticDataInterface $data, DomProcessorResultInterface $result)
__construct(FieldValueManagerInterface $field_value_manager, EntityViewBuilderInterface $view_builder, RendererInterface $renderer)
static create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition)
initializeParagraphsEditorElementTrait(FieldValueManagerInterface $field_value_manager)