Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.48% covered (success)
93.48%
43 / 46
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
AddressOrderStoreService
93.48% covered (success)
93.48%
43 / 46
0.00% covered (danger)
0.00%
0 / 1
4.00
0.00% covered (danger)
0.00%
0 / 1
 store
93.48% covered (success)
93.48%
43 / 46
0.00% covered (danger)
0.00%
0 / 1
4.00
1<?php
2
3namespace Domain\Address\Services;
4
5use App\Events\ServiceProcessed;
6use App\Models\Address\Address;
7use App\Models\Address\AddressOrder;
8use App\Models\Address\AddressOrderGroup;
9use App\Models\User;
10use Domain\Address\DTOs\AddressOrderDTO;
11use Illuminate\Support\Facades\DB;
12use Throwable;
13use function array_merge;
14use function array_slice;
15
16readonly class AddressOrderStoreService
17{
18
19    /**
20     * @param AddressOrderDTO $addressOrderDTO
21     * @param AddressOrderGroup $addressOrderGroup
22     * @param User $user
23     * @return void
24     * @throws Throwable
25     */
26    public function store(AddressOrderDTO $addressOrderDTO, AddressOrderGroup $addressOrderGroup, User $user): void
27    {
28        try {
29            DB::beginTransaction();
30
31            $databaseAddresses = $addressOrderGroup->orders()
32                ->whereNotIn('address_id', $addressOrderDTO->addresses->pluck('id'))
33                ->orderBy('order')
34                ->get()
35                ->mapWithKeys(function (AddressOrder $addressOrder) {
36                    return [
37                        $addressOrder->order => [
38                            'address_id' => $addressOrder->address_id,
39                            'address_order_grouper_id' => $addressOrder->address_order_grouper_id,
40                            'order' => $addressOrder->order,
41                        ],
42                    ];
43                })->toArray();
44
45            $newAddresses = $addressOrderDTO->addresses->mapWithKeys(function (Address $address, int $index) use ($addressOrderDTO, $addressOrderGroup) {
46                return [
47                    $addressOrderDTO->order + $index => [
48                        'address_id' => $address->getKey(),
49                        'address_order_grouper_id' => $addressOrderGroup->getKey(),
50                        'order' => $addressOrderDTO->order + $index,
51                    ],
52                ];
53            })->toArray();
54
55            $beforeArray = array_slice($databaseAddresses, 0, $addressOrderDTO->order - 1);
56            $afterArray = array_slice($databaseAddresses, $addressOrderDTO->order - 1, count($databaseAddresses));
57            $finalAddresses = array_merge($beforeArray, $newAddresses, $afterArray);
58            foreach ($finalAddresses as $order => &$address) {
59                $address['order'] = $order + 1;
60            }
61
62            DB::transaction(function () use ($finalAddresses, $addressOrderGroup) {
63                $addressOrderGroup->orders()->delete();
64
65                // Prepara os dados para o insert em lote
66                $now = now();
67                $insertData = collect($finalAddresses)->map(fn ($address) => [
68                    'address_id' => $address['address_id'],
69                    'address_order_grouper_id' => $address['address_order_grouper_id'],
70                    'order' => $address['order'],
71                    'created_at' => $now,
72                    'updated_at' => $now,
73                ])->all();
74
75                // Insere a nova ordenação em lotes para otimizar a performance.
76                // Isso reduz milhares de queries para apenas algumas.
77                foreach (array_chunk($insertData, 1000) as $chunk) {
78                    AddressOrder::insert($chunk);
79                }
80            });
81
82            DB::commit();
83        } catch (Throwable $throwable) {
84            DB::rollBack();
85            throw $throwable;
86        }
87
88        ServiceProcessed::dispatch(self::class, $user);
89    }
90}