260 lines
10 KiB
PHP
260 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\V4;
|
|
|
|
use App\Enums\ApiError;
|
|
use App\Exceptions\Api\V4\ApiErrorException;
|
|
use App\Exceptions\Api\V4\CrmException;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Api\V4\AuthenticateRequest;
|
|
use App\Http\Requests\Api\V4\StoreCustomerRequest;
|
|
use App\Http\Requests\Api\V4\UpdateCustomerRequest;
|
|
use App\Http\Resources\Api\V4\CustomerResource;
|
|
use App\Jobs\UpdateCustomerLastConnection;
|
|
use App\Models\Customer;
|
|
use App\Models\Service;
|
|
use App\Models\SingleOptin;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
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\V4\StoreCustomerRequest $request
|
|
* @param \App\Models\Service $service
|
|
* @return \Illuminate\Http\Resources\Json\JsonResource
|
|
* @throws \App\Exceptions\Api\V4\ApiErrorException With ApiError::customerExists if
|
|
* the customer with this email already exists.
|
|
* @throws \App\Exceptions\Api\V4\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::query()->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::query()->where('email', $data->email)
|
|
->whereHas('brand', function (Builder $q) use ($service) {
|
|
$q->where('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\V4\UpdateCustomerRequest $request
|
|
* @param \App\Models\Service $service
|
|
* @param \App\Models\Customer $customer
|
|
* @return \Illuminate\Http\Resources\Json\JsonResource
|
|
* @throws \App\Exceptions\Api\V4\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\V4\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);
|
|
|
|
/** @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\V4\AuthenticateRequest $request
|
|
* @param \App\Models\Service $service
|
|
* @return \Illuminate\Http\Resources\Json\JsonResource
|
|
* @throws \App\Exceptions\Api\V4\ApiErrorException With ApiError::authFailed in case
|
|
* of wrong credentials.
|
|
* @throws \App\Exceptions\Api\V4\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::query()->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\V4\ApiErrorException With ApiError::customerAlreadyActive
|
|
* if customer account is already active.
|
|
* @throws \App\Exceptions\Api\V4\CrmException If saving the customer to DB failed.
|
|
*/
|
|
public function activateAccount(Customer $customer, Service $service): JsonResponse
|
|
{
|
|
// * 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();
|
|
}
|
|
}
|