ldc-crm/app/Http/Controllers/Api/V2/CustomerController.php
2023-06-23 17:18:59 +02:00

256 lines
10 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V2;
use App\Enums\ApiError;
use App\Exceptions\Api\V2\ApiErrorException;
use App\Exceptions\Api\V2\CrmException;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V2\AuthenticateRequest;
use App\Http\Requests\Api\V2\StoreCustomerRequest;
use App\Http\Requests\Api\V2\UpdateCustomerRequest;
use App\Http\Resources\Api\V2\CustomerResource;
use App\Jobs\UpdateCustomerLastConnection;
use App\Models\Customer;
use App\Models\Service;
use App\Models\SingleOptin;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Facades\DB;
class CustomerController extends Controller
{
/**
* Creates a new customer for the specified service.
*
* @param \App\Http\Requests\Api\V2\StoreCustomerRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\JsonResource
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::customerExists if
* the customer with this email already exists.
* @throws \App\Exceptions\Api\V2\CrmException If saving the customer to DB failed.
*/
public function store(StoreCustomerRequest $request, Service $service): JsonResource
{
$data = (object)$request->validated();
/** @var \App\Models\Customer */
$customer = Customer::where('email', $data->email)->firstOrCreate([
'email' => $data->email
]);
DB::transaction(function () use (&$customer, $data, $request, $service) {
$saved = $customer->save();
$customer->setOptIn($service->brand, $data->opt_in);
// * Delete Single optin if exists.
/** @var \App\Models\SingleOptin|null $singleOptin */
$singleOptin = SingleOptin::where('email', $data->email)
->where('brand_id', $service->brand->id)->first();
if ($singleOptin) {
$saved = $singleOptin->delete() && $saved;
}
/** @var \App\Models\CustomerInfo */
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_NEW);
if ($customerInfo->exists) {
throw new ApiErrorException(ApiError::customerExists());
}
$customerInfo->fill($request->validated());
$customerInfo->brand()->associate($service->brand);
$customerInfo->customer()->associate($customer);
$saved = $customerInfo->save() && $saved;
/** @var \App\Models\CustomerLogin */
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_NEW);
$customerLogin->fill($request->validated());
$customerLogin->brand()->associate($service->brand);
$customerLogin->customer()->associate($customer);
$saved = $customerLogin->save() && $saved;
if (!$saved) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to store customer.');
// @codeCoverageIgnoreEnd
}
});
return new CustomerResource($service->brand, $customer);
}
/**
* Renders the specific customer infos
*
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\JsonResource
*/
public function show(Service $service, Customer $customer): JsonResource
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
return new CustomerResource($service->brand, $customer);
}
/**
* Updates information for a specific customer
*
* @param \App\Http\Requests\Api\V2\UpdateCustomerRequest $request
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\JsonResource
* @throws \App\Exceptions\Api\V2\CrmException If saving the customer to DB failed.
*/
public function update(UpdateCustomerRequest $request, Service $service, Customer $customer): JsonResource
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
DB::transaction(function () use ($request, $customer, $service) {
// To correct with create.
$customer->fill($request->validated());
$saved = $customer->save();
if ($request->has('opt_in')) {
$customer->setOptIn($service->brand, $request->opt_in);
}
/** @var \App\Models\CustomerInfo */
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
$customerInfo->fill($request->validated());
$saved = $customerInfo->save() && $saved;
/** @var \App\Models\CustomerLogin */
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
$customerLogin->fill($request->validated());
$saved = $customerLogin->save() && $saved;
if (!$saved) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to update customer.');
// @codeCoverageIgnoreEnd
}
});
return new CustomerResource($service->brand, $customer);
}
/**
* Deletes customer information for a specific service
*
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V2\CrmException If removing the customer off the DB failed.
*/
public function destroy(Service $service, Customer $customer): JsonResponse
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
// TODO: Gestion avec mesBR.
/** @var \App\Models\CustomerInfo */
$customerInfo = $customer->info($service->brand, Customer::FIRST_OR_FAIL);
/** @var \App\Models\CustomerLogin */
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
DB::transaction(function () use ($service, $customer, $customerLogin, $customerInfo) {
if (
$customerInfo->delete() === false ||
$customerLogin->delete() === false
) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to delete customer.');
// @codeCoverageIgnoreEnd
};
$customer->optInHistory()->whereHas('brand', function ($q) use ($service) {
$q->where('id', $service->brand_id);
})->delete();
// * Force loading real relation counts.
$customer->unsetRelation('logins');
$customer->unsetRelation('infos');
$logins = $customer->logins()->count();
$infos = $customer->infos()->count();
// If customer is not associated with any brand then delete.
if (!$logins and !$infos) {
if ($customer->delete() === false) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to update customer.');
// @codeCoverageIgnoreEnd
}
}
});
return response()->json();
}
/**
* Authenticate the user
* @param \App\Http\Requests\Api\V2\AuthenticateRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\JsonResource
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::authFailed() in
* case of wrong credentials.
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::customerNotActivated()
* if customer account is not activated.
*/
public function authenticate(AuthenticateRequest $request, Service $service): JsonResource
{
try {
/** @var \App\Models\Customer */
$customer = Customer::where('email', $request->email)->firstOrFail();
/** @var \App\Models\CustomerLogin $customerLogin */
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
if (!$customerLogin->passwordMatch($request->password)) {
throw new ApiErrorException(ApiError::authFailed());
}
if (!$customerLogin->status) {
throw new ApiErrorException(ApiError::customerNotActivated());
}
UpdateCustomerLastConnection::dispatch($customer, $customerLogin, now())
->onConnection('database')->afterResponse();
return new CustomerResource($service->brand, $customer);
} catch (ModelNotFoundException $e) {
throw new ApiErrorException(ApiError::authFailed());
}//end try
}
/**
* Activate the account of the user
* @param \App\Models\Customer $customer
* @param \App\Models\Service $service
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::customerAlreadyActive if
* customer account is already active.
* @throws \App\Exceptions\Api\V2\CrmException If saving the customer to DB failed.
*/
public function activateAccount(Customer $customer, Service $service)
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
/** @var \App\Models\CustomerLogin $login */
$login = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
if ($login->status) {
throw new ApiErrorException(ApiError::customerAlreadyActive());
}
$login->status = true;
if (!$login->save()) {
// @codeCoverageIgnoreStart
throw new CrmException('Failed to save login');
// @codeCoverageIgnoreEnd
}
return response()->json();
}
}