Paragraphs Editor
FieldValueManager.php
Go to the documentation of this file.
1 <?php
2 
4 
12 
13 /**
14  * Manages the paragraphs editor field values.
15  */
17 
18  /**
19  * The storage plugin for the paragraph entity type.
20  *
21  * @var \Drupal\Core\Entity\EntityStorageInterface
22  */
23  protected $storage;
24 
25  /**
26  * The storage plugin for the paragraph type config entity type.
27  *
28  * @var \Drupal\Core\Entity\EntityStorageInterface
29  */
30  protected $bundleStorage;
31 
32  /**
33  * The field value manager service for collecting field information.
34  *
35  * @var \Drupal\Core\Entity\EntityFieldManagerInterface
36  */
38 
39  /**
40  * Element definitions for custom elements that can occur in an editor field.
41  *
42  * @var array
43  */
44  protected $elements;
45 
46  /**
47  * A static cache of paragraph revisions.
48  *
49  * @var \Drupal\paragraphs\ParagraphInterface[]
50  */
51  protected $revisionCache = [];
52 
53  /**
54  * Creates a field value manager object.
55  *
56  * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
57  * The field manager service.
58  * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
59  * The entity type manager service.
60  * @param array $elements
61  * An array of widget binder element definitions.
62  */
63  public function __construct(EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, array $elements) {
64  $this->entityFieldManager = $entity_field_manager;
65  $this->storage = $entity_type_manager->getStorage('paragraph');
66  $this->bundleStorage = $entity_type_manager->getStorage('paragraphs_type');
67  $this->elements = $elements;
68  }
69 
70  /**
71  * {@inheritdoc}
72  */
73  public function getReferencedEntities(EntityReferenceRevisionsFieldItemList $items) {
74  $entities = [];
75  foreach ($items as $item) {
76  $value = $item->getValue();
77  if (!empty($value['entity']) && $value['entity'] instanceof ParagraphInterface) {
78  $entities[] = $item->entity;
79  }
80  elseif ($item->target_revision_id !== NULL) {
81  if (!empty($this->revisionCache[$item->target_revision_id])) {
82  $entities[] = $this->revisionCache[$item->target_revision_id];
83  }
84  else {
85  $entity = $this->storage->loadRevision($item->target_revision_id);
86  $entity = TypeUtility::ensureParagraph($entity);
87  $this->revisionCache[$item->target_revision_id] = $entity;
88  $entities[] = $entity;
89  }
90  }
91  }
92  return $entities;
93  }
94 
95  /**
96  * {@inheritdoc}
97  */
98  public function wrapItems(EntityReferenceRevisionsFieldItemList $items) {
99  $field_definition = TypeUtility::ensureFieldConfig($items->getFieldDefinition());
100  if (!$this->isParagraphsEditorField($field_definition)) {
101  throw new \Exception('Attempt to wrap non-paragraphs editor field.');
102  }
103 
104  // Build a list of refrenced entities and filter out the text entities.
105  $settings = $field_definition->getThirdPartySettings('paragraphs_editor');
106  $markup = '';
107  $entities = [];
108  $text_entity = NULL;
109 
110  foreach ($this->getReferencedEntities($items) as $entity) {
111  if ($entity->bundle() == $settings['text_bundle']) {
112  $markup .= $entity->{$settings['text_field']}->value;
113  if (!$text_entity) {
114  $text_entity = $entity;
115  }
116  }
117  else {
118  $entities[$entity->uuid()] = $entity;
119  }
120  }
121 
122  // If there is no text entity we need to create one.
123  if (!$text_entity) {
124  $text_entity = TypeUtility::ensureParagraph($this->storage->create([
125  'type' => $settings['text_bundle'],
126  ]));
127  }
128 
129  // Reset the text entity markup in case we merged multiple text entities.
130  $text_entity->{$settings['text_field']}->value = $markup;
131  if (empty($text_entity->{$settings['text_field']}->format) && !empty($settings['filter_format'])) {
132  $text_entity->{$settings['text_field']}->format = $settings['filter_format'];
133  }
134 
135  return new FieldValueWrapper($field_definition, $text_entity, $entities);
136  }
137 
138  /**
139  * {@inheritdoc}
140  */
141  public function prepareEntityForSave($entity, $new_revision, $langcode) {
142  $entity->setNewRevision($new_revision);
143 
144  if (isset($langcode) && $entity->get('langcode') != $langcode) {
145  if ($entity->hasTranslation($langcode)) {
146  $entity = $entity->getTranslation($langcode);
147  }
148  else {
149  $entity->set('langcode', $langcode);
150  }
151  }
152 
153  $entity->setNeedsSave(TRUE);
154 
155  return $entity;
156  }
157 
158  /**
159  * {@inheritdoc}
160  */
161  public function setItems(EntityReferenceRevisionsFieldItemList $items, array $entities, $new_revision = FALSE, $langcode = NULL) {
162  $values = [];
163  $delta = 0;
164  foreach ($entities as $entity) {
165  $entity = $this->prepareEntityForSave($entity, $new_revision, $langcode);
166  $values[$delta]['entity'] = $entity;
167  $values[$delta]['target_id'] = $entity->id();
168  $values[$delta]['target_revision_id'] = $entity->getRevisionId();
169  $delta++;
170  }
171 
172  $items->setValue($values);
173  $items->filterEmptyItems();
174  return $items;
175  }
176 
177  /**
178  * {@inheritdoc}
179  */
180  public function getTextBundles(array $allowed_bundles = []) {
181 
182  if (empty($allowed_bundles)) {
183  $results = $this->bundleStorage->getQuery()->execute();
184  if (is_array($results)) {
185  foreach ($results as $name) {
186  $allowed_bundles[$name] = [
187  'label' => $this->bundleStorage->load($name)->label(),
188  ];
189  }
190  }
191  }
192 
193  $bundles = [];
194  foreach ($allowed_bundles as $name => $type) {
195  $text_fields = $this->getTextFields($name);
196  if (count($text_fields) == 1) {
197  $bundles[$name] = [
198  'label' => $type['label'],
199  'text_field' => reset($text_fields),
200  ];
201  }
202  }
203  return $bundles;
204  }
205 
206  /**
207  * {@inheritdoc}
208  */
209  public function isParagraphsField(FieldDefinitionInterface $field_definition) {
210  if ($field_definition->getType() != 'entity_reference_revisions') {
211  return FALSE;
212  }
213  $field_definition = TypeUtility::ensureFieldConfig($field_definition);
214 
215  $target_type = $field_definition->getFieldStorageDefinition()->getSetting('target_type');
216  return $target_type == 'paragraph';
217  }
218 
219  /**
220  * {@inheritdoc}
221  */
222  public function isParagraphsEditorField(FieldDefinitionInterface $field_definition) {
223  if (!$this->isParagraphsField($field_definition)) {
224  return FALSE;
225  }
226  $field_definition = TypeUtility::ensureFieldConfig($field_definition);
227 
228  // We only every allow this widget to be applied to fields that have
229  // unlimited cardinality. Otherwise we'd have to deal with keeping track of
230  // how many paragraphs are in the Editor instance.
231  $cardinality = $field_definition->getFieldStorageDefinition()->getCardinality();
232  if ($cardinality != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
233  return FALSE;
234  }
235 
236  // Make sure it is a pragraphs editor enabled field.
237  $settings = $field_definition->getThirdPartySettings('paragraphs_editor');
238  return !empty($settings['enabled']);
239  }
240 
241  /**
242  * {@inheritdoc}
243  */
244  public function getTextFields($bundle_name) {
245  $matches = [];
246  $field_definitions = $this->entityFieldManager->getFieldDefinitions('paragraph', $bundle_name);
247  foreach ($field_definitions as $field_definition) {
248  if ($this->isTextField($field_definition)) {
249  $matches[] = $field_definition->getName();
250  }
251  }
252  return $matches;
253  }
254 
255  /**
256  * {@inheritdoc}
257  */
258  public function getElement($element_name) {
259  return isset($this->elements[$element_name]) ? $this->elements[$element_name] : NULL;
260  }
261 
262  /**
263  * {@inheritdoc}
264  */
265  public function getAttributeName($element_name, $attribute_name) {
266  $element = $this->getElement($element_name);
267  if (!empty($element['attributes'])) {
268  $map = array_flip($element['attributes']);
269  $key = !empty($map[$attribute_name]) ? $map[$attribute_name] : NULL;
270  return $key;
271  }
272  else {
273  return NULL;
274  }
275  }
276 
277  /**
278  * {@inheritdoc}
279  */
280  public function getSelector($element_name) {
281  $element = $this->getElement($element_name);
282  $selector = !empty($element['tag']) ? $element['tag'] : '';
283  if (!empty($element['attributes']['class'])) {
284  $classes = explode(' ', $element['attributes']['class']);
285  $selector .= '.' . implode('.', $classes);
286  }
287  return $selector;
288  }
289 
290  /**
291  * Helper function to check if a field is a text field.
292  *
293  * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
294  * The field to check.
295  *
296  * @return bool
297  * TRUE if it's a paragraphs editor approved text field, FALSE otherwise.
298  */
299  protected function isTextField(FieldDefinitionInterface $field_definition) {
300  return $field_definition->getType() == 'text_long';
301  }
302 
303 }
static ensureParagraph(EntityInterface $entity=NULL)
Definition: TypeUtility.php:23
setItems(EntityReferenceRevisionsFieldItemList $items, array $entities, $new_revision=FALSE, $langcode=NULL)
isParagraphsField(FieldDefinitionInterface $field_definition)
wrapItems(EntityReferenceRevisionsFieldItemList $items)
isTextField(FieldDefinitionInterface $field_definition)
__construct(EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, array $elements)
getReferencedEntities(EntityReferenceRevisionsFieldItemList $items)
static ensureFieldConfig(FieldDefinitionInterface $field_definition=NULL)
Definition: TypeUtility.php:39
isParagraphsEditorField(FieldDefinitionInterface $field_definition)