Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 122 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 1 |
BuilderDataConverter | |
0.00% |
0 / 122 |
|
0.00% |
0 / 14 |
1260 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
convertFromManageDisplay | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
convertFromLayoutBuilder | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
convertSingleField | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
2 | |||
convertExtraField | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
convertUiPatternsLayout | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
42 | |||
moveUiStylesAttributesSource | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
convertLayout | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
extractUiStylesData | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
convertLayoutBuilderComponent | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
56 | |||
convertFieldBlock | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
convertExtraFieldBlock | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
convertUiPatternsBlock | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
convertBlock | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace Drupal\display_builder_entity_view; |
6 | |
7 | use Drupal\Component\Plugin\Definition\PluginDefinitionInterface; |
8 | use Drupal\Component\Utility\NestedArray; |
9 | use Drupal\Component\Utility\SortArray; |
10 | use Drupal\Core\Block\BlockPluginInterface; |
11 | use Drupal\Core\Entity\EntityFieldManagerInterface; |
12 | use Drupal\layout_builder\Section; |
13 | use Drupal\layout_builder\SectionComponent; |
14 | |
15 | /** |
16 | * Converter data between Layout Builder and Display Builder. |
17 | */ |
18 | class BuilderDataConverter { |
19 | |
20 | public function __construct( |
21 | protected EntityFieldManagerInterface $entityFieldManager, |
22 | ) {} |
23 | |
24 | /** |
25 | * Convert "Manage display" formatters to UI Patterns sources. |
26 | * |
27 | * @param string $entity_type |
28 | * Entity type ID. |
29 | * @param string $bundle |
30 | * Bundle. |
31 | * @param array $fields |
32 | * Configuration of activated fields. |
33 | * |
34 | * @return array |
35 | * List of UI Patterns sources. |
36 | */ |
37 | public function convertFromManageDisplay(string $entity_type, string $bundle, array $fields): array { |
38 | $sources = []; |
39 | \uasort($fields, [SortArray::class, 'sortByWeightElement']); |
40 | |
41 | $definitions = $this->entityFieldManager->getFieldDefinitions($entity_type, $bundle); |
42 | |
43 | foreach ($fields as $field_id => $field) { |
44 | if (!isset($field['type'])) { |
45 | // Probably an extra field. |
46 | $sources[] = $this->convertExtraField($field_id); |
47 | |
48 | // @todo Do we need to check if it is really an extra field? |
49 | continue; |
50 | } |
51 | |
52 | if (!$definitions[$field_id]->isDisplayConfigurable('view')) { |
53 | // Hidden from Manage Display. |
54 | continue; |
55 | } |
56 | $sources[] = $this->convertSingleField($entity_type, $bundle, $field_id, $field); |
57 | } |
58 | |
59 | return $sources; |
60 | } |
61 | |
62 | /** |
63 | * Convert from Layout Builder. |
64 | * |
65 | * @param array $sections |
66 | * A list of layout builder section, so the root of Layout Builder data. |
67 | * |
68 | * @return array |
69 | * A list of UI Patterns sources. |
70 | */ |
71 | public function convertFromLayoutBuilder(array $sections): array { |
72 | $sources = []; |
73 | |
74 | foreach ($sections as $section) { |
75 | $deriver = $section->getLayout()->getPluginDefinition()->getDeriver(); |
76 | |
77 | if ($deriver === 'Drupal\ui_patterns_layouts\Plugin\Derivative\ComponentLayout') { |
78 | $sources[] = $this->convertUiPatternsLayout($section); |
79 | |
80 | continue; |
81 | } |
82 | $sources = \array_merge($sources, $this->convertLayout($section)); |
83 | } |
84 | |
85 | return $sources; |
86 | } |
87 | |
88 | /** |
89 | * Convert field formatter plugin data to a source. |
90 | * |
91 | * Used for conversion from both Manage Display and Layout Builder. |
92 | * |
93 | * @param string $entity_type |
94 | * Entity type ID. |
95 | * @param string $bundle |
96 | * Bundle. |
97 | * @param string $field |
98 | * Field name. |
99 | * @param array $data |
100 | * Field formatter data. |
101 | * |
102 | * @return array |
103 | * A single UI Patterns source. |
104 | */ |
105 | protected function convertSingleField(string $entity_type, string $bundle, string $field, array $data): array { |
106 | $derivable_context = \implode(':', [$entity_type, $bundle, $field]); |
107 | $source = [ |
108 | 'source_id' => 'field_formatter:' . $derivable_context, |
109 | 'source' => $data, |
110 | ]; |
111 | |
112 | return [ |
113 | 'source_id' => 'entity_field', |
114 | 'source' => [ |
115 | 'derivable_context' => 'field:' . $derivable_context, |
116 | 'field:' . $derivable_context => [ |
117 | 'value' => [ |
118 | 'sources' => [$source], |
119 | ], |
120 | ], |
121 | ], |
122 | ]; |
123 | } |
124 | |
125 | /** |
126 | * Convert an extra field. |
127 | * |
128 | * @param string $field_name |
129 | * The machine name of the extra field. |
130 | * |
131 | * @return array |
132 | * A single UI Patterns source. |
133 | */ |
134 | protected function convertExtraField(string $field_name): array { |
135 | return [ |
136 | 'source_id' => 'extra_field', |
137 | 'source' => [ |
138 | 'field' => $field_name, |
139 | ], |
140 | ]; |
141 | } |
142 | |
143 | /** |
144 | * Convert UI Patterns layout plugin. |
145 | * |
146 | * @param \Drupal\layout_builder\Section $section |
147 | * A single layout builder section. |
148 | * |
149 | * @return array |
150 | * A single UI Patterns source. |
151 | */ |
152 | protected function convertUiPatternsLayout(Section $section): array { |
153 | $slots = []; |
154 | $components = $section->getComponents(); |
155 | |
156 | foreach ($components as $component) { |
157 | $source = $this->convertLayoutBuilderComponent($component); |
158 | |
159 | if ($source) { |
160 | $source = $this->extractUiStylesData($component, $source); |
161 | $slots[$component->getRegion()]['sources'][] = $source; |
162 | } |
163 | } |
164 | |
165 | $data = [ |
166 | 'source_id' => 'component', |
167 | 'source' => [ |
168 | 'component' => $section->getLayoutSettings()['ui_patterns'], |
169 | ], |
170 | ]; |
171 | // Sometimes, this value is null, so let's override it. |
172 | $data['source']['component']['component_id'] = \str_replace('ui_patterns:', '', $section->getLayoutId()); |
173 | |
174 | foreach ($section->getThirdPartyProviders() ?: [] as $provider_id) { |
175 | $data['_third_party_settings'][$provider_id] = $section->getThirdPartySettings($provider_id); |
176 | } |
177 | $data = $this->moveUiStylesAttributesSource($data); |
178 | |
179 | if ($slots) { |
180 | $data['source']['component']['slots'] = $slots; |
181 | } |
182 | |
183 | return $data; |
184 | } |
185 | |
186 | /** |
187 | * Move data from UI Style's attribute source to 3rd party settings. |
188 | * |
189 | * @param array $data |
190 | * A UI Patterns source data. |
191 | * |
192 | * @return array |
193 | * The same UI Patterns source data, maybe altered. |
194 | */ |
195 | protected function moveUiStylesAttributesSource(array $data): array { |
196 | if ($data['source']['component']['props']['attributes']['source_id'] !== 'ui_styles_attributes') { |
197 | return $data; |
198 | } |
199 | // We keep this order because it is the priority order in UI styles. |
200 | $styles_1 = [ |
201 | 'selected' => $data['source']['component']['props']['attributes']['source']['styles']['selected'], |
202 | 'extra' => $data['source']['component']['props']['attributes']['source']['extra'], |
203 | ]; |
204 | $styles_2 = $data['_third_party_settings']['ui_styles'] ?? []; |
205 | $data['_third_party_settings']['ui_styles'] = NestedArray::mergeDeep($styles_1, $styles_2); |
206 | $data['source']['component']['props']['attributes'] = []; |
207 | |
208 | return $data; |
209 | } |
210 | |
211 | /** |
212 | * Convert regular layout plugin. |
213 | * |
214 | * We don't really convert the layout here, we extract the blocks and put |
215 | * them as a flat list where the layout is. |
216 | * |
217 | * @todo better layout support https://www.drupal.org/project/display_builder/issues/3531521 |
218 | * |
219 | * @param \Drupal\layout_builder\Section $section |
220 | * A single layout builder section. |
221 | * |
222 | * @return array |
223 | * A list of UI Patterns source. |
224 | */ |
225 | protected function convertLayout(Section $section): array { |
226 | $sources = []; |
227 | $components = $section->getComponents(); |
228 | |
229 | foreach ($components as $component) { |
230 | $source = $this->convertLayoutBuilderComponent($component); |
231 | $source = $this->extractUiStylesData($component, $source); |
232 | $sources[] = $source; |
233 | } |
234 | |
235 | return $sources; |
236 | } |
237 | |
238 | /** |
239 | * Extract UI Styles data. |
240 | * |
241 | * In Display Builder, we don't render through ThemeManager::render() so we |
242 | * don't load block.html.twig and we don't have block wrapper and block title |
243 | * attributes. So, let's ignore title styles and let's merge wrapper styles |
244 | * with block styles (which keep priority). |
245 | * |
246 | * @param \Drupal\layout_builder\SectionComponent $component |
247 | * A single layout builder component. |
248 | * @param array $source |
249 | * The already converted UI Patterns source. |
250 | * |
251 | * @return array |
252 | * The altered UI Patterns source. |
253 | */ |
254 | protected function extractUiStylesData(SectionComponent $component, array $source): array { |
255 | $additional = $component->toArray()['additional'] ?? []; |
256 | |
257 | $styles = \array_unique(\array_merge($additional['ui_styles_wrapper'] ?? [], $additional['ui_styles'] ?? [])); |
258 | |
259 | if ($styles) { |
260 | $source['_third_party_settings']['ui_styles']['selected'] = $styles; |
261 | } |
262 | |
263 | $extra = \trim(($additional['ui_styles_wrapper_extra'] ?? '') . ' ' . ($additional['ui_styles_extra'] ?? '')); |
264 | |
265 | if ($extra) { |
266 | $source['_third_party_settings']['ui_styles']['extra'] = $extra; |
267 | } |
268 | |
269 | return $source; |
270 | } |
271 | |
272 | /** |
273 | * Convert layout builder component. |
274 | * |
275 | * @param \Drupal\layout_builder\SectionComponent $component |
276 | * A single layout builder component. |
277 | * |
278 | * @return array |
279 | * A single UI Patterns source. |
280 | */ |
281 | protected function convertLayoutBuilderComponent(SectionComponent $component): array { |
282 | /** @var \Drupal\Core\Block\BlockPluginInterface $block */ |
283 | $block = $component->getPlugin(); |
284 | $definition = $block->getPluginDefinition(); |
285 | |
286 | $class = $definition instanceof PluginDefinitionInterface ? $definition->getClass() : $definition['class']; |
287 | |
288 | if ($class === 'Drupal\layout_builder\Plugin\Block\FieldBlock') { |
289 | return $this->convertFieldBlock($block); |
290 | } |
291 | |
292 | $provider = $definition instanceof PluginDefinitionInterface ? $definition->getProvider() : $definition['provider']; |
293 | |
294 | if ($provider === 'ui_patterns_blocks') { |
295 | return $this->convertUiPatternsBlock($block); |
296 | } |
297 | |
298 | $id = $definition instanceof PluginDefinitionInterface ? $definition->id() : $definition['id']; |
299 | |
300 | if ($id === 'extra_field_block') { |
301 | return $this->convertExtraFieldBlock($block); |
302 | } |
303 | |
304 | return $this->convertBlock($block); |
305 | } |
306 | |
307 | /** |
308 | * Convert a Layout Builder field block. |
309 | * |
310 | * @param \Drupal\Core\Block\BlockPluginInterface $block |
311 | * A block plugin. |
312 | * |
313 | * @return array |
314 | * A single UI Patterns source. |
315 | */ |
316 | protected function convertFieldBlock(BlockPluginInterface $block): array { |
317 | $config = $block->getConfiguration(); |
318 | [, $entity_type, $bundle, $field_name] = \explode(':', $config['id']); |
319 | |
320 | return $this->convertSingleField($entity_type, $bundle, $field_name, $config['formatter']); |
321 | } |
322 | |
323 | /** |
324 | * Convert an extra field block. |
325 | * |
326 | * @param \Drupal\Core\Block\BlockPluginInterface $block |
327 | * A block plugin. |
328 | * |
329 | * @return array |
330 | * A single UI Patterns source. |
331 | */ |
332 | protected function convertExtraFieldBlock(BlockPluginInterface $block): array { |
333 | [,,, $field_name] = \explode(':', $block->getPluginId()); |
334 | |
335 | return $this->convertExtraField($field_name); |
336 | } |
337 | |
338 | /** |
339 | * Convert a UI Patterns block plugin. |
340 | * |
341 | * @param \Drupal\Core\Block\BlockPluginInterface $block |
342 | * A block plugin. |
343 | * |
344 | * @return array |
345 | * A single UI Patterns source. |
346 | */ |
347 | protected function convertUiPatternsBlock(BlockPluginInterface $block): array { |
348 | $config = $block->getConfiguration(); |
349 | |
350 | // Sometimes, this value is null, so let's override it. |
351 | if (!isset($config['ui_patterns']['component_id'])) { |
352 | // See: \Drupal\ui_patterns_blocks\Plugin\Block\ComponentBlock. |
353 | $config['ui_patterns']['component_id'] = \str_replace('ui_patterns:', '', $config['id']); |
354 | // See: \Drupal\ui_patterns_blocks\Plugin\Block\EntityComponentBlock. |
355 | $config['ui_patterns']['component_id'] = \str_replace('ui_patterns_entity:', '', $config['id']); |
356 | } |
357 | |
358 | return [ |
359 | 'source_id' => 'component', |
360 | 'source' => [ |
361 | 'component' => $config['ui_patterns'], |
362 | ], |
363 | ]; |
364 | } |
365 | |
366 | /** |
367 | * Convert a generic block plugin. |
368 | * |
369 | * @param \Drupal\Core\Block\BlockPluginInterface $block |
370 | * A block plugin. |
371 | * |
372 | * @return array |
373 | * A single UI Patterns source. |
374 | */ |
375 | protected function convertBlock(BlockPluginInterface $block): array { |
376 | $block_id = $block->getPluginId(); |
377 | |
378 | return [ |
379 | 'source_id' => 'block', |
380 | 'source' => [ |
381 | 'plugin_id' => $block_id, |
382 | $block_id => $block->getConfiguration(), |
383 | ], |
384 | ]; |
385 | } |
386 | |
387 | } |