Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
81.67% covered (warning)
81.67%
49 / 60
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
PrimaryReferenceAutocompleteWidget
81.67% covered (warning)
81.67%
49 / 60
80.00% covered (warning)
80.00%
4 / 5
23.72
0.00% covered (danger)
0.00%
0 / 1
 defaultSettings
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 settingsForm
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 settingsSummary
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 formElement
52.17% covered (warning)
52.17%
12 / 23
0.00% covered (danger)
0.00%
0 / 1
7.73
 massageFormValues
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
12
1<?php
2
3namespace Drupal\primary_entity_reference\Plugin\Field\FieldWidget;
4
5use Drupal\Core\Field\Attribute\FieldWidget;
6use Drupal\Core\Field\FieldItemListInterface;
7use Drupal\Core\Form\FormStateInterface;
8use Drupal\Core\StringTranslation\TranslatableMarkup;
9use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
10
11/**
12 * Plugin implementation of the 'primary_entity_reference_autocomplete' widget.
13 */
14#[FieldWidget(
15  id: 'primary_entity_reference_autocomplete',
16  label: new TranslatableMarkup('Autocomplete'),
17  description: new TranslatableMarkup('An autocomplete text field.'),
18  field_types: ['primary_entity_reference'],
19)]
20class PrimaryReferenceAutocompleteWidget extends EntityReferenceAutocompleteWidget {
21
22  /**
23   * {@inheritdoc}
24   */
25  public static function defaultSettings() {
26    return [
27      'show_primary_only' => FALSE,
28    ] + parent::defaultSettings();
29  }
30
31  /**
32   * {@inheritdoc}
33   */
34  public function settingsForm(array $form, FormStateInterface $form_state) {
35    $element = parent::settingsForm($form, $form_state);
36
37    $element['show_primary_only'] = [
38      '#type'          => 'checkbox',
39      '#title'         => $this->t('Show primary only'),
40      '#description'   => $this->t('Only display the primary item in the form. All non-primary items will be preserved but not shown.'),
41      '#default_value' => $this->getSetting('show_primary_only'),
42    ];
43
44    return $element;
45  }
46
47  /**
48   * {@inheritdoc}
49   */
50  public function settingsSummary() {
51    $summary = parent::settingsSummary();
52
53    if ($this->getSetting('show_primary_only')) {
54      $summary[] = $this->t('Only showing primary item in form.');
55    }
56
57    return $summary;
58  }
59
60  /**
61   * {@inheritdoc}
62   */
63  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
64    // If show_primary_only is enabled, only render delta 0.
65    $show_primary_only = $this->getSetting('show_primary_only');
66    if ($show_primary_only && $delta > 0) {
67      return [];
68    }
69
70    $element = parent::formElement($items, $delta, $element, $form, $form_state);
71    /** @var \Drupal\primary_entity_reference\Plugin\Field\FieldType\PrimaryEntityReferenceItem $item */
72    $item = $items[$delta];
73    $field_name = $this->fieldDefinition->getName();
74
75    // Add the primary selection library.
76    $element['#attached']['library'][] = 'primary_entity_reference/primary_selection';
77
78    // Add wrapper class for styling.
79    $element['#attributes']['class'][] = 'primary-entity-reference-autocomplete';
80
81    // Add container wrapper for proper structure.
82    $element['#prefix'] = '<div class="primary-autocomplete-wrapper">';
83    $element['#suffix'] = '</div>';
84
85    // Hide primary radio if show_primary_only is enabled.
86    if (!$show_primary_only) {
87      $element['primary'] = [
88        '#type'          => 'radio',
89        '#return_value'  => $delta,
90        '#title'         => $this->t('Primary'),
91        '#default_value' => $item->get('primary')->getValue() ? $delta : NULL,
92        '#attributes'    => [
93          'id' => $field_name . '-primary-' . $delta,
94        ],
95        '#parents'       => [$field_name, 0, 'primary'],
96        '#weight'        => -100,
97      ];
98    }
99
100    return $element;
101  }
102
103  /**
104   * {@inheritdoc}
105   */
106  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
107    // If show_primary_only is enabled, preserve non-primary items.
108    $show_primary_only = $this->getSetting('show_primary_only');
109    $original_items = [];
110    if ($show_primary_only) {
111      // Get the original entity from form state.
112      $form_object = $form_state->getFormObject();
113      if (method_exists($form_object, 'getEntity')) {
114        $entity = $form_object->getEntity();
115        $field_name = $this->fieldDefinition->getName();
116        if ($entity && $entity->hasField($field_name)) {
117          $original_items = $entity->get($field_name)->getValue();
118        }
119      }
120    }
121
122    $values = parent::massageFormValues($values, $form, $form_state);
123
124    $primary = 0;
125    foreach ($values as $delta => $value) {
126      if (isset($value['primary']) && $value['primary']) {
127        $primary = (int) $value['primary'];
128      }
129    }
130
131    foreach ($values as $delta => $value) {
132      $values[$delta]['primary'] = 0;
133      if ($value['_original_delta'] == $primary) {
134
135        $values[$delta]['primary'] = 1;
136      }
137    }
138
139    // If show_primary_only is enabled, merge back non-primary items.
140    if ($show_primary_only && !empty($original_items)) {
141      // Filter out the original primary item.
142      $non_primary_items = array_filter($original_items, fn($item) => empty($item['primary']));
143
144      // Merge processed values with non-primary items.
145      $values = array_merge($values, $non_primary_items);
146    }
147
148    return $values;
149  }
150
151}