Skip to content

CRM Method Detail Entity

Overview

The crm_method_detail entity provides semantic classification for contact methods, allowing you to specify the context or purpose of each contact method detail (e.g., "home" vs "work" email, "mobile" vs "fax" telephone).

Key Features

  • Configuration Entity: Lightweight, exportable configuration entity
  • Type Filtering: Can be restricted to specific contact method types (address, email, telephone)
  • Negate Logic: Supports inclusive and exclusive type restrictions
  • Status Management: Can be enabled or disabled
  • Flexible Classification: Allows unlimited custom types to be created

Entity Purpose

Method detail types provide a secondary classification layer for contact methods. While the contact method type determines what kind of data is stored (address, email, or telephone), the method detail type provides context about how that data is used:

  • Global Types (all types): home, work, main, other
  • Telephone-Specific: mobile, fax
  • Address-Specific: billing

Relationship to Contact Methods

Every crm_contact_method entity can reference a method detail through its detail field. The available details are automatically filtered based on the contact method's type, using the type filtering logic configured in each method detail.

Entity Structure

classDiagram
  class crm_method_detail {
    +string id [PK]
    +string label
    +string description
    +array bundles
    +bool negate
    +bool status
  }

  class crm_contact_method {
    +int id [PK]
    +string type
    +int detail --> crm_method_detail.id
    +int crm_contact
  }

  class crm_contact_method_type {
    +string id [PK]
    +string label
  }

  %% Relationships
  crm_contact_method }o--|| crm_method_detail : "classified by"
  crm_contact_method }o--|| crm_contact_method_type : "contact method type"

  %% Notes on filtering
  note for crm_method_detail "Type Filtering Logic:\n- Empty bundles[] = applies to all types\n- negate=false: applies to listed types\n- negate=true: applies to all types EXCEPT listed"

Properties Reference

Property Type Required Description
id String Yes Machine name identifier (e.g., "home", "work", "mobile")
label String Yes Human-readable label displayed in forms and views
description Text No Optional description of the type's purpose
bundles Array No Array of contact method type IDs this detail type applies to
negate Boolean No If TRUE, applies to all types EXCEPT those listed
status Boolean No Whether the type is enabled (default: TRUE)

Configuration Reference

Default Method Detail Types

The following method detail types are provided by default:

Type ID Label Description Applicable Types Negate
home Home Personal/home contact information All (empty array) false
work Work Business/work contact information All (empty array) false
main Main Primary contact information All (empty array) false
other Other Alternative contact information All (empty array) false
mobile Mobile Mobile telephone number telephone only false
fax Fax Fax telephone number telephone only false
billing Billing Billing address address only false

Type Filtering Logic

The bundles array and negate flag work together to control which contact method types can use each method detail type:

Case 1: Empty Bundles Array (Global Types)

When bundles is an empty array, the detail type applies to all contact method types regardless of the negate value.

id: home
label: Home
bundles: []
negate: false  # Ignored when bundles is empty

Result: Available for address, email, and telephone types.

Case 2: Inclusive Type Filtering (negate = false)

When bundles contains values and negate is false, the detail type applies only to the listed types.

id: mobile
label: Mobile
bundles:
  - telephone
negate: false

Result: Available only for telephone type contact methods.

Case 3: Exclusive Type Filtering (negate = true)

When bundles contains values and negate is true, the detail type applies to all types except those listed.

id: non_telephone
label: Non-Telephone
bundles:
  - telephone
negate: true

Result: Available for address and email types, but not telephone.

Configuration File Examples

Method detail types are stored as configuration entities in YAML files:

Global Type Example

# config/install/crm.crm_method_detail.home.yml
langcode: en
status: true
dependencies: {}
id: home
label: Home
description: ''
bundles: []
negate: false

Type-Specific Example

# config/install/crm.crm_method_detail.mobile.yml
langcode: en
status: true
dependencies: {}
id: mobile
label: Mobile
description: ''
bundles:
  - telephone
negate: false

Address-Only Type Example

# config/install/crm.crm_method_detail.billing.yml
langcode: en
status: true
dependencies: {}
id: billing
label: Billing
description: ''
bundles:
  - address
negate: false

Selection Plugin

The MethodDetailSelection plugin automatically filters available method details based on the contact method's type. This ensures users only see relevant detail options when creating or editing contact methods.

