WidgetBinderDataCompiler::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 3
dl 0
loc 4
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Drupal\paragraphs_editor\WidgetBinder;
4
5
use Drupal\Core\Entity\EntityTypeManagerInterface;
6
use Drupal\Core\Render\Element;
7
use Drupal\Core\Render\RenderContext;
8
use Drupal\Core\Render\RendererInterface;
9
use Drupal\paragraphs\ParagraphInterface;
10
use Drupal\paragraphs_editor\EditBuffer\EditBufferItemInterface;
11
use Drupal\paragraphs_editor\EditorCommand\CommandContextInterface;
12
use Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface;
13
14
/**
15
 * The default implementation of the widget binder data model compiler.
16
 */
17
class WidgetBinderDataCompiler implements WidgetBinderDataCompilerInterface {
18
19
  /**
20
   * The paragraph entity view builder.
21
   *
22
   * @var \Drupal\Core\Entity\EntityViewBuilderInterface
23
   */
24
  protected $viewBuilder;
25
26
  /**
27
   * The renderer service.
28
   *
29
   * @var \Drupal\Core\Render\RendererInterface
30
   */
31
  protected $renderer;
32
33
  /**
34
   * The paragraphs editor field value manager.
35
   *
36
   * @var \Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface
37
   */
38
  protected $fieldValueManager;
39
40
  /**
41
   * The generator objects to run on compile.
42
   *
43
   * @var \Drupal\paragraphs_editor\WidgetBinder[]
44
   */
45
  protected $generators = [];
46
47
  /**
48
   * Creates a WidgetBinderDataCompiler.
49
   *
50
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
51
   *   The entity type manager service to get the paragraph view builder from.
52
   * @param \Drupal\Core\Render\RendererInterface $renderer
53
   *   The renderer service for rendering the paragraph.
54
   * @param \Drupal\paragraphs_editor\EditorFieldValue\FieldValueManagerInterface $field_value_manager
55
   *   The field value manager service for reading paragraphs editor field
56
   *   information.
57
   */
58 2
  public function __construct(EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer, FieldValueManagerInterface $field_value_manager) {
59 2
    $this->viewBuilder = $entity_type_manager->getViewBuilder('paragraph');
60 2
    $this->renderer = $renderer;
61 2
    $this->fieldValueManager = $field_value_manager;
62 2
  }
63
64
  /**
65
   * {@inheritdoc}
66
   */
67 2
  public function addGenerator(GeneratorInterface $generator) {
68 2
    $this->generators[$generator->id()] = $generator;
69 2
  }
70
71
  /**
72
   * {@inheritdoc}
73
   */
74 1
  public function compile(CommandContextInterface $context, EditBufferItemInterface $item, $view_mode = 'full', $langcode = NULL) {
75 1
    $data = new WidgetBinderData();
76 1
    $state = new WidgetBinderDataCompilerState($this->generators, $data, $context, $item);
77 1
    $paragraph = $item->getEntity();
78 1
    $this->applyGenerators('initialize', $data, $state, $paragraph);
79 1
    $this->traverseParagraph($data, $state, $paragraph);
80
81
    // Attach the view builder that will decorate the view with information
82
    // needed to make nested paragraphs into nested editables.
83 1
    $render_context = new RenderContext();
84 1
    $view = $this->viewBuilder->view($paragraph, $view_mode, $langcode);
85 1
    $this->processElement($view, $state);
86
87
    // Render the rendered markup for the view and collect any bubbled
88
    // attachment information from the context.
89 1
    $renderer = $this->renderer;
90 1
    $markup = $renderer->executeInRenderContext($render_context, function () use ($renderer, $view) {
91 1
      return $renderer->render($view);
92 1
    });
93
94 1
    $this->applyGenerators('complete', $data, $state, $render_context, $markup);
95 1
    return $data;
96
  }
97
98
  /**
99
   * Pre-render callback for attaching the editor state to the render array.
100
   *
101
   * @param array $build
102
   *   The render array to operate on.
103
   *
104
   * @return array
105
   *   The updated render array with the attached state.
106
   */
107 1
  public function buildView(array $build) {
108 1
    $state = $build['#paragraphs_editor_state'];
109
110 1
    foreach (Element::children($build) as $field_name) {
111 1
      if (!empty($build[$field_name]['#items'])) {
112 1
        $items = $build[$field_name]['#items'];
113 1
        $field_definition = $items->getFieldDefinition();
114
115 1
        if ($this->fieldValueManager->isParagraphsField($field_definition)) {
116 1
          $this->processElement($build[$field_name], $state, FALSE);
117
118 1
          if (!$this->fieldValueManager->isParagraphsEditorField($field_definition)) {
119 1
            foreach (Element::children($build[$field_name]) as $delta) {
120 1
              $this->processElement($build[$field_name][$delta], $state);
121
            }
122
          }
123
        }
124
      }
125
    }
126
127 1
    return $build;
128
  }
129
130
  /**
131
   * Marks an element for editor state attachment.
132
   *
133
   * @param array &$element
134
   *   The element to attach the state to.
135
   * @param \Drupal\paragraphs_editor\WidgetBinder\WidgetBinderDataCompilerState $state
136
   *   The state to attach.
137
   * @param bool $process_children
138
   *   TRUE if children should also be passed through the pre-render attachment
139
   *   processor, or if the state attachment should end at this render node.
140
   *   Defaults to TRUE.
141
   */
142 1
  protected function processElement(array &$element, WidgetBinderDataCompilerState $state, $process_children = TRUE) {
143 1
    $element['#paragraphs_editor_state'] = $state;
144 1
    if ($process_children) {
145 1
      $element['#pre_render'][] = [$this, 'buildView'];
146
    }
147 1
  }
148
149
  /**
150
   * Traverses the entire paragraph tree for a paragraph, applying generators.
151
   *
152
   * Each paragraph reference field is recursively followed so that the entire
153
   * tree below the paragraph is covered.
154
   *
155
   * @param \Drupal\paragraphs_editor\WidgetBinder\WidgetBinderData $data
156
   *   The data being compiled.
157
   * @param \Drupal\paragraphs_editor\WidgetBinder\WidgetBinderDataCompilerState $state
158
   *   The current state of the compiler.
159
   * @param \Drupal\paragraphs\ParagraphInterface $paragraph
160
   *   The paragraph to traverse.
161
   */
162 1
  protected function traverseParagraph(WidgetBinderData $data, WidgetBinderDataCompilerState $state, ParagraphInterface $paragraph) {
163 1
    $this->applyGenerators('processParagraph', $data, $state, $paragraph);
164
165 1
    foreach ($paragraph->getFields() as $items) {
166 1
      $field_definition = $items->getFieldDefinition();
167 1
      if ($this->fieldValueManager->isParagraphsField($field_definition)) {
168 1
        $is_editor_field = $this->fieldValueManager->isParagraphsEditorField($field_definition);
169 1
        $this->applyGenerators('processField', $data, $state, $items, $is_editor_field);
170
171 1
        if (!$is_editor_field) {
172 1
          foreach ($this->fieldValueManager->getReferencedEntities($items) as $child_paragraph) {
173 1
            $this->traverseParagraph($data, $state, $child_paragraph);
174
          }
175
        }
176
177 1
        $this->applyGenerators('postprocessField', $data, $state, $items, $is_editor_field);
178
      }
179
    }
180
181 1
    $this->applyGenerators('postprocessParagraph', $data, $state, $paragraph);
182 1
  }
183
184
  /**
185
   * Runs the widget binder data generators.
186
   *
187
   * This function accepts a variable number of arguments. The first argument is
188
   * always the name of the method to invoke on the generator. The rest of the
189
   * arguments are passed through to the generator.
190
   */
191 1
  protected function applyGenerators() {
192 1
    $args = func_get_args();
193 1
    $method = array_shift($args);
194 1
    foreach ($this->generators as $generator) {
195 1
      call_user_func_array([$generator, $method], $args);
196
    }
197 1
  }
198
199
}
200