Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
74 / 74
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
CustomerController
100.00% covered (success)
100.00%
74 / 74
100.00% covered (success)
100.00%
6 / 6
26
100.00% covered (success)
100.00%
1 / 1
 store
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
7
 show
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 update
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
5
 destroy
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
6
 authenticate
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 activateAccount
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3namespace App\Http\Controllers\Api\V2;
4
5use App\Enums\ApiError;
6use App\Exceptions\Api\V2\ApiErrorException;
7use App\Exceptions\Api\V2\CrmException;
8use App\Http\Controllers\Controller;
9use App\Http\Requests\Api\V2\AuthenticateRequest;
10use App\Http\Requests\Api\V2\StoreCustomerRequest;
11use App\Http\Requests\Api\V2\UpdateCustomerRequest;
12use App\Http\Resources\Api\V2\CustomerResource;
13use App\Jobs\UpdateCustomerLastConnection;
14use App\Models\Customer;
15use App\Models\Service;
16use App\Models\SingleOptin;
17use Illuminate\Database\Eloquent\ModelNotFoundException;
18use Illuminate\Http\JsonResponse;
19use Illuminate\Http\Resources\Json\JsonResource;
20use Illuminate\Support\Facades\DB;
21
22class CustomerController extends Controller
23{
24    /**
25     * Creates a new customer for the specified service.
26     *
27     * @param StoreCustomerRequest $request
28     * @param Service              $service
29     * @return JsonResource
30     * @throws ApiErrorException With ApiError::customerExists if the customer with this email already exists.
31     * @throws CrmException If saving the customer to DB failed.
32     */
33    public function store(StoreCustomerRequest $request, Service $service): JsonResource
34    {
35        $data = (object)$request->validated();
36
37        /** @var \App\Models\Customer */
38        $customer = Customer::where('email', $data->email)->firstOrCreate([
39            'email' => $data->email
40        ]);
41
42        DB::transaction(function () use (&$customer, $data, $request, $service) {
43
44            $saved = $customer->save();
45            $customer->setOptIn($service->brand, $data->opt_in);
46
47            // * Delete Single optin if exists.
48            /** @var \App\Models\SingleOptin|null $singleOptin */
49                $singleOptin = SingleOptin::where('email', $data->email)
50                ->where('brand_id', $service->brand->id)->first();
51            if ($singleOptin) {
52                $saved = $singleOptin->delete() && $saved;
53            }
54
55            /** @var \App\Models\CustomerInfo */
56            $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_NEW);
57            if ($customerInfo->exists) {
58                throw new ApiErrorException(ApiError::customerExists());
59            }
60            $customerInfo->fill($request->validated());
61            $customerInfo->brand()->associate($service->brand);
62            $customerInfo->customer()->associate($customer);
63            $saved = $customerInfo->save() && $saved;
64
65            /** @var \App\Models\CustomerLogin */
66            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_NEW);
67            $customerLogin->fill($request->validated());
68            $customerLogin->brand()->associate($service->brand);
69            $customerLogin->customer()->associate($customer);
70            $saved = $customerLogin->save() && $saved;
71
72            if (!$saved) {
73                // @codeCoverageIgnoreStart
74                throw new CrmException('Api failed to store customer.');
75                // @codeCoverageIgnoreEnd
76            }
77        });
78
79        return new CustomerResource($service->brand, $customer);
80    }
81
82    /**
83     * Renders the specific customer infos
84     *
85     * @param  Service  $service
86     * @param  Customer $customer
87     * @return JsonResource
88     */
89    public function show(Service $service, Customer $customer): JsonResource
90    {
91        // * Make sure we are accessing a customer that belongs to our service.
92        $this->authorize('customerBelongsToService', $customer);
93
94        return new CustomerResource($service->brand, $customer);
95    }
96
97    /**
98     * Updates information for a specific customer
99     *
100     * @param  UpdateCustomerRequest $request
101     * @param  Service               $service
102     * @param  Customer              $customer
103     * @return JsonResource
104     * @throws CrmException If saving the customer to DB failed.
105     */
106    public function update(UpdateCustomerRequest $request, Service $service, Customer $customer): JsonResource
107    {
108        // * Make sure we are accessing a customer that belongs to our service.
109        $this->authorize('customerBelongsToService', $customer);
110
111        DB::transaction(function () use ($request, $customer, $service) {
112            // To correct with create.
113            $customer->fill($request->validated());
114            $saved = $customer->save();
115            if ($request->has('opt_in')) {
116                $customer->setOptIn($service->brand, $request->opt_in);
117            }
118
119            /** @var \App\Models\CustomerInfo */
120            $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
121            $customerInfo->fill($request->validated());
122            $saved = $customerInfo->save() && $saved;
123
124            /** @var \App\Models\CustomerLogin */
125            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
126            $customerLogin->fill($request->validated());
127            $saved = $customerLogin->save() && $saved;
128
129            if (!$saved) {
130                // @codeCoverageIgnoreStart
131                throw new CrmException('Api failed to update customer.');
132                // @codeCoverageIgnoreEnd
133            }
134        });
135
136        return new CustomerResource($service->brand, $customer);
137    }
138
139    /**
140     * Deletes customer information for a specific service
141     *
142     * @param  Service  $service
143     * @param  Customer $customer
144     * @return JsonResponse
145     * @throws CrmException If removing the customer off the DB failed.
146     */
147    public function destroy(Service $service, Customer $customer): JsonResponse
148    {
149        // * Make sure we are accessing a customer that belongs to our service.
150        $this->authorize('customerBelongsToService', $customer);
151
152        // TODO: Gestion avec mesBR.
153        /** @var \App\Models\CustomerInfo */
154        $customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
155        /** @var \App\Models\CustomerLogin */
156        $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
157
158        DB::transaction(function () use ($service, $customer, $customerLogin, $customerInfo) {
159            if (
160                $customerInfo->delete() === false ||
161                $customerLogin->delete() === false
162            ) {
163                // @codeCoverageIgnoreStart
164                throw new CrmException('Api failed to delete customer.');
165                // @codeCoverageIgnoreEnd
166            };
167
168            $customer->optInHistory()->whereHas('brand', function ($q) use ($service) {
169                $q->where('id', $service->brand_id);
170            })->delete();
171
172            // * Force loading real relation counts.
173            $customer->unsetRelation('logins');
174            $customer->unsetRelation('infos');
175            $logins = $customer->logins()->count();
176            $infos  = $customer->infos()->count();
177
178            // If customer is not associated with any brand then delete.
179            if (!$logins and !$infos) {
180                if ($customer->delete() === false) {
181                    // @codeCoverageIgnoreStart
182                    throw new CrmException('Api failed to update customer.');
183                    // @codeCoverageIgnoreEnd
184                }
185            }
186        });
187
188        return response()->json();
189    }
190
191    /**
192     * Authenticate the user
193     * @param AuthenticateRequest $request
194     * @param Service             $service
195     * @return JsonResource
196     * @throws ApiErrorException With ApiError::authFailed() in case of wrong credentials.
197     * @throws ApiErrorException With ApiError::customerNotActivated() if customer account is not activated.
198     */
199    public function authenticate(AuthenticateRequest $request, Service $service): JsonResource
200    {
201        try {
202            /** @var \App\Models\Customer */
203            $customer = Customer::where('email', $request->email)->firstOrFail();
204
205            /** @var \App\Models\CustomerLogin $customerLogin */
206            $customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
207            if (!$customerLogin->passwordMatch($request->password)) {
208                throw new ApiErrorException(ApiError::authFailed());
209            }
210
211            if (!$customerLogin->status) {
212                throw new ApiErrorException(ApiError::customerNotActivated());
213            }
214
215            UpdateCustomerLastConnection::dispatch($customer, $customerLogin, now())
216                ->onConnection('database')->afterResponse();
217
218            return new CustomerResource($service->brand, $customer);
219        } catch (ModelNotFoundException $e) {
220            throw new ApiErrorException(ApiError::authFailed());
221        }//end try
222    }
223
224    /**
225     * Activate the account of the user
226     * @param Customer $customer
227     * @param Service  $service
228     * @return \Illuminate\Http\JsonResponse
229     * @throws ApiErrorException With ApiError::customerAlreadyActive if customer account is already active.
230     * @throws CrmException If saving the customer to DB failed.
231     */
232    public function activateAccount(Customer $customer, Service $service)
233    {
234        // * Make sure we are accessing a customer that belongs to our service.
235        $this->authorize('customerBelongsToService', $customer);
236
237        /** @var \App\Models\CustomerLogin $login */
238        $login = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
239
240        if ($login->status) {
241            throw new ApiErrorException(ApiError::customerAlreadyActive());
242        }
243
244        $login->status = true;
245        if (!$login->save()) {
246            // @codeCoverageIgnoreStart
247            throw new CrmException('Failed to save login');
248            // @codeCoverageIgnoreEnd
249        }
250        return response()->json();
251    }
252}