Code Coverage |
||||||||||||||||
Lines |
Branches |
Paths |
Functions and Methods |
Classes and Traits |
||||||||||||
| Total | |
0.00% |
0 / 61 |
|
0.00% |
0 / 18 |
|
0.00% |
0 / 13 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
| ExtraFieldSource | |
0.00% |
0 / 51 |
|
0.00% |
0 / 18 |
|
0.00% |
0 / 13 |
|
0.00% |
0 / 7 |
156 | |
0.00% |
0 / 1 |
| create | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| defaultSettings | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| settingsSummary | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| getPropValue | |
0.00% |
0 / 21 |
|
0.00% |
0 / 7 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
20 | |||
| settingsForm | |
0.00% |
0 / 9 |
|
0.00% |
0 / 4 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
| renderPlaceholder | |
0.00% |
0 / 7 |
|
0.00% |
0 / 3 |
|
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| getDefinitions | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace Drupal\display_builder_entity_view\Plugin\UiPatterns\Source; |
| 6 | |
| 7 | use Drupal\Core\Entity\Entity\EntityViewDisplay; |
| 8 | use Drupal\Core\Entity\EntityFieldManagerInterface; |
| 9 | use Drupal\Core\Form\FormStateInterface; |
| 10 | use Drupal\Core\Plugin\Context\ContextDefinition; |
| 11 | use Drupal\Core\StringTranslation\TranslatableMarkup; |
| 12 | use Drupal\display_builder\RenderableBuilderTrait; |
| 13 | use Drupal\ui_patterns\Attribute\Source; |
| 14 | use Drupal\ui_patterns\SourcePluginBase; |
| 15 | use Symfony\Component\DependencyInjection\ContainerInterface; |
| 16 | |
| 17 | /** |
| 18 | * Plugin implementation of the source. |
| 19 | */ |
| 20 | #[Source( |
| 21 | id: 'extra_field', |
| 22 | label: new TranslatableMarkup('Extra field'), |
| 23 | description: new TranslatableMarkup('An entity extra field.'), |
| 24 | prop_types: ['slot'], |
| 25 | context_definitions: [ |
| 26 | 'entity' => new ContextDefinition('entity', label: new TranslatableMarkup('Entity'), required: TRUE), |
| 27 | 'view_mode' => new ContextDefinition('string', label: new TranslatableMarkup('View mode'), required: FALSE), |
| 28 | ], |
| 29 | metadata: ['group' => new TranslatableMarkup('Fields')], |
| 30 | )] |
| 31 | class ExtraFieldSource extends SourcePluginBase { |
| 32 | |
| 33 | use RenderableBuilderTrait; |
| 34 | |
| 35 | /** |
| 36 | * The entity field manager. |
| 37 | */ |
| 38 | protected EntityFieldManagerInterface $entityFieldManager; |
| 39 | |
| 40 | /** |
| 41 | * {@inheritdoc} |
| 42 | */ |
| 43 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 44 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 45 | $instance->entityFieldManager = $container->get('entity_field.manager'); |
| 46 | |
| 47 | return $instance; |
| 48 | } |
| 49 | |
| 50 | /** |
| 51 | * {@inheritdoc} |
| 52 | */ |
| 53 | public function defaultSettings(): array { |
| 54 | return [ |
| 55 | 'field' => NULL, |
| 56 | ]; |
| 57 | } |
| 58 | |
| 59 | /** |
| 60 | * {@inheritdoc} |
| 61 | */ |
| 62 | public function settingsSummary(): array { |
| 63 | return [ |
| 64 | $this->getDefinitions()[$this->getSetting('field')]['label'] ?? '', |
| 65 | ]; |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * {@inheritdoc} |
| 70 | */ |
| 71 | public function getPropValue(): mixed { |
| 72 | $field = $this->getSetting('field'); |
| 73 | |
| 74 | if (!$field) { |
| 75 | return []; |
| 76 | } |
| 77 | |
| 78 | /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ |
| 79 | $entity = $this->getContextValue('entity'); |
| 80 | $view_mode = $this->getContextValue('view_mode') ?? 'default'; |
| 81 | |
| 82 | if (!$entity->id()) { |
| 83 | return $this->renderPlaceholder($field); |
| 84 | } |
| 85 | |
| 86 | $entity_type_id = $entity->getEntityTypeId(); |
| 87 | $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode); |
| 88 | |
| 89 | // Register the extra field as a component on the display. Implementations |
| 90 | // of hook_entity_view() and hook_ENTITY_TYPE_view() often guard rendering |
| 91 | // with $display->getComponent($field_name), which would silently skip |
| 92 | // the field if it is not enabled in the underlying entity view display. |
| 93 | if (!$display->getComponent($field)) { |
| 94 | $display->setComponent($field); |
| 95 | } |
| 96 | |
| 97 | // Mirror core EntityViewBuilder::buildComponents(): invoke prepare_view |
| 98 | // before the view hooks so implementations can set up their internal state. |
| 99 | // @see \Drupal\Core\Entity\EntityViewBuilder::buildComponents() |
| 100 | $this->moduleHandler->invokeAll('entity_prepare_view', [ |
| 101 | $entity_type_id, |
| 102 | [$entity->id() => $entity], |
| 103 | [$entity->bundle() => $display], |
| 104 | $view_mode, |
| 105 | ]); |
| 106 | |
| 107 | /** @var array<string, mixed> $build */ |
| 108 | $build = []; |
| 109 | |
| 110 | // Invoke both the entity-type-specific and the generic hook so all |
| 111 | // extra-field implementations are reached regardless of which hook they |
| 112 | // use. Core EntityViewBuilder does the same. |
| 113 | // @see \Drupal\Core\Entity\EntityViewBuilder::view() |
| 114 | $this->moduleHandler->invokeAll($entity_type_id . '_view', [&$build, $entity, $display, $view_mode]); |
| 115 | $this->moduleHandler->invokeAll('entity_view', [&$build, $entity, $display, $view_mode]); |
| 116 | |
| 117 | return $build[$field] ?? []; |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * {@inheritdoc} |
| 122 | */ |
| 123 | public function settingsForm(array $form, FormStateInterface $form_state): array { |
| 124 | $options = []; |
| 125 | |
| 126 | foreach ($this->getDefinitions() as $field_name => $definition) { |
| 127 | $options[$field_name] = $definition['label']; |
| 128 | } |
| 129 | $form['field'] = [ |
| 130 | '#type' => 'select', |
| 131 | '#options' => $options, |
| 132 | '#default_value' => $this->getSetting('field'), |
| 133 | ]; |
| 134 | |
| 135 | return $form; |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Render placeholder when a proper entity is not loaded. |
| 140 | * |
| 141 | * @param string $field |
| 142 | * Extra field ID. |
| 143 | * |
| 144 | * @return array |
| 145 | * A renderable array. |
| 146 | */ |
| 147 | protected function renderPlaceholder(string $field): array { |
| 148 | $definition = $this->getDefinitions()[$field]; |
| 149 | |
| 150 | if (!$definition) { |
| 151 | return []; |
| 152 | } |
| 153 | |
| 154 | $label = $definition['label'] ?? ''; |
| 155 | $build = $this->buildPlaceholderButton($this->t('Extra field: @field', ['@field' => $label])); |
| 156 | $build['#attributes']['class'][] = 'db-background'; |
| 157 | |
| 158 | return $build; |
| 159 | } |
| 160 | |
| 161 | /** |
| 162 | * Get extra field definitions. |
| 163 | * |
| 164 | * Extra fields are not plugins but old-fashioned hooks. |
| 165 | * |
| 166 | * @throws \Drupal\Component\Plugin\Exception\ContextException |
| 167 | * |
| 168 | * @return array |
| 169 | * A list of extra field definition. |
| 170 | */ |
| 171 | protected function getDefinitions(): array { |
| 172 | /** @var \Drupal\Core\Entity\FieldableEntityInterface $entity */ |
| 173 | $entity = $this->getContextValue('entity'); |
| 174 | $entity_type_id = $entity->getEntityTypeId(); |
| 175 | $bundle = $entity->bundle(); |
| 176 | |
| 177 | $extra_fields = $this->entityFieldManager->getExtraFields($entity_type_id, $bundle); |
| 178 | |
| 179 | return $extra_fields['display'] ?? []; |
| 180 | } |
| 181 | |
| 182 | } |
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.
| 43 | public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static { |
| 44 | $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition); |
| 45 | $instance->entityFieldManager = $container->get('entity_field.manager'); |
| 46 | |
| 47 | return $instance; |
| 48 | } |
| 55 | 'field' => NULL, |
| 56 | ]; |
| 57 | } |
| 173 | $entity = $this->getContextValue('entity'); |
| 174 | $entity_type_id = $entity->getEntityTypeId(); |
| 175 | $bundle = $entity->bundle(); |
| 176 | |
| 177 | $extra_fields = $this->entityFieldManager->getExtraFields($entity_type_id, $bundle); |
| 178 | |
| 179 | return $extra_fields['display'] ?? []; |
| 180 | } |
| 72 | $field = $this->getSetting('field'); |
| 73 | |
| 74 | if (!$field) { |
| 75 | return []; |
| 79 | $entity = $this->getContextValue('entity'); |
| 80 | $view_mode = $this->getContextValue('view_mode') ?? 'default'; |
| 81 | |
| 82 | if (!$entity->id()) { |
| 83 | return $this->renderPlaceholder($field); |
| 86 | $entity_type_id = $entity->getEntityTypeId(); |
| 87 | $display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode); |
| 88 | |
| 89 | // Register the extra field as a component on the display. Implementations |
| 90 | // of hook_entity_view() and hook_ENTITY_TYPE_view() often guard rendering |
| 91 | // with $display->getComponent($field_name), which would silently skip |
| 92 | // the field if it is not enabled in the underlying entity view display. |
| 93 | if (!$display->getComponent($field)) { |
| 94 | $display->setComponent($field); |
| 95 | } |
| 96 | |
| 97 | // Mirror core EntityViewBuilder::buildComponents(): invoke prepare_view |
| 98 | // before the view hooks so implementations can set up their internal state. |
| 99 | // @see \Drupal\Core\Entity\EntityViewBuilder::buildComponents() |
| 100 | $this->moduleHandler->invokeAll('entity_prepare_view', [ |
| 100 | $this->moduleHandler->invokeAll('entity_prepare_view', [ |
| 101 | $entity_type_id, |
| 102 | [$entity->id() => $entity], |
| 103 | [$entity->bundle() => $display], |
| 104 | $view_mode, |
| 105 | ]); |
| 106 | |
| 107 | /** @var array<string, mixed> $build */ |
| 108 | $build = []; |
| 109 | |
| 110 | // Invoke both the entity-type-specific and the generic hook so all |
| 111 | // extra-field implementations are reached regardless of which hook they |
| 112 | // use. Core EntityViewBuilder does the same. |
| 113 | // @see \Drupal\Core\Entity\EntityViewBuilder::view() |
| 114 | $this->moduleHandler->invokeAll($entity_type_id . '_view', [&$build, $entity, $display, $view_mode]); |
| 115 | $this->moduleHandler->invokeAll('entity_view', [&$build, $entity, $display, $view_mode]); |
| 116 | |
| 117 | return $build[$field] ?? []; |
| 118 | } |
| 147 | protected function renderPlaceholder(string $field): array { |
| 148 | $definition = $this->getDefinitions()[$field]; |
| 149 | |
| 150 | if (!$definition) { |
| 151 | return []; |
| 154 | $label = $definition['label'] ?? ''; |
| 155 | $build = $this->buildPlaceholderButton($this->t('Extra field: @field', ['@field' => $label])); |
| 156 | $build['#attributes']['class'][] = 'db-background'; |
| 157 | |
| 158 | return $build; |
| 159 | } |
| 123 | public function settingsForm(array $form, FormStateInterface $form_state): array { |
| 124 | $options = []; |
| 125 | |
| 126 | foreach ($this->getDefinitions() as $field_name => $definition) { |
| 126 | foreach ($this->getDefinitions() as $field_name => $definition) { |
| 126 | foreach ($this->getDefinitions() as $field_name => $definition) { |
| 126 | foreach ($this->getDefinitions() as $field_name => $definition) { |
| 127 | $options[$field_name] = $definition['label']; |
| 128 | } |
| 129 | $form['field'] = [ |
| 130 | '#type' => 'select', |
| 131 | '#options' => $options, |
| 132 | '#default_value' => $this->getSetting('field'), |
| 133 | ]; |
| 134 | |
| 135 | return $form; |
| 136 | } |
| 64 | $this->getDefinitions()[$this->getSetting('field')]['label'] ?? '', |
| 65 | ]; |
| 66 | } |