Skip to content

CRM User Contact Mapping

The CRM User Contact Mapping system provides a mechanism to associate Drupal user accounts with CRM contact entities. This creates a bridge between the user management system and the contact relationship management functionality.

Overview

The crm_user_contact entity type creates a one-to-one relationship between: - User Entity (user) - A Drupal user account - Contact Entity (crm_contact) - A CRM contact record (specifically limited to 'person' bundle)

This mapping enables the system to: - Automatically create contact records when users are created - Link existing users to contact records - Provide unified access to user and contact information - Enable contact-based permissions for users

Entity Structure

Database Schema (ERD)

The following Entity Relationship Diagram shows the database structure and relationships between the key entities in the user contact mapping system:

erDiagram
    user {
        int uid PK
    }

    crm_user_contact {
        int id PK
        int user FK
        int crm_contact FK
    }

    crm_contact {
        int id PK
    }

    user ||--o| crm_user_contact : "mapped to"
    crm_contact ||--o| crm_user_contact : "mapped from"

Class Architecture

The following Class Diagram illustrates the key classes, interfaces, and their relationships in the user contact mapping system:

classDiagram
    class crm_user_contact {
        int id PK
        int user FK
        int crm_contact FK
        string uuid
        int created
        int changed
    }

UserContact Entity (crm_user_contact)

The UserContact entity is defined in src/Entity/UserContact.php and implements CrmUserContactInterface.

Key Fields: - user - Entity reference to a Drupal user (required, unique) - crm_contact - Entity reference to a CRM contact of type 'person' (required, unique) - created - Timestamp when the mapping was created - changed - Timestamp when the mapping was last modified

Unique Constraints: Both the user and crm_contact fields have UniqueReference constraints, ensuring: - Each user can only be mapped to one contact - Each contact can only be mapped to one user

Automatic User-to-Contact Mapping

Configuration Settings

The system is configured through crm.user_contact.settings:

display_name: false                    # Override user name with CRM contact name
auto_create_crm_user_contact: false    # Automatically create mappings on user creation
auto_create_lookup_contact: false      # Try to find existing contacts by email before creating new ones

User Creation Hook

When a user is created, the system can automatically create a corresponding contact mapping through the hook_user_insert() implementation in src/Hook/UserHooks.php.

Process Flow:

  1. User Creation Trigger - When a new user is created via hook_user_insert()
  2. Configuration Check - Verifies auto_create_crm_user_contact is enabled
  3. Event Dispatch - Creates and dispatches a CrmUserContactEvent
  4. Event Processing - DefaultCrmUserContactSubscriber processes the event
  5. Contact Resolution - Either finds existing contact or creates new one
  6. Mapping Creation - Saves the crm_user_contact entity

Event System

CrmUserContactEvent

Located in src/Event/CrmUserContactEvent.php, this event is dispatched when automatic user contact mapping is triggered.

Event Name: crm_user_contact_event

Event Data: - Contains a CrmUserContactInterface object - Allows event subscribers to modify the mapping before it's saved

DefaultCrmUserContactSubscriber

The default event subscriber (src/EventSubscriber/DefaultCrmUserContactSubscriber.php) handles the automatic contact creation logic:

Contact Lookup Process (when auto_create_lookup_contact is enabled): 1. Search for existing contact details with matching email address 2. Find contacts associated with those email details 3. If exactly one matching 'person' contact is found, use it

Contact Creation Process (when no existing contact is found): 1. Create a new 'person' contact 2. Create an email contact detail with the user's email address 3. Set the contact's full name to the user's display name 4. Associate the email detail with the contact

Permissions System

Permission Types

The CRM module defines several permissions related to user contact mappings:

Administrative Permissions

  • administer crm - Full administrative access to CRM functionality, including user contact mappings

Contact Access Permissions

  • view mapped crm_contact - View contact records that are mapped to the current user
  • edit mapped crm_contact - Edit contact records that are mapped to the current user

