Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
MemberDirectoryAdapter
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
3 / 3
8
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findIdByEmail
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 summariesOf
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace App\Identity\Infrastructure\Trip;
6
7use App\Identity\Domain\UserRepository;
8use App\Identity\Domain\ValueObject\Email;
9use App\Identity\Domain\ValueObject\UserId;
10use App\Identity\Domain\ValueObject\UserStatus;
11use App\Shared\Infrastructure\Storage\SignedImageUrl;
12use App\Trip\Application\Port\MemberDirectory;
13use App\Trip\Application\Port\MemberSummary;
14use App\Trip\Domain\ValueObject\MemberId;
15use InvalidArgumentException;
16
17/**
18 * Identity's implementation of Trip's MemberDirectory port: the one place the
19 * two contexts meet. Translates between Trip's MemberId (a bare user id) and
20 * Identity's User, so neither context imports the other's value objects.
21 */
22final readonly class MemberDirectoryAdapter implements MemberDirectory
23{
24    public function __construct(
25        private UserRepository $users,
26        private SignedImageUrl $imageUrls,
27    ) {
28    }
29
30    public function findIdByEmail(string $email): ?MemberId
31    {
32        try {
33            $user = $this->users->ofEmail(new Email($email));
34        } catch (InvalidArgumentException) {
35            return null; // malformed email simply matches no user
36        }
37
38        // A suspended account can't authenticate, so it isn't invitable — treat it
39        // as no match (same 404 as an unknown email, no account-status leak).
40        if ($user === null || $user->status() === UserStatus::SUSPENDED) {
41            return null;
42        }
43
44        return new MemberId($user->id()->value());
45    }
46
47    public function summariesOf(array $ids): array
48    {
49        $summaries = [];
50
51        foreach ($ids as $id) {
52            $user = $this->users->ofId(new UserId($id->value()));
53            if ($user === null) {
54                continue;
55            }
56
57            $summaries[$id->value()] = new MemberSummary(
58                $user->id()->value(),
59                $user->email()->value(),
60                trim($user->name() . ' ' . $user->surnames()),
61                $this->imageUrls->for($user->avatarName()),
62            );
63        }
64
65        return $summaries;
66    }
67}