UserNameAbbreviator::buildLastNameAbbreviation()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 35
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6.0052

Importance

Changes 0
Metric Value
cc 6
eloc 17
nc 6
nop 2
dl 0
loc 35
ccs 18
cts 19
cp 0.9474
crap 6.0052
rs 9.0777
c 0
b 0
f 0
1
<?php
2
3
namespace App\Module\User\FindAbbreviatedNameList\Service;
4
5
use App\Module\User\Data\UserData;
6
7
class UserNameAbbreviator
8
{
9 24
    public function __construct()
10
    {
11 24
    }
12
13
    /**
14
     * Recursive function that builds abbreviation for lastname.
15
     *
16
     * @param string $lastName
17
     * @param UserData[] $usersToCheck
18
     *
19
     * @return string
20
     */
21 15
    private function buildLastNameAbbreviation(string $lastName, array $usersToCheck): string
22
    {
23 15
        $abbreviatedLastName = '';
24 15
        foreach ($usersToCheck as $userToCheck) {
25
            // Check given lastname against all other lastnames that have the same firstname
26 15
            $buildLastName = static function (string $lastName, string $lastNameToCheck, int $i = 1) use (
27 15
                &$buildLastName
28 15
            ): string {
29
                // When $i (amount of letters) of last name to abbreviate is the same as the full name,
30
                // there is no short form and the function must end as it would cause infinite recursion.
31
                // Checks if short form ($i letters from the beginning of lastName) is contained in name to check
32 15
                if (strlen($lastName) > $i && str_contains($lastNameToCheck, substr($lastName, 0, $i))) {
33
                    // Increase number of letters that should be used of the last name as it exists in the name to check
34 15
                    $i++;
35 15
                    $shortName = $buildLastName($lastNameToCheck, $lastName, $i);
36
                } else {
37
                    // Return first $i letters of lastname
38 15
                    $shortName = substr($lastName, 0, $i);
39
                }
40
41 15
                return $shortName;
42 15
            };
43
            // Always privilege longest lastname abbreviation as it means that this length necessary
44 15
            if (strlen($builtLastName = $buildLastName($lastName, (string)$userToCheck->lastName)) > strlen(
45 15
                $abbreviatedLastName
46 15
            )) {
47 15
                $abbreviatedLastName = $builtLastName;
48
            }
49
        }
50
        // If lastname abbreviation not full lastname, add .
51 15
        if ($abbreviatedLastName !== $lastName) {
52
            $abbreviatedLastName .= '.';
53
        }
54
55 15
        return $abbreviatedLastName;
56
    }
57
58
    /**
59
     * Find all the names of users for the dropdown.
60
     * Firstnames are privileged but if there is a duplicate,
61
     * the first last name chars are added.
62
     *
63
     * @param UserData[] $users original users
64
     *
65
     * @return array<int, string> array of users with abbreviated full names
66
     */
67 21
    public function abbreviateUserNames(array $users): array
68
    {
69 21
        $outputNames = [];
70 21
        $groupedUsers = [];
71
72
        // Group users by first name
73 21
        foreach ($users as $user) {
74
            // Use first_name as array key for duplicates to be grouped
75 20
            $groupedUsers[$user->firstName][$user->id] = $user;
76
        }
77
78
        // Loop over the ordered user array
79
        /** @var UserData[] $usersWithIdenticalFirstName */
80 21
        foreach ($groupedUsers as $firstName => $usersWithIdenticalFirstName) {
81
            // If there is only one entry it means that it's a unique first name
82 20
            if (count($usersWithIdenticalFirstName) === 1) {
83
                // reset() returns the first value of the array
84 5
                $userWithUniqueFirstName = reset($usersWithIdenticalFirstName);
85 5
                $outputNames[(int)$userWithUniqueFirstName->id] = (string)$userWithUniqueFirstName->firstName;
86 5
                continue;
87
            }
88
89
            // Duplicates
90 15
            foreach ($usersWithIdenticalFirstName as $userId => $user) {
91
                // Make copy of users with identical first name to unset it and pass only the "other" users to the function
92 15
                $usersToCheckAgainst = $usersWithIdenticalFirstName;
93
                // Remove currently iterated user from users to be checked against array
94 15
                unset($usersToCheckAgainst[$userId]);
95
                // Call recursive function which compares last name of currently iterated user with other users with same
96
                // first name and returns the shortest version of non-duplicate lastname
97 15
                $lastNameAbbr = $this->buildLastNameAbbreviation((string)$user->lastName, $usersToCheckAgainst);
98 15
                $outputNames[(int)$userId] = $user->firstName . ' ' . $lastNameAbbr;
99
            }
100
        }
101
102 21
        return $outputNames;
103
    }
104
}
105