Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
58.93% covered (warning)
58.93%
33 / 56
53.85% covered (warning)
53.85%
28 / 52
19.30% covered (danger)
19.30%
11 / 57
54.55% covered (warning)
54.55%
6 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
PatternPreset
58.93% covered (warning)
58.93%
33 / 56
53.85% covered (warning)
53.85%
28 / 52
19.30% covered (danger)
19.30%
11 / 57
54.55% covered (warning)
54.55%
6 / 11
605.37
0.00% covered (danger)
0.00%
0 / 1
 getGroup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSummary
100.00% covered (success)
100.00%
4 / 4
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
 getSources
87.50% covered (warning)
87.50%
7 / 8
88.89% covered (warning)
88.89%
8 / 9
16.67% covered (danger)
16.67%
2 / 12
0.00% covered (danger)
0.00%
0 / 1
26.83
 getContexts
66.67% covered (warning)
66.67%
2 / 3
75.00% covered (warning)
75.00%
3 / 4
33.33% covered (danger)
33.33%
1 / 3
0.00% covered (danger)
0.00%
0 / 1
5.67
 calculateDependencies
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 areContextsSatisfied
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
42
 getContextFromSource
75.00% covered (warning)
75.00%
3 / 4
66.67% covered (warning)
66.67%
2 / 3
50.00% covered (danger)
50.00%
1 / 2
0.00% covered (danger)
0.00%
0 / 1
2.50
 getContextsFromComponent
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 fillInternalId
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
8 / 8
8.33% covered (danger)
8.33%
1 / 12
100.00% covered (success)
100.00%
1 / 1
24.26
 sourcePluginManager
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 slotSourceProxy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\display_builder\Entity;
6
7use Drupal\Core\Config\Entity\ConfigEntityBase;
8use Drupal\Core\Entity\Attribute\ConfigEntityType;
9use Drupal\Core\Entity\EntityDeleteForm;
10use Drupal\Core\StringTranslation\TranslatableMarkup;
11use Drupal\display_builder\Form\PatternPresetForm;
12use Drupal\display_builder\PatternPresetInterface;
13use Drupal\display_builder\SlotSourceProxy;
14use Drupal\display_builder_ui\PatternPresetListBuilder;
15use Drupal\ui_patterns\SourceInterface;
16use Drupal\ui_patterns\SourcePluginManager;
17
18/**
19 * Defines the Pattern preset entity type.
20 */
21#[ConfigEntityType(
22  id: 'pattern_preset',
23  label: new TranslatableMarkup('Pattern preset'),
24  label_collection: new TranslatableMarkup('Pattern presets'),
25  label_singular: new TranslatableMarkup('Pattern preset'),
26  label_plural: new TranslatableMarkup('Pattern presets'),
27  entity_keys: [
28    'id' => 'id',
29    'label' => 'label',
30    'theme' => 'theme',
31    'description' => 'description',
32    'group' => 'group',
33    'sources' => 'sources',
34    'weight' => 'weight',
35  ],
36  handlers: [
37    'list_builder' => PatternPresetListBuilder::class,
38    'form' => [
39      'add' => PatternPresetForm::class,
40      'edit' => PatternPresetForm::class,
41      'delete' => EntityDeleteForm::class,
42    ],
43  ],
44  links: [
45    'add-form' => '/admin/structure/display-builder/preset/add',
46    'delete-form' => '/admin/structure/display-builder/preset/{pattern_preset}/delete',
47    'collection' => '/admin/structure/display-builder/preset',
48  ],
49  admin_permission: 'administer Pattern preset',
50  constraints: [
51    'ImmutableProperties' => [
52      'id',
53    ],
54  ],
55  config_export: [
56    'id',
57    'label',
58    'description',
59    'group',
60    'sources',
61    'weight',
62  ],
63)]
64final class PatternPreset extends ConfigEntityBase implements PatternPresetInterface {
65
66  /**
67   * The preset ID.
68   */
69  protected string $id;
70
71  /**
72   * The preset label.
73   */
74  protected string $label = '';
75
76  /**
77   * The preset description.
78   */
79  protected string $description = '';
80
81  /**
82   * The preset group.
83   */
84  protected string $group = '';
85
86  /**
87   * The preset sources.
88   */
89  protected array $sources = [];
90
91  /**
92   * Weight to order the entity in lists.
93   *
94   * @var int
95   */
96  protected $weight = 0;
97
98  /**
99   * The UI Patterns source plugin manager.
100   */
101  protected SourcePluginManager $sourcePluginManager;
102
103  /**
104   * Slot source proxy.
105   */
106  protected SlotSourceProxy $slotSourceProxy;
107
108  /**
109   * {@inheritdoc}
110   */
111  public function getGroup(): string {
112    return $this->group;
113  }
114
115  /**
116   * {@inheritdoc}
117   */
118  public function getSummary(): string {
119    $contexts = [];
120    $data = $this->getSources($contexts, FALSE);
121    $data = $this->slotSourceProxy()->getLabelWithSummary($data);
122
123    return $data['summary'] ?: $data['label'];
124  }
125
126  /**
127   * {@inheritdoc}
128   */
129  public function getSources(array $contexts = [], bool $fillInternalId = TRUE): array {
130    $data = $this->get('sources') ?? [];
131
132    if (isset($data[0]) && \count($data) === 1) {
133      $data = \reset($data);
134    }
135
136    if (empty($data) || !isset($data['source_id'])) {
137      return [];
138    }
139
140    if ($fillInternalId) {
141      self::fillInternalId($data);
142    }
143
144    return $data;
145  }
146
147  /**
148   * {@inheritdoc}
149   *
150   * @see \Drupal\Core\Config\Entity\ConfigEntityInterface
151   */
152  public function getContexts(): array {
153    // The root level is a single nestable source plugin.
154    if (!isset($this->sources['source_id']) || !isset($this->sources['source'])) {
155      return [];
156    }
157
158    return $this->getContextFromSource($this->sources['source_id'], $this->sources['source']);
159  }
160
161  /**
162   * {@inheritdoc}
163   */
164  public function calculateDependencies(): self {
165    parent::calculateDependencies();
166
167    // The root level is a single nestable source plugin.
168    if (!isset($this->sources['source_id'])) {
169      return $this;
170    }
171    // This will automatically be done by parent::calculateDependencies() if we
172    // implement EntityWithPluginCollectionInterface.
173    $configuration = [
174      'settings' => $this->sources['source'] ?? [],
175    ];
176    /** @var \Drupal\ui_patterns\SourceInterface $source */
177    $source = $this->sourcePluginManager()->createInstance($this->sources['source_id'], $configuration);
178    $this->addDependencies($source->calculateDependencies());
179
180    return $this;
181  }
182
183  /**
184   * {@inheritdoc}
185   */
186  public function areContextsSatisfied(array $contexts): bool {
187    $context_definitions = $this->getContexts();
188
189    if (empty($context_definitions)) {
190      return TRUE;
191    }
192
193    foreach ($context_definitions as $key => $context_definition) {
194      if (!$context_definition->isRequired()) {
195        continue;
196      }
197
198      if (!\array_key_exists($key, $contexts)) {
199        return FALSE;
200      }
201
202      if (!$context_definition->isSatisfiedBy($contexts[$key])) {
203        return FALSE;
204      }
205    }
206
207    return TRUE;
208  }
209
210  /**
211   * Recursively get the source contexts.
212   *
213   * @param string $source_id
214   *   Source plugin ID.
215   * @param array $source
216   *   Source plugin configuration.
217   *
218   * @return array
219   *   Context definitions of the source.
220   */
221  private function getContextFromSource(string $source_id, array $source): array {
222    /** @var \Drupal\ui_patterns\SourceInterface $source */
223    $source = $this->sourcePluginManager()->createInstance($source_id, ['settings' => $source]);
224
225    if ($source->getPluginId() === 'component') {
226      return $this->getContextsFromComponent($source);
227    }
228
229    // @todo Traverse also context switchers.
230    return $source->getContextDefinitions();
231  }
232
233  /**
234   * Get contexts from component.
235   *
236   * Go through all slots and props to get the nested sources contexts.
237   *
238   * @param \Drupal\ui_patterns\SourceInterface $source
239   *   Source plugin.
240   *
241   * @return array
242   *   Context definitions of the component source.
243   */
244  private function getContextsFromComponent(SourceInterface $source): array {
245    $contexts = [];
246    $slots = $source->getSetting('component')['slots'] ?? [];
247
248    foreach ($slots as $slot) {
249      foreach ($slot['sources'] ?? [] as $slot_source) {
250        $contexts = \array_merge($contexts, $this->getContextFromSource($slot_source['source_id'], $slot_source['source']));
251      }
252    }
253
254    $props = $source->getSetting('component')['props'] ?? [];
255
256    foreach ($props as $prop_source) {
257      $contexts = \array_merge($contexts, $this->getContextFromSource($prop_source['source_id'], $prop_source['source']));
258    }
259
260    return $contexts;
261  }
262
263  /**
264   * Recursively fill the node_id key.
265   *
266   * @param array $array
267   *   The array reference.
268   */
269  private static function fillInternalId(array &$array): void {
270    if (isset($array['source_id']) && !isset($array['node_id'])) {
271      $array['node_id'] = \uniqid();
272    }
273
274    foreach ($array as &$value) {
275      if (\is_array($value)) {
276        self::fillInternalId($value);
277      }
278    }
279  }
280
281  /**
282   * Gets the source plugin manager.
283   *
284   * @return \Drupal\ui_patterns\SourcePluginManager
285   *   The source plugin manager.
286   */
287  private function sourcePluginManager(): SourcePluginManager {
288    return $this->sourcePluginManager ??= \Drupal::service('plugin.manager.ui_patterns_source');
289  }
290
291  /**
292   * Slot source proxy.
293   *
294   * @return \Drupal\display_builder\SlotSourceProxy
295   *   The slot source proxy.
296   */
297  private function slotSourceProxy(): SlotSourceProxy {
298    return $this->slotSourceProxy ??= \Drupal::service('display_builder.slot_sources_proxy');
299  }
300
301}

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.

