Completed
Push — feature/linting ( f97c5c...925690 )
by Christopher
02:13
created

FieldValueManager::ensureParagraph()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
rs 9.4285
1
<?php
2
3
namespace Drupal\paragraphs_editor\EditorFieldValue;
4
5
use Drupal\Core\Entity\EntityFieldManagerInterface;
6
use Drupal\Core\Entity\EntityInterface;
7
use Drupal\Core\Entity\EntityTypeManagerInterface;
8
use Drupal\Core\Field\FieldConfigInterface;
9
use Drupal\Core\Field\FieldDefinitionInterface;
10
use Drupal\Core\Field\FieldStorageDefinitionInterface;
11
use Drupal\entity_reference_revisions\EntityReferenceRevisionsFieldItemList;
12
use Drupal\paragraphs\ParagraphInterface;
13
14
/**
15
 * Manages the paragraphs editor field values.
16
 */
17
class FieldValueManager implements FieldValueManagerInterface {
18
19
  /**
20
   * The storage plugin for the paragraph entity type.
21
   *
22
   * @var \Drupal\Core\Entity\EntityStorageInterface
23
   */
24
  protected $storage;
25
26
  /**
27
   * The storage plugin for the paragraph type config entity type.
28
   *
29
   * @var \Drupal\Core\Entity\EntityStorageInterface
30
   */
31
  protected $bundleStorage;
32
33
  /**
34
   * The field value manager service for collecting field information.
35
   *
36
   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
37
   */
38
  protected $entityFieldManager;
39
40
  /**
41
   * Element definitions for custom elements that can occur in an editor field.
42
   *
43
   * @var array
44
   */
45
  protected $elements;
46
47
  /**
48
   * A static cache of paragraph revisions.
49
   *
50
   * @var \Drupal\paragraphs\ParagraphInterface[]
51
   */
52
  protected $revisionCache = [];
53
54
  /**
55
   * Creates a field value manager object.
56
   *
57
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
58
   *   The field manager service.
59
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
60
   *   The entity type manager service.
61
   * @param array $elements
62
   *   An array of widget binder element definitions.
63
   */
64
  public function __construct(EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, array $elements) {
65
    $this->entityFieldManager = $entity_field_manager;
66
    $this->storage = $entity_type_manager->getStorage('paragraph');
67
    $this->bundleStorage = $entity_type_manager->getStorage('paragraphs_type');
68
    $this->elements = $elements;
69
  }
70
71
  /**
72
   * {@inheritdoc}
73
   */
74
  public function getReferencedEntities(EntityReferenceRevisionsFieldItemList $items) {
75
    $entities = [];
76
    foreach ($items as $item) {
77
      $value = $item->getValue();
78
      if (!empty($value['entity']) && $value['entity'] instanceof ParagraphInterface) {
79
        $entities[] = $item->entity;
80
      }
81
      elseif ($item->target_revision_id !== NULL) {
82
        if (!empty($this->revisionCache[$item->target_revision_id])) {
83
          $entities[] = $this->revisionCache[$item->target_revision_id];
84
        }
85
        else {
86
          $entity = $this->storage->loadRevision($item->target_revision_id);
87
          $entity = $this->ensureParagraph($entity);
88
          $this->revisionCache[$item->target_revision_id] = $entity;
89
          $entities[] = $entity;
90
        }
91
      }
92
    }
93
    return $entities;
94
  }
95
96
  /**
97
   * {@inheritdoc}
98
   */
99
  public function wrapItems(EntityReferenceRevisionsFieldItemList $items) {
100
    $field_definition = $items->getFieldDefinition();
101
    if (!$this->isParagraphsEditorField($field_definition)) {
102
      throw new \Exception('Attempt to wrap non-paragraphs editor field.');
103
    }
104
105
    // Build a list of refrenced entities and filter out the text entities.
106
    $settings = $field_definition->getThirdPartySettings('paragraphs_editor');
107
    $markup = '';
108
    $entities = [];
109
    $text_entity = NULL;
110
111
    foreach ($this->getReferencedEntities($items) as $entity) {
112
      if ($entity->bundle() == $settings['text_bundle']) {
113
        $markup .= $entity->{$settings['text_field']}->value;
114
        if (!$text_entity) {
115
          $text_entity = $entity;
116
        }
117
      }
118
      else {
119
        $entities[$entity->uuid()] = $entity;
120
      }
121
    }
122
123
    // If there is no text entity we need to create one.
124
    if (!$text_entity) {
125
      $text_entity = $this->ensureParagraph($this->storage->create([
126
        'type' => $settings['text_bundle'],
127
      ]));
128
    }
129
130
    // Reset the text entity markup in case we merged multiple text entities.
131
    $text_entity->{$settings['text_field']}->value = $markup;
132
    if (empty($text_entity->{$settings['text_field']}->format) && !empty($settings['filter_format'])) {
133
      $text_entity->{$settings['text_field']}->format = $settings['filter_format'];
134
    }
135
136
    return new FieldValueWrapper($field_definition, $text_entity, $entities);
0 ignored issues
show
Compatibility introduced by
$field_definition of type object<Drupal\Core\Field...eldDefinitionInterface> is not a sub-type of object<Drupal\Core\Field\FieldConfigInterface>. It seems like you assume a child interface of the interface Drupal\Core\Field\FieldDefinitionInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
137
  }
138
139
  /**
140
   * {@inheritdoc}
141
   */
142
  public function prepareEntityForSave($entity, $new_revision, $langcode) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
143
    $entity->setNewRevision($new_revision);
144
145
    if (isset($langcode) && $entity->get('langcode') != $langcode) {
146
      if ($entity->hasTranslation($langcode)) {
147
        $entity = $entity->getTranslation($langcode);
148
      }
149
      else {
150
        $entity->set('langcode', $langcode);
151
      }
152
    }
153
154
    $entity->setNeedsSave(TRUE);
155
156
    return $entity;
157
  }