Contact Creation Permissions

  • create crm_contact - Create new contact records

Display Name Permissions

  • crm user alter display name - Allow users to customize their display name format using their associated contact information

Permission Implementation

The mapping system integrates with Drupal's access control through: - Entity access control handlers - Relationship-based permissions (mapped vs. any) - Integration with the broader CRM permission system

API Usage

Service: CrmUserContactSyncRelation

The crm.user service (src/Service/CrmUserContactSyncRelation.php) provides programmatic access to user-contact mappings.

Key Methods

// Get contact ID from user ID
$contact_id = $sync_service->getContactIdFromUserId($user_id);

// Get user ID from contact ID
$user_id = $sync_service->getUserIdFromContactId($contact_id);

// Get mapping entity ID from user ID
$relation_id = $sync_service->getRelationIdFromUserId($user_id);

// Get mapping entity ID from contact ID
$relation_id = $sync_service->getRelationIdFromContactId($contact_id);

// Create a user-contact relationship
$contact = $sync_service->relate($user_account, $contact_entity);

Manual Relationship Creation

The relate() method can be used to programmatically create user-contact mappings:

// Create mapping with existing contact
$contact = $sync_service->relate($user, $existing_contact);

// Create mapping with automatic contact creation
$contact = $sync_service->relate($user); // Creates new contact automatically

Direct Entity Operations

use Drupal\crm\Entity\UserContact;

// Create a new user contact mapping
$user_contact = UserContact::create([
  'user' => $user_id,
  'crm_contact' => $contact_id,
]);
$user_contact->save();

// Load existing mapping by user
$user_contacts = \Drupal::entityTypeManager()
  ->getStorage('crm_user_contact')
  ->loadByProperties(['user' => $user_id]);

// Load existing mapping by contact
$user_contacts = \Drupal::entityTypeManager()
  ->getStorage('crm_user_contact')
  ->loadByProperties(['crm_contact' => $contact_id]);

Administrative Interface

Configuration

User contact mapping settings can be configured at: /admin/config/crm/user-contact/settings

Implemented by UserContactSettingsForm in src/Form/UserContactSettingsForm.php.

Available Settings: - Display Name Override: Enable/disable global display name override feature - Auto Create CRM User Contact: Automatically create contact mappings when users are created - Auto Create Lookup Contact: Attempt to find existing contacts by email before creating new ones

Management Interface

User contact mappings can be managed at: - List all mappings: /admin/config/crm/user/list - Add new mapping: /admin/config/crm/user/add - Edit mapping: /admin/config/crm/user/{crm_user_contact}/edit - Delete mapping: /admin/config/crm/user/{crm_user_contact}/delete

Integration Points

Contact Bundle Restriction

The system is specifically designed to work with 'person' type contacts only. This is enforced in: - Entity field definitions (handler_settings limits to person bundle) - User creation hooks (validation checks bundle type) - Event subscriber logic (creates person contacts)

Email Integration

The system automatically: - Creates email contact details for new users - Uses email addresses to lookup existing contacts - Associates user email with the mapped contact's email details

Display Name Control System

The CRM User Contact mapping system includes a comprehensive display name control feature that allows user display names to be overridden with contact information. This provides a unified user experience that reflects CRM data while giving users control over how their names are formatted.

Global Display Name Override

Configuration Setting: display_name in crm.user_contact.settings

When enabled globally, this feature: - Overrides user display names with their associated contact's name - Uses the contact's label() method as the default display format - Only affects users who have an associated CRM contact - Falls back to the original username for users without contact mappings

User-Specific Name Format Control

Users can customize how their name is displayed when they have the appropriate permission.

Permission Required: crm user alter display name

Features: - Format Selection: Users can choose from available name formats provided by the Name module - System Default: Option to use the system-configured format for their contact type - Personal Preference: Individual users can override the system default with their preferred format

Name Formatting Integration

The system integrates with Drupal's Name module to provide sophisticated name formatting:

Format Components: - Title (Mr., Mrs., Dr., etc.) - Given name (first name) - Middle name(s) - Family name (last name) - Generational suffix (Jr., Sr., III, etc.) - Preferred name (nickname) - Alternative names (aliases)

Formatting Process: 1. Retrieves the user's preferred name format from user data storage 2. Extracts name components from the associated contact's full_name field 3. Includes preferred name and aliases from the contact record 4. Applies the selected name format using the Name module's formatter 5. Falls back to contact label if formatting fails or data is incomplete

User Interface Integration

When display name override is enabled, the system adds a CRM section to user edit forms:

Form Elements: - Name Format Selector: Dropdown with available name formats - System Default Option: Shows the current system format for the user's contact type - Format Preview: Users can see how different formats will display their name

Access Control: - Only appears for users editing their own profile - Requires the crm user alter display name permission - Only shows for users with associated CRM contacts

Implementation Details

The display name control system works through several key components:

Hook Implementation:

/**
 * Implements hook_user_format_name_alter().
 */
public function userContactFormatNameAlter(&$name, AccountInterface $account) {
  // Check if display name override is enabled
  // Load user's contact mapping
  // Apply contact name or custom formatting
}

User Data Storage: - Custom name format preferences are stored using Drupal's User Data API - Key: crm.{user_id}.name_format - Value: The selected name format machine name

Form Integration:

/**
 * Implements hook_form_user_form_alter().
 */
public function userFormAlter(&$form, FormStateInterface $form_state) {
  // Add CRM fieldset with name format selector
  // Populate with available formats
  // Handle form submission
}

Fallback and Error Handling

The system includes robust fallback mechanisms:

Fallback Hierarchy: 1. Custom Formatted Name: Using user's preferred format with contact data 2. Contact Label: The contact's display name/label 3. Original Username: Falls back to Drupal's default username

Error Conditions Handled: - User has no associated contact - Contact has incomplete name data - Selected name format is unavailable - Name formatting process fails - Permission checks fail

Configuration Examples

Enable Display Name Override:

# crm.user_contact.settings
display_name: true

User's Custom Format Selection:

// Store user's preferred format
$user_data->set('crm', $user_id, 'name_format', 'given_family');

// Remove custom format (use system default)
$user_data->delete('crm', $user_id, 'name_format');

Use Cases

Professional Context: - Display formal names in business applications - Respect cultural naming conventions - Maintain consistency across the organization

User Preference: - Allow users to choose casual vs. formal name display - Support nickname preferences - Accommodate name changes and preferences

Multi-format Support: - Academic titles (Dr. Smith) - Cultural formats (Smith, John) - Casual formats (John S.) - Full formal formats (Dr. John Q. Smith, Jr.)

Error Handling and Validation

Unique Reference Constraints

Both user and contact fields have unique reference constraints that prevent: - Multiple mappings for the same user - Multiple mappings for the same contact

Access Control

All entity queries include proper access checking to ensure users can only access mappings they have permission to view or modify.

Logging

The system logs mapping creation events for audit purposes:

User @user @uid has been synchronized to the contact @contact_id, relation @rid has been created.

Use Cases

Automatic User Registration

When users register on the site, automatically create corresponding contact records for CRM purposes.

Existing Contact Integration

Link existing CRM contacts to user accounts when users register with matching email addresses.

Permission-Based Access

Allow users to view and edit "their own" contact information through the CRM interface.

Data Synchronization

Keep user account information synchronized with CRM contact data.

Development Notes

Extensibility

The event-driven architecture allows developers to: - Subscribe to CrmUserContactEvent for custom mapping logic - Override the default contact creation behavior - Add additional data synchronization between users and contacts

Performance Considerations

  • Unique reference constraints ensure data integrity
  • Entity queries include access checking for security
  • Mapping lookups are optimized with proper indexing on reference fields

Testing

The system includes comprehensive test coverage in: - tests/src/Kernel/UserContactCreationTest.php - Integration tests for the event system - Form and permission testing