Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
ChangeMemberRoleHandler
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
2 / 2
4
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
 handle
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace App\Trip\Application\Command;
6
7use App\Trip\Application\TripAccess;
8use App\Trip\Domain\Exceptions\MemberNotFound;
9use App\Trip\Domain\TripMemberRepository;
10use App\Trip\Domain\ValueObject\MemberId;
11use App\Trip\Domain\ValueObject\TripId;
12use App\Trip\Domain\ValueObject\TripRole;
13use InvalidArgumentException;
14
15/**
16 * Owner changes an existing member's role (editor <-> viewer). Owner-only via
17 * getForManage: stranger -> TripNotFound (404), non-owner member -> TripAccessDenied
18 * (403). The owner is not a member row, so retargeting the owner 404s.
19 */
20final readonly class ChangeMemberRoleHandler
21{
22    public function __construct(
23        private TripAccess $access,
24        private TripMemberRepository $members,
25    ) {
26    }
27
28    public function handle(ChangeMemberRoleCommand $command): void
29    {
30        $trip = $this->access->getForManage(new TripId($command->tripId), $command->actingUserId);
31
32        $role = TripRole::tryFrom($command->role);
33        if ($role === null || !$role->isStorableMembership()) {
34            throw new InvalidArgumentException('Role must be editor or viewer.');
35        }
36
37        $member = $this->members->find($trip->id(), new MemberId($command->targetUserId))
38            ?? throw MemberNotFound::withId(new MemberId($command->targetUserId));
39
40        $member->changeRole($role);
41        $this->members->save($member);
42    }
43}