158
159
  /**
160
   * {@inheritdoc}
161
   */
162
  public function setItems(EntityReferenceRevisionsFieldItemList $items, array $entities, $new_revision = FALSE, $langcode = NULL) {
163
    $values = [];
164
    $delta = 0;
165
    foreach ($entities as $entity) {
166
      $entity = $this->prepareEntityForSave($entity, $new_revision, $langcode);
167
      $values[$delta]['entity'] = $entity;
168
      $values[$delta]['target_id'] = $entity->id();
169
      $values[$delta]['target_revision_id'] = $entity->getRevisionId();
170
      $delta++;
171
    }
172
173
    $items->setValue($values);
174
    $items->filterEmptyItems();
175
    return $items;
176
  }
177
178
  /**
179
   * {@inheritdoc}
180
   */
181
  public function getTextBundles(array $allowed_bundles = []) {
182
183
    if (!empty($allowed_bundles)) {
184
      $results = $this->bundleStorage->getQuery()->execute();
185
      if (is_array($results)) {
186
        foreach ($results as $name) {
187
          $allowed_bundles[$name] = [
188
            'label' => $this->bundleStorage->load($name)->label(),
189
          ];
190
        }
191
      }
192
    }
193
194
    $bundles = [];
195
    foreach ($allowed_bundles as $name => $type) {
196
      $text_fields = $this->getTextFields($name);
197
      if (count($text_fields) == 1) {
198
        $bundles[$name] = [
199
          'label' => $type['label'],
200
          'text_field' => reset($text_fields),
201
        ];
202
      }
203
    }
204
    return $bundles;
205
  }
206
207
  /**
208
   * {@inheritdoc}
209
   */
210
  public function isParagraphsField(FieldDefinitionInterface $field_definition) {
211
    $field_definition = $this->ensureFieldConfig($field_definition);
212
213
    if ($field_definition->getType() != 'entity_reference_revisions') {
214
      return FALSE;
215
    }
216
217
    $target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type');
218
    return $target_type == 'paragraph';
219
  }
