Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
93.10% covered (success)
93.10%
108 / 116
85.25% covered (warning)
85.25%
52 / 61
28.85% covered (danger)
28.85%
15 / 52
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
BlockLibrarySourceHelper
93.10% covered (success)
93.10%
108 / 116
85.25% covered (warning)
85.25%
52 / 61
28.85% covered (danger)
28.85%
15 / 52
33.33% covered (danger)
33.33%
2 / 6
450.44
0.00% covered (danger)
0.00%
0 / 1
 getGroupedChoices
93.33% covered (success)
93.33%
14 / 15
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
4.00
 sortGroupedChoices
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getChoices
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
11 / 11
28.57% covered (danger)
28.57%
2 / 7
100.00% covered (success)
100.00%
1 / 1
14.11
 isChoiceValid
88.89% covered (warning)
88.89%
8 / 9
88.89% covered (warning)
88.89%
8 / 9
27.27% covered (danger)
27.27%
3 / 11
0.00% covered (danger)
0.00%
0 / 1
19.85
 getSourceGroupLabel
81.82% covered (warning)
81.82%
18 / 22
77.78% covered (warning)
77.78%
14 / 18
31.25% covered (danger)
31.25%
5 / 16
0.00% covered (danger)
0.00%
0 / 1
42.50
 getChoiceGroupLabel
