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 path as identified by Xdebug. Please note a path is not
necessarily coterminous with a line, a line may contain multiple paths and therefore show up more than once.
Please also be aware that some paths may include implicit rather than explicit branches, 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 []; |
| 72 | $field = $this->getSetting('field'); |
| 73 | |
| 74 | if (!$field) { |
| 79 | $entity = $this->getContextValue('entity'); |
| 80 | $view_mode = $this->getContextValue('view_mode') ?? 'default'; |
| 81 | |
| 82 | if (!$entity->id()) { |
| 83 | return $this->renderPlaceholder($field); |
| 72 | $field = $this->getSetting('field'); |
| 73 | |
| 74 | if (!$field) { |
| 79 | $entity = $this->getContextValue('entity'); |
| 80 | $view_mode = $this->getContextValue('view_mode') ?? 'default'; |
| 81 | |
| 82 | if (!$entity->id()) { |
| 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 | } |
| 72 | $field = $this->getSetting('field'); |
| 73 | |
| 74 | if (!$field) { |
| 79 | $entity = $this->getContextValue('entity'); |
| 80 | $view_mode = $this->getContextValue('view_mode') ?? 'default'; |
| 81 | |
| 82 | if (!$entity->id()) { |
| 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)) { |
| 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 []; |
| 147 | protected function renderPlaceholder(string $field): array { |
| 148 | $definition = $this->getDefinitions()[$field]; |
| 149 | |
| 150 | if (!$definition) { |
| 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) { |
| 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 | } |
| 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) { |
| 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 | } |
| 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) { |
| 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 | } |