Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
RelationshipForm
0.00% covered (danger)
0.00%
0 / 101
0.00% covered (danger)
0.00%
0 / 5
600
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 create
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 form
0.00% covered (danger)
0.00%
0 / 52
0.00% covered (danger)
0.00%
0 / 1
156
 save
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 applyValidContactConstraints
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2
3namespace Drupal\crm\Form;
4
5use Drupal\Component\Datetime\TimeInterface;
6use Drupal\Core\Datetime\DateFormatterInterface;
7use Drupal\Core\Entity\ContentEntityForm;
8use Drupal\Core\Entity\EntityRepositoryInterface;
9use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
10use Drupal\Core\Form\FormStateInterface;
11use Drupal\Core\Entity\EntityTypeManagerInterface;
12use Drupal\Core\Session\AccountInterface;
13use Symfony\Component\DependencyInjection\ContainerInterface;
14
15/**
16 * Form controller for the crm relationship entity edit forms.
17 */
18class RelationshipForm extends ContentEntityForm {
19
20  /**
21   * The date formatter service.
22   *
23   * @var \Drupal\Core\Datetime\DateFormatterInterface
24   */
25  protected $dateFormatter;
26
27  /**
28   * The current user.
29   *
30   * @var \Drupal\Core\Session\AccountInterface
31   */
32  protected $currentUser;
33
34  /**
35   * The entity type manager.
36   *
37   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
38   */
39  protected $entityTypeManager;
40
41  /**
42   * Constructs a RelationshipForm object.
43   *
44   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
45   *   The entity repository.
46   * @param \Drupal\Component\Datetime\TimeInterface $time
47   *   The time service.
48   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
49   *   The date formatter service.
50   * @param \Drupal\Core\Session\AccountInterface $current_user
51   *   The current user.
52   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
53   *   The entity type manager.
54   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface|null $entity_type_bundle_info
55   *   The entity type bundle service.
56   */
57  public function __construct(
58    EntityRepositoryInterface $entity_repository,
59    TimeInterface $time,
60    DateFormatterInterface $date_formatter,
61    AccountInterface $current_user,
62    EntityTypeManagerInterface $entity_type_manager,
63    ?EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL,
64  ) {
65
66    parent::__construct($entity_repository, $entity_type_bundle_info, $time);
67    $this->dateFormatter = $date_formatter;
68    $this->currentUser = $current_user;
69    $this->entityTypeManager = $entity_type_manager;
70  }
71
72  /**
73   * {@inheritdoc}
74   */
75  final public static function create(ContainerInterface $container) {
76    return new self(
77      $container->get('entity.repository'),
78      $container->get('datetime.time'),
79      $container->get('date.formatter'),
80      $container->get('current_user'),
81      $container->get('entity_type.manager'),
82      $container->get('entity_type.bundle.info'),
83    );
84  }
85
86  /**
87   * {@inheritdoc}
88   */
89  public function form(array $form, FormStateInterface $form_state) {
90    $form = parent::form($form, $form_state);
91    $relationship = $this->getEntity();
92    $bundle = $relationship->bundle->entity;
93
94    // Normalize contact_type_a to array and convert to target_bundles format.
95    $contact_type_a = $bundle->get('contact_type_a');
96    if (!is_array($contact_type_a)) {
97      $contact_type_a = $contact_type_a !== NULL && $contact_type_a !== '' ? [$contact_type_a] : [];
98    }
99    $target_bundles_a = array_combine($contact_type_a, $contact_type_a);
100    $form['contact_a']['widget'][0]['target_id']['#selection_settings']['target_bundles'] = $target_bundles_a;
101    $form['contact_a']['widget'][0]['target_id']['#title'] = $bundle->get('label_a');
102
103    // Apply valid contacts restriction for Contact A.
104    $this->applyValidContactConstraints($form, $relationship, $bundle, 'contact_a');
105
106    // Normalize contact_type_b to array and convert to target_bundles format.
107    $contact_type_b = $bundle->get('contact_type_b');
108    if (!is_array($contact_type_b)) {
109      $contact_type_b = $contact_type_b !== NULL && $contact_type_b !== '' ? [$contact_type_b] : [];
110    }
111    $target_bundles_b = array_combine($contact_type_b, $contact_type_b);
112    $form['contact_b']['widget'][0]['target_id']['#selection_settings']['target_bundles'] = $target_bundles_b;
113    $form['contact_b']['widget'][0]['target_id']['#title'] = $bundle->get('label_b');
114
115    // Apply valid contacts restriction for Contact B.
116    $this->applyValidContactConstraints($form, $relationship, $bundle, 'contact_b');
117
118    if ($this->getOperation() == 'edit') {
119      $form['contact_a']['widget']['#disabled'] = TRUE;
120      $form['contact_b']['widget']['#disabled'] = TRUE;
121    }
122
123    $form['advanced']['#attributes']['class'][] = 'entity-meta';
124
125    $form['meta'] = [
126      '#type' => 'details',
127      '#group' => 'advanced',
128      '#weight' => -10,
129      '#title' => $this->t('Status'),
130      '#attributes' => ['class' => ['entity-meta__header']],
131      '#tree' => TRUE,
132      '#access' => $this->currentUser->hasPermission('administer crm'),
133    ];
134    $form['meta']['published'] = [
135      '#type' => 'item',
136      '#markup' => $relationship->get('status')->value ? $this->t('Active') : $this->t('Inactive'),
137      '#access' => !$relationship->isNew(),
138      '#wrapper_attributes' => ['class' => ['entity-meta__title']],
139    ];
140    $form['meta']['changed'] = [
141      '#type' => 'item',
142      '#title' => $this->t('Last saved'),
143      '#markup' => !$relationship->isNew() ? $this->dateFormatter->format($relationship->getChangedTime(), 'short') : $this->t('Not saved yet'),
144      '#wrapper_attributes' => ['class' => ['entity-meta__last-saved']],
145    ];
146
147    $form['meta']['status'] = &$form['status'];
148    $form['meta']['status']['#weight'] = 100;
149    unset($form['status']);
150
151    if (isset($form['uid'])) {
152      unset($form['uid']);
153    }
154
155    if (isset($form['created'])) {
156      $form['created']['#weight'] = 200;
157      $form['meta']['created'] = &$form['created'];
158      unset($form['created']);
159    }
160
161    return $form;
162  }
163
164  /**
165   * {@inheritdoc}
166   */
167  public function save(array $form, FormStateInterface $form_state) {
168    $result = parent::save($form, $form_state);
169
170    $relationship = $this->getEntity();
171
172    $label = $relationship->label() ?? 'No label';
173    $message_arguments = ['%label' => $label];
174    $logger_arguments = [
175      '%label' => $label,
176      'link' => $relationship->toLink($this->t('View'))->toString(),
177    ];
178
179    switch ($result) {
180      case SAVED_NEW:
181        $this->messenger()->addStatus($this->t('New crm relationship %label has been created.', $message_arguments));
182        $this->logger('crm')->notice('Created new crm relationship %label', $logger_arguments);
183        break;
184
185      case SAVED_UPDATED:
186        $this->messenger()->addStatus($this->t('The crm relationship %label has been updated.', $message_arguments));
187        $this->logger('crm')->notice('Updated crm relationship %label.', $logger_arguments);
188        break;
189    }
190
191    $form_state->setRedirect('entity.crm_relationship.canonical', ['crm_relationship' => $relationship->id()]);
192
193    return $result;
194  }
195
196  /**
197   * Applies valid contact constraints to a contact field.
198   *
199   * @param array &$form
200   *   The form array.
201   * @param \Drupal\crm\CrmRelationshipInterface $relationship
202   *   The relationship entity.
203   * @param \Drupal\crm\CrmRelationshipTypeInterface $bundle
204   *   The relationship type entity.
205   * @param string $field_name
206   *   The field name ('contact_a' or 'contact_b').
207   */
208  protected function applyValidContactConstraints(array &$form, $relationship, $bundle, string $field_name): void {
209    // Get valid contacts based on field name.
210    $valid_contact_ids = $field_name === 'contact_a'
211      ? $bundle->getValidContactsA()
212      : $bundle->getValidContactsB();
213
214    // If no valid contacts are configured, no restrictions apply.
215    if (empty($valid_contact_ids)) {
216      return;
217    }
218
219    // Load the valid contacts to verify they exist.
220    $valid_contacts = $this->entityTypeManager
221      ->getStorage('crm_contact')
222      ->loadMultiple($valid_contact_ids);
223
224    // Filter to only existing contacts.
225    $existing_valid_ids = array_keys($valid_contacts);
226
227    if (empty($existing_valid_ids)) {
228      return;
229    }
230
231    // If exactly one valid contact exists, auto-select and lock the field.
232    if (count($existing_valid_ids) === 1) {
233      $single_contact = reset($valid_contacts);
234
235      // Set the default value if not already set.
236      if ($relationship->isNew() || empty($form[$field_name]['widget'][0]['target_id']['#default_value'])) {
237        $form[$field_name]['widget'][0]['target_id']['#default_value'] = $single_contact;
238      }
239
240      // Disable the field to prevent changes.
241      $form[$field_name]['widget']['#disabled'] = TRUE;
242
243      // Add description explaining why it's locked.
244      $form[$field_name]['widget'][0]['target_id']['#description'] = $this->t('This contact is automatically selected because it is the only valid option for this relationship type.');
245    }
246    else {
247      // Multiple valid contacts, use custom selection handler to restrict.
248      $form[$field_name]['widget'][0]['target_id']['#selection_handler'] = 'valid_contacts:crm_contact';
249      $form[$field_name]['widget'][0]['target_id']['#selection_settings']['valid_contact_ids'] = $existing_valid_ids;
250    }
251  }
252
253}