87.50% covered (warning)
87.50%
14 / 16
78.57% covered (warning)
78.57%
11 / 14
36.36% covered (danger)
36.36%
4 / 11
0.00% covered (danger)
0.00%
0 / 1
19.63
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder;
6
7use Drupal\Component\Render\MarkupInterface;
8use Drupal\Core\StringTranslation\TranslatableMarkup;
9use Drupal\Core\Url;
10
11/**
12 * Helper class for block library source handling.
13 */
14class BlockLibrarySourceHelper {
15
16  private const HIDE_BLOCK = [
17    'htmx_loader',
18    'broken',
19    'system_main_block',
20    'page_title_block',
21  ];
22
23  /**
24   * Get the choices grouped by category.
25   *
26   * @param array $sources
27   *   An array of all possible sources.
28   * @param array $exclude_provider
29   *   (Optional) An array of providers to hide.
30   *
31   * @return array
32   *   An array of grouped choices.
33   */
34  public static function getGroupedChoices(array $sources, array $exclude_provider = []): array {
35    $choices = self::getChoices($sources, $exclude_provider);
36    $default_category = (string) new TranslatableMarkup('Others');
37
38    $categories = [];
39
40    foreach ($choices as $choice) {
41      $category = $choice['group'] ?? $default_category;
42
43      if ($category instanceof MarkupInterface) {
44        $category = (string) $category;
45      }
46
47      if (!isset($categories[$category])) {
48        $categories[$category] = [
49          'label' => $category,
50          'choices' => [],
51        ];
52      }
53      $categories[$category]['choices'][] = $choice;
54    }
55    self::sortGroupedChoices($categories);
56
57    return $categories;
58  }
59
60  /**
61   * Sorts the grouped choices based on arbitrary weight.
62   *
63   * @param array $categories
64   *   The categories to sort, passed by reference.
65   */
66  public static function sortGroupedChoices(array &$categories): void {
67    // Public for reuse by PresetLibraryPanel.
68    $category_weight = [
69      // Different builder contexts.
70      (string) new TranslatableMarkup('Page') => 1,
71      (string) new TranslatableMarkup('Views') => 1,
72      (string) new TranslatableMarkup('Fields') => 1,
73      (string) new TranslatableMarkup('Referenced entities') => 2,
74      // Global categories.
75      (string) new TranslatableMarkup('Menus') => 3,
76      (string) new TranslatableMarkup('System') => 3,
77      (string) new TranslatableMarkup('User') => 3,
78      (string) new TranslatableMarkup('Utilities') => 3,
79      (string) new TranslatableMarkup('Forms') => 4,
80      (string) new TranslatableMarkup('Lists (Views)') => 5,
81      (string) new TranslatableMarkup('Others') => 6,
82      (string) new TranslatableMarkup('Dev tools') => 99,
83    ];
84
85    // Sort categories by predefined weight, then by natural string comparison
86    // of labels.
87    \uasort($categories, static function ($a, $b) use ($category_weight) {
88      $weight_a = $category_weight[$a['label']] ?? 98;
89      $weight_b = $category_weight[$b['label']] ?? 98;
90
91      if ($weight_a === $weight_b) {
92        return \strnatcmp($a['label'], $b['label']);
93      }
94
95      return $weight_a <=> $weight_b;
96    });
97  }
98
99  /**
100   * Get the choices from all sources.
101   *
102   * @param array $sources
103   *   An array of all possible sources.
104   * @param array $exclude_provider
105   *   An array of providers to hide.
106   *
107   * @return array
108   *   An array of choices.
109   */
110  private static function getChoices(array $sources, array $exclude_provider): array {
111    $result_choices = [];
112
113    foreach ($sources as $source_id => $source_data) {
114      $definition = $source_data['definition'];
115      $source = $source_data['source'];
116
117      // If no choices, add the source as a single choice.
118      if (!isset($source_data['choices'])) {
119        $label = $definition['label'] ?? $source_id;
120        $keywords = \sprintf('%s %s %s', $definition['id'], $definition['label'] ?? $source_id, $definition['description'] ?? '');
121
122        $result_choices[] = [
123          'label' => (string) $label,
124          'data' => ['source_id' => $source_id],
125          'keywords' => $keywords,
126          'preview' => FALSE,
127          'group' => self::getSourceGroupLabel($definition),
128        ];
129
130        continue;
131      }
132
133      // Mostly for block source, iterate over choices.
134      $choices = $source_data['choices'];
135
136      foreach ($choices as $choice_id => $choice) {
137        if (!self::isChoiceValid($choice, $definition, $exclude_provider)) {
138          continue;
139        }
140
141        $label = $choice['label'] ?? $choice_id;
142        $keywords = \sprintf('%s %s %s %s', $definition['id'], $label, $definition['description'] ?? '', $choice_id);
143        $preview_url = Url::fromRoute('display_builder.api_block_preview', ['block_id' => $choice_id]);
144
145        $result_choices[] = [
146          'label' => (string) $label,
147          'keywords' => $keywords,
148          'data' => [
149            'source_id' => $source_id,
150            'source' => $source->getChoiceSettings($choice_id),
151          ],
152          'preview' => $preview_url,
153          'group' => self::getChoiceGroupLabel($choice, $definition),
154        ];
155      }
156    }
157
158    return $result_choices;
159  }
160
161  /**
162   * Validate a choice against the source definition and allowed providers.
163   *
164   * @param array $choice
165   *   The choice to validate.
166   * @param array $source_definition
167   *   The source definition.
168   * @param array $exclude_provider
169   *   The excluded providers.
170   *
171   * @return bool
172   *   Whether the choice is valid or not.
173   */
174  private static function isChoiceValid(array &$choice, array &$source_definition, array $exclude_provider = []): bool {
175    $provider = $choice['provider'] ?? '';
176
177    if ($provider) {
178      if (\in_array($provider, $exclude_provider, TRUE)) {
179        return FALSE;
180      }
181    }
182
183    if ($source_definition['id'] === 'block') {
184      $block_id = $choice['original_id'] ?? '';
185
186      if ($block_id && \in_array($block_id, self::HIDE_BLOCK, TRUE)) {
187        return FALSE;
188      }
189    }
190
191    return TRUE;
192  }
193
194  /**
195   * Get the group label for a source.
196   *
197   * @param array $source_definition
198   *   The source definition to use for the group.
199   *
200   * @return string
201   *   The group label for the choice.
202   */
203  private static function getSourceGroupLabel(array $source_definition): string {
204    $group = (string) new TranslatableMarkup('Others');
205
206    $provider = $source_definition['provider'] ?? NULL;
207
208    if (!$provider) {
209      return $group;
210    }
211
212    if (isset($source_definition['metadata']['group'])) {
213      return (string) $source_definition['metadata']['group'];
214    }
215
216    switch ($provider) {
217      case 'display_builder_page_layout':
218        $group = (string) new TranslatableMarkup('Page');
219
220        break;
221
222      case 'display_builder_views':
223      case 'ui_patterns_views':
224        $group = (string) new TranslatableMarkup('Views');
225
226        break;
227
228      case 'display_builder_dev_tools':
229        $group = (string) new TranslatableMarkup('Dev tools');
230
231        break;
232
233      case 'ui_icons_patterns':
234      case 'ui_patterns':
235        $group = (string) new TranslatableMarkup('Utilities');
236
237        break;
238
239      default:
240        break;
241    }
242
243    return $group;
244  }
245
246  /**
247   * Get the group label for a choice.
248   *
249   * @param array $choice
250   *   The choice to get the group for.
251   * @param array $source_definition
252   *   The source definition to use for the group.
253   *
254   * @return string
255   *   The group label for the choice.
256   */
257  private static function getChoiceGroupLabel(array $choice, array $source_definition): string {
258    $group = (string) new TranslatableMarkup('Others');
259    $source_id = $source_definition['id'] ?? NULL;
260
261    if (!$source_id) {
262      return $group;
263    }
264
265    switch ($source_id) {
266      case 'block':
267        if (!empty($choice['group'])) {
268          $group = $choice['group'];
269        }
270
271        break;
272
273      case 'entity_reference':
274        $group = (string) new TranslatableMarkup('Referenced entities');
275
276        break;
277
278      case 'entity_field':
279        $group = (string) new TranslatableMarkup('Fields');
280
281        break;
282
283      default:
284        break;
285    }
286
287    return $group;
288  }
289
290}

