- Make all tests pass Client (V4),CRMV2,CRMV4
- Simplify client submodule path
- Move App_url to .env.testing
This commit is contained in:
Alexis Gousseau 2023-09-06 17:24:53 +02:00 • committed by Jérémy Munsch
parent 782a030ae3
commit 303834bbca
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
589 changed files with 43093 additions and 64401 deletions

1
.gitignore vendored
View file

@ -23,3 +23,4 @@ Homestead.yaml
npm-debug.log
yarn-error.log
.npmrc
.phpdoc

4
.gitmodules vendored
View file

@ -1,3 +1,3 @@
[submodule "clients/V2"]
path = clients/V2
[submodule "client"]
path = client
url = git@lab.s-web.fr:ldc-web/projets/crm-client.git

View file

@ -22,5 +22,5 @@
"commandCenter.border": "#15202b99"
},
"peacock.remoteColor": "#73b137",
"intelephense.environment.phpVersion": "7.4"
"intelephense.environment.phpVersion": "8.2"
}

View file

@ -1,4 +1,4 @@
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/ldcweb-production-7.3:latest
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/ldcweb-production-8.2:latest
COPY --chown=www-data:www-data . /app/srcs
COPY --chown=www-data:www-data ./public /app/srcs/public
COPY --chown=www-data:www-data ./vendor /app/srcs/vendor

View file

