care-8.x-1.x-dev/care_user/CareUnknownUser.inc
care_user/CareUnknownUser.inc
<?php
/**
* @file
* Define an object to match with contacts in CARE.
*/
/**
* Class CareUnknownUser Finds matches in CARE.
*/
class CareUnknownUser extends CareUser {
protected $testEmail;
protected $testAddressField;
protected $female_titles = [
'Ms',
'Miss',
'Mrs',
];
protected $unisex_titles = [
'Dr',
'Prof',
'Rev',
];
/**
* Constructor.
*/
public function __construct($account = NULL) {
if (!$account) {
$account = user_load(0, TRUE);
}
parent::__construct($account);
}
/**
* Set this user's membership number, if known.
*/
public function setMembershipNumber($contact_number) {
$this->membershipNumber = $contact_number;
}
/**
* Set the name field to one from a form.
*/
public function setNameField($care_name_field) {
if ($care_name_field && $care_name_field->value()) {
if ($this->nameField != $care_name_field) {
$this->nameField = $care_name_field;
$this->nameField->local_unsent_changes = CARE_FIELDS_CHANGED;
}
}
}
/**
* Set the DoB field to one from a form.
*/
public function setDobField($care_dob_field) {
if ($care_dob_field && $care_dob_field->value()) {
if ($this->dobField != $care_dob_field) {
$this->dobField = $care_dob_field;
$this->dobField->local_unsent_changes = CARE_FIELDS_CHANGED;
}
}
}
/**
* Set the email address to find contacts for.
*/
public function setTestEmail($email) {
$this->testEmail = $email;
}
/**
* Set the address field to one from a form.
*/
public function setTestAddressField($address_field) {
$this->testAddressField = $address_field;
}
/**
* Find existing CARE contacts that might match this user.
*/
public function findMatchingContacts() {
watchdog('care_user', 'Finding possible matching CARE contacts');
$matches = [];
if ($this->testEmail) {
// Find contacts with this account's email address.
// ToDo: look for all email addresses for this account?
$data = [
'EMailAddress' => $this->testEmail,
];
$resultxml = care_call_method('FindContacts', $data);
foreach ($resultxml as $result) {
$contact_number = (int) $result->ContactNumber;
$data = [
'ContactNumber' => $contact_number,
'Email' => $this->testEmail,
'Title' => (string) $result->Title,
'Initial' => substr(trim((string) $result->Initials), 0, 1),
'Forenames' => (string) $result->Forenames,
'PreferredForename' => (string) $result->PreferredForename,
'Surname' => (string) $result->Surname,
'Address' => (string) $result->Address,
'Town' => (string) $result->Town,
'Postcode' => (string) $result->Postcode,
'Matches' => [
'Email',
],
'Matchesfuzzy' => [],
'Mismatches' => [],
// 'Data' => _care_pretty_xml($result)
];
$matches[$contact_number] = $data;
}
}
// Find additional contacts with this account's surname.
if ($this->nameField->value()) {
// Look for contacts with this surname:
if ($this->nameField->type() == 'care_name') {
$surname = $this->nameField->surname->value();
}
else {
$surname = $this->nameField->value();
}
$data = [
'Surname' => $surname,
'UseSearchNames' => 'Y',
'UseSoundex' => 'Y',
];
if ($this->testAddressField && $this->testAddressField->value() && $this->testAddressField->postcode->value()) {
$data['Postcode'] = $this->testAddressField->postcode->value();
}
$resultxml = care_call_method('FindContacts', $data);
foreach ($resultxml as $result) {
$result_postcode = (string) $result->Postcode;
$result_postcode = preg_replace('/\s/', '', $result_postcode);
if (in_array($result_postcode, [
'',
'UNKNOWN',
])) {
continue;
}
$contact_number = (int) $result->ContactNumber;
$data = [
'ContactNumber' => $contact_number,
'Email' => '?',
'Title' => (string) $result->Title,
'Initial' => substr(trim((string) $result->Initials), 0, 1),
'Forenames' => (string) $result->Forenames,
'PreferredForename' => (string) $result->PreferredForename,
'Surname' => (string) $result->Surname,
'Address' => (string) $result->Address,
'Town' => (string) $result->Town,
'Postcode' => (string) $result->Postcode,
'Matches' => [],
'Matchesfuzzy' => [],
'Mismatches' => [],
// 'Data' => _care_pretty_xml($result)
];
// Add any new ContactNumbers to the list.
if (!isset($matches[$contact_number])) {
$matches[$contact_number] = $data;
}
}
}
$candidate_count = count($matches);
// Find matches and non-matches for all fields.
foreach ($matches as $index => $data) {
if (!in_array('Email', $data['Matches'])) {
$matches[$index]['Mismatches'][] = 'Email';
}
if ($this->nameField && $this->nameField->value()) {
// Match title, with some flexibility.
if ($data['Title'] && isset($this->nameField->title) && $this->nameField->title->value()) {
$our_title = $this->nameField->title->value();
if ($data['Title'] == $our_title) {
$matches[$index]['Matches'][] = 'Title';
}
elseif (in_array($data['Title'], $this->female_titles) && in_array($our_title, $this->female_titles)) {
$matches[$index]['Matchesfuzzy'][] = 'Title';
}
elseif (in_array($data['Title'], $this->unisex_titles) or in_array($our_title, $this->unisex_titles)) {
$matches[$index]['Matchesfuzzy'][] = 'Title';
}
else {
$matches[$index]['Mismatches'][] = 'Title';
}
}
// Match case-insensitively, or initial if CARE doesn't have forename.
if (isset($this->nameField->forenames) && $this->nameField->forenames->value()) {
$entered_forename = strtolower($this->nameField->forenames->value());
if ($data['Forenames']) {
if (strtolower($data['Forenames']) == $entered_forename or strtolower($data['PreferredForename']) == $entered_forename) {
$matches[$index]['Matches'][] = 'Forenames';
}
else {
$matches[$index]['Mismatches'][] = 'Forenames';
}
}
elseif ($data['Initial']) {
if ($data['Initial'] == substr($entered_forename, 0, 1)) {
$matches[$index]['Matches'][] = 'Initial';
}
else {
$matches[$index]['Mismatches'][] = 'Initial';
}
}
}
// Match surname case-insensitively.
if ($data['Surname'] && $surname) {
if (strtolower($data['Surname']) == strtolower($surname)) {
$matches[$index]['Matches'][] = 'Surname';
}
else {
$matches[$index]['Mismatches'][] = 'Surname';
}
}
}
if ($this->testAddressField && $this->testAddressField->value()) {
// ToDo: look for all addresses for this account?
$address1 = strtolower($this->testAddressField->address_line_1->value());
$address2 = strtolower($this->testAddressField->address_line_2->value());
$address3 = strtolower($this->testAddressField->address_line_3->value());
$address_matches = [
$address1,
$address1 . ' ' . $address2,
$address1 . ' ' . $address2 . ' ' . $address3,
$address1 . ', ' . $address2,
$address1 . ', ' . $address2 . ', ' . $address3,
$address1 . "\n" . $address2,
$address1 . "\n" . $address2 . "\n" . $address3,
];
if (strtolower($data['Address']) && $address1) {
if (in_array(strtolower($data['Address']), $address_matches)) {
$matches[$index]['Matches'][] = 'Address';
}
else {
$matches[$index]['Mismatches'][] = 'Address';
}
}
if ($data['Town'] && $this->testAddressField->town->value()) {
if (strtolower($data['Town']) == strtolower($this->testAddressField->town->value())) {
$matches[$index]['Matches'][] = 'Town';
}
else {
$matches[$index]['Mismatches'][] = 'Town';
}
}
if ($data['Postcode'] && $this->testAddressField->postcode->value()) {
if (strtolower(str_replace(' ', '', $data['Postcode'])) == strtolower(str_replace(' ', '', $this->testAddressField->postcode->value()))) {
$matches[$index]['Matches'][] = 'Postcode';
}
else {
$matches[$index]['Mismatches'][] = 'Postcode';
}
}
}
}
// Sort by likelihood.
uasort($matches, function ($a, $b) {
$score_a = count($a['Matches']);
$score_b = count($b['Matches']);
if ($score_a == $score_b) {
$mis_score_a = count($a['Mismatches']);
$mis_score_b = count($b['Mismatches']);
if ($mis_score_a == $mis_score_b) {
return ($a['ContactNumber'] > $b['ContactNumber']);
}
return ($mis_score_a > $mis_score_b);
}
return ($score_a < $score_b);
});
// Remove definite mismatches.
foreach ($matches as $index => $match) {
$matches_email = in_array('Email', $match['Matches']);
if ($this->nameField && $this->nameField->value()) {
$matches_title = (in_array('Title', $match['Matches']) or in_array('Title', $match['Matchesfuzzy']));
$matches_name = (in_array('Initial', $match['Matches']) or in_array('Forenames', $match['Matches']));
$matches_surname = in_array('Surname', $match['Matches']);
if (!$matches_title || !$matches_name || !$matches_surname) {
unset($matches[$index]);
}
}
if ($this->addressField && $this->addressField->value()) {
$matches_postcode = in_array('Postcode', $match['Matches']);
if (!$matches_email || !$matches_postcode) {
unset($matches[$index]);
}
}
}
watchdog('care_user', 'Match summary: !candidate_count candidates, !match_count matches: Best match: !best_contact', [
'!candidate_count' => $candidate_count,
'!match_count' => count($matches),
'!best_contact' => reset($matches)['ContactNumber'],
]);
return $matches;
}
/**
* Return the ContactNumber for the best match in CARE.
*/
public function getBestMatchingContact() {
if ($this->membershipNumber) {
return $this->membershipNumber;
}
$matches = $this->findMatchingContacts();
watchdog('care_user', 'Matching CARE contacts: <pre>!matches</pre>', [
'!matches' => print_r($matches, TRUE),
]);
if (count($matches)) {
return reset($matches)['ContactNumber'];
}
return 0;
}
}