Branches

Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once. Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

BlockLibrarySourceHelper->getChoiceGroupLabel
257  private static function getChoiceGroupLabel(array $choice, array $source_definition): string {
258    $group = (string) new TranslatableMarkup('Others');
259    $source_id = $source_definition['id'] ?? NULL;
260
261    if (!$source_id) {
262      return $group;
265    switch ($source_id) {
266      case 'block':
273      case 'entity_reference':
278      case 'entity_field':
278      case 'entity_field':
267        if (!empty($choice['group'])) {
268          $group = $choice['group'];
269        }
270
271        break;
271        break;
274        $group = (string) new TranslatableMarkup('Referenced entities');
275
276        break;
279        $group = (string) new TranslatableMarkup('Fields');
280
281        break;
284        break;
287    return $group;
288  }
BlockLibrarySourceHelper->getChoices
110  private static function getChoices(array $sources, array $exclude_provider): array {
111    $result_choices = [];
112
113    foreach ($sources as $source_id => $source_data) {
113    foreach ($sources as $source_id => $source_data) {
113    foreach ($sources as $source_id => $source_data) {
114      $definition = $source_data['definition'];
115      $source = $source_data['source'];
116
117      // If no choices, add the source as a single choice.
118      if (!isset($source_data['choices'])) {
119        $label = $definition['label'] ?? $source_id;
120        $keywords = \sprintf('%s %s %s', $definition['id'], $definition['label'] ?? $source_id, $definition['description'] ?? '');
121
122        $result_choices[] = [
123          'label' => (string) $label,
124          'data' => ['source_id' => $source_id],
125          'keywords' => $keywords,
126          'preview' => FALSE,
127          'group' => self::getSourceGroupLabel($definition),
128        ];
129
130        continue;
134      $choices = $source_data['choices'];
135
136      foreach ($choices as $choice_id => $choice) {
136      foreach ($choices as $choice_id => $choice) {
136      foreach ($choices as $choice_id => $choice) {
137        if (!self::isChoiceValid($choice, $definition, $exclude_provider)) {
138          continue;
136      foreach ($choices as $choice_id => $choice) {
137        if (!self::isChoiceValid($choice, $definition, $exclude_provider)) {
138          continue;
139        }
140
141        $label = $choice['label'] ?? $choice_id;
113    foreach ($sources as $source_id => $source_data) {
114      $definition = $source_data['definition'];
115      $source = $source_data['source'];
116
117      // If no choices, add the source as a single choice.
118      if (!isset($source_data['choices'])) {
119        $label = $definition['label'] ?? $source_id;
120        $keywords = \sprintf('%s %s %s', $definition['id'], $definition['label'] ?? $source_id, $definition['description'] ?? '');
121
122        $result_choices[] = [
123          'label' => (string) $label,
124          'data' => ['source_id' => $source_id],
125          'keywords' => $keywords,
126          'preview' => FALSE,
127          'group' => self::getSourceGroupLabel($definition),
128        ];
129
130        continue;
131      }
132
133      // Mostly for block source, iterate over choices.
134      $choices = $source_data['choices'];
135
136      foreach ($choices as $choice_id => $choice) {
113    foreach ($sources as $source_id => $source_data) {
114      $definition = $source_data['definition'];
115      $source = $source_data['source'];
116
117      // If no choices, add the source as a single choice.
118      if (!isset($source_data['choices'])) {
119        $label = $definition['label'] ?? $source_id;
120        $keywords = \sprintf('%s %s %s', $definition['id'], $definition['label'] ?? $source_id, $definition['description'] ?? '');
121
122        $result_choices[] = [
123          'label' => (string) $label,
124          'data' => ['source_id' => $source_id],
125          'keywords' => $keywords,
126          'preview' => FALSE,
127          'group' => self::getSourceGroupLabel($definition),
128        ];
129
130        continue;
131      }
132
133      // Mostly for block source, iterate over choices.
134      $choices = $source_data['choices'];
135
136      foreach ($choices as $choice_id => $choice) {
137        if (!self::isChoiceValid($choice, $definition, $exclude_provider)) {
138          continue;
139        }
140
141        $label = $choice['label'] ?? $choice_id;
142        $keywords = \sprintf('%s %s %s %s', $definition['id'], $label, $definition['description'] ?? '', $choice_id);
143        $preview_url = Url::fromRoute('display_builder.api_block_preview', ['block_id' => $choice_id]);
144
145        $result_choices[] = [
146          'label' => (string) $label,
147          'keywords' => $keywords,
148          'data' => [
149            'source_id' => $source_id,
150            'source' => $source->getChoiceSettings($choice_id),
151          ],
152          'preview' => $preview_url,
153          'group' => self::getChoiceGroupLabel($choice, $definition),
154        ];
155      }
156    }
157
158    return $result_choices;
159  }
BlockLibrarySourceHelper->getGroupedChoices
34  public static function getGroupedChoices(array $sources, array $exclude_provider = []): array {
35    $choices = self::getChoices($sources, $exclude_provider);
36    $default_category = (string) new TranslatableMarkup('Others');
37
38    $categories = [];
39
40    foreach ($choices as $choice) {
40    foreach ($choices as $choice) {
41      $category = $choice['group'] ?? $default_category;
42
43      if ($category instanceof MarkupInterface) {
44        $category = (string) $category;
45      }
46
47      if (!isset($categories[$category])) {
47      if (!isset($categories[$category])) {
49          'label' => $category,
50          'choices' => [],
51        ];
52      }
53      $categories[$category]['choices'][] = $choice;
40    foreach ($choices as $choice) {
41      $category = $choice['group'] ?? $default_category;
42
43      if ($category instanceof MarkupInterface) {
44        $category = (string) $category;
45      }
46
47      if (!isset($categories[$category])) {
48        $categories[$category] = [
49          'label' => $category,
50          'choices' => [],
51        ];
52      }
53      $categories[$category]['choices'][] = $choice;
40    foreach ($choices as $choice) {
41      $category = $choice['group'] ?? $default_category;
42
43      if ($category instanceof MarkupInterface) {
44        $category = (string) $category;
45      }
46
47      if (!isset($categories[$category])) {
48        $categories[$category] = [
49          'label' => $category,
50          'choices' => [],
51        ];
52      }
53      $categories[$category]['choices'][] = $choice;
54    }
55    self::sortGroupedChoices($categories);
56
57    return $categories;
58  }
BlockLibrarySourceHelper->getSourceGroupLabel
203  private static function getSourceGroupLabel(array $source_definition): string {
204    $group = (string) new TranslatableMarkup('Others');
205
206    $provider = $source_definition['provider'] ?? NULL;
207
208    if (!$provider) {
209      return $group;
212    if (isset($source_definition['metadata']['group'])) {
213      return (string) $source_definition['metadata']['group'];
216    switch ($provider) {
217      case 'display_builder_page_layout':
222      case 'display_builder_views':
223      case 'ui_patterns_views':
228      case 'display_builder_dev_tools':
233      case 'ui_icons_patterns':
234      case 'ui_patterns':
234      case 'ui_patterns':
218        $group = (string) new TranslatableMarkup('Page');
219
220        break;
224        $group = (string) new TranslatableMarkup('Views');
225
226        break;
229        $group = (string) new TranslatableMarkup('Dev tools');
230
231        break;
235        $group = (string) new TranslatableMarkup('Utilities');
236
237        break;
240        break;
243    return $group;
244  }
BlockLibrarySourceHelper->isChoiceValid
174  private static function isChoiceValid(array &$choice, array &$source_definition, array $exclude_provider = []): bool {
175    $provider = $choice['provider'] ?? '';
176
177    if ($provider) {
178      if (\in_array($provider, $exclude_provider, TRUE)) {
179        return FALSE;
183    if ($source_definition['id'] === 'block') {
184      $block_id = $choice['original_id'] ?? '';
185
186      if ($block_id && \in_array($block_id, self::HIDE_BLOCK, TRUE)) {
186      if ($block_id && \in_array($block_id, self::HIDE_BLOCK, TRUE)) {
186      if ($block_id && \in_array($block_id, self::HIDE_BLOCK, TRUE)) {
187        return FALSE;
191    return TRUE;
192  }
BlockLibrarySourceHelper->sortGroupedChoices
66  public static function sortGroupedChoices(array &$categories): void {
67    // Public for reuse by PresetLibraryPanel.
68    $category_weight = [
69      // Different builder contexts.
70      (string) new TranslatableMarkup('Page') => 1,
71      (string) new TranslatableMarkup('Views') => 1,
72      (string) new TranslatableMarkup('Fields') => 1,
73      (string) new TranslatableMarkup('Referenced entities') => 2,
74      // Global categories.
75      (string) new TranslatableMarkup('Menus') => 3,
76      (string) new TranslatableMarkup('System') => 3,
77      (string) new TranslatableMarkup('User') => 3,
78      (string) new TranslatableMarkup('Utilities') => 3,
79      (string) new TranslatableMarkup('Forms') => 4,
80      (string) new TranslatableMarkup('Lists (Views)') => 5,
81      (string) new TranslatableMarkup('Others') => 6,
82      (string) new TranslatableMarkup('Dev tools') => 99,
83    ];
84
85    // Sort categories by predefined weight, then by natural string comparison
86    // of labels.
87    \uasort($categories, static function ($a, $b) use ($category_weight) {
88      $weight_a = $category_weight[$a['label']] ?? 98;
89      $weight_b = $category_weight[$b['label']] ?? 98;
90
91      if ($weight_a === $weight_b) {
92        return \strnatcmp($a['label'], $b['label']);
93      }
94
95      return $weight_a <=> $weight_b;
96    });
97  }
{closure:/var/www/html/web/modules/custom/display_builder/src/BlockLibrarySourceHelper.php:87-96}
87    \uasort($categories, static function ($a, $b) use ($category_weight) {
88      $weight_a = $category_weight[$a['label']] ?? 98;
89      $weight_b = $category_weight[$b['label']] ?? 98;
90
91      if ($weight_a === $weight_b) {
92        return \strnatcmp($a['label'], $b['label']);
95      return $weight_a <=> $weight_b;
96    });