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:
- User Creation Trigger - When a new user is created via
hook_user_insert()
- Configuration Check - Verifies
auto_create_crm_user_contact
is enabled - Event Dispatch - Creates and dispatches a
CrmUserContactEvent
- Event Processing -
DefaultCrmUserContactSubscriber
processes the event - Contact Resolution - Either finds existing contact or creates new one
- 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 useredit 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