@ -9,6 +9,8 @@ use Illuminate\Validation\ValidationException;
/**
* Creates a new user
*
* @codeCoverageIgnore
*/
class CreateAdmin extends Command
{
@ -26,77 +28,155 @@ class CreateAdmin extends Command
*/
protected $description = 'Create a backend admin user';
/**
* The firstname of the new user.
*
* @var string
*/
protected $firstname;
/**
* The lastname of the new user.
*
* @var string
*/
protected $lastname;
/**
* The email of the new user.
*
* @var string
*/
protected $email;
/**
* The password of the new user.
*
* @var string
*/
protected $password;
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
public function handle(): int
{
$email = null;
while (is_null($email)) {
try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);
$validator->validate();
$email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$firstname = null;
while (is_null($firstname)) {
$this->firstname = null;
$this->addFirstname();
$this->lastname = null;
$this->addLastname();
$this->email = null;
$this->addEmail();
$this->password = null;
$this->addPassword();
$user = new User([
'firstname' => $this->firstname,
'lastname' => $this->lastname,
'email' => $this->email,
'password' => $this->password,
'remember_token' => null,
]);
$user->saveOrFail();
$this->info('User created 👌');
return 0;
}
/**
* Add a firstname to the user.
*
* @return void
*/
private function addFirstname(): void
{
while (is_null($this->firstname)) {
try {
$tmp = $this->ask('Type the wanted user firstname', 'Admin');
$validator = Validator::make(['firstname' => $tmp], [
'firstname' => 'required|min:5,max:255'
'firstname' => 'required|min:3|max:255'
]);
$validator->validate();
$firstname = $tmp;
$this->firstname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid firstname %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
} //end try
}; //end while
}
$lastname = null;
while (is_null($lastname)) {
/**
* Add a lastname to the user.
*
* @return void
*/
private function addLastname(): void
{
while (is_null($this->lastname)) {
try {
$tmp = $this->ask('Type the wanted user lastname', 'Saïtama');
$validator = Validator::make(['lastname' => $tmp], [
'lastname' => 'required|min:5,max:255'
'lastname' => 'required|min:3|max:255'
]);
$validator->validate();
$lastname = $tmp;
$this->lastname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid lastname %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
} //end try
}; //end while
}
$password = $this->secret('Type the wanted user password');
/**
* Add an email to the user.
*
* @return void
*/
private function addEmail(): void
{
while (is_null($this->email)) {
try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);
$validator->validate();
$this->email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
$admin = new User([
'lastname' => $lastname,
'firstname' => $firstname,
'email' => $email,
'password' => $password
]);
$admin->saveOrFail();
$this->info('User created.');
return 0;
/**
* Add a password to the user.
*
* @return void
*/
private function addPassword(): void
{
while (
($this->password = $this->secret(
(!\is_null($this->password) ? 'Failed to confirm, ' : '') . 'Type the wanted user password'
)) !== $this->secret('Confirm the password')
) {
continue;
}; //end while
}
}

View file

@ -35,10 +35,11 @@ class DeleteUnconfirmedCustomers extends Command
*
* @return integer
*/
public function handle()
public function handle(): int
{
/** @var \Illuminate\Database\Eloquent\Collection $customersLogins */
$customersLogins = CustomerLogin::whereStatus(false)
$customersLogins = CustomerLogin::query()
->where('status', false)
->where('created_at', '<', Carbon::now()->subDay(1))
->with(['brand', 'customer'])
->get();

View file

@ -7,18 +7,20 @@ use App\Enums\ApiStatus;
use App\Enums\ArcadiaOrderType;
use App\Enums\Country;
use App\Enums\QuestionType;
use App\Exceptions\Api\V2\ApiErrorException;
use App\Exceptions\Api\V2\CrmException;
use App\Exceptions\Api\V4\ApiErrorException;
use App\Exceptions\Api\V4\CrmException;
use App\Traits\Enums\BaseEnum;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class GenerateCrmClient extends Command
class GenerateClient extends Command
{
private const TARGET = 'clients/V2/src';
private const TARGET = 'client/src';
private const CLIENTNAMESPACE = 'LDC\\LDCCRM';
/**
@ -33,15 +35,16 @@ class GenerateCrmClient extends Command
*
* @var string
*/
protected $description = 'Generate the crm v2 client in clients/V2 folder';
protected $description = 'Generate the crm V4 client in client folder';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
public function handle(): int
{
$this->copyTraits();
$this->copyEnums();
$this->copyExceptions();
$this->copyRoutesList();
@ -54,7 +57,7 @@ class GenerateCrmClient extends Command
*
* @return void
*/
private function copyApiConfigInfo()
private function copyApiConfigInfo(): void
{
$this->info('Copy Api Config');
$rootTarget = app_path('../' . self::TARGET);
@ -72,13 +75,13 @@ class GenerateCrmClient extends Command
*
* @return void
*/
private function copyRoutesList()
private function copyRoutesList(): void
{
$this->info('Copy Routes');
$routes = [];
/** @var \Illuminate\Routing\Route */
foreach (Route::getRoutes() as $route) {
if (\strpos($route->getPrefix(), 'v2') !== false) {
if (\strpos($route->getPrefix(), 'v4') !== false) {
$routes[$route->getName()] = [
'uri' => $route->uri(),
'methods' => $route->methods()
@ -104,15 +107,36 @@ class GenerateCrmClient extends Command
{
$this->info('Copy Exceptions');
$rootTarget = app_path('../' . self::TARGET);
$this->copyToFolder([
$classes = [
CrmException::class,
ApiErrorException::class
], 'Exceptions');
$this->replaceInPhpFile(
"$rootTarget/Exceptions/ApiErrorException.php",
'App\Enums\ApiError',
'LDC\LDCCRM\Enums\ApiError'
);
];
$this->copyToFolder($classes, 'Exceptions');
foreach ($classes as $class) {
$classBasename = class_basename($class);
$dest = "$rootTarget/Exceptions/$classBasename.php";
$this->replaceInPhpFile($dest, 'App\\', self::CLIENTNAMESPACE . '\\');
}
}
/**
* Copy base enums classes
*
* @return void
*/
private function copyTraits(): void
{
$this->info('Copy Traits');
$rootTarget = app_path('../' . self::TARGET);
$classes = [
BaseEnum::class
];
$this->copyToFolder($classes, 'Traits');
foreach ($classes as $class) {
$classBasename = class_basename($class);
$dest = "$rootTarget/Traits/$classBasename.php";
$this->replaceInPhpFile($dest, 'App\\', self::CLIENTNAMESPACE . '\\');
}
}
/**
@ -123,7 +147,8 @@ class GenerateCrmClient extends Command
private function copyEnums(): void
{
$this->info('Copy Enums');
$classes = [
$rootTarget = app_path('../' . self::TARGET);
$classes = [
ApiError::class,
ApiStatus::class,
ArcadiaOrderType::class,
@ -131,11 +156,10 @@ class GenerateCrmClient extends Command
QuestionType::class
];
$this->copyToFolder($classes, 'Enums');
$rootTarget = app_path('../' . self::TARGET);
foreach ($classes as $class) {
$classBasename = class_basename($class);
$dest = "$rootTarget/Enums/$classBasename.php";
$this->replaceInPhpFile($dest, 'BaseEnumRoutable', 'BaseEnum');
$this->replaceInPhpFile($dest, 'App\\', self::CLIENTNAMESPACE . '\\');
}
}
@ -148,9 +172,10 @@ class GenerateCrmClient extends Command
*/
private function copyToFolder(array $classes, string $folder): void
{
$rootTarget = app_path('../' . self::TARGET);
$rootTarget = app_path('../' . self::TARGET);
$folderNamespace = \str_replace('/', '\\', $folder);
foreach ($classes as $class) {
$classFileName = \str_replace("App\\$folder\\", '', sprintf('%s.php', $class));
$classFileName = \str_replace("App\\$folderNamespace\\", '', sprintf('%s.php', $class));
$classFileName = \str_replace('\\', '/', $classFileName);
$classBasename = class_basename($class);
$source = \app_path("$folder/$classFileName");
@ -175,11 +200,15 @@ class GenerateCrmClient extends Command
{
$fContent = File::get($source);
// Remove trailing \Api\V2 .
if ($pos = strpos($fContent, '\Api\V2')) {
$fContent = substr_replace($fContent, '', $pos, strlen('\Api\V2'));
// Remove trailing \Api\V4 .
if ($pos = strpos($fContent, '\Api\V4')) {
$fContent = substr_replace($fContent, '', $pos, strlen('\Api\V4'));
}
// Remove trailing \Api .
$fContent = Str::of($fContent)->replaceMatches("/\\\(Api)([\s;\\\])/", '$2');
$fContent = Str::of($fContent)->replaceMatches("/\\\Traits\\\Enums/", '\Traits');
// Replace namespace top level.
$pos = strpos($fContent, $search);
if ($pos !== false) {

View file

@ -12,6 +12,7 @@ use Illuminate\Console\Command;
* Author URI: vincent.bridier@ldc.fr
*
* @codeCoverageIgnore
*
*/
class LdcWebStatusChecker extends Command
{
@ -34,7 +35,7 @@ class LdcWebStatusChecker extends Command
*
* @return mixed
*/
public function handle()
public function handle(): mixed
{
$data = [];
$data['uuid'] = config('ldcweb.project_uuid');

View file

@ -4,7 +4,6 @@ namespace App\Console\Commands;
use App\Lib\MigrateOldData;
use App\Models\Brand;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
@ -33,20 +32,20 @@ class MigrateCrmUsers extends Command
*
* @return integer
*/
public function handle()
public function handle(): int
{
try {
$table = $this->ask('What is the tmp table name with users data ?');
try {
DB::connection(config('database.oldDB'))->table($table)->exists();
} catch (Exception $e) {
} catch (\Exception $e) {
$this->error(sprintf('%s table not found on old crm database', $table));
return -1;
}
$brandname = $this->ask('What is the brand name ?');
$brand = Brand::where('name', 'LIKE', "%$brandname%")->first();
$brand = Brand::query()->where('name', 'LIKE', "%$brandname%")->first();
if (!$brand) {
if (!$this->confirm(sprintf('Would you like to use %s ? ', $brandname))) {
$this->info('Command cancelled');
@ -64,10 +63,10 @@ class MigrateCrmUsers extends Command
$mgOldData = new MigrateOldData();
$mgOldData->runMigration($brand, $table);
} catch (Exception $e) {
} catch (\Exception $e) {
$this->error(sprintf('Failed %s', $e));
return -1;
}//end try
} //end try
return 0;
}
}

View file

@ -29,7 +29,7 @@ class StartStatJobs extends Command
*
* @return integer
*/
public function handle()
public function handle(): int
{
\dispatch(new JobsStartStatJobs())->afterResponse();
return 0;

View file

@ -7,9 +7,6 @@ use App\Jobs\StartStatJobs;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
/**
* @codeCoverageIgnore
*/
class Kernel extends ConsoleKernel
{
/**
@ -25,10 +22,12 @@ class Kernel extends ConsoleKernel
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
protected function schedule(Schedule $schedule): void
{
// @codeCoverageIgnoreStart
$schedule->job(StartStatJobs::class)->dailyAt('03:30');
$schedule->command(DeleteUnconfirmedCustomers::class)->dailyAt('05:30');
// @codeCoverageIgnoreEnd
}
/**
@ -36,7 +35,7 @@ class Kernel extends ConsoleKernel
*
* @return void
*/
protected function commands()
protected function commands(): void
{
$this->load(__DIR__ . '/Commands');

View file

@ -516,7 +516,7 @@ enum Country: int
/**
* Get valid countries label.
*
* @return array
* @return array<string>
*/
public static function getValidCountriesLabel(): array
{
@ -528,12 +528,12 @@ enum Country: int
/**
* Get valid countries value.
*
* @return array
* @return array<string|integer>
*/
public static function getValidCountriesValues(): array
{
return collect(self::getValidCountries())->map(function (self $country) {
return $country->value;
return $country->value();
})->all();
}
@ -580,20 +580,32 @@ enum Country: int
protected static function isoCodes(): array
{
return [
self::france->name => 'FR',
self::belgium->name => 'BE',
self::luxembourg->name => 'LU',
self::guadeloupe->name => 'GP',
self::martinique->name => 'MQ',
self::frenchGuiana->name => 'GF',
self::reunion->name => 'RE',
self::saintPierreAndMiquelon->name => 'PM',
self::mayotte->name => 'YT',
self::saintBarthelemy->name => 'BL',
self::saintMartinFrenchPart->name => 'MF',
self::wallisAndFutuna->name => 'WF',
self::frenchPolynesia->name => 'PF',
self::newCaledonia->name => 'NC',
'france' => 'FR',
'belgium' => 'BE',
'luxembourg' => 'LU',
'guadeloupe' => 'GP',
'martinique' => 'MQ',
'frenchGuiana' => 'GF',
'reunion' => 'RE',
'saintPierreAndMiquelon' => 'PM',
'mayotte' => 'YT',
'saintBarthelemy' => 'BL',
'saintMartinFrenchPart' => 'MF',
'wallisAndFutuna' => 'WF',
'frenchPolynesia' => 'PF',
'newCaledonia' => 'NC',
];
}
/**
* @param string $attribute
* @return integer|string|null
*/
public function get(string $attribute): int|string|null
{
if ($attribute === 'iso') {
return $this->isoCodes()[$this->name] ?? null;
}
return $this->{$attribute};
}
}

View file

@ -2,13 +2,12 @@
namespace App\Exceptions;
use Exception;
/**
* This class Exception is used to skip a row import from
* database migration (2016 -> 2020)
*
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
*/
class AbortRowImportException extends Exception
class AbortRowImportException extends \RuntimeException
{
}

View file

@ -10,11 +10,11 @@ use Throwable;
/**
* This would be thrown if any of the case
* referenced in \App\Enums\ApiError would
* occur within the crm
* occur within the crm.
*
* On the client an ApiErrorException would
* also be throw with the ApiError corresponding to
* the situation
* the situation.
*
* @package LDCCrmClient
* @subpackage exceptions
@ -34,7 +34,7 @@ class ApiErrorException extends Exception
/**
* This is an explicit error message,
* ou may print this to the front or not depending
* on $error value
* on $error value.
*
* @var string
*/
@ -52,10 +52,10 @@ class ApiErrorException extends Exception
/**
* Create a new exception base on ApiError enum.
*
* @param ApiError $error
* @param integer $httpCode
* @param string $message
* @param Throwable|null $previous
* @param \App\Enums\ApiError $error
* @param integer $httpCode
* @param string $message
* @param Throwable|null $previous
*/
public function __construct(ApiError $error, int $httpCode = 400, string $message = '', Throwable $previous = null)
{
@ -64,15 +64,15 @@ class ApiErrorException extends Exception
$this->httpCode = $httpCode;
parent::__construct(
strlen($this->message) ? $this->message : $error->label(),
$error->value,
$error->value(),
$previous
);
}
/**
* Get the Api Error
* Get the Api Error.
*
* @return ApiError
* @return \App\Enums\ApiError
*/
public function getApiError(): ApiError
{
@ -80,7 +80,7 @@ class ApiErrorException extends Exception
}
/**
* Get the http code
* Get the http code.
*
* @return integer
*/
@ -95,7 +95,7 @@ class ApiErrorException extends Exception
*
* @return void
*/
public function report()
public function report(): void
{
// @codeCoverageIgnoreStart
Log::debug(sprintf('CRM API: %s', $this->getMessage()));

View file

@ -12,7 +12,6 @@ use Illuminate\Support\Facades\Log;
*
* @package LDCCrmClient
* @subpackage exceptions
* @codeCoverageIgnore
*/
class CrmException extends Exception
{
@ -22,7 +21,7 @@ class CrmException extends Exception
*
* @return void
*/
public function report()
public function report(): void
{
Log::critical(sprintf('CRM API: %s', $this->getMessage()));
}

View file

@ -0,0 +1,102 @@
<?php
namespace App\Exceptions\Api\V4;
use App\Enums\ApiError;
use Illuminate\Support\Facades\Log;
/**
* This would be thrown if any of the case
* referenced in \App\Enums\ApiError would
* occur within the crm.
*
* On the client an ApiErrorException would
* also be throw with the ApiError corresponding to
* the situation.
*
* @package LDCCrmClient
* @subpackage exceptions
*/
class ApiErrorException extends \RuntimeException
{
/**
* Api Error
* This value makes the error more explicit.
* Handling these errors are normal as inputs or
* request may be undesired or refused by the crm.
*
* @var \App\Enums\ApiError
*/
protected $error;
/**
* This is an explicit error message,
* ou may print this to the front or not depending
* on $error value.
*
* @var string
*/
protected $message;
/**
* This is the http code returned by the crm.
* This value could be used to handle errors.
* But please take in consideration $error first.
*
* @var integer
*/
protected $httpCode;
/**
* Create a new exception base on ApiError enum.
*
* @param \App\Enums\ApiError $error
* @param integer $httpCode
* @param string $message
* @param \Throwable|null $previous
*/
public function __construct(ApiError $error, int $httpCode = 400, string $message = '', \Throwable $previous = null)
{
$this->error = $error;
$this->message = $message;
$this->httpCode = $httpCode;
parent::__construct(
strlen($this->message) ? $this->message : $error->label(),
$error->value(),
$previous
);
}
/**
* Get the Api Error.
*
* @return \App\Enums\ApiError
*/
public function getApiError(): ApiError
{
return $this->error;
}
/**
* Get the http code.
*
* @return integer
*/
public function getStatusCode(): int
{
return $this->httpCode;
}
/**
* This is used by laravel only.
* It uses Log class to print the error.
*
* @return void
*/
public function report(): void
{
// @codeCoverageIgnoreStart
Log::debug(sprintf('CRM API: %s', $this->getMessage()));
// @codeCoverageIgnoreEnd
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Exceptions\Api\V4;
use Illuminate\Support\Facades\Log;
/**
* This is a critical error.
* The request could not be handled. You may dump any value you can
* get before submitting a ticker to https://support.uwil.fr/
*
* @package LDCCrmClient
* @subpackage exceptions
*
*/
class CrmException extends \RuntimeException
{
/**
* This is used by laravel only.
* It uses Log class to print the error.
*
* @return void
*/
public function report(): void
{
Log::critical(sprintf('CRM API: %s', $this->getMessage()));
}
}

View file

@ -4,8 +4,10 @@ namespace App\Exceptions;
use App\Enums\ApiError;
use App\Enums\ApiStatus;
use App\Exceptions\Api\V2\ApiErrorException;
use App\Exceptions\Api\V2\ApiErrorException as V2ApiErrorException;
use App\Exceptions\Api\V4\ApiErrorException as V4ApiErrorException;
use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
use App\Http\Resources\Api\V4\ApiResponse as V4ApiResponse;
use App\Lib\Helper;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
@ -15,6 +17,7 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
class Handler extends ExceptionHandler
@ -44,7 +47,7 @@ class Handler extends ExceptionHandler
*
* @return void
*/
public function register()
public function register(): void
{
$this->reportable(function (\Throwable $e) {
if (app()->bound('sentry')) {
@ -60,10 +63,10 @@ class Handler extends ExceptionHandler
* @param \Throwable $e
* @return \Symfony\Component\HttpFoundation\Response
*/
public function render(mixed $request, \Throwable $e)
public function render(mixed $request, \Throwable $e): Response
{
if (Helper::isRequestForApiV2($request) and $request->isJson()) {
return $this->handleApiV2Exception($request, $e);
if ((Helper::isRequestForApiV2($request) || Helper::isRequestForApiV4($request)) and $request->isJson()) {
return $this->handleApiException($request, $e);
} //end if
// @codeCoverageIgnoreStart
return parent::render($request, $e);
@ -71,13 +74,13 @@ class Handler extends ExceptionHandler
}
/**
* Generate a json response for any apiV2 error case
* Generate a json response for any apiV4 error case.
*
* @param \Illuminate\Http\Request $request
* @param \Throwable $e
* @return \Illuminate\Http\JsonResponse
*/
private function handleApiV2Exception(Request $request, \Throwable $e): JsonResponse
private function handleApiException(Request $request, \Throwable $e): JsonResponse
{
switch (get_class($e)) {
case AuthenticationException::class:
@ -85,7 +88,7 @@ class Handler extends ExceptionHandler
return $this->getReponse(
$request,
401,
ApiError::apiAuthFailed->value,
ApiError::apiAuthFailed->value(),
sprintf('%s, %s', ApiError::apiAuthFailed->label(), $e->getMessage())
);
case MethodNotAllowedHttpException::class:
@ -93,7 +96,7 @@ class Handler extends ExceptionHandler
return $this->getReponse(
$request,
$e->getStatusCode(),
ApiError::methodNotAllowed->value,
ApiError::methodNotAllowed->value(),
sprintf('%s, %s', ApiError::methodNotAllowed->label(), $e->getMessage())
);
case ValidationException::class:
@ -101,7 +104,7 @@ class Handler extends ExceptionHandler
return $this->getReponse(
$request,
$e->status,
ApiError::validationFailed->value,
ApiError::validationFailed->value(),
sprintf(ApiError::validationFailed->label(), $e->getMessage()),
['errors' => $e->errors()]
);
@ -110,14 +113,22 @@ class Handler extends ExceptionHandler
return $this->getReponse(
$request,
404,
ApiError::modelNotFound->value,
ApiError::modelNotFound->value(),
sprintf(
ApiError::modelNotFound->label(),
\array_slice(explode('\\', $e->getModel()), -1)[0]
)
);
case ApiErrorException::class:
/** @var ApiErrorException $e */
case V4ApiErrorException::class:
/** @var \App\Exceptions\Api\V4\ApiErrorException $e */
return $this->getReponse(
$request,
$e->getStatusCode(),
$e->getCode(),
$e->getMessage()
);
case V2ApiErrorException::class:
/** @var \App\Exceptions\Api\V2\ApiErrorException $e */
return $this->getReponse(
$request,
$e->getStatusCode(),
@ -129,7 +140,7 @@ class Handler extends ExceptionHandler
return $this->getReponse(
$request,
429,
ApiError::throttleRequestsException->value,
ApiError::throttleRequestsException->value(),
ApiError::throttleRequestsException->label()
);
case AuthorizationException::class:
@ -137,7 +148,7 @@ class Handler extends ExceptionHandler
return $this->getReponse(
$request,
403,
ApiError::authorizationException->value,
ApiError::authorizationException->value(),
ApiError::authorizationException->label()
);
} //end switch
@ -151,7 +162,7 @@ class Handler extends ExceptionHandler
}
/**
* Generate an ApiV2 json response
* Generate an ApiV4 json response.
*
* @param \Illuminate\Http\Request $request
* @param integer $httpCode
@ -167,7 +178,13 @@ class Handler extends ExceptionHandler
string $message,
array $data = []
): JsonResponse {
return (new V2ApiResponse(
if (Helper::isRequestForApiV2($request)) {
return (new V2ApiResponse(
(object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
ApiStatus::KO
))->toResponse($request)->setStatusCode($httpCode);
}
return (new V4ApiResponse(
(object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
ApiStatus::KO
))->toResponse($request)->setStatusCode($httpCode);

View file

@ -11,18 +11,15 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class AnswerController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Question $questionModel */
$questionModel = $request->question;
@ -52,9 +49,9 @@ class AnswerController extends Controller
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Answer $answer
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function create(Request $request, Answer $answer)
public function create(Request $request, Answer $answer): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Question $questionModel */
$questionModel = $request->question;
@ -69,9 +66,9 @@ class AnswerController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Answers\StoreAnswerRequest $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreAnswerRequest $request)
public function store(StoreAnswerRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$answer = new Answer();
@ -98,9 +95,9 @@ class AnswerController extends Controller
*
* @param \App\Models\Question $question
* @param \App\Models\Answer $answers
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function edit(Question $question, Answer $answers)
public function edit(Question $question, Answer $answers): \Illuminate\Contracts\View\View
{
return view('answers.edit', [
'answerModel' => $answers,
@ -114,10 +111,13 @@ class AnswerController extends Controller
* @param \App\Http\Requests\Answers\UpdateAnswerRequest $request
* @param \App\Models\Question $question
* @param \App\Models\Answer $answer
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateAnswerRequest $request, Question $question, Answer $answer)
{
public function update(
UpdateAnswerRequest $request,
Question $question,
Answer $answer
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $question, $answer) {
$answer->fill($request->validated());
if ($answer->saveOrFail()) {

View file

@ -12,7 +12,7 @@ use Illuminate\Http\JsonResponse;
class AnswerController extends Controller
{
/**
* Store the customer answers
* Store the customer answers.
*
* @param \App\Http\Requests\Api\V2\StoreCustomerAnswers $request
* @param \App\Models\Service $service
@ -28,7 +28,7 @@ class AnswerController extends Controller
$this->authorize('customerBelongsToService', $customer);
$existingAnswerIds = $customer->getAnswers($service)->pluck('id');
$answersID = Answer::whereIn('id', $request->answers)->pluck('id');
$answersID = Answer::query()->whereIn('id', $request->answers)->pluck('id');
$customer->answers()->sync($answersID->merge($existingAnswerIds));
return response()->json();
}

View file

@ -8,7 +8,7 @@ use Illuminate\Http\JsonResponse;
class ApiController extends Controller
{
/**
* Just returns HTTP 200 response
* Just returns HTTP 200 response.
*
* @return \Illuminate\Http\JsonResponse
*/

View file

@ -29,7 +29,7 @@ use Illuminate\Support\Str;
class ArcadiaController extends Controller
{
/**
* Send arcadia default mail (reclamations)
* Send arcadia default mail (reclamations).
*
* @param \App\Http\Requests\Api\V2\SendArchadiaDefaultMailRequest $request
* @return \Illuminate\Http\JsonResponse
@ -40,7 +40,7 @@ class ArcadiaController extends Controller
}
/**
* Send arcadia game mail (games)
* Send arcadia game mail (games).
*
* @param \App\Http\Requests\Api\V2\SendArchadiaGameMailRequest $request
* @return \Illuminate\Http\JsonResponse
@ -52,7 +52,7 @@ class ArcadiaController extends Controller
/**
* Form xml message and save it in the apropriate storage location
* to be picked up by the arcadia job
* to be picked up by the arcadia job.
*
* @param \App\Http\Requests\Api\V2\BaseApiRequest $request
* @param \App\Enums\ArcadiaMailType $type
@ -67,14 +67,14 @@ class ArcadiaController extends Controller
$xml = 'none';
try {
$customerInfo = \array_intersect_key($request->all()['deliver'], [
'email' => '',
'email' => '',
'firstname' => '',
'lastname' => '',
'address' => '',
'address2' => '',
'city' => '',
'postcode' => '',
'phone' => ''
'lastname' => '',
'address' => '',
'address2' => '',
'city' => '',
'postcode' => '',
'phone' => ''
]);
$orderType = ArcadiaOrderType::make(intval($request->state_order));
$xml = $this->makeXml($request, $type, $orderType, $customerInfo);
@ -109,11 +109,11 @@ class ArcadiaController extends Controller
}
throw $e;
// @codeCoverageIgnoreEnd
}//end try
} //end try
}
/**
* render Arcadia xml for order
* Render Arcadia xml for order.
*
* @param \App\Http\Requests\Api\V2\BaseApiRequest $request
* @param \App\Enums\ArcadiaMailType $type
@ -130,9 +130,9 @@ class ArcadiaController extends Controller
$arcadiaMailType = $type->equals(ArcadiaMailType::default) ?
'mail.arcadiaOrders.default' : 'mail.arcadiaOrders.game';
return view($arcadiaMailType, [
'request' => $request,
'type' => $type,
'orderType' => $orderType,
'request' => $request,
'type' => $type,
'orderType' => $orderType,
'customerInfo' => $customerInfo
])->render();
}

View file

@ -37,7 +37,7 @@ class CustomerController extends Controller
$data = (object)$request->validated();
/** @var \App\Models\Customer */
$customer = Customer::where('email', $data->email)->firstOrCreate([
$customer = Customer::query()->where('email', $data->email)->firstOrCreate([
'email' => $data->email
]);
@ -84,7 +84,7 @@ class CustomerController extends Controller
}
/**
* Renders the specific customer infos
* Renders the specific customer infos.
*
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
@ -99,7 +99,7 @@ class CustomerController extends Controller
}
/**
* Updates information for a specific customer
* Updates information for a specific customer.
*
* @param \App\Http\Requests\Api\V2\UpdateCustomerRequest $request
* @param \App\Models\Service $service
@ -141,7 +141,7 @@ class CustomerController extends Controller
}
/**
* Deletes customer information for a specific service
* Deletes customer information for a specific service.
*
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
@ -193,7 +193,7 @@ class CustomerController extends Controller
}
/**
* Authenticate the user
* Authenticate the user.
*
* @param \App\Http\Requests\Api\V2\AuthenticateRequest $request
* @param \App\Models\Service $service
@ -207,7 +207,7 @@ class CustomerController extends Controller
{
try {
/** @var \App\Models\Customer */
$customer = Customer::where('email', $request->email)->firstOrFail();
$customer = Customer::query()->where('email', $request->email)->firstOrFail();
/** @var \App\Models\CustomerLogin $customerLogin */
$customerLogin = $customer->login($service->brand, Customer::FIRST_OR_FAIL);
@ -229,7 +229,7 @@ class CustomerController extends Controller
}
/**
* Activate the account of the user
* Activate the account of the user.
*
* @param \App\Models\Customer $customer
* @param \App\Models\Service $service
@ -238,7 +238,7 @@ class CustomerController extends Controller
* 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)
public function activateAccount(Customer $customer, Service $service): JsonResponse
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);

View file

@ -17,7 +17,7 @@ use App\Models\SingleOptin;
class OptinController extends Controller
{
/**
* Creates a new Single optin or destroys it if exists
* Creates a new Single optin or destroys it if exists.
*
* @param \App\Http\Requests\Api\V2\SingleOptinRequest $request
* @param \App\Models\Service $service
@ -48,14 +48,14 @@ class OptinController extends Controller
}
/**
* Creates a new Single optin or destroys it if exists using hashcode
* Creates a new Single optin or destroys it if exists using hashcode.
*
* @param \App\Http\Requests\Api\V2\SingleOptinRemoveWithHashcodeRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V2\CrmException If the single optin could not be removed.
*/
public function optoutWithHashcode(SingleOptinRemoveWithHashcodeRequest $request, Service $service)
public function optoutWithHashcode(SingleOptinRemoveWithHashcodeRequest $request, Service $service): JsonResponse
{
/** @var \App\Models\SingleOptin */
$singleOptin = SingleOptin::query()->where('hashcode', $request->hashcode)
@ -73,7 +73,7 @@ class OptinController extends Controller
}
/**
* Changes the optin state
* Changes the optin state.
*
* @param \App\Models\Service $service
* @param \App\Http\Requests\Api\V2\BaseApiRequest $request

View file

@ -52,10 +52,10 @@ class QuestionController extends Controller
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
$questions = Question::whereHas('brand', function ($q) use ($service) {
$questions = Question::query()->whereHas('brand', function ($q) use ($service) {
$q->where('id', $service->brand->id);
})->whereDoesntHave('answers', function ($q) use ($customer) {
$q->whereIn('id', Answer::whereHas('customers', function ($q) use ($customer) {
$q->whereIn('id', Answer::query()->whereHas('customers', function ($q) use ($customer) {
$q->where('id', $customer->id);
})->pluck('id'));
})->with('answers')->get();

View file

@ -18,7 +18,7 @@ use Illuminate\Support\Collection;
class StatsController extends Controller
{
/**
* Get specific brand statistics
* Get specific brand statistics.
*
* @param \App\Http\Requests\Api\V2\GetBrandStatsRequest $request
* @param \App\Models\Service $service
@ -26,14 +26,14 @@ class StatsController extends Controller
*/
public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
{
$stats = BrandStat::whereDate('created_at', '>=', $request->start)
$stats = BrandStat::query()->whereDate('created_at', '>=', $request->start)
->whereDate('created_at', '<=', $request->end)
->whereBrandId($service->brand->id)->get();
return new BrandStatsResource($stats);
}
/**
* Get statistics for a specific question
* Get statistics for a specific question.
*
* @param \App\Http\Requests\Api\V2\GetQuestionStatsRequest $request
* @param \App\Models\Question $question
@ -55,7 +55,7 @@ class StatsController extends Controller
}
/**
* Total réponses par dates
* Total réponses par dates.
*
* @param \Illuminate\Support\Collection $answers
* @param \Illuminate\Support\Carbon $start
@ -65,13 +65,13 @@ class StatsController extends Controller
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
{
// * Totalize by date all answers.
$answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
$answersByDays = CustomerAnswer::query()->whereDate('created_at', '>=', $start)
->whereDate('created_at', '<=', $end)
->whereIn('answer_id', $answers->pluck('id'))
->get()->map(function ($custAnswer) {
return [
'answer_id' => $custAnswer->answer_id,
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
];
})->groupBy(['created_at', 'answer_id'])->map(function ($day) {
return $day->map(function ($custAnswers) {

View file

@ -0,0 +1,35 @@
<?php
namespace App\Http\Controllers\Api\V4;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V4\StoreCustomerAnswers;
use App\Models\Answer;
use App\Models\Customer;
use App\Models\Service;
use Illuminate\Http\JsonResponse;
class AnswerController extends Controller
{
/**
* Store the customer answers.
*
* @param \App\Http\Requests\Api\V4\StoreCustomerAnswers $request
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\JsonResponse
*/
public function storeCustomerAnswers(
StoreCustomerAnswers $request,
Service $service,
Customer $customer
): JsonResponse {
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
$existingAnswerIds = $customer->getAnswers($service)->pluck('id');
$answersID = Answer::query()->whereIn('id', $request->answers)->pluck('id');
$customer->answers()->sync($answersID->merge($existingAnswerIds));
return response()->json();
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Api\V4;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
class ApiController extends Controller
{
/**
* Just returns HTTP 200 response.
*
* @return \Illuminate\Http\JsonResponse
*/
public function ping(): JsonResponse
{
return response()->json();
}
}

View file

@ -0,0 +1,139 @@
<?php
namespace App\Http\Controllers\Api\V4;
use App\Enums\ArcadiaMailType;
use App\Enums\ArcadiaOrderType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V4\BaseApiRequest;
use App\Http\Requests\Api\V4\SendArchadiaDefaultMailRequest;
use App\Http\Requests\Api\V4\SendArchadiaGameMailRequest;
use App\Mail\CourrierArcadiaError;
use DOMDocument;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
/**
* ! Ici c'est du sale à cause d'arcadia
* Ils ont fait le choix de rajouter une fonctionnalité
* sur une methode existante en rajoutant des conditions
* sales. Ils ont donc une seule méthode pour envoyer plusieurs
* types de réclas avec des champs différents...
*
* Donc une séparation est faite sur les endpoints CRM et dans le client
* pour éviter une confusion de nos utilisateurs
*/
class ArcadiaController extends Controller
{
/**
* Send arcadia default mail (reclamations).
*
* @param \App\Http\Requests\Api\V4\SendArchadiaDefaultMailRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function outgoingMailDefault(SendArchadiaDefaultMailRequest $request): JsonResponse
{
return $this->handleArcadiaMail($request, ArcadiaMailType::default);
}
/**
* Send arcadia game mail (games).
*
* @param \App\Http\Requests\Api\V4\SendArchadiaGameMailRequest $request
* @return \Illuminate\Http\JsonResponse
*/
public function outgoingMailGame(SendArchadiaGameMailRequest $request): JsonResponse
{
return $this->handleArcadiaMail($request, ArcadiaMailType::game);
}
/**
* Form xml message and save it in the apropriate storage location
* to be picked up by the arcadia job.
*
* @param \App\Http\Requests\Api\V4\BaseApiRequest $request
* @param \App\Enums\ArcadiaMailType $type
* @return \Illuminate\Http\JsonResponse
* @throws \Exception If an error occurs while generating and saving the xml file.
*/
private function handleArcadiaMail(BaseApiRequest $request, ArcadiaMailType $type): JsonResponse
{
$request = $request->merge([
'order_num' => str_pad(\intval($request->order_num), 9, '0', STR_PAD_LEFT)
]);
$xml = 'none';
try {
$customerInfo = \array_intersect_key($request->all()['deliver'], [
'email' => '',
'firstname' => '',
'lastname' => '',
'address' => '',
'address2' => '',
'city' => '',
'postcode' => '',
'phone' => ''
]);
$orderType = ArcadiaOrderType::make(intval($request->state_order));
$xml = $this->makeXml($request, $type, $orderType, $customerInfo);
// * Format xml.
$doc = new DOMDocument('1.0', 'utf-8');
$doc->xmlStandalone = true;
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$doc->loadXML($xml);
$xml = $doc->saveXML();
Storage::disk('arcadia')->put(Str::slug(sprintf(
'v4_order_%s_%s_%s%s',
$request->order_num,
$type->label(),
date('Ymd_His'),
\microtime(true)
)) . '.xml', $xml);
return response()->json();
// @codeCoverageIgnoreStart
} catch (\Exception $e) {
Log::critical("ARCADIA XML CREATION FAILED FOR $request->order_num : " . $e->getMessage());
if (!config('app.debug')) {
dispatch(function () use ($xml, $e, $request) {
Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(
"ARCADIA XML CREATION FAILED FOR $request->order_num",
"Une erreur est survenue lors de la création du fichier XML : " . $e->getMessage(),
$xml ?: ''
));
})->afterResponse();
}
throw $e;
// @codeCoverageIgnoreEnd
} //end try
}
/**
* Render Arcadia xml for order.
*
* @param \App\Http\Requests\Api\V4\BaseApiRequest $request
* @param \App\Enums\ArcadiaMailType $type
* @param \App\Enums\ArcadiaOrderType $orderType
* @param array $customerInfo
* @return string
*/
private function makeXml(
BaseApiRequest $request,
ArcadiaMailType $type,
ArcadiaOrderType $orderType,
array $customerInfo
): string {
$arcadiaMailType = $type->equals(ArcadiaMailType::default) ?
'mail.arcadiaOrders.default' : 'mail.arcadiaOrders.game';
return view($arcadiaMailType, [
'request' => $request,
'type' => $type,
'orderType' => $orderType,
'customerInfo' => $customerInfo
])->render();
}
}

View file

@ -0,0 +1,261 @@
<?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);
// 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\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();
}
}

View file

@ -0,0 +1,93 @@
<?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\BaseApiRequest;
use App\Http\Requests\Api\V4\SingleOptinRequest;
use App\Http\Requests\Api\V4\SingleOptinRemoveWithHashcodeRequest;
use App\Models\CustomerInfo;
use App\Models\Service;
use Illuminate\Http\JsonResponse;
use App\Models\SingleOptin;
class OptinController extends Controller
{
/**
* Creates a new Single optin or destroys it if exists.
*
* @param \App\Http\Requests\Api\V4\SingleOptinRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V4\ApiErrorException With ApiError::singleOptinCustomerExists
* if this email already has a full account.
* @throws \App\Exceptions\Api\V4\CrmException If the single optin could not be added or removed.
*/
public function optin(SingleOptinRequest $request, Service $service): JsonResponse
{
if (CustomerInfo::getFromEmail($service->brand, $request->email)->exists()) {
throw new ApiErrorException(ApiError::singleOptinCustomerExists);
}
/** @var \App\Models\SingleOptin */
$singleOptin = SingleOptin::query()->where('email', $request->email)
->whereHas('brand', function ($q) use ($service) {
$q->where('id', $service->brand->id);
})->firstOrNew();
if (!$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to update single optin.');
// @codeCoverageIgnoreEnd
}
return response()->json();
}
/**
* Creates a new Single optin or destroys it if exists using hashcode.
*
* @param \App\Http\Requests\Api\V4\SingleOptinRemoveWithHashcodeRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\JsonResponse
* @throws \App\Exceptions\Api\V4\CrmException If the single optin could not be removed.
*/
public function optoutWithHashcode(SingleOptinRemoveWithHashcodeRequest $request, Service $service): JsonResponse
{
/** @var \App\Models\SingleOptin */
$singleOptin = SingleOptin::query()->where('hashcode', $request->hashcode)
->whereHas('brand', function ($q) use ($service) {
$q->whereId($service->brand->id);
})->firstOrFail();
if ($singleOptin and !$this->changeOptin($service, $request, \boolval($request->opt_in), $singleOptin)) {
// @codeCoverageIgnoreStart
throw new CrmException('Api failed to update single optin.');
// @codeCoverageIgnoreEnd
}
return response()->json();
}
/**
* Changes the optin state.
*
* @param \App\Models\Service $service
* @param \App\Http\Requests\Api\V4\BaseApiRequest $request
* @param boolean $optin
* @param \App\Models\SingleOptin $singleOptin
* @return boolean Returns false in case of failure.
*/
private function changeOptin(Service $service, BaseApiRequest $request, bool $optin, SingleOptin $singleOptin): bool
{
if ($optin) {
$singleOptin->fill($request->validated());
$singleOptin->brand()->associate($service->brand);
return $singleOptin->save();
}
return $singleOptin->delete() !== false;
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Api\V4;
use App\Http\Controllers\Controller;
use App\Http\Resources\Api\V4\QuestionsResource;
use App\Models\Answer;
use App\Models\Customer;
use App\Models\Question;
use App\Models\Service;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\ResourceCollection;
class QuestionController extends Controller
{
/**
* Returns all service questions.
*
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\ResourceCollection
*/
public function index(Service $service): ResourceCollection
{
return new QuestionsResource($service->brand->questions);
}
/**
* Returns all service questions with user answers for all of them.
*
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\ResourceCollection
*/
public function indexWithResponse(Service $service, Customer $customer): ResourceCollection
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
$customerAnswers = $customer->getAnswers($service);
return new QuestionsResource($service->brand->questions, $customerAnswers);
}
/**
* Returns all service questions that was not answered by a user.
*
* @param \App\Models\Service $service
* @param \App\Models\Customer $customer
* @return \Illuminate\Http\Resources\Json\JsonResource
*/
public function unAnsweredBy(Service $service, Customer $customer): JsonResource
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('customerBelongsToService', $customer);
$questions = Question::query()->whereHas('brand', function ($q) use ($service) {
$q->where('id', $service->brand->id);
})->whereDoesntHave('answers', function ($q) use ($customer) {
$q->whereIn('id', Answer::query()->whereHas('customers', function ($q) use ($customer) {
$q->where('id', $customer->id);
})->pluck('id'));
})->with('answers')->get();
return new QuestionsResource($questions);
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Http\Controllers\Api\V4;
use App\Http\Controllers\Controller;
use App\Http\Requests\Api\V4\GetBrandStatsRequest;
use App\Http\Requests\Api\V4\GetQuestionStatsRequest;
use App\Http\Resources\Api\V4\BrandStatsResource;
use App\Http\Resources\Api\V4\QuestionStatsResource;
use App\Models\BrandStat;
use App\Models\CustomerAnswer;
use App\Models\Question;
use App\Models\Service;
use Illuminate\Http\Resources\Json\ResourceCollection;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
class StatsController extends Controller
{
/**
* Get specific brand statistics.
*
* @param \App\Http\Requests\Api\V4\GetBrandStatsRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Resources\Json\ResourceCollection
*/
public function brandStats(GetBrandStatsRequest $request, Service $service): ResourceCollection
{
$stats = BrandStat::query()->whereDate('created_at', '>=', $request->start)
->whereDate('created_at', '<=', $request->end)
->whereBrandId($service->brand->id)->get();
return new BrandStatsResource($stats);
}
/**
* Get statistics for a specific question.
*
* @param \App\Http\Requests\Api\V4\GetQuestionStatsRequest $request
* @param \App\Models\Question $question
* @return \App\Http\Resources\Api\V4\QuestionStatsResource
*/
public function questionStats(GetQuestionStatsRequest $request, Question $question): QuestionStatsResource
{
// * Make sure we are accessing a customer that belongs to our service.
$this->authorize('questionBelongsToService', $question);
$answers = $question->answers;
$start = Carbon::parse($request->start);
$end = Carbon::parse($request->end);
return new QuestionStatsResource(
$this->answersByDays($answers, $start, $end),
$question
);
}
/**
* Total réponses par dates.
*
* @param \Illuminate\Support\Collection $answers
* @param \Illuminate\Support\Carbon $start
* @param \Illuminate\Support\Carbon $end
* @return \Illuminate\Support\Collection
*/
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
{
// * Totalize by date all answers.
$answersByDays = CustomerAnswer::query()->whereDate('created_at', '>=', $start)
->whereDate('created_at', '<=', $end)
->whereIn('answer_id', $answers->pluck('id'))
->get()->map(function ($custAnswer) {
return [
'answer_id' => $custAnswer->answer_id,
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
];
})->groupBy(['created_at', 'answer_id'])->map(function ($day) {
return $day->map(function ($custAnswers) {
return $custAnswers->count();
});
});
do {
$currentDate = $start->isoFormat('YYYY-MM-DD');
/** @var \Illuminate\Support\Collection $answersByDays */
if (!$answersByDays->has($currentDate)) {
$answersByDays->put(
$currentDate,
$answers->mapWithKeys(function ($answer) {
return [$answer->id => 0];
})
);
} else {
$curentAnswers = $answersByDays->pull($currentDate);
$answersByDays->put(
$currentDate,
$answers->mapWithKeys(function ($answer) use ($curentAnswers) {
return [$answer->id =>
$curentAnswers->has($answer->id) ?
$curentAnswers->get($answer->id) : 0];
})
);
}
$start = $start->addDay();
} while ($start->lessThanOrEqualTo($end));
return $answersByDays;
}
}

View file

@ -11,18 +11,15 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class BrandController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Brand::query();
@ -52,9 +49,9 @@ class BrandController extends Controller
* Show the form for creating a new resource.
*
* @param \App\Models\Brand $brands
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function create(Brand $brands)
public function create(Brand $brands): \Illuminate\Contracts\View\View
{
return view('brands.create', [
'brandModel' => $brands
@ -65,9 +62,9 @@ class BrandController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Brands\StoreBrandRequest $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreBrandRequest $request)
public function store(StoreBrandRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$brand = new Brand();
@ -93,9 +90,9 @@ class BrandController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Brand $brands
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function edit(Brand $brands)
public function edit(Brand $brands): \Illuminate\Contracts\View\View
{
return view('brands.edit', [
'brandModel' => $brands
@ -107,9 +104,9 @@ class BrandController extends Controller
*
* @param \App\Http\Requests\Brands\UpdateBrandRequest $request
* @param \App\Models\Brand $brand
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateBrandRequest $request, Brand $brand)
public function update(UpdateBrandRequest $request, Brand $brand): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $brand) {
$brand->fill($request->validated());
@ -133,12 +130,12 @@ class BrandController extends Controller
/**
* Store a newly created resource in storage.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
*/
public function jsonPaginate()
public function jsonPaginate(): \Illuminate\Http\JsonResponse
{
return DB::transaction(function () {
/** @var \App\Models\Tag */
/** @var \App\Models\Brand */
$brandModels = Brand::paginate(12);
return \response()->json($brandModels ?? 500);
});

View file

@ -10,9 +10,6 @@ use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
/**
* @codeCoverageIgnore
*/
class Controller extends BaseController
{
use AuthorizesRequests;
@ -21,7 +18,7 @@ class Controller extends BaseController
/**
* Build Search query on specified fields
* splitting search into words or using whole sentence
* splitting search into words or using whole sentence.
*
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
* @param string $search The query string.
@ -36,7 +33,7 @@ class Controller extends BaseController
string $search,
callable $searchQuery = null,
string ...$fields
): Builder {
): \Illuminate\Database\Eloquent\Builder {
if (count($fields)) {
// Search using words.
$words = explode(' ', $search);
@ -58,15 +55,18 @@ class Controller extends BaseController
}
/**
* Build query search on all fields
* Build query search on all fields.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param array $fields
* @return \Illuminate\Database\Eloquent\Builder
*/
private function searchOnFields(Builder $query, string $search, array $fields): Builder
{
private function searchOnFields(
Builder $query,
string $search,
array $fields
): \Illuminate\Database\Eloquent\Builder {
$table = $query->getModel()->getTable();
foreach ($fields as $f) {
$f = str_replace('?', '\?', $f);
@ -88,13 +88,13 @@ class Controller extends BaseController
}
/**
* Sort columns with a query
* Sort columns with a query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array $ignore
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function sortQuery(Builder $query, array $ignore = []): Builder
protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();

View file

@ -20,34 +20,29 @@ use Illuminate\Support\Str;
use League\Csv\Writer;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* @codeCoverageIgnore
*/
class CustomerController extends Controller
{
/**
* View all customers
* View all customers.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
public function index(Request $request): \Illuminate\Contracts\View\View
{
$modelTable = (new Customer())->getTable();
$modelClass = Customer::class;
$brands = Brand::select('id', 'name')->get();
$brands = Brand::query()->select('id', 'name')->get();
$columns = ['customer_infos.lastname', 'customer_infos.firstname'];
// * Jointure pour pouvoir trier les colonnes.
/** @var \Illuminate\Database\Eloquent\Builder */
$query = CustomerInfo::join('customer_logins', function (JoinClause $join) {
$query = CustomerInfo::query()->join('customer_logins', function (JoinClause $join) {
$join->on('customer_logins.brand_id', '=', 'customer_infos.brand_id')
->on('customer_logins.customer_id', '=', 'customer_infos.customer_id');
})
->join('customers', function (JoinClause $join) {
->on('customer_logins.customer_id', '=', 'customer_infos.customer_id');
})->join('customers', function (JoinClause $join) {
$join->on('customers.id', '=', 'customer_infos.customer_id');
})
->join('brands', function (JoinClause $join) {
})->join('brands', function (JoinClause $join) {
$join->on('brands.id', '=', 'customer_infos.brand_id');
});
@ -89,15 +84,15 @@ class CustomerController extends Controller
Session::has("crud.$modelTable.sort_col") and
Session::has("crud.$modelTable.sort_way") and
(Schema::hasColumn($modelTable, Session::get("crud.$modelTable.sort_col")) or
in_array(Session::get("crud.$modelTable.sort_col"), [
'brands.name',
'customers.email',
'customer_infos.firstname',
'customer_infos.lastname',
'customer_infos.opt_in',
'customer_logins.last_connection',
'customer_logins.status'
]))
in_array(Session::get("crud.$modelTable.sort_col"), [
'brands.name',
'customers.email',
'customer_infos.firstname',
'customer_infos.lastname',
'customer_infos.opt_in',
'customer_logins.last_connection',
'customer_logins.status'
]))
) {
$query = $query->orderBy(
Session::get("crud.$modelTable.sort_col"),
@ -120,17 +115,17 @@ class CustomerController extends Controller
}
/**
* Show a specific custmer
* Show a specific custmer.
*
* @param \App\Models\Customer $customer
* @param \App\Models\Brand $brand
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function show(Customer $customer, Brand $brand)
public function show(Customer $customer, Brand $brand): \Illuminate\Contracts\View\View
{
$customerInfos = CustomerInfo::where('customer_id', $customer->id)
$customerInfos = CustomerInfo::query()->where('customer_id', $customer->id)
->where('brand_id', $brand->id)->firstOrFail();
$customerLogin = CustomerLogin::where('customer_id', $customer->id)
$customerLogin = CustomerLogin::query()->where('customer_id', $customer->id)
->where('brand_id', $brand->id)->first();
return view('customers.show', compact(
@ -140,13 +135,13 @@ class CustomerController extends Controller
}
/**
* Delete a specific customer info
* Delete a specific customer info.
*
* @param \App\Models\Customer $customer
* @param \App\Models\Brand $brand
* @return \Illuminate\Http\RedirectResponse
*/
public function delete(Customer $customer, Brand $brand)
public function delete(Customer $customer, Brand $brand): \Illuminate\Http\RedirectResponse
{
try {
// TODO: Gestion avec mesBR.
@ -185,18 +180,18 @@ class CustomerController extends Controller
});
} catch (\ErrorException $e) {
return redirect()->route('customers.index')
->with('error', trans('Le compte n\'a pas pu être supprimé.'));
->with('error', trans('Le compte n\'a pas pu être supprimé.'));
} //end try
return redirect()->route('customers.index')->with('warning', trans('Compte supprimé.'));
}
/**
* Show export the customers form
* Show export the customers form.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function export()
public function export(): \Illuminate\Contracts\View\View
{
$brands = Brand::all();
return view('customers.export', compact(
@ -205,13 +200,13 @@ class CustomerController extends Controller
}
/**
* Exports customers following the requested export type
* Exports customers following the requested export type.
*
* @param \App\Http\Requests\CustomerExport $request
* @return \Symfony\Component\HttpFoundation\StreamedResponse
* @throws \Exception If the request export type in not handled.
*/
public function exportSubmit(CustomerExport $request)
public function exportSubmit(CustomerExport $request): \Symfony\Component\HttpFoundation\StreamedResponse
{
$pagination = 2000;
$header = '"lastname";"firstname";"email";"hashcode"';
@ -233,7 +228,7 @@ class CustomerController extends Controller
sprintf(
'attachment; filename="Export pour newsletter %s %s.csv"',
Carbon::now()->isoFormat('DD-MM-YYYY_HH-mm'),
Str::slug($exportType)
Str::slug($exportType->label())
)
);
@ -244,8 +239,8 @@ class CustomerController extends Controller
case CustomerExportType::customer:
$page = 1;
/** @var \Illuminate\Database\Eloquent\Builder $req */
$req = CustomerInfo::where('brand_id', $request->brand)
->where('opt_in', true);
$req = CustomerInfo::query()->where('brand_id', $request->brand)
->where('opt_in', true);
$total = $req->count();
$pages = \ceil($total / $pagination);
do {
@ -284,7 +279,7 @@ class CustomerController extends Controller
});
break;
default:
throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label));
throw new \Exception(sprintf('Unhandled CustomerExportType %s', $exportType->label()));
} //end switch
/*

View file

@ -5,28 +5,25 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* @codeCoverageIgnore
*/
class LoginController extends Controller
{
/**
* Shows the login view
* Shows the login view.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function index()
public function index(): \Illuminate\Contracts\View\View
{
return view('login');
}
/**
* Undocumented function
* Authenticate specific user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function authenticate(Request $request)
public function authenticate(Request $request): \Illuminate\Http\RedirectResponse
{
if (
Auth::attempt(
@ -36,17 +33,17 @@ class LoginController extends Controller
) {
return redirect()->route('customers.index');
} else {
return back()->withInput($request->except("password"))
return redirect()->back()->withInput($request->except("password"))
->withErrors("Les identifiants entrés sont incorrects, veuillez réessayer.");
}
}
/**
* Undocumented function
* Logout user connected.
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function logout()
public function logout(): \Illuminate\Http\RedirectResponse
{
Auth::logout();
return redirect()->route('login');

View file

@ -15,24 +15,21 @@ use Illuminate\Support\Str;
use League\Csv\Writer;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* @codeCoverageIgnore
*/
class QuestionController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Question::query();
if ($request->brand) {
$query = $query->where('brand_id', '=', request()->brand);
$query = $query->where('brand_id', '=', $request->brand);
}
if ($request->search) {
$this->searchQuery(
@ -56,11 +53,11 @@ class QuestionController extends Controller
* Show the form for creating a new resource.
*
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function create(Question $question)
public function create(Question $question): \Illuminate\Contracts\View\View
{
$brandModels = Brand::get();
$brandModels = Brand::all();
return view('questions.create', [
'questionModel' => $question,
@ -72,9 +69,9 @@ class QuestionController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Questions\StoreQuestionRequest $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreQuestionRequest $request)
public function store(StoreQuestionRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$question = new Question();
@ -100,11 +97,11 @@ class QuestionController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function edit(Question $question)
public function edit(Question $question): \Illuminate\Contracts\View\View
{
$brandModels = Brand::get();
$brandModels = Brand::all();
return view('questions.edit', [
'questionModel' => $question,
@ -117,9 +114,9 @@ class QuestionController extends Controller
*
* @param \App\Http\Requests\Questions\UpdateQuestionRequest $request
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateQuestionRequest $request, Question $question)
public function update(UpdateQuestionRequest $request, Question $question): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $question) {
$question->fill($request->validated());
@ -144,9 +141,9 @@ class QuestionController extends Controller
* Show the specified resource.
*
* @param \App\Models\Question $question
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function show(Question $question)
public function show(Question $question): \Illuminate\Contracts\View\View
{
return view('questions.show', [
'questionModel' => $question
@ -178,9 +175,9 @@ class QuestionController extends Controller
/**
* Shows the question export form
*
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function export()
public function export(): \Illuminate\Contracts\View\View
{
$brands = Brand::all();
return view('questions.export', compact('brands'));
@ -192,10 +189,11 @@ class QuestionController extends Controller
* @param \App\Http\Requests\QuestionExport $request
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function exportSubmit(QuestionExport $request)
public function exportSubmit(QuestionExport $request): \Symfony\Component\HttpFoundation\StreamedResponse
{
$results = [];
$brand = Brand::findOrFail($request->brand);
$results = [];
/** @var \App\Models\Brand $brand */
$brand = Brand::query()->where('id', $request->brand)->firstOrFail();
$questions = $brand->questions->load('answers');
foreach ($questions as $question) {

View file

@ -14,24 +14,21 @@ use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class ServiceController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = Service::query();
if ($request->brand) {
$query = $query->where('brand_id', '=', request()->brand);
$query = $query->where('brand_id', '=', $request->brand);
}
if ($request->search) {
$this->searchQuery(
@ -54,11 +51,11 @@ class ServiceController extends Controller
* Show the form for creating a new resource.
*
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function create(Service $service)
public function create(Service $service): \Illuminate\Contracts\View\View
{
$brandModels = Brand::get();
$brandModels = Brand::all();
return view('services.create', [
'serviceModel' => $service,
@ -70,9 +67,9 @@ class ServiceController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Services\StoreServiceRequest $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreServiceRequest $request)
public function store(StoreServiceRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$service = new Service();
@ -100,11 +97,11 @@ class ServiceController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function edit(Service $service)
public function edit(Service $service): \Illuminate\Contracts\View\View
{
$brandModels = Brand::get();
$brandModels = Brand::all();
return view('services.edit', [
'serviceModel' => $service,
@ -117,9 +114,9 @@ class ServiceController extends Controller
*
* @param \App\Http\Requests\Services\UpdateServiceRequest $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function update(UpdateServiceRequest $request, Service $service)
public function update(UpdateServiceRequest $request, Service $service): \Illuminate\Contracts\View\View
{
return DB::transaction(function () use ($request, $service) {
$service->fill($request->validated());
@ -144,9 +141,9 @@ class ServiceController extends Controller
* Show the specified resource.
*
* @param \App\Models\Service $service
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function show(Service $service)
public function show(Service $service): \Illuminate\Contracts\View\View
{
return view('services.show', [
'serviceModel' => $service
@ -176,15 +173,17 @@ class ServiceController extends Controller
}
/**
* Regen service credentials
* Regen service credentials.
*
* @param \App\Http\Requests\GenerateServiceToken $request
* @param \App\Models\Service $service
* @return \Illuminate\Http\RedirectResponse
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed, Squiz.Commenting.FunctionComment.Missing
public function generateServicePassword(Service $service, GenerateServiceToken $request = null)
{
public function generateServicePassword(
Service $service,
GenerateServiceToken $request = null
): \Illuminate\Http\RedirectResponse {
/** @var \App\Models\User $user */
$user = auth()->user();

View file

@ -7,25 +7,22 @@ use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
/**
* @codeCoverageIgnore
*/
class SingleOptinController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = SingleOptin::query();
if ($request->brand) {
$query = $query->whereHas('brand', function (Builder $q) {
$q->where('id', request()->brand);
$query = $query->whereHas('brand', function (Builder $q) use ($request) {
$q->where('id', $request->brand);
});
}
if ($request->search) {

View file

@ -13,13 +13,10 @@ use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Schema;
use League\Csv\Writer;
/**
* @codeCoverageIgnore
*/
class StatsController extends Controller
{
/**
* Shows stats by brand
* Shows stats by brand.
*
* @param \App\Http\Requests\StatsByBrandRequest $request
* @return \Illuminate\Http\Response
@ -28,7 +25,7 @@ class StatsController extends Controller
{
$stats = null;
if ($request->post()) {
$stats = BrandStat::whereDate('created_at', '>=', $request->start)
$stats = BrandStat::query()->whereDate('created_at', '>=', $request->start)
->whereDate('created_at', '<=', $request->end)
->whereBrandId($request->brand)->get();
}
@ -36,15 +33,15 @@ class StatsController extends Controller
}
/**
* Exports stats by brand as a csv file
* Exports stats by brand as a csv file.
*
* @param \App\Http\Requests\StatsByBrandRequest $request
* @return \Illuminate\Http\Response
*/
public function exportByBrandCsv(StatsByBrandRequest $request)
public function exportByBrandCsv(StatsByBrandRequest $request): \Illuminate\Http\Response
{
$brand = Brand::findOrFail($request->brand);
$stats = BrandStat::with('brand')->whereDate('created_at', '>=', $request->start)
$brand = Brand::query()->findOrFail($request->brand);
$stats = BrandStat::query()->with('brand')->whereDate('created_at', '>=', $request->start)
->whereDate('created_at', '<=', $request->end)
->whereBrandId($request->brand)->get();
@ -80,18 +77,18 @@ class StatsController extends Controller
}
/**
* Views stats for a specific question
* Views stats for a specific question.
*
* @param \App\Http\Requests\StatsByQuestionRequest $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function viewByQuestion(StatsByQuestionRequest $request)
public function viewByQuestion(StatsByQuestionRequest $request): \Illuminate\Contracts\View\View
{
$question = null;
$answers = null;
$answersByDays = null;
if ($request->post()) {
$question = Question::findOrFail($request->question);
$question = Question::query()->findOrFail($request->question);
$answers = $question->answers;
$start = Carbon::parse($request->start);
$end = Carbon::parse($request->end);
@ -103,14 +100,14 @@ class StatsController extends Controller
}
/**
* Exports stats for a specific question as csv
* Exports stats for a specific question as csv.
*
* @param \App\Http\Requests\StatsByQuestionRequest $request
* @return \Illuminate\Http\Response
*/
public function exportByQuestionCsv(StatsByQuestionRequest $request)
public function exportByQuestionCsv(StatsByQuestionRequest $request): \Illuminate\Http\Response
{
$question = Question::findOrFail($request->question);
$question = Question::query()->findOrFail($request->question);
$answers = $question->answers;
$start = Carbon::parse($request->start);
$end = Carbon::parse($request->end);
@ -144,7 +141,7 @@ class StatsController extends Controller
}
/**
* Makes a collection of total users answers by day
* Makes a collection of total users answers by day.
*
* @param \Illuminate\Support\Collection $answers A collection of Answer.
* @param \Illuminate\Support\Carbon $start The wanted start period.
@ -152,16 +149,16 @@ class StatsController extends Controller
* @return \Illuminate\Support\Collection A collection with date as Y_m_d index
* with array of answers and their total.
*/
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): Collection
private function answersByDays(Collection $answers, Carbon $start, Carbon $end): \Illuminate\Support\Collection
{
// * Totalize by date all answers.
$answersByDays = CustomerAnswer::whereDate('created_at', '>=', $start)
$answersByDays = CustomerAnswer::query()->whereDate('created_at', '>=', $start)
->whereDate('created_at', '<=', $end)
->whereIn('answer_id', $answers->pluck('id'))
->get()->map(function ($custAnswer) {
return [
'answer_id' => $custAnswer->answer_id,
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
'created_at' => $custAnswer->created_at->isoFormat('YYYY-MM-DD'),
];
})->groupBy(['created_at', 'answer_id'])->map(function ($day) {
return $day->map(function ($custAnswers) {

View file

@ -55,15 +55,14 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'check-brand' => \App\Http\Middleware\CheckBrand::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}

View file

@ -3,7 +3,7 @@
namespace App\Http\Middleware;
use App\Enums\ApiError;
use App\Exceptions\Api\V2\ApiErrorException;
use App\Exceptions\Api\V4\ApiErrorException;
use App\Lib\Helper;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
@ -21,13 +21,13 @@ class ApiCheckTimeStamps
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @throws \App\Exceptions\Api\V2\ApiErrorException With ApiError::wrongTimeStamp
* @throws \App\Exceptions\Api\V4\ApiErrorException With ApiError::wrongTimeStamp
* if timestamp has an incorrect
* format or is timed out.
*/
public function handle(Request $request, \Closure $next)
public function handle(Request $request, \Closure $next): mixed
{
if (Helper::isRequestForApiV2($request)) {
if (Helper::isRequestForApiV2($request) || Helper::isRequestForApiV4($request)) {
if (!\is_string($request->timestamp) or !Carbon::hasFormat($request->timestamp, 'Y-m-d H:i:s')) {
throw new ApiErrorException(ApiError::wrongTimeStamp);
}

View file

@ -4,6 +4,8 @@ namespace App\Http\Middleware;
use App\Enums\ApiStatus;
use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
use App\Http\Resources\Api\V4\ApiResponse as V4ApiResponse;
use App\Lib\Helper;
use App\Models\Service;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@ -18,7 +20,7 @@ class ApiResponse
* @return mixed
* @throws \ErrorException If the received response is not a JsonResponse.
*/
public function handle(Request $request, \Closure $next)
public function handle(Request $request, \Closure $next): mixed
{
/** @var \Illuminate\Http\JsonResponse $response */
$response = $next($request);
@ -40,18 +42,24 @@ class ApiResponse
/** @var object $data */
$data = $response->getData();
// If reponse is already a V2ApiResponse then just return it. This happens if error handler comes to action.
/** Check if it's V2 or V4 response. */
if (
gettype($data) === "object" and
isset($data->status) and
isset($data->data) and
isset($data->version) and
in_array($data->status, [ApiStatus::OK->value, ApiStatus::KO->value])
in_array($data->status, [ApiStatus::OK->value(), ApiStatus::KO->value()])
) {
/** Return direct ApiResponse */
return $response;
}
/** @var \App\Enums\ApiStatus $status */
$status = intval($response->getStatusCode() / 100) == 2 ? ApiStatus::OK : ApiStatus::KO;
return $response->setData(new V2ApiResponse((object)$response->getData(), $status));
if (Helper::isRequestForApiV2($request)) {
return $response->setData(new V2ApiResponse((object)$data, $status));
}
return $response->setData(new V4ApiResponse((object)$data, $status));
}
}

View file

@ -2,60 +2,30 @@
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
/**
* Updated for ApiV2
* Updated for ApiV4.
*/
class Authenticate extends Middleware
{
/**
* Handle an incoming request.
*
* @param mixed $request
* @param \Closure $next
* @param mixed ...$guards
* @return mixed
*
* @throws \Illuminate\Auth\AuthenticationException If authentification fails.
*/
public function handle(mixed $request, Closure $next, mixed ...$guards)
{
// Use Laravel auth check mecanism.
return \call_user_func_array(
[$this, 'parent::' . __FUNCTION__],
array_merge([$request, $next], $guards)
);
}
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param mixed $request
* @param \Illuminate\Http\Request $request
* @return string|null
* @codeCoverageIgnore
*
*/
protected function redirectTo(mixed $request)
protected function redirectTo(Request $request): ?string
{
$logins = [
'backend' => route('login')
];
if (!$request->expectsJson()) {
// * Notify must login
Session::flash('warning', trans('Veuillez vous connecter pour continuer'));
foreach ($request->route()->gatherMiddleware() as $middleware) {
if (\is_string($middleware) and strpos($middleware, 'auth:') !== false) {
$route = $logins[str_replace('auth:', '', $middleware)] ?? null;
if ($route) {
return $route;
}
}
}
return route('login');
}
abort(401);
return null;
}

View file

@ -1,40 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Lib\Helper;
use App\Models\Service;
use Closure;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
/**
* @codeCoverageIgnore
*/
class CheckBrand
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$apiToken = Helper::getBearerTokenFromRequest($request) ?? $request->api_token ?? '';
if ($apiToken === null) {
abort(401, 'Le token d\'authentification est incorrect ' . \json_encode($request->all()));
}
try {
$brand = Service::getFromBearerToken($apiToken)->brand;
$request->request->add(['brand_id' => $brand->id]);
} catch (ModelNotFoundException $e) {
abort(401, 'Le token d\'authentification est incorrect ' . $e->getMessage());
}
return $next($request);
}
}

View file

@ -13,7 +13,7 @@ class JsonOnly
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, \Closure $next)
public function handle(Request $request, \Closure $next): mixed
{
if (
(!$request->isJson() or

View file

@ -7,9 +7,6 @@ use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
/**
* @codeCoverageIgnore
*/
class RedirectIfAuthenticated
{
/**
@ -20,7 +17,7 @@ class RedirectIfAuthenticated
* @param string|null ...$guards
* @return mixed
*/
public function handle(Request $request, Closure $next, ...$guards)
public function handle(Request $request, Closure $next, ...$guards): mixed
{
$guards = empty($guards) ? [null] : $guards;

View file

@ -7,7 +7,6 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;
class SecureHeaders
{
@ -31,7 +30,7 @@ class SecureHeaders
* @return \Symfony\Component\HttpFoundation\Response
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function handle(Request $request, Closure $next): Response
public function handle(Request $request, Closure $next): \Symfony\Component\HttpFoundation\Response
{
// phpcs:enable
View::share('nonce', $nonce = Vite::useCspNonce());
@ -71,7 +70,7 @@ class SecureHeaders
* @param array $headerList
* @return void
*/
private function removeUnwantedHeaders(array $headerList)
private function removeUnwantedHeaders(array $headerList): void
{
foreach ($headerList as $header) {
header_remove($header);

View file

@ -4,9 +4,6 @@ namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
/**
* @codeCoverageIgnore
*/
class TrustHosts extends Middleware
{
/**
@ -14,10 +11,11 @@ class TrustHosts extends Middleware
*
* @return array
*/
public function hosts()
public function hosts(): array
{
return [
$this->allSubdomainsOfApplicationUrl(),
'172.18.0.6'
];
}
}

View file

@ -13,7 +13,7 @@ class XssSanitizer
* @param \Closure $next
* @return mixed
*/
public function handle(mixed $request, Closure $next)
public function handle(mixed $request, Closure $next): mixed
{
$input = $request->all();
array_walk_recursive($input, function (&$input) {

View file

@ -2,6 +2,9 @@
namespace App\Http\Requests\Api\V2;
/**
* @codeCoverageIgnore
*/
class AuthenticateRequest extends BaseApiRequest
{
/**
@ -9,7 +12,7 @@ class AuthenticateRequest extends BaseApiRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return true;
}

View file

@ -11,12 +11,16 @@ use ArondeParon\RequestSanitizer\Sanitizers\TrimDuplicateSpaces;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
use Illuminate\Foundation\Http\FormRequest;
/**
* @codeCoverageIgnore
*/
abstract class BaseApiRequest extends FormRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request
* Using these sanitizers before proceeding the request.
*
* @var array
*/
protected $sanitizers = [
@ -36,7 +40,7 @@ abstract class BaseApiRequest extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}

View file

@ -6,6 +6,9 @@ use App\Http\Requests\Api\V2\BaseApiRequest;
use Illuminate\Support\Carbon;
use Illuminate\Validation\ValidationException;
/**
* @codeCoverageIgnore
*/
class GetBrandStatsRequest extends BaseApiRequest
{
/**

View file

@ -7,6 +7,9 @@ use Exception;
use Illuminate\Support\Carbon;
use Illuminate\Validation\ValidationException;
/**
* @codeCoverageIgnore
*/
class GetQuestionStatsRequest extends BaseApiRequest
{
/**

View file

@ -9,12 +9,16 @@ use ArondeParon\RequestSanitizer\Sanitizers\Trim;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
use Illuminate\Validation\Rules\Enum;
/**
* @codeCoverageIgnore
*/
class SendArchadiaDefaultMailRequest extends BaseApiRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request
* Using these sanitizers before proceeding the request.
*
* @var array
*/
protected $sanitizers = [
@ -65,11 +69,11 @@ class SendArchadiaDefaultMailRequest extends BaseApiRequest
}
/**
* Attributes
* Attributes.
*
* @return array
*/
public function attributes()
public function attributes(): array
{
return [
'label_type_support' => trans('label type support'),
@ -92,11 +96,11 @@ class SendArchadiaDefaultMailRequest extends BaseApiRequest
}
/**
* Messages
* Messages.
*
* @return array
*/
public function messages()
public function messages(): array
{
return [
'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros

View file

@ -6,12 +6,16 @@ use App\Http\Sanitizers\RemoveSpaces;
use ArondeParon\RequestSanitizer\Sanitizers\Trim;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
/**
* @codeCoverageIgnore
*/
class SendArchadiaGameMailRequest extends BaseApiRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request
* Using these sanitizers before proceeding the request.
*
* @var array
*/
protected $sanitizers = [
@ -59,11 +63,11 @@ class SendArchadiaGameMailRequest extends BaseApiRequest
}
/**
* Attributes
* Attributes.
*
* @return array
*/
public function attributes()
public function attributes(): array
{
return [
'code_on_pack' => trans('code ean emballage'),
@ -85,11 +89,11 @@ class SendArchadiaGameMailRequest extends BaseApiRequest
}
/**
* Messages
* Messages.
*
* @return array
*/
public function messages()
public function messages(): array
{
return [
'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte

View file

@ -2,6 +2,9 @@
namespace App\Http\Requests\Api\V2;
/**
* @codeCoverageIgnore
*/
class SingleOptinRemoveWithHashcodeRequest extends BaseApiRequest
{
/**

View file

@ -2,6 +2,9 @@
namespace App\Http\Requests\Api\V2;
/**
* @codeCoverageIgnore
*/
class SingleOptinRequest extends BaseApiRequest
{
/**

View file

@ -4,7 +4,11 @@ namespace App\Http\Requests\Api\V2;
use App\Models\Answer;
use App\Models\Question;
use App\Models\Service;
/**
* @codeCoverageIgnore
*/
class StoreCustomerAnswers extends BaseApiRequest
{
/**
@ -15,9 +19,9 @@ class StoreCustomerAnswers extends BaseApiRequest
public function rules(): array
{
// * Resolve Service.
$service = app()->make(\App\Models\Service::class);
$service = app()->make(Service::class);
$brandId = $service->brand->id;
$question = Question::findOrFail($this->question);
$question = Question::query()->findOrFail($this->question);
$questionTableName = (new Question())->getTable();
$answerTableName = (new Answer())->getTable();
return [
@ -37,7 +41,7 @@ class StoreCustomerAnswers extends BaseApiRequest
'distinct',
"exists:$answerTableName,id",
function ($attribute, $value, $fail) use ($question) {
if (Answer::findOrFail($value)->question->id !== $question->id) {
if (Answer::query()->findOrFail($value)->question->id !== $question->id) {
$fail("$attribute does not belong to question $question->id");
}
}

View file

@ -6,6 +6,9 @@ use App\Enums\Country;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;
/**
* @codeCoverageIgnore
*/
class StoreCustomerRequest extends BaseApiRequest
{
/**
@ -15,6 +18,21 @@ class StoreCustomerRequest extends BaseApiRequest
*/
protected $currentCountry = null;
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
try {
$this->currentCountry = Country::make(\intval($this->country));
} catch (\TypeError | \BadMethodCallException $e) {
$this->currentCountry = null;
}
parent::prepareForValidation();
}
/**
* Get the validation rules that apply to the request.
*
@ -22,6 +40,8 @@ class StoreCustomerRequest extends BaseApiRequest
*/
public function rules(): array
{
$countryList = ($this->currentCountry && $this->currentCountry->get('iso')) ?
$this->currentCountry->get('iso') : implode(',', Country::getValidIsoAplha2Codes());
return [
'firstname' => 'required|string|min:1|max:255',
'lastname' => 'required|string|min:1|max:255',
@ -39,8 +59,7 @@ class StoreCustomerRequest extends BaseApiRequest
'birth_date' => 'required|date_format:Y-m-d|max:255',
'address' => 'sometimes|required_with:address2|string|max:255',
'address2' => 'sometimes|string|max:255',
'phone' => ['sometimes', 'string', 'phone:' . ($this->currentCountry ?
$this->currentCountry->iso : implode(',', Country::getValidIsoAplha2Codes()))],
'phone' => ['sometimes', 'string', "phone:{$countryList}"],
'country' => [
'sometimes',
'numeric',
@ -51,35 +70,17 @@ class StoreCustomerRequest extends BaseApiRequest
)
],
'city' => 'sometimes|string|min:2|max:255',
'postcode' => ['sometimes', 'string', 'postal_code:' . ($this->currentCountry ?
$this->currentCountry->iso : implode(',', Country::getValidIsoAplha2Codes()))],
'postcode' => ['sometimes', 'string', "postal_code:{$countryList}"],
'opt_in' => 'required|boolean',
];
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
try {
if (is_array($this->country)) {
$this->currentCountry = Country::make(\intval($this->country));
}
} catch (\TypeError | \BadMethodCallException $e) {
$this->currentCountry = null;
}
parent::prepareForValidation();
}
/**
* Messages
* Messages.
*
* @return array
*/
public function messages()
public function messages(): array
{
return [
'phone.phone' => trans(

View file

@ -6,6 +6,9 @@ use App\Enums\Country;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;
/**
* @codeCoverageIgnore
*/
class UpdateCustomerRequest extends StoreCustomerRequest
{
/**
@ -41,8 +44,10 @@ class UpdateCustomerRequest extends StoreCustomerRequest
'birth_date' => [$atLeast('birth_date'), 'date_format:Y-m-d', 'max:255'],
'address' => [$atLeast('address'), 'required_with:address2', 'string', 'max:255'],
'address2' => ['sometimes', 'string', 'max:255'],
'phone' => [$atLeast('phone'), 'string', 'phone:' . ($this->currentCountry ?
$this->currentCountry->iso : implode(',', Country::getValidIsoAplha2Codes())), 'max:255'],
'phone' => [$atLeast('phone'), 'string', 'phone:' . (
($this->currentCountry && $this->currentCountry->get('iso')) ?
$this->currentCountry->get('iso') : implode(',', Country::getValidIsoAplha2Codes())
), 'max:255'],
'country' => [
$atLeast('country'),
'numeric',
@ -53,8 +58,10 @@ class UpdateCustomerRequest extends StoreCustomerRequest
)
],
'city' => [$atLeast('city'), 'string', 'min:2', 'max:255'],
'postcode' => [$atLeast('postcode'), 'string', 'postal_code:' . ($this->currentCountry ?
$this->currentCountry->iso : implode(',', Country::getValidIsoAplha2Codes())), 'max:255'],
'postcode' => [$atLeast('postcode'), 'string', 'postal_code:' . (
($this->currentCountry && $this->currentCountry->get('iso')) ?
$this->currentCountry->get('iso') : implode(',', Country::getValidIsoAplha2Codes())
), 'max:255'],
'opt_in' => [$atLeast('opt_in'), 'boolean'],
];
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Http\Requests\Api\V4;
class AuthenticateRequest extends BaseApiRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'email' => 'required|email:rfc',
'password' => 'required'
];
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Http\Sanitizers\RemoveSpaces;
use ArondeParon\RequestSanitizer\Sanitizers\Capitalize;
use ArondeParon\RequestSanitizer\Sanitizers\Lowercase;
use ArondeParon\RequestSanitizer\Sanitizers\RemoveNonNumeric;
use ArondeParon\RequestSanitizer\Sanitizers\Trim;
use ArondeParon\RequestSanitizer\Sanitizers\TrimDuplicateSpaces;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
use Illuminate\Foundation\Http\FormRequest;
abstract class BaseApiRequest extends FormRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request.
*
* @var array
*/
protected $sanitizers = [
'firstname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
'lastname' => [Trim::class, TrimDuplicateSpaces::class, Capitalize::class],
'address' => [Trim::class, TrimDuplicateSpaces::class],
'address2' => [Trim::class, TrimDuplicateSpaces::class],
'city' => [Trim::class, TrimDuplicateSpaces::class],
'password' => Trim::class,
'phone' => [Trim::class, RemoveSpaces::class],
'email' => [Lowercase::class, Trim::class, RemoveSpaces::class],
'postcode' => [RemoveNonNumeric::class, Trim::class]
];
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
abstract public function rules(): array;
}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Http\Requests\Api\V4\BaseApiRequest;
use Illuminate\Support\Carbon;
use Illuminate\Validation\ValidationException;
class GetBrandStatsRequest extends BaseApiRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of
* period is bigger than 3 months.
* @throws \Exception If carbon date processing failed.
*/
public function rules(): array
{
try {
if (Carbon::parse($this->end)->floatDiffInMonths(Carbon::parse($this->start)) > 3) {
throw ValidationException::withMessages([
'Vous devez sélectionner une période de maximum 3 mois'
]);
}
} catch (\Exception $e) {
if (\get_class($e) !== ValidationException::class) {
throw ValidationException::withMessages([
'Les dates séléectionnées ne sembles pas être valides'
]);
}
throw $e;
}
return [
'start' => 'required|date|date_format:Y-m-d',
'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
];
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Http\Requests\Api\V4\BaseApiRequest;
use Illuminate\Support\Carbon;
use Illuminate\Validation\ValidationException;
class GetQuestionStatsRequest extends BaseApiRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of
* period is bigger than 3 months.
* @throws \Exception If carbon date processing failed.
*/
public function rules(): array
{
try {
if (Carbon::parse($this->end)->floatDiffInMonths(Carbon::parse($this->start)) > 3) {
throw ValidationException::withMessages([
'Vous devez sélectionner une période de maximum 3 mois'
]);
}
} catch (\Exception $e) {
if (\get_class($e) !== ValidationException::class) {
throw ValidationException::withMessages([
'Les dates sélectionnées ne sembles pas être valides'
]);
}
throw $e;
}
return [
'start' => 'required|date|date_format:Y-m-d',
'end' => 'required|date|date_format:Y-m-d|after_or_equal:start',
];
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Enums\ArcadiaOrderType;
use App\Http\Sanitizers\RemoveSpaces;
use ArondeParon\RequestSanitizer\Sanitizers\RemoveNonNumeric;
use ArondeParon\RequestSanitizer\Sanitizers\Trim;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
use Illuminate\Validation\Rules\Enum;
class SendArchadiaDefaultMailRequest extends BaseApiRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request.
*
* @var array
*/
protected $sanitizers = [
'label_type_support' => Trim::class,
'operation' => Trim::class,
'order_date' => Trim::class,
'order_num' => RemoveNonNumeric::class,
'package_unit_weight' => RemoveNonNumeric::class,
'support_code' => Trim::class,
'deliver.firstname' => Trim::class,
'deliver.lastname' => Trim::class,
'deliver.email' => Trim::class,
'deliver.address' => Trim::class,
'deliver.address2' => Trim::class,
'deliver.city' => Trim::class,
'deliver.postcode' => Trim::class,
'deliver.phone' => [Trim::class, RemoveSpaces::class]
];
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'label_type_support' => 'required|string',
'operation' => 'required|int',
'order_date' => 'required|date',
'order_num' => 'required|numeric',
'package_unit_weight' => 'required|integer',
'state_order' => [
'required',
new Enum(ArcadiaOrderType::class)
],
'support_code' => 'required|int',
'deliver' => 'required|array',
'deliver.firstname' => 'required|string|min:1|max:255',
'deliver.lastname' => 'required|string|min:1|max:255',
'deliver.email' => 'required|email:rfc,dns,spoof,filter|max:255',
'deliver.address' => 'required|string|min:2|max:255',
'deliver.address2' => 'sometimes|string|min:2|max:255',
'deliver.city' => 'required|string|min:2|max:255',
'deliver.postcode' => 'required|string|postal_code:FR|max:255',
'deliver.phone' => 'present|string|nullable|phone:FR|max:255',
];
}
/**
* Attributes.
*
* @return array
*/
public function attributes(): array
{
return [
'label_type_support' => trans('label type support'),
'operation' => trans('code opération'),
'order_date' => trans('date de la commande'),
'order_num' => trans('numero de commande'),
'package_unit_weight' => trans('poids unitaire du paquet'),
'state_order' => trans('type de commande arcadia'),
'support_code' => trans('code de support'),
'deliver' => trans('coordonées de livraison'),
'deliver.civility' => trans('civilitée dans coordonées de livraison'),
'deliver.firstname' => trans('prénom dans coordonées de livraison'),
'deliver.lastname' => trans('nom dans coordonées de livraison'),
'deliver.address' => trans('adresse dans coordonées de livraison'),
'deliver.postcode' => trans('code postal dans coordonées de livraison'),
'deliver.phone' => trans('téléphone dans coordonées de livraison'),
'deliver.city' => trans('ville dans coordonées de livraison'),
'deliver.email' => trans('e-mail dans coordonées de livraison'),
];
}
/**
* Messages.
*
* @return array
*/
public function messages(): array
{
return [
'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte les numéros
des pays suivants: France.'),
'deliver.postcode.postal_code' => trans('Le champ :attribute accepte les codes postaux des pays
suivants :countries comme par exemple :examples.'),
];
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Http\Sanitizers\RemoveSpaces;
use ArondeParon\RequestSanitizer\Sanitizers\Trim;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
class SendArchadiaGameMailRequest extends BaseApiRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request.
*
* @var array
*/
protected $sanitizers = [
'code_on_pack' => Trim::class,
'code_unique' => Trim::class,
'label_type_support' => Trim::class,
'operation' => Trim::class,
'order_date' => Trim::class,
'support_code' => Trim::class,
'type' => Trim::class,
'deliver.firstname' => Trim::class,
'deliver.lastname' => Trim::class,
'deliver.email' => Trim::class,
'deliver.address' => Trim::class,
'deliver.address2' => Trim::class,
'deliver.city' => Trim::class,
'deliver.postcode' => Trim::class,
'deliver.phone' => [Trim::class, RemoveSpaces::class]
];
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'code_on_pack' => 'sometimes|string|max:50',
'code_unique' => 'required|string',
'label_type_support' => 'required|string',
'operation' => 'required|int',
'order_date' => 'required|date',
'support_code' => 'required|int',
'deliver' => 'required|array',
'deliver.firstname' => 'required|string|min:1|max:255',
'deliver.lastname' => 'required|string|min:1|max:255',
'deliver.email' => 'required|email:rfc,dns,spoof,filter|max:255',
'deliver.address' => 'required|string|min:2|max:255',
'deliver.address2' => 'sometimes|string|min:2|max:255',
'deliver.city' => 'required|string|min:2|max:255',
'deliver.postcode' => 'required|string|postal_code:FR|max:255',
'deliver.phone' => 'present|string|nullable|phone:FR|max:255',
];
}
/**
* Attributes.
*
* @return array
*/
public function attributes(): array
{
return [
'code_on_pack' => trans('code ean emballage'),
'code_unique' => trans('code unique'),
'label_type_support' => trans('label type support'),
'operation' => trans('code opération'),
'order_date' => trans('date de la commande'),
'support_code' => trans('code de support'),
'deliver' => trans('coordonées de livraison'),
'deliver.civility' => trans('civilitée dans coordonées de livraison'),
'deliver.firstname' => trans('prénom dans coordonées de livraison'),
'deliver.lastname' => trans('nom dans coordonées de livraison'),
'deliver.address' => trans('adresse dans coordonées de livraison'),
'deliver.postcode' => trans('code postal dans coordonées de livraison'),
'deliver.phone' => trans('téléphone dans coordonées de livraison'),
'deliver.city' => trans('ville dans coordonées de livraison'),
'deliver.email' => trans('e-mail dans coordonées de livraison'),
];
}
/**
* Messages.
*
* @return array
*/
public function messages(): array
{
return [
'deliver.phone.phone' => trans('Le champ :attribute est invalide, il accepte
les numéros des pays suivants: France.'),
'deliver.postcode.postal_code' => trans('Le champ :attribute accepte les codes postaux
des pays suivants :countries comme par exemple :examples.'),
];
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Http\Requests\Api\V4;
class SingleOptinRemoveWithHashcodeRequest extends BaseApiRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'hashcode' => 'required|string|size:36'
];
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Requests\Api\V4;
class SingleOptinRequest extends BaseApiRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'email' => 'required|email:rfc,dns,spoof,filter',
'opt_in' => 'required|boolean'
];
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Models\Answer;
use App\Models\Question;
use App\Models\Service;
class StoreCustomerAnswers extends BaseApiRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
// * Resolve Service.
$service = app()->make(Service::class);
$brandId = $service->brand->id;
$question = Question::query()->findOrFail($this->question);
$questionTableName = (new Question())->getTable();
$answerTableName = (new Answer())->getTable();
return [
'question' => [
'required',
"exists:$questionTableName,id",
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) use ($question, $brandId) {
if ($question->brand->id !== $brandId) {
$fail("$attribute does not belong to brand $brandId");
}
}
],
'answers' => 'required|array',
'answers.*' => [
'int',
'distinct',
"exists:$answerTableName,id",
function ($attribute, $value, $fail) use ($question) {
if (Answer::query()->findOrFail($value)->question->id !== $question->id) {
$fail("$attribute does not belong to question $question->id");
}
}
]
];
}
}

View file

@ -0,0 +1,95 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Enums\Country;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;
class StoreCustomerRequest extends BaseApiRequest
{
/**
* Current country
*
* @var \App\Enums\Country|null
*/
protected $currentCountry = null;
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
try {
$this->currentCountry = Country::make(\intval($this->country));
} catch (\TypeError | \BadMethodCallException $e) {
$this->currentCountry = null;
}
parent::prepareForValidation();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$countryList = ($this->currentCountry && $this->currentCountry->get('iso')) ?
$this->currentCountry->get('iso') : implode(',', Country::getValidIsoAplha2Codes());
return [
'firstname' => 'required|string|min:1|max:255',
'lastname' => 'required|string|min:1|max:255',
'email' => 'required|email:rfc,dns,spoof,filter|max:255',
'password' => [
'required',
'max:255',
Password::min(8)
->mixedCase()
->letters()
->numbers()
->symbols()
->uncompromised()
],
'birth_date' => 'required|date_format:Y-m-d|max:255',
'address' => 'sometimes|required_with:address2|string|max:255',
'address2' => 'sometimes|string|max:255',
'phone' => ['sometimes', 'string', "phone:{$countryList}"],
'country' => [
'sometimes',
'numeric',
new Enum(Country::class),
sprintf(
'in:%s',
implode(',', Country::getValidCountriesValues())
)
],
'city' => 'sometimes|string|min:2|max:255',
'postcode' => ['sometimes', 'string', "postal_code:{$countryList}"],
'opt_in' => 'required|boolean',
];
}
/**
* Messages.
*
* @return array
*/
public function messages(): array
{
return [
'phone.phone' => trans(
'Le champ :attribute est invalide, il accepte les numéros des pays
suivants :countries.',
['countries' => implode(',', Country::getValidCountriesLabel())]
),
'country.in' => trans(
'Le champ :attribute est invalide, il accepte les pays suivants
:countries.',
['countries' => implode(',', Country::getValidCountriesLabel())]
)
];
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace App\Http\Requests\Api\V4;
use App\Enums\Country;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;
class UpdateCustomerRequest extends StoreCustomerRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$fields = [
'firstname', 'lastname', 'email', 'password',
'birth_date', 'address', 'phone', 'country',
'city', 'postcode', 'opt_in'
];
$atLeast = function ($attribute) use ($fields) {
return 'required_without_all:' . collect($fields)
->filter(function ($fieldName) use ($attribute) {
return $fieldName !== $attribute;
})->implode(',');
};
return [
'firstname' => [$atLeast('firstname'), 'string', 'min:1', 'max:255'],
'lastname' => [$atLeast('lastname'), 'string', 'min:1', 'max:255'],
'email' => [$atLeast('email'), 'email:rfc,dns,spoof,filter', 'max:255'],
'password' => [
$atLeast('password'), 'max:255', Password::min(8)
->mixedCase()
->letters()
->numbers()
->symbols()
->uncompromised()
],
'birth_date' => [$atLeast('birth_date'), 'date_format:Y-m-d', 'max:255'],
'address' => [$atLeast('address'), 'required_with:address2', 'string', 'max:255'],
'address2' => ['sometimes', 'string', 'max:255'],
'phone' => [$atLeast('phone'), 'string', 'phone:' . (
($this->currentCountry && $this->currentCountry->get('iso')) ?
$this->currentCountry->get('iso') : implode(',', Country::getValidIsoAplha2Codes())
), 'max:255'],
'country' => [
$atLeast('country'),
'numeric',
new Enum(Country::class),
sprintf(
'in:%s',
implode(',', Country::getValidCountriesValues())
)
],
'city' => [$atLeast('city'), 'string', 'min:2', 'max:255'],
'postcode' => [$atLeast('postcode'), 'string', 'postal_code:' . (
($this->currentCountry && $this->currentCountry->get('iso')) ?
$this->currentCountry->get('iso') : implode(',', Country::getValidIsoAplha2Codes())
), 'max:255'],
'opt_in' => [$atLeast('opt_in'), 'boolean'],
];
}
}

View file

@ -5,9 +5,6 @@ namespace App\Http\Requests;
use App\Enums\CustomerExportType;
use Illuminate\Foundation\Http\FormRequest;
/**
* @codeCoverageIgnore
*/
class CustomerExport extends FormRequest
{
/**
@ -15,7 +12,7 @@ class CustomerExport extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -25,7 +22,7 @@ class CustomerExport extends FormRequest
*
* @return array
*/
public function rules()
public function rules(): array
{
return [
'brand' => 'required|numeric|exists:brands,id',

View file

@ -4,9 +4,6 @@ namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
/**
* @codeCoverageIgnore
*/
class GenerateServiceToken extends FormRequest
{
/**
@ -14,7 +11,7 @@ class GenerateServiceToken extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -24,7 +21,7 @@ class GenerateServiceToken extends FormRequest
*
* @return array
*/
public function rules()
public function rules(): array
{
return [];
}

View file

@ -6,15 +6,13 @@ use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
use Illuminate\Foundation\Http\FormRequest;
/**
* @codeCoverageIgnore
*/
class ManageBrandRequest extends FormRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request
*
* @var array
*/
protected $sanitizers = [
@ -30,7 +28,7 @@ class ManageBrandRequest extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -40,7 +38,7 @@ class ManageBrandRequest extends FormRequest
*
* @return array
*/
public function rules()
public function rules(): array
{
if ($this->isMethod('post')) {
return [

View file

@ -6,15 +6,13 @@ use ArondeParon\RequestSanitizer\Sanitizers\FilterVars;
use ArondeParon\RequestSanitizer\Traits\SanitizesInputs;
use Illuminate\Foundation\Http\FormRequest;
/**
* @codeCoverageIgnore
*/
class ManageServiceRequest extends FormRequest
{
use SanitizesInputs;
/**
* Using these sanitizers before proceeding the request
*
* @var array
*/
protected $sanitizers = [
@ -30,7 +28,7 @@ class ManageServiceRequest extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -40,7 +38,7 @@ class ManageServiceRequest extends FormRequest
*
* @return array
*/
public function rules()
public function rules(): array
{
if ($this->isMethod('post')) {
return [

View file

@ -4,9 +4,6 @@ namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
/**
* @codeCoverageIgnore
*/
class QuestionExport extends FormRequest
{
/**
@ -14,7 +11,7 @@ class QuestionExport extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -24,7 +21,7 @@ class QuestionExport extends FormRequest
*
* @return array
*/
public function rules()
public function rules(): array
{
return [
'brand' => 'required|numeric|exists:brands,id'

View file

@ -2,14 +2,10 @@
namespace App\Http\Requests;
use Exception;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Carbon;
use Illuminate\Validation\ValidationException;
/**
* @codeCoverageIgnore
*/
class StatsByBrandRequest extends FormRequest
{
/**
@ -17,7 +13,7 @@ class StatsByBrandRequest extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -26,10 +22,11 @@ class StatsByBrandRequest extends FormRequest
* Get the validation rules that apply to the request.
*
* @return array
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of period is bigger than 1 year.
* @throws \Illuminate\Validation\ValidationException If given dates are unparsable of period
* is bigger than 1 year.
* @throws \Exception If carbon date processing failed.
*/
public function rules()
public function rules(): array
{
if ($this->post()) {
try {
@ -38,7 +35,7 @@ class StatsByBrandRequest extends FormRequest
'Vous devez sélectionner une période de maximum 1 ans'
]);
}
} catch (Exception $e) {
} catch (\Exception $e) {
if (\get_class($e) !== ValidationException::class) {
throw ValidationException::withMessages([
'Les dates séléectionnées ne sembles pas être valides'

View file

@ -2,14 +2,10 @@
namespace App\Http\Requests;
use Exception;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Carbon;
use Illuminate\Validation\ValidationException;
/**
* @codeCoverageIgnore
*/
class StatsByQuestionRequest extends FormRequest
{
/**
@ -17,7 +13,7 @@ class StatsByQuestionRequest extends FormRequest
*
* @return boolean
*/
public function authorize()
public function authorize(): bool
{
return auth()->check();
}
@ -30,7 +26,7 @@ class StatsByQuestionRequest extends FormRequest
* is bigger than 1 year.
* @throws \Exception If carbon date processing failed.
*/
public function rules()
public function rules(): array
{
if ($this->post()) {
try {
@ -39,10 +35,10 @@ class StatsByQuestionRequest extends FormRequest
'Vous devez sélectionner une période de maximum 1 ans'
]);
}
} catch (Exception $e) {
} catch (\Exception $e) {
if (\get_class($e) !== ValidationException::class) {
throw ValidationException::withMessages([
'Les dates séléectionnées ne sembles pas être valides'
'Les dates sélectionnées ne sembles pas être valides'
]);
}
throw $e;

View file

@ -3,6 +3,7 @@
namespace App\Http\Resources\Api\V2;
use App\Models\Answer;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
class AnswersResource extends JsonResourceCollection
@ -10,12 +11,13 @@ class AnswersResource extends JsonResourceCollection
/**
* Transform the resource into an array.
*
* @param mixed $request
* @return array
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Support\Collection
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
public function toArray(Request $request): \Illuminate\Support\Collection
{
//phpcs:enable
return $this->collection->map(function (Answer $answer) {
return [
'id' => $answer->id,

View file

@ -3,22 +3,23 @@
namespace App\Http\Resources\Api\V2;
use App\Enums\ApiStatus;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use stdClass;
class ApiResponse extends JsonResource
{
/** @var \stdClass */
/** @var \stdClass|null */
protected $data;
/** @var \App\Enums\ApiStatus */
protected $status;
/**
* @param \stdClass $data
* @param \stdClass|null $data
* @param \App\Enums\ApiStatus $status
*/
public function __construct(stdClass $data = null, ApiStatus $status)
public function __construct(stdClass|null $data = null, ApiStatus $status)
{
$this->status = $status;
$this->data = $data;
@ -27,16 +28,17 @@ class ApiResponse extends JsonResource
/**
* Transform the resource into an array.
*
* @param mixed $request
* @param \Illuminate\Http\Request $request
* @return array
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request): array
public function toArray(Request $request): array
{
//phpcs:enable
return [
'status' => $this->status->value,
'data' => (array)$this->data,
'version' => config('api.version')
'status' => $this->status->value(),
'data' => (array)($this->data ?? []),
'version' => '3.0.6'
];
}
}

View file

@ -3,6 +3,7 @@
namespace App\Http\Resources\Api\V2;
use App\Models\BrandStat;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
class BrandStatsResource extends JsonResourceCollection
@ -10,22 +11,23 @@ class BrandStatsResource extends JsonResourceCollection
/**
* Transform the resource into an array.
*
* @param mixed $request
* @param \Illuminate\Http\Request $request
* @return array
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
public function toArray(Request $request): array
{
//phpcs:enable
return [
'stats' => $this->collection->map(function (BrandStat $brandStat) {
return [
'date' => $brandStat->created_at->isoFormat('YYYY-MM-DD'),
'active' => $brandStat->active,
'inactive' => $brandStat->inactive,
'old_accounts' => $brandStat->old_accounts,
'new_accounts' => $brandStat->new_accounts,
'date' => $brandStat->created_at->isoFormat('YYYY-MM-DD'),
'active' => $brandStat->active,
'inactive' => $brandStat->inactive,
'old_accounts' => $brandStat->old_accounts,
'new_accounts' => $brandStat->new_accounts,
'total_accounts' => $brandStat->total_accounts,
'total_opt_in' => $brandStat->total_opt_in
'total_opt_in' => $brandStat->total_opt_in
];
})
];

View file

@ -19,24 +19,24 @@ class CustomerResource extends JsonResource
/** @var array */
private static $skeleton = [
'hashcode' => '',
'firstname' => '',
'lastname' => '',
'email' => '',
'hashcode' => '',
'firstname' => '',
'lastname' => '',
'email' => '',
'birth_date' => '',
'address' => '',
'address2' => '',
'phone' => '',
'country' => '',
'city' => '',
'postcode' => '',
'opt_in' => '',
'status' => '',
'address' => '',
'address2' => '',
'phone' => '',
'country' => '',
'city' => '',
'postcode' => '',
'opt_in' => '',
'status' => '',
'last_connection' => ''
];
/**
* CustomerResource
* CustomerResource.
*
* @param \App\Models\Brand $brand
* @param \App\Models\Customer $customer
@ -67,10 +67,11 @@ class CustomerResource extends JsonResource
* + hashcode string
* + status boolean
* + last_connection date
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
*/
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request = null)
public function toArray($request = null): array
{
//phpcs:enable
$data = array_intersect_key(
array_merge(
[
@ -83,7 +84,7 @@ class CustomerResource extends JsonResource
$this->getSkeleton()
);
$data['country'] = $this->info->country->value;
$data['country'] = $this->info->country->value();
$data['address2'] = $this->info->address2 ?? '';
return $data;
}

View file

@ -3,6 +3,7 @@
namespace App\Http\Resources\Api\V2;
use App\Models\Question;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
use Illuminate\Support\Collection;
@ -27,12 +28,13 @@ class QuestionStatsResource extends JsonResourceCollection
/**
* Transform the resource into an array.
*
* @param mixed $request
* @param \Illuminate\Http\Request $request
* @return array
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
public function toArray(Request $request): array
{
//phpcs:enable
return [
'answers' => (new AnswersResource($this->question->answers))->toArray($request),
'stats' => $this->collection->toArray()

View file

@ -4,23 +4,26 @@ namespace App\Http\Resources\Api\V2;
use App\Models\Answer;
use App\Models\Question;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
use Illuminate\Support\Collection;
class QuestionsResource extends JsonResourceCollection
{
/** @var \Illuminate\Support\Collection */
/** @var \Illuminate\Support\Collection<\App\Models\CustomerAnswer> */
protected $userAnwsers = null;
/**
* Create a new resource instance.
*
* @param mixed $resource
* @param \Illuminate\Support\Collection $userAnwsers
* @param mixed $resource
* @param \Illuminate\Support\Collection<\App\Models\CustomerAnswer> $userAnwsers
* @return void
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function __construct($resource, Collection $userAnwsers = null)
public function __construct(mixed $resource, Collection $userAnwsers = null)
{
//phpcs:enable
$this->userAnwsers = $userAnwsers;
parent::__construct($resource);
}
@ -28,18 +31,19 @@ class QuestionsResource extends JsonResourceCollection
/**
* Transform the resource into an array.
*
* @param mixed $request
* @return array
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Support\Collection
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
public function toArray(Request $request): \Illuminate\Support\Collection
{
//phpcs:enable
return $this->collection->map(function (Question $question) {
/** @var \Illuminate\Support\Collection */
$answers = $question->answers;
$question = [
'id' => $question->id,
'type' => $question->type->value,
'type' => $question->type->value(),
'question' => $question->question,
'answers' => new AnswersResource($answers)
];

View file

@ -0,0 +1,28 @@
<?php
namespace App\Http\Resources\Api\V4;
use App\Models\Answer;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
class AnswersResource extends JsonResourceCollection
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Support\Collection
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray(Request $request): \Illuminate\Support\Collection
{
//phpcs:enable
return $this->collection->map(function (Answer $answer) {
return [
'id' => $answer->id,
'answer' => $answer->answer
];
});
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Http\Resources\Api\V4;
use App\Enums\ApiStatus;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use stdClass;
class ApiResponse extends JsonResource
{
/** @var \stdClass|null */
protected $data;
/** @var \App\Enums\ApiStatus */
protected $status;
/**
* @param \stdClass|null $data
* @param \App\Enums\ApiStatus $status
*/
public function __construct(stdClass|null $data = null, ApiStatus $status)
{
$this->status = $status;
$this->data = $data;
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray(Request $request): array
{
//phpcs:enable
return [
'status' => $this->status->value(),
'data' => (array)($this->data ?? []),
'version' => config('api.version')
];
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources\Api\V4;
use App\Models\BrandStat;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
class BrandStatsResource extends JsonResourceCollection
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray(Request $request)
{
//phpcs:enable
return [
'stats' => $this->collection->map(function (BrandStat $brandStat) {
return [
'date' => $brandStat->created_at->isoFormat('YYYY-MM-DD'),
'active' => $brandStat->active,
'inactive' => $brandStat->inactive,
'old_accounts' => $brandStat->old_accounts,
'new_accounts' => $brandStat->new_accounts,
'total_accounts' => $brandStat->total_accounts,
'total_opt_in' => $brandStat->total_opt_in
];
})
];
}
}

View file

@ -0,0 +1,101 @@
<?php
namespace App\Http\Resources\Api\V4;
use App\Models\Brand;
use App\Models\Customer;
use Illuminate\Http\Resources\Json\JsonResource;
class CustomerResource extends JsonResource
{
/** @var \App\Models\Customer */
private $customer;
/** @var \App\Models\CustomerInfo */
private $info;
/** @var \App\Models\CustomerLogin */
private $login;
/** @var array */
private static $skeleton = [
'hashcode' => '',
'firstname' => '',
'lastname' => '',
'email' => '',
'birth_date' => '',
'address' => '',
'address2' => '',
'phone' => '',
'country' => '',
'city' => '',
'postcode' => '',
'opt_in' => '',
'status' => '',
'last_connection' => ''
];
/**
* CustomerResource.
*
* @param \App\Models\Brand $brand
* @param \App\Models\Customer $customer
*/
public function __construct(Brand $brand, Customer $customer)
{
$this->customer = $customer;
$this->info = $customer->info($brand, Customer::FIRST_OR_FAIL);
$this->login = $customer->login($brand, Customer::FIRST_OR_FAIL);
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request|null $request
* @return array
* + email string
* + firstname string
* + lastname string
* + birth_date datetime
* + address string
* + address2 string
* + phone string
* + country string
* + city string
* + postcode string
* + opt_in boolean
* + hashcode string
* + status boolean
* + last_connection date
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
*/
public function toArray($request = null): array
{
//phpcs:enable
$data = array_intersect_key(
array_merge(
[
'email' => $this->customer->email
],
$this->login->toArray(),
$this->info->toArray(),
['status' => $this->login->status]
),
$this->getSkeleton()
);
$data['country'] = $this->info->country->value();
$data['address2'] = $this->info->address2 ?? '';
return $data;
}
/**
* Returns the resource skeletton
*
* @return array
*/
public static function getSkeleton(): array
{
return self::$skeleton;
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Resources\Api\V4;
use App\Models\Question;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
use Illuminate\Support\Collection;
class QuestionStatsResource extends JsonResourceCollection
{
/** @var \App\Models\Question */
protected $question = null;
/**
* Create a new resource instance.
*
* @param \Illuminate\Support\Collection $questionStats
* @param \App\Models\Question $question
* @return void
*/
public function __construct(Collection $questionStats = null, Question $question)
{
$this->question = $question;
parent::__construct($questionStats);
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray(Request $request): array
{
//phpcs:enable
return [
'answers' => (new AnswersResource($this->question->answers))->toArray($request),
'stats' => $this->collection->toArray()
];
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace App\Http\Resources\Api\V4;
use App\Models\Answer;
use App\Models\Question;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection as JsonResourceCollection;
use Illuminate\Support\Collection;
class QuestionsResource extends JsonResourceCollection
{
/** @var \Illuminate\Support\Collection<\App\Models\CustomerAnswer> */
protected $userAnwsers = null;
/**
* Create a new resource instance.
*
* @param mixed $resource
* @param \Illuminate\Support\Collection<\App\Models\CustomerAnswer> $userAnwsers
* @return void
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function __construct(mixed $resource, Collection $userAnwsers = null)
{
//phpcs:enable
$this->userAnwsers = $userAnwsers;
parent::__construct($resource);
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Support\Collection
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray(Request $request): \Illuminate\Support\Collection
{
//phpcs:enable
return $this->collection->map(function (Question $question) {
/** @var \Illuminate\Support\Collection */
$answers = $question->answers;
$question = [
'id' => $question->id,
'type' => $question->type->value(),
'question' => $question->question,
'answers' => new AnswersResource($answers)
];
if ($this->userAnwsers) {
$question['customerAnswers'] = new AnswersResource($answers->filter(function (Answer $answer) {
return $this->userAnwsers->keyBy('id')->has($answer->id);
}));
}
return $question;
});
}
}

View file

@ -2,21 +2,19 @@
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @codeCoverageIgnore
*/
class CustomerResource extends JsonResource
{
/** @var integer */
private $brandId;
/**
* @param mixed $resource
* @param mixed $brandId
* @param mixed $resource
* @param integer $brandId
*/
public function __construct($resource, $brandId)
public function __construct(mixed $resource, int $brandId)
{
parent::__construct($resource);
$this->brandId = $brandId;
@ -25,31 +23,34 @@ class CustomerResource extends JsonResource
/**
* Transform the resource into an array.
*
* @param mixed $request
* @param \Illuminate\Http\Request $request
* @return array
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass, Squiz.Commenting.FunctionComment.Missing
public function toArray($request)
public function toArray(Request $request): array
{
//phpcs:disable
/** @var \App\Models\CustomerInfo */
$infos = $this->infos()->where('brand_id', $this->brandId)->first();
/** @var \App\Models\CustomerLogin */
$login = $this->logins()->where('brand_id', $this->brandId)->first();
return [
'status' => 'OK',
'user' => [
'crm_id' => $this->id,
'firstname' => $infos->firstname,
'lastname' => $infos->lastname,
'email' => $this->email,
'crm_id' => $this->id,
'firstname' => $infos->firstname,
'lastname' => $infos->lastname,
'email' => $this->email,
'birth_date' => $infos->birth_date,
'address' => $infos->address,
'address2' => $infos->address2,
'phone' => $infos->phone,
'country' => $infos->country->label,
'city' => $infos->city,
'postcode' => $infos->postcode,
'opt_in' => $infos->opt_in,
'hashcode' => $infos->hashcode,
'status' => $login->status,
'address' => $infos->address,
'address2' => $infos->address2,
'phone' => $infos->phone,
'country' => $infos->country->label(),
'city' => $infos->city,
'postcode' => $infos->postcode,
'opt_in' => $infos->opt_in,
'hashcode' => $infos->hashcode,
'status' => $login->status,
]
];
}

View file

@ -4,9 +4,6 @@ namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @codeCoverageIgnore
*/
class StatusResource extends JsonResource
{
}

View file

@ -9,9 +9,11 @@ class RemoveSpaces implements Sanitizer
/**
* @param mixed $input
* @return string
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*/
public function sanitize($input): string
{
//phpcs:enable
return preg_replace('~\s{1,}~', '', $input);
}
}

View file

@ -11,9 +11,6 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
/**
* @codeCoverageIgnore
*/
class SetServiceStats implements ShouldQueue
{
use Dispatchable;
@ -42,7 +39,7 @@ class SetServiceStats implements ShouldQueue
*
* @return void
*/
public function handle()
public function handle(): void
{
$serviceStat = ServiceStat::firstOrNew([
'date' => date('Y-m-d'),
@ -62,7 +59,7 @@ class SetServiceStats implements ShouldQueue
*
* @return \DateTime
*/
public function retryUntil()
public function retryUntil(): \DateTime
{
return now()->addMinutes(rand(0, 360));
}
@ -72,7 +69,7 @@ class SetServiceStats implements ShouldQueue
*
* @return array
*/
public function middleware()
public function middleware(): array
{
return [
(new WithoutOverlapping(self::KEY))

View file

@ -8,9 +8,6 @@ use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
/**
* @codeCoverageIgnore
*/
class StartStatJobs implements ShouldQueue
{
use Dispatchable;
@ -22,7 +19,7 @@ class StartStatJobs implements ShouldQueue
*
* @return void
*/
public function handle()
public function handle(): void
{
$brands = Brand::all();
foreach ($brands as $brand) {

View file

@ -16,9 +16,6 @@ use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
/**
* @codeCoverageIgnore
*/
class StatJob implements ShouldQueue
{
use Dispatchable;
@ -55,7 +52,7 @@ class StatJob implements ShouldQueue
*
* @return void
*/
public function handle()
public function handle(): void
{
$stats = new BrandStat([
'active' => CustomerLogin::query()->whereHas('brand', function (Builder $q) {
@ -91,7 +88,7 @@ class StatJob implements ShouldQueue
*
* @return array
*/
public function middleware()
public function middleware(): array
{
// * Dispatch randomly overlaping jobs if overlapping.
return [(new WithoutOverlapping(self::JOBKEY))->releaseAfter(rand(20, 90))];

View file

@ -11,9 +11,6 @@ use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
/**
* @codeCoverageIgnore
*/
class UpdateCustomerLastConnection implements ShouldQueue
{
use Dispatchable;
@ -53,7 +50,7 @@ class UpdateCustomerLastConnection implements ShouldQueue
*
* @return void
*/
public function handle()
public function handle(): void
{
if ($this->customerLogin->last_connection < $this->date) {
$this->customerLogin->last_connection = $this->date;

View file

@ -2,12 +2,11 @@
namespace App\Lib;
use App\Exceptions\Api\V2\CrmException;
use App\Exceptions\Api\V4\CrmException;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
/**
* Helper Class
@ -15,19 +14,19 @@ use Illuminate\Support\Str;
class Helper
{
/**
* Package infos
* Package infos.
*
* @var array
*/
private static $packageInfos;
/**
* Tells if the request is within apiV2 routes
* Tells if the request is within apiV2 routes.
*
* @param \Illuminate\Http\Request $request
* @return boolean
*/
public static function isRequestForApiV2(Request $request)
public static function isRequestForApiV2(Request $request): bool
{
// * Small hack to ensure to know if route is From APIv2.
$routeName = static::getRouteName($request) ??
@ -35,63 +34,56 @@ class Helper
return strpos($routeName, 'apiV2.') === 0;
}
/**
* Tells if the request is within apiV4 routes.
*
* @param \Illuminate\Http\Request $request
* @return boolean
*/
public static function isRequestForApiV4(Request $request): bool
{
// * Small hack to ensure to know if route is From APIv4.
$routeName = static::getRouteName($request) ??
(strpos($request->path(), 'api/v4') === 0 ? 'apiV4.' : null) ?? 'none';
return strpos($routeName, 'apiV4.') === 0;
}
/**
* Try to get current route name
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public static function getRouteName(Request $request)
public static function getRouteName(Request $request): ?string
{
return Route::currentRouteName() ?? ($request->route() ? $request->route()->getName() : null);
}
/**
* Get the bearer token from request
* Try to get current route prefix.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public static function getBearerTokenFromRequest(Request $request): ?string
{
$header = $request->header('Authorization', '');
if (Str::contains($header, 'Bearer ')) {
$auths = \explode(',', $header);
foreach ($auths as $auth) {
if (Str::contains($auth, 'Bearer ')) {
return Str::substr($auth, 7);
}
}
}
return null;
}
/**
* Try to get current route prefix
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public static function getRoutePrefix(Request $request)
public static function getRoutePrefix(Request $request): ?string
{
return (Route::getCurrentRoute() ? Route::getCurrentRoute()->getPrefix() : null) ??
($request->route() ? $request->route()->getPrefix() : null);
}
/**
* Try to get route from request
* Try to get route from request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route|null
*/
public static function getRoute(Request $request)
public static function getRoute(Request $request): \Illuminate\Routing\Route|null
{
return Route::getRoutes()->getByName(static::getRouteName($request));
}
/**
* Get composer.json infos
* Get composer.json infos.
*
* @return array
*/
@ -104,14 +96,14 @@ class Helper
}
/**
* Calculate the request signature
* Calculate the request signature.
*
* @param string $secret
* @param string $uri
* @param string $method
* @param array $parameters
* @return string request signature
* @throws \App\Exceptions\Api\V2\CrmException If an error occurs.
* @throws \App\Exceptions\Api\V4\CrmException If an error occurs.
*/
public static function signRequest(string $secret, string $uri, string $method, array $parameters): string
{
@ -175,6 +167,6 @@ class Helper
throw new CrmException('Failed to decrypt service credentials', 0, $e);
} catch (\Exception $e) {
throw new CrmException('An error occured when authenticating service', 0, $e);
}//end try
} //end try
}
}

Some files were not shown because too many files have changed in this diff Show more