PatternPreset->areContextsSatisfied
186  public function areContextsSatisfied(array $contexts): bool {
187    $context_definitions = $this->getContexts();
188
189    if (empty($context_definitions)) {
190      return TRUE;
193    foreach ($context_definitions as $key => $context_definition) {
193    foreach ($context_definitions as $key => $context_definition) {
193    foreach ($context_definitions as $key => $context_definition) {
194      if (!$context_definition->isRequired()) {
195        continue;
198      if (!\array_key_exists($key, $contexts)) {
199        return FALSE;
202      if (!$context_definition->isSatisfiedBy($contexts[$key])) {
203        return FALSE;
193    foreach ($context_definitions as $key => $context_definition) {
194      if (!$context_definition->isRequired()) {
195        continue;
196      }
197
198      if (!\array_key_exists($key, $contexts)) {
199        return FALSE;
200      }
201
202      if (!$context_definition->isSatisfiedBy($contexts[$key])) {
203        return FALSE;
204      }
205    }
206
207    return TRUE;
PatternPreset->calculateDependencies
165    parent::calculateDependencies();
166
167    // The root level is a single nestable source plugin.
168    if (!isset($this->sources['source_id'])) {
169      return $this;
174      'settings' => $this->sources['source'] ?? [],
175    ];
176    /** @var \Drupal\ui_patterns\SourceInterface $source */
177    $source = $this->sourcePluginManager()->createInstance($this->sources['source_id'], $configuration);
178    $this->addDependencies($source->calculateDependencies());
179
180    return $this;
PatternPreset->fillInternalId
269  private static function fillInternalId(array &$array): void {
270    if (isset($array['source_id']) && !isset($array['node_id'])) {
270    if (isset($array['source_id']) && !isset($array['node_id'])) {
271      $array['node_id'] = \uniqid();
272    }
273
274    foreach ($array as &$value) {
274    foreach ($array as &$value) {
274    foreach ($array as &$value) {
275      if (\is_array($value)) {
274    foreach ($array as &$value) {
275      if (\is_array($value)) {
276        self::fillInternalId($value);
274    foreach ($array as &$value) {
275      if (\is_array($value)) {
276        self::fillInternalId($value);
277      }
278    }
279  }
PatternPreset->getContextFromSource
221  private function getContextFromSource(string $source_id, array $source): array {
222    /** @var \Drupal\ui_patterns\SourceInterface $source */
223    $source = $this->sourcePluginManager()->createInstance($source_id, ['settings' => $source]);
224
225    if ($source->getPluginId() === 'component') {
226      return $this->getContextsFromComponent($source);
230    return $source->getContextDefinitions();
PatternPreset->getContexts
154    if (!isset($this->sources['source_id']) || !isset($this->sources['source'])) {
154    if (!isset($this->sources['source_id']) || !isset($this->sources['source'])) {
155      return [];
158    return $this->getContextFromSource($this->sources['source_id'], $this->sources['source']);
PatternPreset->getContextsFromComponent
244  private function getContextsFromComponent(SourceInterface $source): array {
245    $contexts = [];
246    $slots = $source->getSetting('component')['slots'] ?? [];
247
248    foreach ($slots as $slot) {
248    foreach ($slots as $slot) {
249      foreach ($slot['sources'] ?? [] as $slot_source) {
249      foreach ($slot['sources'] ?? [] as $slot_source) {
249      foreach ($slot['sources'] ?? [] as $slot_source) {
250        $contexts = \array_merge($contexts, $this->getContextFromSource($slot_source['source_id'], $slot_source['source']));
248    foreach ($slots as $slot) {
249      foreach ($slot['sources'] ?? [] as $slot_source) {
248    foreach ($slots as $slot) {
249      foreach ($slot['sources'] ?? [] as $slot_source) {
250        $contexts = \array_merge($contexts, $this->getContextFromSource($slot_source['source_id'], $slot_source['source']));
251      }
252    }
253
254    $props = $source->getSetting('component')['props'] ?? [];
255
256    foreach ($props as $prop_source) {
256    foreach ($props as $prop_source) {
256    foreach ($props as $prop_source) {
257      $contexts = \array_merge($contexts, $this->getContextFromSource($prop_source['source_id'], $prop_source['source']));
256    foreach ($props as $prop_source) {
257      $contexts = \array_merge($contexts, $this->getContextFromSource($prop_source['source_id'], $prop_source['source']));
258    }
259
260    return $contexts;
PatternPreset->getGroup
112    return $this->group;
PatternPreset->getSources
129  public function getSources(array $contexts = [], bool $fillInternalId = TRUE): array {
130    $data = $this->get('sources') ?? [];
131
132    if (isset($data[0]) && \count($data) === 1) {
132    if (isset($data[0]) && \count($data) === 1) {
133      $data = \reset($data);
134    }
135
136    if (empty($data) || !isset($data['source_id'])) {
136    if (empty($data) || !isset($data['source_id'])) {
136    if (empty($data) || !isset($data['source_id'])) {
137      return [];
140    if ($fillInternalId) {
141      self::fillInternalId($data);
142    }
143
144    return $data;
144    return $data;
PatternPreset->getSummary
119    $contexts = [];
120    $data = $this->getSources($contexts, FALSE);
121    $data = $this->slotSourceProxy()->getLabelWithSummary($data);
122
123    return $data['summary'] ?: $data['label'];
PatternPreset->slotSourceProxy
298    return $this->slotSourceProxy ??= \Drupal::service('display_builder.slot_sources_proxy');
PatternPreset->sourcePluginManager
288    return $this->sourcePluginManager ??= \Drupal::service('plugin.manager.ui_patterns_source');