Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.87% covered (success)
97.87%
46 / 47
83.33% covered (warning)
83.33%
5 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
AddressGrantPermissionService
97.87% covered (success)
97.87%
46 / 47
83.33% covered (warning)
83.33%
5 / 6
14
0.00% covered (danger)
0.00%
0 / 1
 grant
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 assignAddressPermissions
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 grantOrigin
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 grantFamilies
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 grantProducts
93.75% covered (success)
93.75%
15 / 16
0.00% covered (danger)
0.00%
0 / 1
5.01
 grantType
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace Domain\Address\Services;
4
5use App\Models\Address\Address;
6use App\Models\Address\AddressPermission;
7use App\Models\Category\Family;
8use App\Models\Category\Origin;
9use App\Models\Product\Product;
10use App\Models\User;
11use Domain\Address\DTOs\AddressPermissionDTO;
12use Exception;
13use Illuminate\Database\Eloquent\Collection;
14use Illuminate\Support\Facades\DB;
15use Symfony\Component\HttpKernel\Exception\HttpException;
16use function array_fill_keys;
17
18readonly class AddressGrantPermissionService
19{
20    /**
21     * @param AddressPermissionDTO $addressPermissionDTO
22     * @param Address $address
23     * @param User $user
24     * @return void
25     * @throws Exception
26     */
27    public function grant(AddressPermissionDTO $addressPermissionDTO, Address $address, User $user): void
28    {
29        try {
30            DB::beginTransaction();
31
32            $address->deleteChildrenAddress();
33            $this->assignAddressPermissions($addressPermissionDTO, $address);
34
35            DB::commit();
36        } catch (Exception $exception) {
37            DB::rollBack();
38            throw $exception;
39        }
40    }
41
42    /**
43     * @param AddressPermissionDTO $addressPermissionDTO
44     * @param Address $address
45     * @param Address|null $owner
46     * @return void
47     */
48    private function assignAddressPermissions(AddressPermissionDTO $addressPermissionDTO, Address $address, ?Address $owner = null): void
49    {
50        $this->grantOrigin($address, $addressPermissionDTO, $owner);
51        $this->grantFamilies($addressPermissionDTO, $address, $owner);
52        $this->grantProducts($addressPermissionDTO, $address, $owner);
53        $this->grantType($addressPermissionDTO, $address);
54
55        $address->children->each(function (Address $children) use ($addressPermissionDTO, $owner, $address) {
56            $this->assignAddressPermissions($addressPermissionDTO, $children, $owner ?? $address);
57        });
58    }
59
60    /**
61     * @param Address $address
62     * @param AddressPermissionDTO $addressPermissionDTO
63     * @param Address|null $owner
64     * @return void
65     */
66    private function grantOrigin(Address $address, AddressPermissionDTO $addressPermissionDTO, ?Address $owner): void
67    {
68        $toSync = array_fill_keys($addressPermissionDTO->origins->pluck('id')->toArray(), ['owner_id' => $owner?->getKey()]);
69        $address->permissions(Origin::class)->sync($toSync);
70    }
71
72    /**
73     * @param AddressPermissionDTO $addressPermissionDTO
74     * @param Address $address
75     * @param Address|null $owner
76     * @return void
77     * @throws HttpException
78     */
79    private function grantFamilies(AddressPermissionDTO $addressPermissionDTO, Address $address, ?Address $owner): void
80    {
81        $families = [];
82        $addressPermissionDTO->families->each(function (Family $family) use ($address, $addressPermissionDTO, &$families) {
83            if ($addressPermissionDTO->origins->isNotEmpty()) {
84                $originIDs = $addressPermissionDTO->origins->pluck('id');
85                if ($originIDs->doesntContain($family->origin->getKey())) {
86                    throw new HttpException(422, __('The family does not belong to the origin'));
87                }
88            }
89            $families[] = $family->getKey();
90        });
91
92        $toSync = array_fill_keys($families, ['owner_id' => $owner?->getKey()]);
93        $address->permissions(Family::class)->sync($toSync);
94    }
95
96    /**
97     * @param AddressPermissionDTO $addressPermissionDTO
98     * @param Address $address
99     * @param Address|null $owner
100     * @return void
101     * @throws HttpException
102     */
103    private function grantProducts(AddressPermissionDTO $addressPermissionDTO, Address $address, ?Address $owner): void
104    {
105        $products = [];
106        $addressPermissionDTO->products->each(function (Product $product) use ($address, $addressPermissionDTO, &$products) {
107            if ($addressPermissionDTO->origins->isNotEmpty()) {
108
109                /** @var Collection $families */
110                $families = $addressPermissionDTO->origins->flatMap(function (Origin $origin) {
111                    return $origin->families->pluck('id');
112                });
113
114                if ($families->doesntContain($product->family->getKey())) {
115                    throw new HttpException(422, __('The product does not belong to the origin'));
116                }
117            }
118
119            if ($addressPermissionDTO->families->isNotEmpty()) {
120                $familiesIDs = $addressPermissionDTO->families->pluck('id');
121                if ($familiesIDs->doesntContain($product->family->getKey())) {
122                    throw new HttpException(422, __('The product does not belong to the family'));
123                }
124            }
125
126            $products[] = $product->getKey();
127        });
128        $toSync = array_fill_keys($products, ['owner_id' => $owner?->getKey()]);
129        $address->permissions(Product::class)->sync($toSync);
130    }
131
132    /**
133     * @param AddressPermissionDTO $addressPermissionDTO
134     * @param Address $address
135     * @return void
136     */
137    public function grantType(AddressPermissionDTO $addressPermissionDTO, Address $address): void
138    {
139        foreach ($addressPermissionDTO->types as $type) {
140            $addressPermission = new AddressPermission();
141            $addressPermission->setAttribute('storage_type', $type);
142            $addressPermission->address()->associate($address);
143            $addressPermission->save();
144        }
145    }
146
147}