Plugin ID: default:crm_method_detail

Filtering Logic: 1. Only shows enabled types (status = TRUE) 2. Checks type compatibility using the logic described above 3. Applies during entity reference field autocomplete and select lists

Administrative Interface

Admin URLs

Page URL Description
Collection /admin/structure/crm/method-detail List all method details
Add Form /admin/structure/crm/method-detail/add Create a new method detail
Edit Form /admin/structure/crm/method-detail/manage/{id} Edit an existing method detail
Delete Form /admin/structure/crm/method-detail/manage/{id}/delete Delete a method detail

Required Permissions

All method detail type operations require the administer crm permission.

Form Fields

The method detail type form includes the following fields:

Label

  • Field Type: Text field
  • Required: Yes
  • Description: Human-readable name displayed to users
  • Example: "Home", "Work", "Mobile"

Machine Name (ID)

  • Field Type: Machine name
  • Required: Yes
  • Editable: Only when creating new types
  • Generated From: Label field
  • Example: "home", "work", "mobile"

Enabled

  • Field Type: Checkbox
  • Required: No
  • Default: Checked
  • Description: Disabled types are not available for selection but existing references remain intact

Description

  • Field Type: Textarea
  • Required: No
  • Description: Optional text to explain the type's purpose

Bundles

  • Field Type: Checkboxes
  • Options: Dynamically loaded from crm_contact_method_type entities
  • Address
  • Email
  • Telephone
  • Description: Select which contact method types this detail type applies to
  • Note: Leave empty for global types that apply to all types

Negate

  • Field Type: Checkbox
  • Required: No
  • Default: Unchecked (false)
  • Description: If checked, this detail type applies to all types except those selected above

Form Handler

The MethodDetailForm class handles form building and submission:

Location: src/Form/MethodDetailForm.php

Key Features: - Validates machine name uniqueness - Filters empty checkbox values from bundles array - Provides contextual help text when no contact method types exist - Redirects to collection page after save

Programmatic Usage

Loading Method Detail Configuration Entities

<?php

declare(strict_types=1);

use Drupal\crm\Entity\MethodDetail;

// Load a specific method detail type.
$storage = \Drupal::entityTypeManager()->getStorage('crm_method_detail');
$method_detail = $storage->load('mobile');

if ($method_detail === NULL) {
  \Drupal::logger('my_module')->warning('Method detail type "mobile" not found.');
  return;
}

// Get the configuration values.
$label = $method_detail->label();
$bundles = $method_detail->get('bundles');
$negate = $method_detail->get('negate');
$status = $method_detail->get('status');

\Drupal::logger('my_module')->info('Method detail: @label (bundles: @bundles, negate: @negate)', [
  '@label' => $label,
  '@bundles' => implode(', ', $bundles),
  '@negate' => $negate ? 'true' : 'false',
]);

Creating Method Detail Types Programmatically

<?php

declare(strict_types=1);

use Drupal\crm\Entity\MethodDetail;

// Create a new method detail type for emergency contacts.
$method_detail = MethodDetail::create([
  'id' => 'emergency',
  'label' => 'Emergency',
  'description' => 'Emergency contact information',
  'bundles' => ['telephone'],
  'negate' => FALSE,
  'status' => TRUE,
]);

try {
  $method_detail->save();
  \Drupal::logger('my_module')->info('Created method detail type: @id', ['@id' => $method_detail->id()]);
}
catch (\Exception $e) {
  \Drupal::logger('my_module')->error('Failed to create method detail type: @message', [
    '@message' => $e->getMessage(),
  ]);
}

Checking if a Detail Type Applies to a Bundle

<?php

declare(strict_types=1);

/**
 * Check if a method detail type applies to a specific contact method bundle.
 *
 * @param string $detail_id
 *   The method detail type ID.
 * @param string $bundle
 *   The contact method bundle (address, email, or telephone).
 *
 * @return bool
 *   TRUE if the detail type applies to the bundle, FALSE otherwise.
 */
