Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
219 / 219
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
Contact
100.00% covered (success)
100.00%
219 / 219
100.00% covered (success)
100.00%
3 / 3
18
100.00% covered (success)
100.00%
1 / 1
 preSave
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
11
 postSave
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
6
 baseFieldDefinitions
100.00% covered (success)
100.00%
182 / 182
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Drupal\crm\Entity;
6
7use Drupal\Core\Entity\Attribute\ContentEntityType;
8use Drupal\Core\Entity\EntityChangedTrait;
9use Drupal\Core\Entity\EntityStorageInterface;
10use Drupal\Core\Entity\EntityTypeInterface;
11use Drupal\Core\Entity\RevisionableContentEntityBase;
12use Drupal\Core\Entity\RevisionLogEntityTrait;
13use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
14use Drupal\Core\Field\BaseFieldDefinition;
15use Drupal\Core\StringTranslation\TranslatableMarkup;
16use Drupal\crm\ContactAccessControlHandler;
17use Drupal\crm\ContactListBuilder;
18use Drupal\crm\CrmContactInterface;
19use Drupal\crm\Form\ContactForm;
20use Drupal\inline_entity_form\Plugin\Field\FieldWidget\InlineEntityFormComplex;
21use Drupal\views\EntityViewsData;
22use Drupal\Core\Entity\ContentEntityDeleteForm;
23use Drupal\crm\Field\AgeFieldItemList;
24
25/**
26 * CRM contact.
27 *
28 * A contact can be a person, an organization, a household, etc.
29 */
30#[ContentEntityType(
31  id: 'crm_contact',
32  label: new TranslatableMarkup('CRM Contact'),
33  label_collection: new TranslatableMarkup('CRM Contacts'),
34  label_singular: new TranslatableMarkup('crm contact'),
35  label_plural: new TranslatableMarkup('crm contacts'),
36  label_count: [
37    'singular' => '@count crm contact',
38    'plural' => '@count crm contacts',
39  ],
40  bundle_label: new TranslatableMarkup('Contact type'),
41  handlers: [
42    'list_builder' => ContactListBuilder::class,
43    'views_data' => EntityViewsData::class,
44    'access' => ContactAccessControlHandler::class,
45    'form' => [
46      'default' => ContactForm::class,
47      'delete' => ContentEntityDeleteForm::class,
48    ],
49    'route_provider' => [
50      'html' => AdminHtmlRouteProvider::class,
51    ],
52  ],
53  base_table: 'crm_contact',
54  revision_table: 'crm_contact_revision',
55  show_revision_ui: TRUE,
56  bundle_entity_type: 'crm_contact_type',
57  field_ui_base_route: 'entity.crm_contact_type.edit_form',
58  translatable: FALSE,
59  admin_permission: 'administer crm',
60  entity_keys: [
61    'id' => 'id',
62    'revision' => 'revision_id',
63    'bundle' => 'bundle',
64    'label' => 'name',
65    'uuid' => 'uuid',
66    'status' => 'status',
67  ],
68  revision_metadata_keys: [
69    'revision_user' => 'revision_uid',
70    'revision_created' => 'revision_timestamp',
71    'revision_log_message' => 'revision_log',
72  ],
73  links: [
74    'add-page' => '/crm/contact/add',
75    'add-form' => '/crm/contact/add/{crm_contact_type}',
76    'canonical' => '/crm/contact/{crm_contact}',
77    'edit-form' => '/crm/contact/{crm_contact}/edit',
78    'delete-form' => '/crm/contact/{crm_contact}/delete',
79    'collection' => '/admin/content/crm/contact',
80  ],
81)]
82class Contact extends RevisionableContentEntityBase implements CrmContactInterface {
83  use EntityChangedTrait;
84  use RevisionLogEntityTrait;
85
86  /**
87   * {@inheritdoc}
88   */
89  public function preSave(EntityStorageInterface $storage) {
90    // If type is person, set the label field to the name field.
91    if ($this->hasField('full_name') && !$this->get('full_name')->isEmpty()) {
92      $name_array = $this->get('full_name')->getValue();
93      if (!empty($name_array)) {
94        $name_array = $name_array[0];
95
96        // Get field settings for full_name field.
97        $field_definition = $this->getFieldDefinition('full_name');
98        $preferred_field_reference = $field_definition->getSetting('preferred_field_reference');
99        $preferred_field_reference_separator = $field_definition->getSetting('preferred_field_reference_separator') ?? ', ';
100        $alternative_field_reference = $field_definition->getSetting('alternative_field_reference');
101        $alternative_field_reference_separator = $field_definition->getSetting('alternative_field_reference_separator') ?? ', ';
102
103        // Add preferred name if configured and field exists.
104        if ($preferred_field_reference && $this->hasField($preferred_field_reference) && !$this->get($preferred_field_reference)->isEmpty()) {
105          $preferred_values = array_map(function ($item) {
106            return $item['value'];
107          }, $this->get($preferred_field_reference)->getValue());
108          $name_array['preferred'] = implode($preferred_field_reference_separator, $preferred_values);
109        }
110
111        // Add alternative names if configured and field exists.
112        if ($alternative_field_reference && $this->hasField($alternative_field_reference) && !$this->get($alternative_field_reference)->isEmpty()) {
113          $alternative_values = array_map(function ($alias) {
114            return $alias['value'];
115          }, $this->get($alternative_field_reference)->getValue());
116          $name_array['alternative'] = implode($alternative_field_reference_separator, $alternative_values);
117        }
118
119        $name_format = $this->bundle->entity->getThirdPartySetting('crm', 'name_format') ?? 'default';
120        $name_formatter = \Drupal::service('name.formatter');
121        $formatted_name = $name_formatter->format($name_array, $name_format);
122        $this->set('name', $formatted_name);
123      }
124    }
125
126    if ($this->get('name')->isEmpty()) {
127      $type = $this->get('bundle')->entity->label();
128      $uuid = $this->uuid();
129      $this->set('name', $type . ' ' . $uuid);
130    }
131
132    parent::preSave($storage);
133    $this->setNewRevision();
134  }
135
136  /**
137   * {@inheritdoc}
138   */
139  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
140    parent::postSave($storage, $update);
141
142    $detail_fields = ['emails', 'telephones', 'addresses'];
143    foreach ($detail_fields as $detail_field) {
144      if (!$this->hasField($detail_field)) {
145        continue;
146      }
147      foreach ($this->get($detail_field) as $item) {
148        $detail = $item->entity;
149        if ($detail && $detail->get('crm_contact')->isEmpty()) {
150          $detail->set('crm_contact', $this->id());
151          $detail->save();
152        }
153      }
154
155    }
156  }
157
158  /**
159   * {@inheritdoc}
160   */
161  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
162    $fields = parent::baseFieldDefinitions($entity_type);
163
164    $fields['name'] = BaseFieldDefinition::create('string')
165      ->setRevisionable(TRUE)
166      ->setLabel(t('Name'))
167      ->setRequired(TRUE)
168      ->setSetting('max_length', 255)
169      ->setDisplayOptions('form', [
170        'type' => 'string_textfield',
171        'weight' => -5,
172      ])
173      ->setDisplayConfigurable('form', TRUE)
174      ->setDisplayOptions('view', [
175        'label' => 'hidden',
176        'type' => 'string',
177        'weight' => -5,
178      ])
179      ->setDisplayConfigurable('view', TRUE);
180
181    $fields['emails'] = BaseFieldDefinition::create('primary_entity_reference')
182      ->setLabel(t('Emails'))
183      ->setRevisionable(TRUE)
184      ->setCardinality(-1)
185      ->setSetting('target_type', 'crm_contact_detail')
186      ->setSetting('handler_settings', ['target_bundles' => ['email']])
187      ->setDisplayConfigurable('form', TRUE)
188      ->setDisplayOptions('form', [
189        'type' => 'primary_entity_reference_inline_form',
190        'weight' => 8,
191        'settings' => [
192          'allow_new' => TRUE,
193          'allow_existing' => FALSE,
194          'removed_reference' => InlineEntityFormComplex::REMOVED_DELETE,
195          'match_operator' => 'CONTAINS',
196          'allow_duplicate' => FALSE,
197          'form_mode' => 'default',
198          'override_labels' => TRUE,
199          'label_singular' => t('email'),
200          'label_plural' => t('emails'),
201          'collapsible' => FALSE,
202          'collapsed' => FALSE,
203          'revision' => TRUE,
204        ],
205      ])
206      ->setDisplayConfigurable('view', TRUE)
207      ->setDisplayOptions('view', [
208        'label' => 'hidden',
209        'type' => 'primary_entity_reference_entity_view',
210        'weight' => -5,
211        'settings' => [
212          'view_mode' => 'default',
213          'link' => FALSE,
214        ],
215      ]);
216
217    $fields['telephones'] = BaseFieldDefinition::create('primary_entity_reference')
218      ->setLabel(t('Telephones'))
219      ->setRevisionable(TRUE)
220      ->setCardinality(-1)
221      ->setSetting('target_type', 'crm_contact_detail')
222      ->setSetting('handler_settings', ['target_bundles' => ['telephone']])
223      ->setDisplayConfigurable('form', TRUE)
224      ->setDisplayOptions('form', [
225        'type' => 'primary_entity_reference_inline_form',
226        'weight' => 9,
227        'settings' => [
228          'allow_new' => TRUE,
229          'allow_existing' => FALSE,
230          'removed_reference' => InlineEntityFormComplex::REMOVED_DELETE,
231          'match_operator' => 'CONTAINS',
232          'allow_duplicate' => FALSE,
233          'form_mode' => 'default',
234          'override_labels' => TRUE,
235          'label_singular' => t('telephone'),
236          'label_plural' => t('telephones'),
237          'collapsible' => FALSE,
238          'collapsed' => FALSE,
239          'revision' => TRUE,
240        ],
241      ])
242      ->setDisplayConfigurable('view', TRUE)
243      ->setDisplayOptions('view', [
244        'label' => 'hidden',
245        'type' => 'primary_entity_reference_entity_view',
246        'weight' => -5,
247        'settings' => [
248          'view_mode' => 'default',
249          'link' => FALSE,
250        ],
251      ]);
252
253    $fields['addresses'] = BaseFieldDefinition::create('primary_entity_reference')
254      ->setLabel('Addresses')
255      ->setRevisionable(TRUE)
256      ->setCardinality(-1)
257      ->setSetting('target_type', 'crm_contact_detail')
258      ->setSetting('handler_settings', ['target_bundles' => ['address']])
259      ->setDisplayConfigurable('form', TRUE)
260      ->setDisplayOptions('form', [
261        'type' => 'primary_entity_reference_inline_form',
262        'weight' => 10,
263        'settings' => [
264          'allow_new' => TRUE,
265          'allow_existing' => FALSE,
266          'removed_reference' => InlineEntityFormComplex::REMOVED_DELETE,
267          'match_operator' => 'CONTAINS',
268          'allow_duplicate' => FALSE,
269          'form_mode' => 'default',
270          'override_labels' => TRUE,
271          'label_singular' => t('address'),
272          'label_plural' => t('addresses'),
273          'collapsible' => FALSE,
274          'collapsed' => FALSE,
275          'revision' => TRUE,
276        ],
277      ])
278      ->setDisplayConfigurable('view', TRUE)
279      ->setDisplayOptions('view', [
280        'label' => 'hidden',
281        'type' => 'primary_entity_reference_entity_view',
282        'weight' => 0,
283        'settings' => [
284          'view_mode' => 'default',
285          'link' => FALSE,
286        ],
287      ]);
288
289    $fields['status'] = BaseFieldDefinition::create('boolean')
290      ->setLabel(t('Status'))
291      ->setDescription(t('A boolean indicating whether the contact is active.'))
292      ->setDefaultValue(TRUE)
293      ->setDisplayOptions('form', [
294        'type' => 'boolean_checkbox',
295        'settings' => [
296          'display_label' => TRUE,
297        ],
298        'weight' => 120,
299      ])
300      ->setSetting('on_label', 'Status')
301      ->setDisplayConfigurable('form', TRUE);
302
303    $fields['start_date'] = BaseFieldDefinition::create('datetime')
304      ->setLabel(t('Start Date'))
305      ->setDescription(t('When the contact starts.'))
306      ->setRevisionable(TRUE)
307      ->setSettings([
308        'datetime_type' => 'date',
309      ])
310      ->setDefaultValue('')
311      ->setDisplayConfigurable('form', TRUE)
312      ->setDisplayConfigurable('view', TRUE)
313      ->setRevisionable(TRUE);
314
315    $fields['end_date'] = BaseFieldDefinition::create('datetime')
316      ->setLabel(t('End Date'))
317      ->setDescription(t('When the contact ends.'))
318      ->setRevisionable(TRUE)
319      ->setSettings([
320        'datetime_type' => 'date',
321      ])
322      ->setDefaultValue('')
323      ->setDisplayConfigurable('form', TRUE)
324      ->setDisplayConfigurable('view', TRUE)
325      ->setRevisionable(TRUE);
326
327    $fields['age'] = BaseFieldDefinition::create('integer')
328      ->setLabel(t('Age'))
329      ->setDescription(t('The age of the contact.'))
330      ->setComputed(TRUE)
331      ->setClass(AgeFieldItemList::class)
332      ->setDisplayConfigurable('form', FALSE)
333      ->setDisplayConfigurable('view', TRUE);
334
335    $fields['created'] = BaseFieldDefinition::create('created')
336      ->setLabel(t('Created on'))
337      ->setDescription(t('The time that the contact was created.'))
338      ->setDisplayOptions('view', [
339        'label' => 'above',
340        'type' => 'timestamp',
341        'weight' => 20,
342      ])
343      ->setDisplayConfigurable('form', TRUE)
344      ->setDisplayOptions('form', [
345        'type' => 'datetime_timestamp',
346        'weight' => 20,
347      ])
348      ->setDisplayConfigurable('view', TRUE);
349
350    $fields['changed'] = BaseFieldDefinition::create('changed')
351      ->setLabel(t('Changed'))
352      ->setDescription(t('The time that the contact was last edited.'));
353
354    return $fields;
355  }
356
357}