220
221
  /**
222
   * {@inheritdoc}
223
   */
224
  public function isParagraphsEditorField(FieldDefinitionInterface $field_definition) {
225
    $field_definition = $this->ensureFieldConfig($field_definition);
226
227
    if (!$this->isParagraphsField($field_definition)) {
228
      return FALSE;
229
    }
230
231
    // We only every allow this widget to be applied to fields that have
232
    // unlimited cardinality. Otherwise we'd have to deal with keeping track of
233
    // how many paragraphs are in the Editor instance.
234
    $cardinality = $field_definition->getFieldStorageDefinition()->getCardinality();
235
    if ($cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
236
      return FALSE;
237
    }
238
239
    // Make sure it is a pragraphs editor enabled field.
240
    $settings = $field_definition->getThirdPartySettings('paragraphs_editor');
241
    return !empty($settings['enabled']);
242
  }
243
244
  /**
245
   * {@inheritdoc}
246
   */
247
  public function getTextFields($bundle_name) {
248
    $matches = [];
249
    $field_definitions = $this->entityFieldManager->getFieldDefinitions('paragraph', $bundle_name);
250
    foreach ($field_definitions as $field_definition) {
251
      if ($this->isTextField($field_definition)) {
252
        $matches[] = $field_definition->getName();
253
      }
254
    }
255
    return $matches;
256
  }
257
258
  /**
259
   * {@inheritdoc}
260
   */
261
  public function getElement($element_name) {
262
    return isset($this->elements[$element_name]) ? $this->elements[$element_name] : NULL;
263
  }
264
265
  /**
266
   * {@inheritdoc}
267
   */
268
  public function getAttributeName($element_name, $attribute_name) {
269
    $element = $this->getElement($element_name);
270
    if (!empty($element['attributes'])) {
271
      $map = array_flip($element['attributes']);
272
      $key = !empty($map[$attribute_name]) ? $map[$attribute_name] : NULL;
273
      return $key;
274
    }
275
    else {
276
      return NULL;
277
    }
278
  }
279
280
  /**
281
   * {@inheritdoc}
282
   */
283
  public function getSelector($element_name) {
284
    $element = $this->getElement($element_name);
285
    $selector = !empty($element['tag']) ? $element['tag'] : '';
286
    if (!empty($element['attributes']['class'])) {
287
      $classes = explode(' ', $element['attributes']['class']);
288
      $selector .= '.' . implode('.', $classes);
289
    }
290
    return $selector;
291
  }
292
293
  /**
294
   * Helper function to check if a field is a text field.
295
   *
296
   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_config
297
   *   The field to check.
298
   *
299
   * @return bool
300
   *   TRUE if it's a paragraphs editor approved text field, FALSE otherwise.
301
   */
302
  protected function isTextField(FieldDefinitionInterface $field_config) {
303
    return $field_config->getType() == 'text_long';
304
  }
305
306
  /**
307
   * Enforces that an entity is a paragraph entity.
308
   *
309
   * @return \Drupal\paragraphs\ParagraphInterface|null
310
   *   The filtered entity.
311
   */
312
  protected function ensureParagraph(EntityInterface $entity = NULL) {
313
    if (!$entity instanceof ParagraphInterface) {
314
      throw new \Exception('Not a paragraph.');
315
    }
316
    return $entity;
317
  }
318
319
  /**
320
   * Enforces that an entity is a field config entity.
321
   *
322
   * @param \Drupal\Core\Field\FieldDefinitionInterface|null $field_definition
323
   *
324
   * @return \Drupal\Core\Field\FieldConfigInterface
325
   *   The config object.
326
   */
327
  protected function ensureFieldConfig(FieldDefinitionInterface $field_definition = NULL) {
328
    if (!$field_definition instanceof FieldConfigInterface) {
329
      throw new \Exception('Not a field config.');
330
    }
331
    return $field_definition;
332
  }
333
334
}
335