function check_detail_applies_to_bundle(string $detail_id, string $bundle): bool {
  $storage = \Drupal::entityTypeManager()->getStorage('crm_method_detail');
  $method_detail = $storage->load($detail_id);

  if ($method_detail === NULL || !$method_detail->get('status')) {
    return FALSE;
  }

  $bundles = $method_detail->get('bundles');
  $negate = $method_detail->get('negate');

  // Empty bundles array = applies to all bundles.
  if (empty($bundles)) {
    return TRUE;
  }

  // Negate logic: applies to all bundles EXCEPT those listed.
  if ($negate) {
    return !in_array($bundle, $bundles, TRUE);
  }

  // Inclusive logic: applies only to listed bundles.
  return in_array($bundle, $bundles, TRUE);
}

// Example usage:
if (check_detail_applies_to_bundle('mobile', 'telephone')) {
  \Drupal::logger('my_module')->info('Mobile detail type applies to telephone bundle.');
}

Loading All Applicable Details for a Bundle

<?php

declare(strict_types=1);

/**
 * Get all method detail types that apply to a specific bundle.
 *
 * @param string $bundle
 *   The contact method bundle (address, email, or telephone).
 *
 * @return array
 *   Array of method detail entity IDs that apply to the bundle.
 */
function get_applicable_details_for_bundle(string $bundle): array {
  $storage = \Drupal::entityTypeManager()->getStorage('crm_method_detail');
  $all_details = $storage->loadMultiple();

  $applicable = [];
  foreach ($all_details as $id => $detail) {
    // Skip disabled details.
    if (!$detail->get('status')) {
      continue;
    }

    $bundles = $detail->get('bundles');
    $negate = $detail->get('negate');

    // Empty bundles = applies to all.
    if (empty($bundles)) {
      $applicable[$id] = $detail->label();
      continue;
    }

    // Check negate logic.
    $in_list = in_array($bundle, $bundles, TRUE);
    if (($negate && !$in_list) || (!$negate && $in_list)) {
      $applicable[$id] = $detail->label();
    }
  }

  return $applicable;
}

// Example usage:
$telephone_details = get_applicable_details_for_bundle('telephone');
foreach ($telephone_details as $id => $label) {
  \Drupal::logger('my_module')->info('Available for telephone: @label (@id)', [
    '@label' => $label,
    '@id' => $id,
  ]);
}

Configuration Examples

Creating Custom Method Detail Types

Example 1: Emergency Contact (Phone Only)

Create a method detail type for emergency contact phone numbers:

id: emergency
label: Emergency
description: 'Emergency contact telephone number'
bundles:
  - telephone
negate: false

Example 2: Shipping Address (Address Only)

Create a method detail type specifically for shipping addresses:

id: shipping
label: Shipping
description: 'Address for shipping deliveries'
bundles:
  - address
negate: false

Example 3: Personal (Email and Phone Only)

Create a type for personal contact methods, excluding addresses:

id: personal
label: Personal
description: 'Personal email or phone'
bundles:
  - address
negate: true

This configuration would make "Personal" available for email and telephone bundles, but not for addresses.

Integration with Contact Methods

When creating or editing a contact method, the detail field automatically filters available options based on:

  1. The contact method's type (address, email, or telephone)
  2. Each method detail type's bundles and negate configuration
  3. The method detail type's status (enabled/disabled)

Example Workflow

  1. User creates a new telephone contact method
  2. System checks all method detail types:
  3. "home" (empty bundles) ✓ Available
  4. "work" (empty bundles) ✓ Available
  5. "mobile" (bundles: [telephone]) ✓ Available
  6. "billing" (bundles: [address]) ✗ Not available
  7. User sees filtered dropdown with only: home, work, main, other, mobile, fax

Best Practices

Naming Conventions

  • Use lowercase machine names with underscores: emergency_contact, not Emergency-Contact
  • Keep labels short and descriptive: "Emergency" not "Emergency Contact Number"
  • Use consistent terminology across your organization

Type Configuration

  • Leave bundles empty for detail types that apply universally (home, work, other)
  • Use specific types for context-specific detail types (mobile for telephone, billing for address)
  • Avoid over-segmentation: Don't create type-specific versions of generic detail types

Negate Flag Usage

  • Use negate: true sparingly, as it can be confusing
  • Document the reasoning when using negate in configuration comments
  • Prefer explicit positive bundle lists over negation when possible

Status Management

  • Disable rather than delete types that are no longer needed
  • Disabled types preserve existing data while preventing new usage
  • Re-enable types if they become relevant again