Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
100 / 100
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
NameAdditionalPreferredTrait
100.00% covered (success)
100.00%
100 / 100
100.00% covered (success)
100.00%
8 / 8
20
100.00% covered (success)
100.00%
1 / 1
 getDefaultAdditionalPreferredSettings
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getNameAdditionalPreferredSettingsForm
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
1 / 1
1
 appendAdditionalPreferredSummary
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
4
 resolveAdditionalSourceLabel
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 settingsNameAdditionalPreferredSummary
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getAdditionalSources
100.00% covered (success)
100.00%
27 / 27
100.00% covered (success)
100.00%
1 / 1
8
 getEmptyOption
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getTraitUsageIsField
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\name\Traits;
6
7use Drupal\Core\Entity\EntityFieldManagerInterface;
8use Drupal\Core\Entity\EntityTypeManagerInterface;
9
10/**
11 * Name form for preferred and alternative settings trait.
12 */
13trait NameAdditionalPreferredTrait {
14
15  /**
16   * The entity type manager.
17   *
18   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|null
19   */
20  protected ?EntityTypeManagerInterface $entityTypeManager = NULL;
21
22  /**
23   * The entity field manager.
24   *
25   * @var \Drupal\Core\Entity\EntityFieldManagerInterface|null
26   */
27  protected ?EntityFieldManagerInterface $entityFieldManager = NULL;
28
29  /**
30   * Gets the default settings for alternative and preferred fields.
31   *
32   * @return array
33   *   Default settings.
34   */
35  protected static function getDefaultAdditionalPreferredSettings() {
36    return [
37      "preferred_field_reference" => "",
38      "preferred_field_reference_separator" => ", ",
39      "alternative_field_reference" => "",
40      "alternative_field_reference_separator" => ", ",
41    ];
42  }
43
44  /**
45   * Returns a form for the default settings defined above.
46   *
47   * @return array
48   *   The form definition for the field settings.
49   */
50  protected function getNameAdditionalPreferredSettingsForm() {
51    $elements = [];
52    $elements['preferred_field_reference'] = [
53      '#type' => 'select',
54      '#title' => $this->t('Preferred component source'),
55      '#default_value' => $this->getSetting('preferred_field_reference'),
56      '#empty_option' => $this->getEmptyOption(),
57      '#options' => $this->getAdditionalSources(),
58      '#description' => $this->t('A data source to use as the preferred given name within the name formats. A common use-case would be for a users nickname.<br>i.e. "q" and "v", plus the conditional "p", "d" and "D" name format options.'),
59    ];
60    $elements['preferred_field_reference_separator'] = [
61      '#type' => 'textfield',
62      '#title' => $this->t('Preferred component source multivalue separator'),
63      '#default_value' => $this->getSetting('preferred_field_reference_separator'),
64      '#description' => $this->t('Used to separate multi-value items in an inline list.'),
65      '#states' => [
66        'invisible' => [
67          ':input[name$="[preferred_field_reference]"]' => ['value' => ''],
68        ],
69      ],
70    ];
71
72    $elements['alternative_field_reference'] = [
73      '#type' => 'select',
74      '#title' => $this->t('Alternative component source'),
75      '#default_value' => $this->getSetting('alternative_field_reference'),
76      '#empty_option' => $this->getEmptyOption(),
77      '#options' => $this->getAdditionalSources(),
78      '#description' => $this->t('A data source to use as the alternative component within the name formats. Possible use-cases include; providing a custom fully formatted name alternative to use in citations; a separate field for a users accreditation / post-nominal letters.<br>i.e. "a" and "A" name format options.'),
79    ];
80    $elements['alternative_field_reference_separator'] = [
81      '#type' => 'textfield',
82      '#title' => $this->t('Alternative component source multivalue separator'),
83      '#default_value' => $this->getSetting('alternative_field_reference_separator'),
84      '#description' => $this->t('Used to separate multi-value items in an inline list.'),
85      '#states' => [
86        'invisible' => [
87          ':input[name$="[alternative_field_reference]"]' => ['value' => ''],
88        ],
89      ],
90    ];
91
92    return $elements;
93  }
94
95  /**
96   * Appends one preferred or alternative source line to the settings summary.
97   *
98   * @param array $summary
99   *   The summary array to add the settings to.
100   * @param string $setting_key
101   *   The setting key for the source reference.
102   * @param string $label_prefix
103   *   The summary label prefix, e.g. Preferred or Alternative.
104   */
105  private function appendAdditionalPreferredSummary(array &$summary, string $setting_key, string $label_prefix): void {
106    $type = $this->getSetting($setting_key);
107    if ($type) {
108      $summary[] = $this->t('@prefix: @label', [
109        '@prefix' => $label_prefix,
110        '@label' => $this->resolveAdditionalSourceLabel($type),
111      ]);
112      return;
113    }
114    if ($this->getTraitUsageIsField()) {
115      return;
116    }
117    $field_value = $this->fieldDefinition->getSetting($setting_key);
118    if ($field_value) {
119      $summary[] = $this->t('@prefix: field default (@label)', [
120        '@prefix' => $label_prefix,
121        '@label' => $this->resolveAdditionalSourceLabel($field_value),
122      ]);
123      return;
124    }
125    $summary[] = $this->t('@prefix: field default (-- none --)', [
126      '@prefix' => $label_prefix,
127    ]);
128  }
129
130  /**
131   * Resolves a source reference key to a display label.
132   *
133   * @param string $type
134   *   The source reference key.
135   *
136   * @return string|\Drupal\Core\StringTranslation\TranslatableMarkup
137   *   The resolved label, or an invalid marker.
138   */
139  private function resolveAdditionalSourceLabel(string $type) {
140    $targets = $this->getAdditionalSources();
141    return empty($targets[$type]) ? t('-- invalid --') : $targets[$type];
142  }
143
144  /**
145   * The preferred and alternative settings to the summary.
146   *
147   * @param array $summary
148   *   The summary array to add the settings to.
149   */
150  protected function settingsNameAdditionalPreferredSummary(&$summary) {
151    $this->appendAdditionalPreferredSummary($summary, 'preferred_field_reference', 'Preferred');
152    $this->appendAdditionalPreferredSummary($summary, 'alternative_field_reference', 'Alternative');
153  }
154
155  /**
156   * Helper function to find attached fields to use as alternative sources.
157   *
158   * Currently field items do not support dependencies injected.
159   *
160   * To refactor once https://www.drupal.org/node/2053415 gets in.
161   *
162   * @return array
163   *   The discovered additional sources.
164   */
165  protected function getAdditionalSources() {
166    if (!isset($this->entityTypeManager)) {
167      $this->entityTypeManager = \Drupal::entityTypeManager();
168    }
169    if (!isset($this->entityFieldManager)) {
170      $this->entityFieldManager = \Drupal::service('entity_field.manager');
171    }
172
173    $field_definition = $this->getFieldDefinition();
174    $entity_type_id = $field_definition->getTargetEntityTypeId();
175    $entity_type = $this->entityTypeManager
176      ->getStorage($entity_type_id)
177      ->getEntityType();
178    $bundle = $field_definition->getTargetBundle();
179    $entity_type_label = $entity_type->getBundleLabel();
180    if (!$entity_type_label) {
181      $entity_type_label = $entity_type->getLabel();
182    }
183    $sources = [
184      '_self' => $this->t('@label label', ['@label' => $entity_type_label]),
185    ];
186    if ($entity_type_id == 'user') {
187      $sources['_self_property_name'] = $this->t('@label login name', ['@label' => $entity_type_label]);
188    }
189    $fields = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle);
190    foreach ($fields as $field_name => $field) {
191      $is_configurable_field = (
192        !$field->getFieldStorageDefinition()->isBaseField()
193        && $field_name != $field_definition->getName()
194      );
195      if ($is_configurable_field) {
196        $sources[$field->getName()] = $field->getLabel();
197      }
198    }
199    return $sources;
200  }
201
202  /**
203   * Returns the empty option for the select list.
204   */
205  protected function getEmptyOption() {
206    if ($this->getTraitUsageIsField()) {
207      return $this->t('-- none --');
208    }
209
210    return $this->t('-- field default --');
211  }
212
213  /**
214   * Helper function to determine if the trait is being used in a field.
215   */
216  protected function getTraitUsageIsField() {
217    return is_subclass_of($this, 'Drupal\Core\Field\FieldItemBase');
218  }
219
220}