Compare commits

..

5 commits

Author SHA1 Message Date
Alexis Gousseau
1732a52355
chore: 🔖 release v4.3.0 2026-10-20 12:28:50 +02:00
Alexis Gousseau
dafef6e5d5
fix: 🚨 resolve linters errors 2026-10-20 12:27:49 +02:00
Alexis Gousseau
e5380fcacf
fix: 🐛 disable password rehash for CRM customers 2026-10-20 12:19:28 +02:00
Alexis Gousseau
2cf5c76b35
chore: 🔧 update sitemap generator configuration 2026-10-20 12:19:28 +02:00
Alexis Gousseau
2fd635c116
chore: ⬆️ bump laravel (v11.0) and dependencies 2026-10-20 12:19:28 +02:00
279 changed files with 9657 additions and 7481 deletions

26
.gitignore vendored
View file

@ -31,16 +31,42 @@ resources/theme
.npm/ .npm/
.composer/ .composer/
.zshrc .zshrc
.rector-cache/
# Ignore public favicon # Ignore public favicon
/public/android-chrome-192x192.png /public/android-chrome-192x192.png
/public/android-chrome-512x512.png /public/android-chrome-512x512.png
/public/android-icon-144x144.png
/public/android-icon-192x192.png
/public/android-icon-36x36.png
/public/android-icon-48x48.png
/public/android-icon-72x72.png
/public/android-icon-96x96.png
/public/apple-chrome-192x192.png
/public/apple-chrome-512x512.png
/public/apple-icon-114x114.png
/public/apple-icon-120x120.png
/public/apple-icon-144x144.png
/public/apple-icon-152x152.png
/public/apple-icon-180x180.png
/public/apple-icon-57x57.png
/public/apple-icon-60x60.png
/public/apple-icon-72x72.png
/public/apple-icon-76x76.png
/public/apple-icon-precomposed.png
/public/apple-icon.png
/public/apple-touch-icon.png /public/apple-touch-icon.png
/public/browserconfig.xml /public/browserconfig.xml
/public/browserconfig.xml /public/browserconfig.xml
/public/favicon-16x16.png /public/favicon-16x16.png
/public/favicon-32x32.png /public/favicon-32x32.png
/public/favicon-96x96.png
/public/favicon.ico /public/favicon.ico
/public/manifest.json
/public/ms-icon-144x144.png
/public/ms-icon-150x150.png
/public/ms-icon-310x310.png
/public/ms-icon-70x70.png
/public/mstile-150x150.png /public/mstile-150x150.png
/public/safari-pinned-tab.svg /public/safari-pinned-tab.svg
/public/site.webmanifest /public/site.webmanifest

View file

@ -1,20 +1,27 @@
<?php <?php
declare(strict_types=1);
namespace App\Auth; namespace App\Auth;
use App\Models\Customer; use App\Models\Customer;
use ErrorException;
use Exception;
use Illuminate\Auth\EloquentUserProvider; use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable as UserContract; use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider as UserProviderContract; use Illuminate\Contracts\Auth\UserProvider as UserProviderContract;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use LDC\LDCCRM\CrmApiV4;
use LDC\LDCCRM\Enums\ApiError; use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException; use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException; use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Models\Customer as CrmCustomer; use RuntimeException;
use SensitiveParameter;
use Throwable;
/** /**
* Crm user provider to integrate nicely with Laravel * Crm user provider to integrate nicely with Laravel
@ -23,32 +30,28 @@ class CrmUserProvider extends EloquentUserProvider implements UserProviderContra
{ {
/** /**
* The model to use, specified in auth config guard. * The model to use, specified in auth config guard.
*
* @var string
*/ */
private $usedModel = Customer::class; private string $usedModel = Customer::class;
/** /**
* Retrieve a user by the given credentials. * Retrieve a user by the given credentials.
* *
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null * @return \Illuminate\Contracts\Auth\Authenticatable|null
*/ */
public function retrieveByCredentials(array $credentials) public function retrieveByCredentials(array $credentials)
{ {
// * Let laravel find a model // * Let laravel find a model
$model = parent::retrieveByCredentials($credentials) ?? $model = parent::retrieveByCredentials($credentials) ?? self::retrieveTrashed($credentials);
self::retrieveTrashed($credentials);
try { try {
/** @var \LDC\LDCCRM\CrmApiV4 */ /** @var CrmApiV4 */
$crm = app('crmAPI'); $crm = resolve('crmAPI');
// Si pas de compte local. // Si pas de compte local.
if (!$model) { if (! $model) {
$crmCustomer = $crm->getCustomer($credentials['email']); $crmCustomer = $crm->getCustomer($credentials['email']);
if (!$crmCustomer) { if (! $crmCustomer) {
// * Customer is not found on CRM. // * Customer is not found on CRM.
return null; return null;
} }
@ -63,40 +66,38 @@ class CrmUserProvider extends EloquentUserProvider implements UserProviderContra
} }
report($e); report($e);
Log::error('Unhandled response ' . $e->getApiError()->label()); Log::error('Unhandled response ' . $e->getApiError()->label());
} catch (CrmException | \Exception $e) { } catch (CrmException | Exception $e) {
report($e); report($e);
// Une erreur critique est survenue avec la CRM. // Une erreur critique est survenue avec la CRM.
return null; return null;
} //end try } // end try
Log::error(__CLASS__ . '::retrieveByCredentials reached end of possibilities.'); Log::error(self::class . '::retrieveByCredentials reached end of possibilities.');
return null; return null;
} }
/** /**
* Validate a user against the given credentials. * Validate a user against the given credentials.
* *
* @param UserContract $model * @throws RuntimeException If model provided is not a Customer or connection to server fails.
* @param array $credentials * @throws CrmException On CRM call error.
* @return boolean|null * @throws Exception On CRM call error.
* @throws \RuntimeException If model provided is not a Customer or connection to server fails.
* @throws \LDC\LDCCRM\Exceptions\CrmException On CRM call error.
* @throws \Exception On CRM call error.
*/ */
public function validateCredentials(UserContract $model, array $credentials): bool|null public function validateCredentials(UserContract $model, array $credentials): ?bool
{ {
/** @var \App\Models\Customer $model */ /** @var Customer $model */
if (\get_class($model) !== $this->usedModel) { if ($model::class !== $this->usedModel) {
throw new \RuntimeException('Cannot authenticate any thing else than ' . $this->usedModel); throw new RuntimeException('Cannot authenticate any thing else than ' . $this->usedModel);
} }
try { try {
/** @var \LDC\LDCCRM\CrmApiV4 */ /** @var CrmApiV4 */
$crm = app('crmAPI'); $crm = resolve('crmAPI');
// * If has trait soft delete and customer trashed, then restore customer. // * If has trait soft delete and customer trashed, then restore customer.
if ( if (
\in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, \class_uses_recursive($model)) and \in_array(SoftDeletes::class, \class_uses_recursive($model)) && $model->trashed()
$model->trashed()
) { ) {
$model->restore(); $model->restore();
} }
@ -105,10 +106,10 @@ class CrmUserProvider extends EloquentUserProvider implements UserProviderContra
return $crm->login($credentials['email'], $credentials['password']); return $crm->login($credentials['email'], $credentials['password']);
} catch (ApiErrorException $e) { } catch (ApiErrorException $e) {
if ( if (
$e->getApiError()->equals(ApiError::modelNotFound) || $e->getApiError()->equals(ApiError::modelNotFound) || ($e->getApiError()->equals(ApiError::authFailed) && ! $model->existsInCrm())
($e->getApiError()->equals(ApiError::authFailed) && !$model->existsInCrm())
) { ) {
$model->delete(); $model->delete();
return false; return false;
} }
if ($e->getApiError()->equals(ApiError::authFailed)) { if ($e->getApiError()->equals(ApiError::authFailed)) {
@ -120,32 +121,42 @@ class CrmUserProvider extends EloquentUserProvider implements UserProviderContra
$model->save(); $model->save();
$model->sendEmailVerificationNotification(); $model->sendEmailVerificationNotification();
Session::flash('warning', \trans('front.messages.account_not_activated')); Session::flash('warning', \trans('front.messages.account_not_activated'));
return true; return true;
} }
} catch (CrmException | \Exception $e) { } catch (CrmException | Exception $e) {
report($e); report($e);
if (config('app.debug')) { throw_if(config('app.debug'), $e);
throw $e;
} return false;
return false; } // end try
} //end try
Log::error(self::class . '::validateCredentials reached end of possibilities.');
Log::error(__CLASS__ . '::validateCredentials reached end of possibilities.');
return null; return null;
} }
/** /**
* Create a local customer * CRM customer passwordless (Laravel 11 introduces password rehashing after login).
*/
public function rehashPasswordIfRequired(
UserContract $user,
#[SensitiveParameter] array $credentials,
bool $force = false
): void {
//
}
/**
* Create a local customer.
* *
* @param \LDC\LDCCRM\Models\Customer $crmCustomer * @throws Throwable If saving to DB failed.
* @return \Illuminate\Database\Eloquent\Model
* @throws \Throwable If saving to DB failed.
*/ */
private function createLocalCustomer(\LDC\LDCCRM\Models\Customer $crmCustomer): Model private function createLocalCustomer(\LDC\LDCCRM\Models\Customer $crmCustomer): Model
{ {
/** @var \App\Models\Customer $model */ /** @var Customer $model */
$model = $this->usedModel::withTrashed()->firstOrNew([ $model = $this->usedModel::withTrashed()->firstOrNew([
'email' => $crmCustomer->email 'email' => $crmCustomer->email,
]); ]);
$model->hashcode = $crmCustomer->hashcode; $model->hashcode = $crmCustomer->hashcode;
$model->email_verified_at = $crmCustomer->status ? now() : null; $model->email_verified_at = $crmCustomer->status ? now() : null;
@ -160,14 +171,12 @@ class CrmUserProvider extends EloquentUserProvider implements UserProviderContra
/** /**
* Retrieve a trashed customer by the given credentials. * Retrieve a trashed customer by the given credentials.
* *
* @param array $credentials * @throws ErrorException If cannot connect to LDAP.
* @return \Illuminate\Contracts\Auth\Authenticatable|null
* @throws \ErrorException If cannot connect to LDAP.
*/ */
private function retrieveTrashed(array $credentials): ?Authenticatable private function retrieveTrashed(array $credentials): ?UserContract
{ {
// * Make changes // * Make changes
if (empty($credentials)) { if ($credentials === []) {
return null; return null;
} }
@ -177,12 +186,12 @@ class CrmUserProvider extends EloquentUserProvider implements UserProviderContra
$query = $this->createModel()->newQuery(); $query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) { foreach ($credentials as $key => $value) {
if (!Str::contains($key, 'password')) { if (! Str::contains($key, 'password')) {
$query->where($key, $value); $query->where($key, $value);
} }
} }
/** @var \App\Models\Customer|null $query */ /** @var Customer|null $query */
return $query->withTrashed()->first(); return $query->withTrashed()->first();
} }
} }

View file

@ -1,26 +1,30 @@
<?php <?php
declare(strict_types=1);
namespace App\Casts; namespace App\Casts;
use App\Lib\Discount;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
class AsArrayOfDiscount implements CastsAttributes class AsArrayOfDiscount implements CastsAttributes
{ {
/** /**
* Cast the given value. * Cast the given value.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
* @param string $key * @param mixed $value
* @param mixed $value * @return array<Discount>
* @param array $attributes *
* @return array<\App\Lib\Discount>
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing * @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*/ */
public function get($model, string $key, $value, array $attributes): array public function get($model, string $key, $value, array $attributes): array
{ {
// phpcs:enable // phpcs:enable
return \collect(\json_decode($value, true))->map(function ($input) use ($model, $key, $attributes) { return \collect(\json_decode($value, true))->map(function ($input) use ($model, $key, $attributes): Discount {
$inputCast = new AsDiscount(); $inputCast = new AsDiscount();
return $inputCast->get($model, $key, \json_encode($input), $attributes); return $inputCast->get($model, $key, \json_encode($input), $attributes);
})->all(); })->all();
} }
@ -28,11 +32,10 @@ class AsArrayOfDiscount implements CastsAttributes
/** /**
* Prepare the given value for storage. * Prepare the given value for storage.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
* @param string $key * @param Discount $value
* @param \App\Lib\Discount $value
* @param array $attributes
* @return string Json encoded array<\App\Lib\Discount> * @return string Json encoded array<\App\Lib\Discount>
*
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing * @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*/ */
public function set($model, string $key, $value, array $attributes) public function set($model, string $key, $value, array $attributes)
@ -40,6 +43,7 @@ class AsArrayOfDiscount implements CastsAttributes
// phpcs:enable // phpcs:enable
return \json_encode(\collect($value)->map(function ($input) use ($model, $key, $attributes) { return \json_encode(\collect($value)->map(function ($input) use ($model, $key, $attributes) {
$inputCast = new AsDiscount(); $inputCast = new AsDiscount();
return \json_decode($inputCast->set($model, $key, $input, $attributes)); return \json_decode($inputCast->set($model, $key, $input, $attributes));
})->all()); })->all());
} }

View file

@ -1,64 +1,69 @@
<?php <?php
declare(strict_types=1);
namespace App\Casts; namespace App\Casts;
use App\Lib\Discount; use App\Lib\Discount;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
use RuntimeException;
class AsDiscount implements CastsAttributes class AsDiscount implements CastsAttributes
{ {
/** /**
* Cast the given value. * Cast the given value.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
* @param string $key * @param mixed $value
* @param mixed $value *
* @param array $attributes
* @return \App\Lib\Discount
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing * @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/ */
public function get($model, string $key, $value, array $attributes): Discount public function get($model, string $key, $value, array $attributes): Discount
{ {
// phpcs:enable // phpcs:enable
$inputData = \json_decode($value, true); $inputData = \json_decode($value, true);
return Discount::make(...\array_values($inputData)); return Discount::make(...\array_values($inputData));
} }
/** /**
* Prepare the given value for storage. * Prepare the given value for storage.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
* @param string $key * @param mixed $value
* @param mixed $value
* @param array $attributes
* @return string Json encoded \App\Lib\Inputs\Definition\Input * @return string Json encoded \App\Lib\Inputs\Definition\Input
*
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing * @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/ */
public function set($model, string $key, $value, array $attributes) public function set($model, string $key, $value, array $attributes)
{ {
// phpcs:enable // phpcs:enable
$this->assertIsInput($value); $this->assertIsInput($value);
/** @var \App\Lib\Discount $value */
/** @var Discount $value */
return $value->toJson(); return $value->toJson();
} }
/** /**
* Assert is instance of \App\Lib\Discount * Assert is instance of \App\Lib\Discount
* *
* @param mixed $value * @throws RuntimeException If the value is not an input.
* @return void
* @throws \RuntimeException If the value is not an input.
*/ */
private function assertIsInput(mixed $value): void private function assertIsInput(mixed $value): void
{ {
if (!\is_object($value) or !($value instanceof Discount)) { if (! \is_object($value) || ! ($value instanceof Discount)) {
$value = \json_encode($value); $value = \json_encode($value);
throw new \RuntimeException( throw new RuntimeException(
__CLASS__ . ' needs value to be array of \'' . Discount::class . "' given is {$value}" self::class . ' needs value to be array of \'' . Discount::class . "' given is {$value}"
); );
} }
} }

View file

@ -1,9 +1,14 @@
<?php <?php
declare(strict_types=1);
namespace App\Casts; namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use IPLib\Address\AddressInterface;
use IPLib\Factory;
use RuntimeException;
/** /**
* @template TGet * @template TGet
@ -14,30 +19,30 @@ class Ip implements CastsAttributes
/** /**
* Transform the attribute from the underlying model values. * Transform the attribute from the underlying model values.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param array<string,mixed> $attributes
* @param string $key * @return AddressInterface|null
* @param mixed $value *
* @param array<string,mixed> $attributes
* @return \IPLib\Address\AddressInterface|null
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/ */
public function get(Model $model, string $key, mixed $value, array $attributes) public function get(Model $model, string $key, mixed $value, array $attributes)
{ {
// phpcs:enable // phpcs:enable
return \IPLib\Factory::parseAddressString($value); return Factory::parseAddressString($value);
} }
/** /**
* Transform the attribute to its underlying model values. * Transform the attribute to its underlying model values.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param AddressInterface|string|null $value
* @param string $key * @param array<string,mixed> $attributes
* @param \IPLib\Address\AddressInterface|string|null $value
* @param array<string,mixed> $attributes
* @return mixed * @return mixed
* @throws \RuntimeException If value is not or instance of \IPLib\Address\AddressInterface. *
* @throws RuntimeException If value is not or instance of \IPLib\Address\AddressInterface.
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
*/ */
public function set(Model $model, string $key, mixed $value, array $attributes) public function set(Model $model, string $key, mixed $value, array $attributes)
@ -47,15 +52,12 @@ class Ip implements CastsAttributes
return null; return null;
} }
if (is_string($value)) { if (is_string($value)) {
$value = \IPLib\Factory::parseAddressString($value); $value = Factory::parseAddressString($value);
}
if (!is_object($value) or !($value instanceof \IPLib\Address\AddressInterface)) {
throw new \RuntimeException(
"{$key} has to be instance of " .
\IPLib\Address\AddressInterface::class
);
} }
throw_if(! is_object($value) || ! ($value instanceof AddressInterface), RuntimeException::class, "{$key} has to be instance of " .
AddressInterface::class);
$value->getAddressType(); $value->getAddressType();
return $value; return $value;
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Console\Commands; namespace App\Console\Commands;
use Illuminate\Console\Command; use Illuminate\Console\Command;
@ -12,6 +14,7 @@ class TestMail extends Command
* *
* @var string * @var string
*/ */
#[\Override]
protected $signature = 'uwil:mail:test'; protected $signature = 'uwil:mail:test';
/** /**
@ -19,16 +22,15 @@ class TestMail extends Command
* *
* @var string * @var string
*/ */
#[\Override]
protected $description = 'Send test email to equipe.web@ldc.fr'; protected $description = 'Send test email to equipe.web@ldc.fr';
/** /**
* Execute the console command. * Execute the console command.
*
* @return void
*/ */
public function handle() public function handle(): void
{ {
Mail::raw(sprintf('UWIL mail-test from %s %s', \config('app.name'), \config('app.url')), function ($msg) { Mail::raw(sprintf('UWIL mail-test from %s %s', \config('app.name'), \config('app.url')), function ($msg): void {
$msg->to('equipe.web@ldc.fr')->subject('Test Email'); $msg->to('equipe.web@ldc.fr')->subject('Test Email');
}); });
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Console\Commands\Theme; namespace App\Console\Commands\Theme;
use App\Lib\Helpers\ToolboxHelper; use App\Lib\Helpers\ToolboxHelper;
@ -18,6 +20,7 @@ class SwitchCommand extends Command implements PromptsForMissingInput
* *
* @var string * @var string
*/ */
#[\Override]
protected $signature = 'uwil:theme:switch {theme : The theme folder name on theme-merge project}'; protected $signature = 'uwil:theme:switch {theme : The theme folder name on theme-merge project}';
/** /**
@ -25,6 +28,7 @@ class SwitchCommand extends Command implements PromptsForMissingInput
* *
* @var string * @var string
*/ */
#[\Override]
protected $description = 'Switch GGW theme and install it'; protected $description = 'Switch GGW theme and install it';
/** /**
@ -35,22 +39,20 @@ class SwitchCommand extends Command implements PromptsForMissingInput
protected function promptForMissingArgumentsUsing() protected function promptForMissingArgumentsUsing()
{ {
return [ return [
'theme' => fn () => search( 'theme' => fn (): int | string => search(
label: 'Search for a theme:', label: 'Search for a theme:',
placeholder: 'default-theme',
options: fn (string $value): array => \collect(File::directories(\resource_path('theme'))) options: fn (string $value): array => \collect(File::directories(\resource_path('theme')))
->map(fn (string $dir_path) => \pathinfo($dir_path, \PATHINFO_BASENAME)) ->map(fn (string $dir_path): string => \pathinfo($dir_path, \PATHINFO_BASENAME))
->filter(fn (string $dir_path) => $value ? Str::of($dir_path)->contains($value) : true) ->filter(fn (string $dir_path) => $value ? Str::of($dir_path)->contains($value) : true)
->filter(fn (string $dir_path) => $dir_path !== 'mounted') ->filter(fn (string $dir_path): bool => $dir_path !== 'mounted')
->all() ->all(),
) placeholder: 'default-theme'
),
]; ];
} }
/** /**
* Execute the console command. * Execute the console command.
*
* @return void
*/ */
public function handle(): void public function handle(): void
{ {
@ -65,7 +67,7 @@ class SwitchCommand extends Command implements PromptsForMissingInput
"{$mount_path} is not a link, cannot remove it" "{$mount_path} is not a link, cannot remove it"
); );
\throw_if( \throw_if(
!File::delete($mount_path), ! File::delete($mount_path),
"{$mount_path} Failed to remove file" "{$mount_path} Failed to remove file"
); );
} }

View file

@ -1,11 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Enums\Users\RoleEnum; use App\Enums\Users\RoleEnum;
use App\Models\User; use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -16,13 +18,15 @@ class UserCreate extends Command
* *
* @var string * @var string
*/ */
#[\Override]
protected $signature = 'uwil:user:create'; protected $signature = 'uwil:user:create';
/** /**
* The console command description. * The console command description.
* *
* @var string|null * @var string
*/ */
#[\Override]
protected $description = 'Create a backend admin user'; protected $description = 'Create a backend admin user';
/** /**
@ -62,8 +66,6 @@ class UserCreate extends Command
/** /**
* Execute the console command. * Execute the console command.
*
* @return integer
*/ */
public function handle(): int public function handle(): int
{ {
@ -83,13 +85,13 @@ class UserCreate extends Command
$this->addRole(); $this->addRole();
$user = new User([ $user = new User([
'first_name' => $this->first_name, 'first_name' => $this->first_name,
'last_name' => $this->last_name, 'last_name' => $this->last_name,
'email' => $this->email, 'email' => $this->email,
'password' => $this->password, 'password' => $this->password,
'role' => $this->role, 'role' => $this->role,
'published' => true, 'published' => true,
'published_at' => Carbon::now(), 'published_at' => Date::now(),
]); ]);
$user->saveOrFail(); $user->saveOrFail();
@ -100,8 +102,6 @@ class UserCreate extends Command
/** /**
* Add a first name to the user. * Add a first name to the user.
*
* @return void
*/ */
private function addFirstName(): void private function addFirstName(): void
{ {
@ -109,7 +109,7 @@ class UserCreate extends Command
try { try {
$tmp = $this->ask('Type the wanted user first name', 'Admin'); $tmp = $this->ask('Type the wanted user first name', 'Admin');
$validator = Validator::make(['first_name' => $tmp], [ $validator = Validator::make(['first_name' => $tmp], [
'first_name' => 'required|min:3|max:255' 'first_name' => 'required|min:3|max:255',
]); ]);
$validator->validate(); $validator->validate();
$this->first_name = $tmp; $this->first_name = $tmp;
@ -118,15 +118,14 @@ class UserCreate extends Command
'Please provide a valid first name %s', 'Please provide a valid first name %s',
\implode(',', collect($e->errors())->flatten()->all()) \implode(',', collect($e->errors())->flatten()->all())
)); ));
continue; continue;
} //end try } // end try
}; //end while } // end while
} }
/** /**
* Add a last name to the user. * Add a last name to the user.
*
* @return void
*/ */
private function addLastName(): void private function addLastName(): void
{ {
@ -134,7 +133,7 @@ class UserCreate extends Command
try { try {
$tmp = $this->ask('Type the wanted user last name', 'Saïtama'); $tmp = $this->ask('Type the wanted user last name', 'Saïtama');
$validator = Validator::make(['last_name' => $tmp], [ $validator = Validator::make(['last_name' => $tmp], [
'last_name' => 'required|min:3|max:255' 'last_name' => 'required|min:3|max:255',
]); ]);
$validator->validate(); $validator->validate();
$this->last_name = $tmp; $this->last_name = $tmp;
@ -143,15 +142,14 @@ class UserCreate extends Command
'Please provide a valid last_name %s', 'Please provide a valid last_name %s',
\implode(',', collect($e->errors())->flatten()->all()) \implode(',', collect($e->errors())->flatten()->all())
)); ));
continue; continue;
} //end try } // end try
}; //end while } // end while
} }
/** /**
* Add an email to the user. * Add an email to the user.
*
* @return void
*/ */
private function addEmail(): void private function addEmail(): void
{ {
@ -159,7 +157,7 @@ class UserCreate extends Command
try { try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr'); $tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [ $validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns' 'email' => 'required|unique:users,email|email:rfc,dns',
]); ]);
$validator->validate(); $validator->validate();
$this->email = $tmp; $this->email = $tmp;
@ -168,31 +166,26 @@ class UserCreate extends Command
'Please provide a valid email %s', 'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all()) \implode(',', collect($e->errors())->flatten()->all())
)); ));
continue; continue;
} //end try } // end try
}; //end while } // end while
} }
/** /**
* Add a password to the user. * Add a password to the user.
*
* @return void
*/ */
private function addPassword(): void private function addPassword(): void
{ {
while ( do {
($this->password = $this->secret( $prefix = $this->password !== null ? 'Failed to confirm, ' : '';
(!\is_null($this->password) ? 'Failed to confirm, ' : '') . 'Type the wanted user password' $this->password = $this->secret($prefix . 'Type the wanted user password');
)) !== $this->secret('Confirm the password') $confirmation = $this->secret('Confirm the password');
) { } while ($this->password !== $confirmation);
continue;
}; //end while
} }
/** /**
* Add a role to the user. * Add a role to the user.
*
* @return void
*/ */
private function addRole(): void private function addRole(): void
{ {
@ -215,7 +208,7 @@ class UserCreate extends Command
$tmp = RoleEnum::marketing; $tmp = RoleEnum::marketing;
} }
$validator = Validator::make(['role' => $tmp], [ $validator = Validator::make(['role' => $tmp], [
'role' => 'required' 'role' => 'required',
]); ]);
$validator->validate(); $validator->validate();
$this->role = $tmp; $this->role = $tmp;
@ -224,8 +217,9 @@ class UserCreate extends Command
'Please choose from the selection %s', 'Please choose from the selection %s',
\implode(',', collect($e->errors())->flatten()->all()) \implode(',', collect($e->errors())->flatten()->all())
)); ));
continue; continue;
} //end try } // end try
}; //end while } // end while
} }
} }

View file

@ -1,8 +1,9 @@
<?php <?php
declare(strict_types=1);
namespace App\Console; namespace App\Console;
use App\Jobs\DeleteOlds\DeleteOldCustomers;
use Illuminate\Console\Scheduling\Schedule; use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@ -13,12 +14,12 @@ class Kernel extends ConsoleKernel
* *
* @var array * @var array
*/ */
#[\Override]
protected $commands = []; protected $commands = [];
/** /**
* Define the application's command schedule. * Define the application's command schedule.
* *
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void * @return void
*/ */
protected function schedule(Schedule $schedule) protected function schedule(Schedule $schedule)

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Enums\ActivityLogs; namespace App\Enums\ActivityLogs;
use App\Traits\Enums\BaseEnum; use App\Traits\Enums\BaseEnum;
@ -25,7 +27,6 @@ enum ActivityLogsEventEnum: int
self::deleted->name => 'Suppression', self::deleted->name => 'Suppression',
]; ];
/** /**
* Custom added classes definition * Custom added classes definition
*/ */
@ -38,8 +39,6 @@ enum ActivityLogsEventEnum: int
/** /**
* Get Class * Get Class
*
* @return string
*/ */
public function bootstrapClass(): string public function bootstrapClass(): string
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Enums; namespace App\Enums;
use App\Traits\Enums\BaseEnum; use App\Traits\Enums\BaseEnum;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Enums; namespace App\Enums;
use App\Traits\Enums\BaseEnum; use App\Traits\Enums\BaseEnum;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Enums\Pagination; namespace App\Enums\Pagination;
use App\Traits\Enums\BaseEnum; use App\Traits\Enums\BaseEnum;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Enums; namespace App\Enums;
use App\Traits\Enums\BaseEnum; use App\Traits\Enums\BaseEnum;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Enums\Theme; namespace App\Enums\Theme;
use App\Traits\Enums\BaseEnum; use App\Traits\Enums\BaseEnum;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Enums\Users; namespace App\Enums\Users;
use App\Traits\Enums\BaseEnum; use App\Traits\Enums\BaseEnum;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Exceptions; namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
@ -10,15 +12,17 @@ class Handler extends ExceptionHandler
/** /**
* A list of exception types with their corresponding custom log levels. * A list of exception types with their corresponding custom log levels.
* *
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*> * @var array<class-string<Throwable>, \Psr\Log\LogLevel::*>
*/ */
#[\Override]
protected $levels = []; protected $levels = [];
/** /**
* A list of the exception types that are not reported. * A list of the exception types that are not reported.
* *
* @var array<int, class-string<\Throwable>> * @var array<int, class-string<Throwable>>
*/ */
#[\Override]
protected $dontReport = []; protected $dontReport = [];
/** /**
@ -26,6 +30,7 @@ class Handler extends ExceptionHandler
* *
* @var array<int, string> * @var array<int, string>
*/ */
#[\Override]
protected $dontFlash = [ protected $dontFlash = [
'current_password', 'current_password',
'password', 'password',
@ -34,14 +39,12 @@ class Handler extends ExceptionHandler
/** /**
* Register the exception handling callbacks for the application. * Register the exception handling callbacks for the application.
*
* @return void
*/ */
public function register() public function register(): void
{ {
$this->reportable(function (Throwable $e) { $this->reportable(function (Throwable $e): void {
if (app()->bound('sentry')) { if (app()->bound('sentry')) {
app('sentry')->captureException($e); resolve('sentry')->captureException($e);
} }
}); });
} }

View file

@ -1,11 +1,15 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\ActivityLog; use App\Models\ActivityLog;
use App\Traits\Controllers\ChangesModelOrder; use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished; use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class ActivityLogController extends Controller class ActivityLogController extends Controller
@ -15,14 +19,12 @@ class ActivityLogController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
$this->authorize('isConceptor'); $this->authorize('isConceptor');
/** @var \Illuminate\Database\Eloquent\Builder $query */
/** @var Builder $query */
$query = ActivityLog::query()->with('user'); $query = ActivityLog::query()->with('user');
/** @var string $search Search field */ /** @var string $search Search field */
@ -45,25 +47,32 @@ class ActivityLogController extends Controller
/** Custom pagination */ /** Custom pagination */
$activitylogModels = $this->paginate($query); $activitylogModels = $this->paginate($query);
return view('back::pages.activity_logs.index', compact('activitylogModels', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.activity_logs.index';
return view($view, [
'activitylogModels' => $activitylogModels,
'search' => $search,
'searchFields' => $searchFields,
]);
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\ActivityLog $activity_log
* @return \Illuminate\Contracts\View\View
*/ */
public function show(ActivityLog $activity_log): \Illuminate\Contracts\View\View public function show(ActivityLog $activity_log): View
{ {
$this->authorize('isConceptor'); $this->authorize('isConceptor');
$targetModelClass = $activity_log->model_class; $targetModelClass = $activity_log->model_class;
$targetModel = $activity_log->model_class::where( $targetModel = $activity_log->model_class::where(
(new $targetModelClass())->getRouteKeyName(), new $targetModelClass()->getRouteKeyName(),
$activity_log->model_id $activity_log->model_id
)->first(); )->first();
return view('back::pages.activity_logs.show', [ /** @var view-string $view */
$view = 'back::pages.activity_logs.show';
return view($view, [
'activitylogModel' => $activity_log, 'activitylogModel' => $activity_log,
'targetModel' => $targetModel, 'targetModel' => $targetModel,
]); ]);

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo\Auth; namespace App\Http\Controllers\Bo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -15,8 +17,6 @@ class LoginController extends Controller
/** /**
* Create a new controller instance. * Create a new controller instance.
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -25,8 +25,6 @@ class LoginController extends Controller
/** /**
* Get the guard to be used during authentication. * Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/ */
protected function guard(): StatefulGuard protected function guard(): StatefulGuard
{ {
@ -35,18 +33,17 @@ class LoginController extends Controller
/** /**
* Show the application's login form. * Show the application's login form.
*
* @return \Illuminate\Contracts\View\View
*/ */
public function showLoginForm(): View public function showLoginForm(): View
{ {
return view('back::auth.login'); /** @var view-string $view */
$view = 'back::auth.login';
return view($view);
} }
/** /**
* Where to redirect users after login. * Where to redirect users after login.
*
* @return string
*/ */
protected function redirectTo(): string protected function redirectTo(): string
{ {
@ -56,14 +53,14 @@ class LoginController extends Controller
/** /**
* Attempt to log the user into the application. * Attempt to log the user into the application.
* *
* @param \Illuminate\Http\Request $request * @return bool
* @return boolean
*/ */
protected function attemptLogin(Request $request) protected function attemptLogin(Request $request)
{ {
$credentials = $this->credentials($request); $credentials = $this->credentials($request);
// * Attempt login only using published users. // * Attempt login only using published users.
$credentials['published'] = true; $credentials['published'] = true;
return $this->guard()->attempt( return $this->guard()->attempt(
$credentials, $credentials,
$request->boolean('remember') $request->boolean('remember')

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo\Auth; namespace App\Http\Controllers\Bo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -13,8 +15,6 @@ class ResetPasswordController extends Controller
/** /**
* Get the guard to be used during authentication. * Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/ */
protected function guard(): StatefulGuard protected function guard(): StatefulGuard
{ {
@ -23,8 +23,6 @@ class ResetPasswordController extends Controller
/** /**
* Where to redirect users after login. * Where to redirect users after login.
*
* @return string
*/ */
protected function redirectTo(): string protected function redirectTo(): string
{ {

View file

@ -1,11 +1,16 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\BannedIps\StoreBannedIpRequest; use App\Http\Requests\Bo\BannedIps\StoreBannedIpRequest;
use App\Http\Requests\Bo\BannedIps\UpdateBannedIpRequest; use App\Http\Requests\Bo\BannedIps\UpdateBannedIpRequest;
use App\Models\BannedIp; use App\Models\BannedIp;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -22,13 +27,10 @@ class BannedIpController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $bannedipModels */ /** @var Builder $bannedipModels */
$bannedipModels = BannedIp::query(); $bannedipModels = BannedIp::query();
/** @var string $search Search field */ /** @var string $search Search field */
@ -49,43 +51,42 @@ class BannedIpController extends Controller
/** Custom pagination */ /** Custom pagination */
$bannedipModels = $this->paginate($bannedipModels); $bannedipModels = $this->paginate($bannedipModels);
return view('back::pages.banned-ips.index', compact('bannedipModels', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.banned-ips.index';
return view($view, ['bannedipModels' => $bannedipModels, 'search' => $search, 'searchFields' => $searchFields]);
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Contracts\View\View
*/ */
public function create(BannedIp $banned_ip): \Illuminate\Contracts\View\View public function create(BannedIp $banned_ip): View
{ {
return view('back::pages.banned-ips.create', [ /** @var view-string $view */
$view = 'back::pages.banned-ips.create';
return view($view, [
'bannedipModel' => $banned_ip, 'bannedipModel' => $banned_ip,
]); ]);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\BannedIps\StoreBannedIpRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(StoreBannedIpRequest $request): \Illuminate\Http\RedirectResponse public function store(StoreBannedIpRequest $request): RedirectResponse
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
$banned_ip = new BannedIp(); $banned_ip = new BannedIp();
$banned_ip->fill($request->validated()); $banned_ip->fill($request->validated());
if ($banned_ip->saveOrFail()) { if ($banned_ip->saveOrFail()) {
return redirect() return to_route('bo.banned_ips.edit', $banned_ip)
->route('bo.banned_ips.edit', $banned_ip)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_created', 'crud.messages.has_been_created',
['model' => Str::singular(trans('models.classes.banned_ips'))] ['model' => Str::singular(trans('models.classes.banned_ips'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_created', 'crud.messages.cannot_be_created',
['model' => Str::singular(trans('models.classes.banned_ips'))] ['model' => Str::singular(trans('models.classes.banned_ips'))]
@ -95,38 +96,33 @@ class BannedIpController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(BannedIp $banned_ip): \Illuminate\Contracts\View\View public function edit(BannedIp $banned_ip): View
{ {
return view('back::pages.banned-ips.edit', [ /** @var view-string $view */
$view = 'back::pages.banned-ips.edit';
return view($view, [
'bannedipModel' => $banned_ip, 'bannedipModel' => $banned_ip,
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\BannedIps\UpdateBannedIpRequest $request
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update(UpdateBannedIpRequest $request, BannedIp $banned_ip): \Illuminate\Http\RedirectResponse public function update(UpdateBannedIpRequest $request, BannedIp $banned_ip): RedirectResponse
{ {
return DB::transaction(function () use ($request, $banned_ip) { return DB::transaction(function () use ($request, $banned_ip) {
$banned_ip->fill($request->validated()); $banned_ip->fill($request->validated());
if ($banned_ip->saveOrFail()) { if ($banned_ip->saveOrFail()) {
return redirect() return to_route('bo.banned_ips.edit', $banned_ip)
->route('bo.banned_ips.edit', $banned_ip)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_updated', 'crud.messages.has_been_updated',
['model' => Str::singular(trans('models.classes.banned_ips'))] ['model' => Str::singular(trans('models.classes.banned_ips'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_updated', 'crud.messages.cannot_be_updated',
['model' => Str::singular(trans('models.classes.banned_ips'))] ['model' => Str::singular(trans('models.classes.banned_ips'))]
@ -136,23 +132,19 @@ class BannedIpController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(BannedIp $banned_ip): \Illuminate\Http\RedirectResponse public function destroy(BannedIp $banned_ip): RedirectResponse
{ {
return DB::transaction(function () use ($banned_ip) { return DB::transaction(function () use ($banned_ip) {
if ($banned_ip->deleteOrFail()) { if ($banned_ip->deleteOrFail()) {
return redirect() return to_route('bo.banned_ips.index')
->route('bo.banned_ips.index')
->with('success', __( ->with('success', __(
'crud.messages.has_been_deleted', 'crud.messages.has_been_deleted',
['model' => Str::singular(trans('models.classes.banned_ips'))] ['model' => Str::singular(trans('models.classes.banned_ips'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', __( ->with('error', __(
':crud.messages.cannot_be_deleted', ':crud.messages.cannot_be_deleted',
['model' => Str::singular(trans('models.classes.banned_ips'))] ['model' => Str::singular(trans('models.classes.banned_ips'))]

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -12,21 +14,22 @@ use App\Models\Code;
use App\Models\Participation; use App\Models\Participation;
use App\Models\Settings; use App\Models\Settings;
use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Queue; use Illuminate\Support\Facades\Queue;
use Kwaadpepper\Serial\Exceptions\SerialCasterException; use Kwaadpepper\Serial\Exceptions\SerialCasterException;
use Throwable;
class CodeController extends Controller class CodeController extends Controller
{ {
/** /**
* Show code form to generate and export * Show code form to generate and export
*
* @return \Illuminate\Contracts\View\View
*/ */
public function index(): \Illuminate\Contracts\View\View public function index(): View
{ {
$this->authorize('generate', Code::class); $this->authorize('generate', Code::class);
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
$nbCodeBase = Code::query()->max('id'); $nbCodeBase = Code::query()->max('id');
$nbCodeUsed = Code::query()->whereHas( $nbCodeUsed = Code::query()->whereHas(
'participations', 'participations',
@ -35,22 +38,23 @@ class CodeController extends Controller
)->count(); )->count();
$nbCodeExported = Code::query()->where('code_exported', true)->count(); $nbCodeExported = Code::query()->where('code_exported', true)->count();
$nbCodeRemainingToExport = Code::query()->where('code_exported', false)->count(); $nbCodeRemainingToExport = Code::query()->where('code_exported', false)->count();
return view('back::pages.codes.index', compact(
'settings', /** @var view-string $view */
'nbCodeBase', $view = 'back::pages.codes.index';
'nbCodeUsed',
'nbCodeExported', return view($view, [
'nbCodeRemainingToExport' 'settings' => $settings,
)); 'nbCodeBase' => $nbCodeBase,
'nbCodeUsed' => $nbCodeUsed,
'nbCodeExported' => $nbCodeExported,
'nbCodeRemainingToExport' => $nbCodeRemainingToExport,
]);
} }
/** /**
* Run generate codes action * Run generate codes action
*
* @param \App\Http\Requests\Bo\Codes\GenerateCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function generate(GenerateCodesRequest $request): \Illuminate\Http\RedirectResponse public function generate(GenerateCodesRequest $request): RedirectResponse
{ {
$this->authorize('generate', Code::class); $this->authorize('generate', Code::class);
Queue::push( Queue::push(
@ -66,7 +70,7 @@ class CodeController extends Controller
// phpcs:ignore Generic.Files.LineLength.TooLong // phpcs:ignore Generic.Files.LineLength.TooLong
'La tache de génération à été ajoutée, un mail de notification sera envoyé à l\'adresse :email lorsque le traitement sera terminé.', 'La tache de génération à été ajoutée, un mail de notification sera envoyé à l\'adresse :email lorsque le traitement sera terminé.',
[ [
'email' => config('mail.admin') 'email' => config('mail.admin'),
] ]
) )
); );
@ -74,11 +78,8 @@ class CodeController extends Controller
/** /**
* Run export codes * Run export codes
*
* @param \App\Http\Requests\Bo\Codes\ExportCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function export(ExportCodesRequest $request): \Illuminate\Http\RedirectResponse public function export(ExportCodesRequest $request): RedirectResponse
{ {
$this->authorize('export', Code::class); $this->authorize('export', Code::class);
Queue::push( Queue::push(
@ -93,25 +94,22 @@ class CodeController extends Controller
'success', 'success',
// phpcs:ignore Generic.Files.LineLength.TooLong // phpcs:ignore Generic.Files.LineLength.TooLong
__('L\'export des codes à commencé, un mail de notification sera envoyé à l\'adresse :emaillorsque le traitement sera terminé.', [ __('L\'export des codes à commencé, un mail de notification sera envoyé à l\'adresse :emaillorsque le traitement sera terminé.', [
'email' => config('mail.admin') 'email' => config('mail.admin'),
]) ])
); );
} }
/** /**
* Search for a specific code. * Search for a specific code.
*
* @param \App\Http\Requests\Bo\Codes\CodeSearchRequest $request
* @return \Illuminate\Contracts\View\View
*/ */
public function search(CodeSearchRequest $request): \Illuminate\Contracts\View\View public function search(CodeSearchRequest $request): View
{ {
$this->authorize('search', Code::class); $this->authorize('search', Code::class);
$code = null; $code = null;
try { try {
$code = Code::query()->where('id', Code::serialToInt($request->code))->first(); $code = Code::query()->where('id', Code::serialToInt($request->code))->first();
} catch (\Throwable $e) { } catch (Throwable $e) {
if (\get_class($e) === SerialCasterException::class) { if ($e::class === SerialCasterException::class) {
$code = false; $code = false;
} }
} finally { } finally {
@ -126,10 +124,13 @@ class CodeController extends Controller
$used_counter = null; $used_counter = null;
if ($code) { if ($code) {
$used_counter = Participation::query() $used_counter = Participation::query()
->whereHas('code', fn(Builder $q) => $q->where('id', $code->id)) ->whereHas('code', fn (Builder $q) => $q->where('id', $code->id))
->count(); ->count();
} }
return view('back::pages.codes.search', compact('code', 'used_counter')); /** @var view-string $view */
$view = 'back::pages.codes.search';
return view($view, ['code' => $code, 'used_counter' => $used_counter]);
} }
} }

View file

@ -1,60 +1,62 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Participation; use App\Models\Participation;
use App\Models\Settings; use App\Models\Settings;
use Carbon\Carbon; use Illuminate\Contracts\View\View;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
class DashboardController extends Controller class DashboardController extends Controller
{ {
/** /**
* Dashboard Page * Dashboard Page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function index(): \Illuminate\Contracts\View\View public function index(): View
{ {
// * Pour les stats. // * Pour les stats.
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
$avgParticipUser = 0; $avgParticipUser = 0;
$participationPeriod = [];
$statsUnique = []; $statsUnique = [];
$query = DB::table((new Participation())->getTable()); $query = DB::table(new Participation()->getTable());
$dateFrmtr = function (object $row) { $dateFrmtr = function (object $row): object {
$row->date = Carbon::parse($row->date)->isoFormat('DD/MM/YYYY'); $row->date = Date::parse($row->date)->isoFormat('DD/MM/YYYY');
return $row; return $row;
}; };
/** @var \Illuminate\Support\Collection<string,object> */ /** @var Collection<string, object> */
$participationsPerDay = (clone $query) $participationsPerDay = (clone $query)
->selectRaw('COUNT(*) AS count, DATE(created_at) AS date') ->selectRaw('COUNT(*) AS count, DATE(created_at) AS date')
->groupByRaw('DATE(created_at)')->get()->map($dateFrmtr)->keyBy('date'); ->groupByRaw('DATE(created_at)')->get()->map($dateFrmtr)->keyBy('date');
/** @var \Illuminate\Support\Collection<object> */ /** @var Collection<object> */
$participationsUniquePerDay = (clone $query) $participationsUniquePerDay = (clone $query)
->selectRaw('DISTINCT customer_id AS user, COUNT(*) AS count, DATE(created_at) AS date') ->selectRaw('DISTINCT customer_id AS user, COUNT(*) AS count, DATE(created_at) AS date')
->groupByRaw('DATE(created_at), user')->get()->map($dateFrmtr); ->groupByRaw('DATE(created_at), user')->get()->map($dateFrmtr);
/** @var \Illuminate\Support\Collection<object> */ /** @var Collection<object> */
$participationsUniquePerCustomer = (clone $query) $participationsUniquePerCustomer = (clone $query)
->selectRaw("DISTINCT customer_id AS user, COUNT(*) AS count") ->selectRaw('DISTINCT customer_id AS user, COUNT(*) AS count')
->groupBy('user')->orderBy('user')->get(); ->groupBy('user')->orderBy('user')->get();
$participationsTotal = Participation::query()->count(); $participationsTotal = Participation::query()->count();
/** @var \Illuminate\Support\Collection<object> 3 codes EAN les plus utilisés */ /** @var Collection<object> 3 codes EAN les plus utilisés */
$codesEan = (clone $query) $codesEan = (clone $query)
->selectRaw('COUNT(product_id) AS nb_usage, product_id, ean_code') ->selectRaw('COUNT(product_id) AS nb_usage, product_id, ean_code')
->join('products', 'product_id', '=', 'products.id') ->join('products', 'product_id', '=', 'products.id')
->groupBy('product_id', 'ean_code') ->groupBy('product_id', 'ean_code')
->orderByDesc('nb_usage')->limit(3)->get(); ->orderByDesc('nb_usage')->limit(3)->get();
$participationsUniquePerDay->each(function (object $row) use (&$statsUnique) { $participationsUniquePerDay->each(function (object $row) use (&$statsUnique): void {
Arr::set($statsUnique, $row->date, (Arr::get($statsUnique, $row->date) ?? 0) + 1); Arr::set($statsUnique, $row->date, (Arr::get($statsUnique, $row->date) ?? 0) + 1);
}); });
@ -62,23 +64,22 @@ class DashboardController extends Controller
if ($participationsUniquePerCustomer->count() > 0) { if ($participationsUniquePerCustomer->count() > 0) {
$avgParticipUser = round($participationsTotal / $participationsUniquePerCustomer->count(), 1); $avgParticipUser = round($participationsTotal / $participationsUniquePerCustomer->count(), 1);
} }
now()->lt($settings->end_date) ? now() : $settings->end_date;
// * Création des tableaux avec toutes les dates depuis le début du jeu
// * et le nombre de participations associées pour le graph JS.
$start_date = $settings->start_date;
$end_date = now()->lt($settings->end_date) ? now() : $settings->end_date;
$dates = $participationsPerDay->keys(); $dates = $participationsPerDay->keys();
$count = $participationsPerDay->pluck('count'); $count = $participationsPerDay->pluck('count');
$countUnique = $dates->map(fn (string $formated_date) => Arr::get($statsUnique, $formated_date, 0)); $countUnique = $dates->map(fn (string $formated_date) => Arr::get($statsUnique, $formated_date, 0));
return view('back::pages.dashboard.index', compact( /** @var view-string $view */
'dates', $view = 'back::pages.dashboard.index';
'count',
'countUnique', return view($view, [
'participationsTotal', 'dates' => $dates,
'avgParticipUser', 'count' => $count,
'codesEan', 'countUnique' => $countUnique,
'settings' 'participationsTotal' => $participationsTotal,
)); 'avgParticipUser' => $avgParticipUser,
'codesEan' => $codesEan,
'settings' => $settings,
]);
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -7,6 +9,9 @@ use App\Http\Requests\Bo\FaqQuestions\StoreFaqQuestionRequest;
use App\Http\Requests\Bo\FaqQuestions\UpdateFaqQuestionRequest; use App\Http\Requests\Bo\FaqQuestions\UpdateFaqQuestionRequest;
use App\Models\FaqQuestion; use App\Models\FaqQuestion;
use App\Models\FaqTheme; use App\Models\FaqTheme;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -23,13 +28,10 @@ class FaqQuestionController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $faqQuestionModels */ /** @var Builder $faqQuestionModels */
$faqQuestionModels = FaqQuestion::query(); $faqQuestionModels = FaqQuestion::query();
/** @var string $search Search field */ /** @var string $search Search field */
@ -55,38 +57,42 @@ class FaqQuestionController extends Controller
/** Custom pagination */ /** Custom pagination */
$faqQuestionModels = $this->paginate($faqQuestionModels); $faqQuestionModels = $this->paginate($faqQuestionModels);
return view('back::pages.faq-questions.index', compact('faqQuestionModels', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.faq-questions.index';
return view($view, [
'faqQuestionModels' => $faqQuestionModels,
'search' => $search,
'searchFields' => $searchFields,
]);
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Contracts\View\View
*/ */
public function create(FaqQuestion $faqQuestion): \Illuminate\Contracts\View\View public function create(FaqQuestion $faqQuestion): View
{ {
$faqThemeModels = FaqTheme::all(); $faqThemeModels = FaqTheme::all();
return view('back::pages.faq-questions.create', [
/** @var view-string $view */
$view = 'back::pages.faq-questions.create';
return view($view, [
'faqQuestionModel' => $faqQuestion, 'faqQuestionModel' => $faqQuestion,
'faqThemeModels' => $faqThemeModels 'faqThemeModels' => $faqThemeModels,
]); ]);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\FaqQuestions\StoreFaqQuestionRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(StoreFaqQuestionRequest $request): \Illuminate\Http\RedirectResponse public function store(StoreFaqQuestionRequest $request): RedirectResponse
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
$faqQuestion = new FaqQuestion(); $faqQuestion = new FaqQuestion();
$faqQuestion->fill($request->validated()); $faqQuestion->fill($request->validated());
if ($faqQuestion->saveOrFail()) { if ($faqQuestion->saveOrFail()) {
return redirect() return to_route('bo.faq-questions.edit', $faqQuestion)
->route('bo.faq-questions.edit', $faqQuestion)
->with( ->with(
'success', 'success',
trans( trans(
@ -95,8 +101,8 @@ class FaqQuestionController extends Controller
) )
); );
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_created', 'crud.messages.cannot_be_created',
['model' => trans_choice('models.classes.faq-questions', 1)] ['model' => trans_choice('models.classes.faq-questions', 1)]
@ -106,45 +112,38 @@ class FaqQuestionController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(FaqQuestion $faqQuestion): \Illuminate\Contracts\View\View public function edit(FaqQuestion $faqQuestion): View
{ {
$faqThemeModels = FaqTheme::all(); $faqThemeModels = FaqTheme::all();
return view('back::pages.faq-questions.edit', [
/** @var view-string $view */
$view = 'back::pages.faq-questions.edit';
return view($view, [
'faqQuestionModel' => $faqQuestion, 'faqQuestionModel' => $faqQuestion,
'faqThemeModels' => $faqThemeModels 'faqThemeModels' => $faqThemeModels,
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\FaqQuestions\UpdateFaqQuestionRequest $request
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update( public function update(
UpdateFaqQuestionRequest $request, UpdateFaqQuestionRequest $request,
FaqQuestion $faqQuestion FaqQuestion $faqQuestion
): \Illuminate\Http\RedirectResponse { ): RedirectResponse {
return DB::transaction(function () use ($request, $faqQuestion) { return DB::transaction(function () use ($request, $faqQuestion) {
$faqQuestion->fill($request->validated()); $faqQuestion->fill($request->validated());
if ($faqQuestion->saveOrFail()) { if ($faqQuestion->saveOrFail()) {
return redirect() return to_route('bo.faq-questions.edit', $faqQuestion)
->route(
'bo.faq-questions.edit',
$faqQuestion
)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_updated', 'crud.messages.has_been_updated',
['model' => trans_choice('models.classes.faq-questions', 1)] ['model' => trans_choice('models.classes.faq-questions', 1)]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_updated', 'crud.messages.cannot_be_updated',
['model' => trans_choice('models.classes.faq-questions', 1)] ['model' => trans_choice('models.classes.faq-questions', 1)]
@ -154,23 +153,19 @@ class FaqQuestionController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(FaqQuestion $faqQuestion): \Illuminate\Http\RedirectResponse public function destroy(FaqQuestion $faqQuestion): RedirectResponse
{ {
return DB::transaction(function () use ($faqQuestion) { return DB::transaction(function () use ($faqQuestion) {
if ($faqQuestion->deleteOrFail()) { if ($faqQuestion->deleteOrFail()) {
return redirect() return to_route('bo.faq-questions.index')
->route('bo.faq-questions.index')
->with('success', __( ->with('success', __(
'crud.messages.has_been_deleted', 'crud.messages.has_been_deleted',
['model' => trans_choice('models.classes.faq-questions', 1)] ['model' => trans_choice('models.classes.faq-questions', 1)]
)); ));
} }
return redirect()
->back() return back()
->with('error', __( ->with('error', __(
':crud.messages.cannot_be_deleted', ':crud.messages.cannot_be_deleted',
['model' => trans_choice('models.classes.faq-questions', 1)] ['model' => trans_choice('models.classes.faq-questions', 1)]

View file

@ -1,11 +1,16 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\FaqThemes\StoreFaqThemeRequest; use App\Http\Requests\Bo\FaqThemes\StoreFaqThemeRequest;
use App\Http\Requests\Bo\FaqThemes\UpdateFaqThemeRequest; use App\Http\Requests\Bo\FaqThemes\UpdateFaqThemeRequest;
use App\Models\FaqTheme; use App\Models\FaqTheme;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -22,13 +27,10 @@ class FaqThemeController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $faqThemeModels */ /** @var Builder $faqThemeModels */
$faqThemeModels = FaqTheme::query(); $faqThemeModels = FaqTheme::query();
/** @var string $search Search field */ /** @var string $search Search field */
@ -49,36 +51,35 @@ class FaqThemeController extends Controller
/** Custom pagination */ /** Custom pagination */
$faqThemeModels = $this->paginate($faqThemeModels); $faqThemeModels = $this->paginate($faqThemeModels);
return view('back::pages.faq-themes.index', compact('faqThemeModels', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.faq-themes.index';
return view($view, ['faqThemeModels' => $faqThemeModels, 'search' => $search, 'searchFields' => $searchFields]);
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Contracts\View\View
*/ */
public function create(FaqTheme $faqTheme): \Illuminate\Contracts\View\View public function create(FaqTheme $faqTheme): View
{ {
return view('back::pages.faq-themes.create', [ /** @var view-string $view */
$view = 'back::pages.faq-themes.create';
return view($view, [
'faqThemeModel' => $faqTheme, 'faqThemeModel' => $faqTheme,
]); ]);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\FaqThemes\StoreFaqThemeRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(StoreFaqThemeRequest $request): \Illuminate\Http\RedirectResponse public function store(StoreFaqThemeRequest $request): RedirectResponse
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
$faqTheme = new FaqTheme(); $faqTheme = new FaqTheme();
$faqTheme->fill($request->validated()); $faqTheme->fill($request->validated());
if ($faqTheme->saveOrFail()) { if ($faqTheme->saveOrFail()) {
return redirect() return to_route('bo.faq-themes.edit', $faqTheme)
->route('bo.faq-themes.edit', $faqTheme)
->with( ->with(
'success', 'success',
trans( trans(
@ -87,8 +88,8 @@ class FaqThemeController extends Controller
) )
); );
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_created', 'crud.messages.cannot_be_created',
['model' => trans_choice('models.classes.faq-themes', 1)] ['model' => trans_choice('models.classes.faq-themes', 1)]
@ -98,43 +99,35 @@ class FaqThemeController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(FaqTheme $faqTheme): \Illuminate\Contracts\View\View public function edit(FaqTheme $faqTheme): View
{ {
return view('back::pages.faq-themes.edit', [ /** @var view-string $view */
$view = 'back::pages.faq-themes.edit';
return view($view, [
'faqThemeModel' => $faqTheme, 'faqThemeModel' => $faqTheme,
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\FaqThemes\UpdateFaqThemeRequest $request
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update( public function update(
UpdateFaqThemeRequest $request, UpdateFaqThemeRequest $request,
FaqTheme $faqTheme FaqTheme $faqTheme
): \Illuminate\Http\RedirectResponse { ): RedirectResponse {
return DB::transaction(function () use ($request, $faqTheme) { return DB::transaction(function () use ($request, $faqTheme) {
$faqTheme->fill($request->validated()); $faqTheme->fill($request->validated());
if ($faqTheme->saveOrFail()) { if ($faqTheme->saveOrFail()) {
return redirect() return to_route('bo.faq-themes.edit', $faqTheme)
->route(
'bo.faq-themes.edit',
$faqTheme
)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_updated', 'crud.messages.has_been_updated',
['model' => trans_choice('models.classes.faq-themes', 1)] ['model' => trans_choice('models.classes.faq-themes', 1)]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_updated', 'crud.messages.cannot_be_updated',
['model' => trans_choice('models.classes.faq-themes', 1)] ['model' => trans_choice('models.classes.faq-themes', 1)]
@ -144,23 +137,19 @@ class FaqThemeController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(FaqTheme $faqTheme): \Illuminate\Http\RedirectResponse public function destroy(FaqTheme $faqTheme): RedirectResponse
{ {
return DB::transaction(function () use ($faqTheme) { return DB::transaction(function () use ($faqTheme) {
if ($faqTheme->deleteOrFail()) { if ($faqTheme->deleteOrFail()) {
return redirect() return to_route('bo.faq-themes.index')
->route('bo.faq-themes.index')
->with('success', __( ->with('success', __(
'crud.messages.has_been_deleted', 'crud.messages.has_been_deleted',
['model' => trans_choice('models.classes.faq-themes', 1)] ['model' => trans_choice('models.classes.faq-themes', 1)]
)); ));
} }
return redirect()
->back() return back()
->with('error', __( ->with('error', __(
'crud.messages.cannot_be_deleted', 'crud.messages.cannot_be_deleted',
['model' => trans_choice('models.classes.faq-themes', 1)] ['model' => trans_choice('models.classes.faq-themes', 1)]

View file

@ -1,15 +1,21 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\InstantWin; use App\Models\InstantWin;
use App\Models\Prize; use App\Models\Prize;
use App\Models\Settings; use App\Models\Settings;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use Carbon\CarbonInterval; use Carbon\CarbonInterval;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use stdClass;
class InstantWinController extends Controller class InstantWinController extends Controller
{ {
@ -23,22 +29,21 @@ class InstantWinController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/ */
public function index(): \Illuminate\Contracts\View\View public function index(): View
{ {
$instantWinModelList = InstantWin::query()->orderBy('available_at')->get(); $instantWinModelList = InstantWin::query()->oldest('available_at')->get();
return view('back::pages.instant-win.index', [ /** @var view-string $view */
'instantWinModelList' => $instantWinModelList $view = 'back::pages.instant-win.index';
return view($view, [
'instantWinModelList' => $instantWinModelList,
]); ]);
} }
/** /**
* Returns generated instant win. * Returns generated instant win.
*
* @return \Illuminate\Http\JsonResponse
*/ */
public function generate(): JsonResponse public function generate(): JsonResponse
{ {
@ -56,23 +61,20 @@ class InstantWinController extends Controller
*/ */
$day_end = now()->setTime(22, 29); $day_end = now()->setTime(22, 29);
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
$prizeModelList = Prize::all(); $prizeModelList = Prize::all();
/** @var \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinDayCalendar> */ /** @var Collection<array-key, WinDayCalendar> */
$datesGlobal = collect(); $datesGlobal = collect();
/** /** @var Collection<array-key, WinTimeElement> Liste de tous les lots à gagner */
* @var \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinTimeElement>
* Liste de tous les lots à gagner
*/
$lotsGlobal = collect(); $lotsGlobal = collect();
// * Seed srand ! // * Seed srand !
\mt_srand(time()); \mt_srand(time());
// * Init prize list // * Init prize list
$prizeModelList->each(function (Prize $prize) use ($lotsGlobal, $day_start, $day_end) { $prizeModelList->each(function (Prize $prize) use ($lotsGlobal, $day_start, $day_end): void {
for ($i = 0; $i < $prize->quantity; $i++) { for ($i = 0; $i < $prize->quantity; $i++) {
$el = new WinTimeElement(); $el = new WinTimeElement();
$el->prize = $prize; $el->prize = $prize;
@ -84,7 +86,7 @@ class InstantWinController extends Controller
} }
}); });
$lotsGlobal->shuffle(time()); $lotsGlobal->shuffle();
// * Init days calendar // * Init days calendar
foreach ($settings->start_date->toPeriod($settings->end_date, CarbonInterval::make('1day')) as $date) { foreach ($settings->start_date->toPeriod($settings->end_date, CarbonInterval::make('1day')) as $date) {
@ -95,8 +97,8 @@ class InstantWinController extends Controller
} }
// * Save Instant wins // * Save Instant wins
$this->generateArray($lotsGlobal, $datesGlobal, 0)->each(function (WinDayCalendar $winDayCalendar) { $this->generateArray($lotsGlobal, $datesGlobal, 0)->each(function (WinDayCalendar $winDayCalendar): void {
$winDayCalendar->lots->each(function (WinTimeElement $winTimeElement) { $winDayCalendar->lots->each(function (WinTimeElement $winTimeElement): void {
$instantWin = new InstantWin(); $instantWin = new InstantWin();
$instantWin->available_at = $winTimeElement->time; $instantWin->available_at = $winTimeElement->time;
$instantWin->prize()->associate($winTimeElement->prize); $instantWin->prize()->associate($winTimeElement->prize);
@ -114,11 +116,11 @@ class InstantWinController extends Controller
* *
* Même algo que celui implémenté par Romain. * Même algo que celui implémenté par Romain.
* *
* @param \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinTimeElement> $lotsGlobal * @param Collection<array-key, WinTimeElement> $lotsGlobal
* Liste de tous les lots à gagner * Liste de tous les lots à gagner
* @param \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinDayCalendar> $dateGlobal * @param Collection<array-key, WinDayCalendar> $dateGlobal
* @param integer $prizesDone * @return Collection<array-key, WinDayCalendar>
* @return \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinDayCalendar> *
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/ */
private function generateArray(Collection $lotsGlobal, Collection $dateGlobal, int $prizesDone): Collection private function generateArray(Collection $lotsGlobal, Collection $dateGlobal, int $prizesDone): Collection
@ -129,7 +131,7 @@ class InstantWinController extends Controller
// ? À-t-on déjà assigné un lot par jour (au moins) ? // ? À-t-on déjà assigné un lot par jour (au moins) ?
// ? Le jour aléatoire sélectionné à déja un lot ? // ? Le jour aléatoire sélectionné à déja un lot ?
if ($prizesDone < $dateGlobal->count() and $dateGlobal->get($rKey)->lots->count() > 0) { if ($prizesDone < $dateGlobal->count() && $dateGlobal->get($rKey)->lots->count() > 0) {
return $this->generateArray($lotsGlobal, $dateGlobal, $prizesDone); return $this->generateArray($lotsGlobal, $dateGlobal, $prizesDone);
} }
@ -142,15 +144,15 @@ class InstantWinController extends Controller
} }
// * Tri par heure et assignation du jour correct. // * Tri par heure et assignation du jour correct.
$dateGlobal->each(function (WinDayCalendar $date) { $dateGlobal->each(function (WinDayCalendar $date): void {
$date->lots = $date->lots->sort(fn (WinTimeElement $a, WinTimeElement $b) => $date->lots = $date->lots->sort(fn (WinTimeElement $a, WinTimeElement $b): int | float => $a->time->timestamp - $b->time->timestamp)
$a->time->timestamp - $b->time->timestamp) ->map(function (WinTimeElement $time) use ($date): WinTimeElement {
->map(function (WinTimeElement $time) use ($date) {
$time->time->setDate( $time->time->setDate(
$date->time->year, $date->time->year,
$date->time->month, $date->time->month,
$date->time->day $date->time->day
); );
return $time; return $time;
}); });
}); });
@ -160,23 +162,25 @@ class InstantWinController extends Controller
} }
/** /**
* @property \App\Models\Prize $prize Le lot à gagner. * @property Prize $prize Le lot à gagner.
* @property \Carbon\Carbon $time Une heure aléatoire. * @property Carbon $time Une heure aléatoire.
*
* @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses * @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
*/ */
class WinTimeElement extends \stdClass class WinTimeElement extends stdClass
{ {
// phpcs:enable // phpcs:enable
} }
/** /**
* @property \Carbon\Carbon|\Carbon\CarbonInterface $time * @property Carbon|CarbonInterface $time
* Un jour dans le temps. * Un jour dans le temps.
* @property \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinTimeElement> $lots * @property Collection<array-key, WinTimeElement> $lots
* Liste des lots tombant ce jour. * Liste des lots tombant ce jour.
*
* @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses * @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
*/ */
class WinDayCalendar extends \stdClass class WinDayCalendar extends stdClass
{ {
// phpcs:enable // phpcs:enable
} }

View file

@ -1,10 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Participation; use App\Models\Participation;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
class ParticipationController extends Controller class ParticipationController extends Controller
@ -19,15 +22,15 @@ class ParticipationController extends Controller
/** /**
* View all winners * View all winners
*
* @return \Illuminate\Contracts\View\View
*/ */
public function viewWinnerList(): View public function viewWinnerList(): View
{ {
$this->authorize('viewWinnerList', Participation::class); $this->authorize('viewWinnerList', Participation::class);
/** @var \Illuminate\Database\Eloquent\Builder $participationModels */ /** @var Builder $participationModels */
$participationModels = Participation::query()->with(['customer', 'code', 'instantWin', 'product'])->whereHas('instantWin'); $participationModels = Participation::query()
->with(['customer', 'code', 'instantWin', 'product'])
->whereHas('instantWin');
/** Sort columns with a query */ /** Sort columns with a query */
$this->sortQuery($participationModels); $this->sortQuery($participationModels);
@ -35,36 +38,37 @@ class ParticipationController extends Controller
/** Custom pagination */ /** Custom pagination */
$participationModels = $this->paginate($participationModels); $participationModels = $this->paginate($participationModels);
return view('back::pages.winners-list.index', compact( /** @var view-string $view */
'participationModels' $view = 'back::pages.winners-list.index';
));
return view($view, ['participationModels' => $participationModels]);
} }
/** /**
* Display participation form * Display participation form
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Contracts\View\View
*/ */
public function setComment(Participation $participation): View public function setComment(Participation $participation): View
{ {
$this->authorize('setComment', $participation); $this->authorize('setComment', $participation);
return view('back::pages.winners-list.set-comment', [
'participationModel' => $participation /** @var view-string $view */
$view = 'back::pages.winners-list.set-comment';
return view($view, [
'participationModel' => $participation,
]); ]);
} }
/** /**
* Set Comment * Set Comment
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Http\RedirectResponse
*/ */
public function setCommentSubmit(Participation $participation): RedirectResponse public function setCommentSubmit(Participation $participation): RedirectResponse
{ {
$this->authorize('setComment', $participation); $this->authorize('setComment', $participation);
$participation->comment = request()->string('comment');
$participation->comment = request()->string('comment')->toString();
$participation->saveOrFail(); $participation->saveOrFail();
return back()->with('success', \trans( return back()->with('success', \trans(
'Le commentaire à bien été mis à jour' 'Le commentaire à bien été mis à jour'
)); ));
@ -72,15 +76,13 @@ class ParticipationController extends Controller
/** /**
* Change prize sent * Change prize sent
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Http\RedirectResponse
*/ */
public function changePrizeSent(Participation $participation): RedirectResponse public function changePrizeSent(Participation $participation): RedirectResponse
{ {
$this->authorize('changePrizeSent', $participation); $this->authorize('changePrizeSent', $participation);
$participation->prize_sent = true; $participation->prize_sent = true;
$participation->saveOrFail(); $participation->saveOrFail();
return back()->with('success', \trans( return back()->with('success', \trans(
'Le lot est bien marqué comme envoyé et une notification à été envoyé au gagant' 'Le lot est bien marqué comme envoyé et une notification à été envoyé au gagant'
)); ));

View file

@ -1,11 +1,16 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Prizes\StorePrizeRequest; use App\Http\Requests\Bo\Prizes\StorePrizeRequest;
use App\Http\Requests\Bo\Prizes\UpdatePrizeRequest; use App\Http\Requests\Bo\Prizes\UpdatePrizeRequest;
use App\Models\Prize; use App\Models\Prize;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -22,13 +27,10 @@ class PrizeController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $prizeModels */ /** @var Builder $prizeModels */
$prizeModels = Prize::query(); $prizeModels = Prize::query();
/** @var string $search Search field */ /** @var string $search Search field */
@ -52,54 +54,53 @@ class PrizeController extends Controller
/** Custom pagination */ /** Custom pagination */
$prizeModels = $this->paginate($prizeModels); $prizeModels = $this->paginate($prizeModels);
return view('back::pages.prizes.index', compact('prizeModels', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.prizes.index';
return view($view, ['prizeModels' => $prizeModels, 'search' => $search, 'searchFields' => $searchFields]);
} }
/** /**
* Display the specified resource. * Display the specified resource.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/ */
public function show(Prize $prize): \Illuminate\Contracts\View\View public function show(Prize $prize): View
{ {
return view('back::pages.prizes.show', ['prizeModel' => $prize]); /** @var view-string $view */
$view = 'back::pages.prizes.show';
return view($view, ['prizeModel' => $prize]);
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/ */
public function create(Prize $prize): \Illuminate\Contracts\View\View public function create(Prize $prize): View
{ {
return view('back::pages.prizes.create', [ /** @var view-string $view */
'prizeModel' => $prize $view = 'back::pages.prizes.create';
return view($view, [
'prizeModel' => $prize,
]); ]);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Prizes\StorePrizeRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(StorePrizeRequest $request): \Illuminate\Http\RedirectResponse public function store(StorePrizeRequest $request): RedirectResponse
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
$prize = new Prize(); $prize = new Prize();
$prize->fill($request->validated()); $prize->fill($request->validated());
if ($prize->saveOrFail()) { if ($prize->saveOrFail()) {
return redirect() return to_route('bo.prizes.edit', $prize)
->route('bo.prizes.edit', $prize)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_created', 'crud.messages.has_been_created',
['model' => Str::singular(__('models.classes.prize'))] ['model' => Str::singular(__('models.classes.prize'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_created', 'crud.messages.cannot_be_created',
['model' => Str::singular(__('models.classes.prize'))] ['model' => Str::singular(__('models.classes.prize'))]
@ -109,38 +110,33 @@ class PrizeController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(Prize $prize): \Illuminate\Contracts\View\View public function edit(Prize $prize): View
{ {
return view('back::pages.prizes.edit', [ /** @var view-string $view */
'prizeModel' => $prize $view = 'back::pages.prizes.edit';
return view($view, [
'prizeModel' => $prize,
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Prizes\UpdatePrizeRequest $request
* @param \App\Models\Prize $prize
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update(UpdatePrizeRequest $request, Prize $prize): \Illuminate\Http\RedirectResponse public function update(UpdatePrizeRequest $request, Prize $prize): RedirectResponse
{ {
return DB::transaction(function () use ($request, $prize) { return DB::transaction(function () use ($request, $prize) {
$prize->fill($request->validated()); $prize->fill($request->validated());
if ($prize->saveOrFail()) { if ($prize->saveOrFail()) {
return redirect() return to_route('bo.prizes.edit', $prize)
->route('bo.prizes.edit', $prize)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_updated', 'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.prize'))] ['model' => Str::singular(__('models.classes.prize'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_updated', 'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.prize'))] ['model' => Str::singular(__('models.classes.prize'))]
@ -150,23 +146,19 @@ class PrizeController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(Prize $prize): \Illuminate\Http\RedirectResponse public function destroy(Prize $prize): RedirectResponse
{ {
return DB::transaction(function () use ($prize) { return DB::transaction(function () use ($prize) {
if ($prize->deleteOrFail()) { if ($prize->deleteOrFail()) {
return redirect() return to_route('bo.prizes.index')
->route('bo.prizes.index')
->with('success', __( ->with('success', __(
'crud.messages.has_been_deleted', 'crud.messages.has_been_deleted',
['model' => Str::singular(__('models.classes.prize'))] ['model' => Str::singular(__('models.classes.prize'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', __( ->with('error', __(
':crud.messages.cannot_be_deleted', ':crud.messages.cannot_be_deleted',
['model' => Str::singular(__('models.classes.prize'))] ['model' => Str::singular(__('models.classes.prize'))]

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -7,6 +9,9 @@ use App\Http\Requests\Bo\Products\StoreProductRequest;
use App\Http\Requests\Bo\Products\UpdateProductRequest; use App\Http\Requests\Bo\Products\UpdateProductRequest;
use App\Models\Product; use App\Models\Product;
use App\Models\Range; use App\Models\Range;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -23,13 +28,10 @@ class ProductController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $productModels */ /** @var Builder $productModels */
$productModels = Product::query()->with('range'); $productModels = Product::query()->with('range');
/** @var string $search Search field */ /** @var string $search Search field */
@ -55,63 +57,63 @@ class ProductController extends Controller
/** Custom pagination */ /** Custom pagination */
$productModels = $this->paginate($productModels); $productModels = $this->paginate($productModels);
return view('back::pages.products.index', compact('productModels', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.products.index';
return view($view, ['productModels' => $productModels, 'search' => $search, 'searchFields' => $searchFields]);
} }
/** /**
* Display the specified resource. * Display the specified resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/ */
public function show(Product $product): \Illuminate\Contracts\View\View public function show(Product $product): View
{ {
return view('back::pages.products.show', ['productModel' => $product]); /** @var view-string $view */
$view = 'back::pages.products.show';
return view($view, ['productModel' => $product]);
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/ */
public function create(Product $product): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse public function create(Product $product): View | RedirectResponse
{ {
$rangeModels = Range::all(); $rangeModels = Range::all();
if (!count($rangeModels)) { if (! count($rangeModels)) {
return \redirect()->route('bo.ranges.index') return to_route('bo.ranges.index')
->with( ->with(
'error', 'error',
'Vous devez ajouter une gamme avant de pouvoir ajouter un produit' 'Vous devez ajouter une gamme avant de pouvoir ajouter un produit'
); );
} }
return view('back::pages.products.create', [
/** @var view-string $view */
$view = 'back::pages.products.create';
return view($view, [
'productModel' => $product, 'productModel' => $product,
'rangeModels' => $rangeModels 'rangeModels' => $rangeModels,
]); ]);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Products\StoreProductRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(StoreProductRequest $request): \Illuminate\Http\RedirectResponse public function store(StoreProductRequest $request): RedirectResponse
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
$product = new Product(); $product = new Product();
$product->fill($request->validated()); $product->fill($request->validated());
if ($product->saveOrFail()) { if ($product->saveOrFail()) {
return redirect() return to_route('bo.products.edit', $product)
->route('bo.products.edit', $product)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_created', 'crud.messages.has_been_created',
['model' => Str::singular(__('models.classes.product'))] ['model' => Str::singular(__('models.classes.product'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_created', 'crud.messages.cannot_be_created',
['model' => Str::singular(__('models.classes.product'))] ['model' => Str::singular(__('models.classes.product'))]
@ -121,40 +123,36 @@ class ProductController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(Product $product): \Illuminate\Contracts\View\View public function edit(Product $product): View
{ {
$rangeModels = Range::all(); $rangeModels = Range::all();
return view('back::pages.products.edit', [
/** @var view-string $view */
$view = 'back::pages.products.edit';
return view($view, [
'productModel' => $product, 'productModel' => $product,
'rangeModels' => $rangeModels 'rangeModels' => $rangeModels,
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Products\UpdateProductRequest $request
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update(UpdateProductRequest $request, Product $product): \Illuminate\Http\RedirectResponse public function update(UpdateProductRequest $request, Product $product): RedirectResponse
{ {
return DB::transaction(function () use ($request, $product) { return DB::transaction(function () use ($request, $product) {
$product->fill($request->validated()); $product->fill($request->validated());
if ($product->saveOrFail()) { if ($product->saveOrFail()) {
return redirect() return to_route('bo.products.edit', $product)
->route('bo.products.edit', $product)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_updated', 'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.product'))] ['model' => Str::singular(__('models.classes.product'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_updated', 'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.product'))] ['model' => Str::singular(__('models.classes.product'))]
@ -164,23 +162,19 @@ class ProductController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(Product $product): \Illuminate\Http\RedirectResponse public function destroy(Product $product): RedirectResponse
{ {
return DB::transaction(function () use ($product) { return DB::transaction(function () use ($product) {
if ($product->deleteOrFail()) { if ($product->deleteOrFail()) {
return redirect() return to_route('bo.products.index')
->route('bo.products.index')
->with('success', __( ->with('success', __(
'crud.messages.has_been_deleted', 'crud.messages.has_been_deleted',
['model' => Str::singular(__('models.classes.product'))] ['model' => Str::singular(__('models.classes.product'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', __( ->with('error', __(
':crud.messages.cannot_be_deleted', ':crud.messages.cannot_be_deleted',
['model' => Str::singular(__('models.classes.product'))] ['model' => Str::singular(__('models.classes.product'))]

View file

@ -1,11 +1,16 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Ranges\StoreRangeRequest; use App\Http\Requests\Bo\Ranges\StoreRangeRequest;
use App\Http\Requests\Bo\Ranges\UpdateRangeRequest; use App\Http\Requests\Bo\Ranges\UpdateRangeRequest;
use App\Models\Range; use App\Models\Range;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -22,13 +27,10 @@ class RangeController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $rangeModels */ /** @var Builder $rangeModels */
$rangeModels = Range::query(); $rangeModels = Range::query();
/** @var string $search Search field */ /** @var string $search Search field */
@ -49,54 +51,53 @@ class RangeController extends Controller
/** Custom pagination */ /** Custom pagination */
$rangeModels = $this->paginate($rangeModels); $rangeModels = $this->paginate($rangeModels);
return view('back::pages.ranges.index', compact('rangeModels', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.ranges.index';
return view($view, ['rangeModels' => $rangeModels, 'search' => $search, 'searchFields' => $searchFields]);
} }
/** /**
* Display the specified resource. * Display the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/ */
public function show(Range $range): \Illuminate\Contracts\View\View public function show(Range $range): View
{ {
return view('back::pages.ranges.show', ['rangeModel' => $range]); /** @var view-string $view */
$view = 'back::pages.ranges.show';
return view($view, ['rangeModel' => $range]);
} }
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/ */
public function create(Range $range): \Illuminate\Contracts\View\View public function create(Range $range): View
{ {
return view('back::pages.ranges.create', [ /** @var view-string $view */
$view = 'back::pages.ranges.create';
return view($view, [
'rangeModel' => $range, 'rangeModel' => $range,
]); ]);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Ranges\StoreRangeRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(StoreRangeRequest $request): \Illuminate\Http\RedirectResponse public function store(StoreRangeRequest $request): RedirectResponse
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
$range = new Range(); $range = new Range();
$range->fill($request->validated()); $range->fill($request->validated());
if ($range->saveOrFail()) { if ($range->saveOrFail()) {
return redirect() return to_route('bo.ranges.edit', $range)
->route('bo.ranges.edit', $range)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_created', 'crud.messages.has_been_created',
['model' => Str::singular(__('models.classes.range'))] ['model' => Str::singular(__('models.classes.range'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_created', 'crud.messages.cannot_be_created',
['model' => Str::singular(__('models.classes.range'))] ['model' => Str::singular(__('models.classes.range'))]
@ -106,38 +107,33 @@ class RangeController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(Range $range): \Illuminate\Contracts\View\View public function edit(Range $range): View
{ {
return view('back::pages.ranges.edit', [ /** @var view-string $view */
'rangeModel' => $range $view = 'back::pages.ranges.edit';
return view($view, [
'rangeModel' => $range,
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Ranges\UpdateRangeRequest $request
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update(UpdateRangeRequest $request, Range $range): \Illuminate\Http\RedirectResponse public function update(UpdateRangeRequest $request, Range $range): RedirectResponse
{ {
return DB::transaction(function () use ($request, $range) { return DB::transaction(function () use ($request, $range) {
$range->fill($request->validated()); $range->fill($request->validated());
if ($range->saveOrFail()) { if ($range->saveOrFail()) {
return redirect() return to_route('bo.ranges.edit', $range)
->route('bo.ranges.edit', $range)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_updated', 'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.range'))] ['model' => Str::singular(__('models.classes.range'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_updated', 'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.range'))] ['model' => Str::singular(__('models.classes.range'))]
@ -147,28 +143,25 @@ class RangeController extends Controller
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(Range $range): \Illuminate\Http\RedirectResponse public function destroy(Range $range): RedirectResponse
{ {
if ($range->products()->count()) { if ($range->products()->count()) {
return back()->withError( return back()->withError(
'Cette gamme est associé a des produits, la supression n\'est pas possible' 'Cette gamme est associé a des produits, la supression n\'est pas possible'
); );
} }
return DB::transaction(function () use ($range) { return DB::transaction(function () use ($range) {
if ($range->deleteOrFail()) { if ($range->deleteOrFail()) {
return redirect() return to_route('bo.ranges.index')
->route('bo.ranges.index')
->with('success', __( ->with('success', __(
'crud.messages.has_been_deleted', 'crud.messages.has_been_deleted',
['model' => Str::singular(__('models.classes.range'))] ['model' => Str::singular(__('models.classes.range'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', __( ->with('error', __(
':crud.messages.cannot_be_deleted', ':crud.messages.cannot_be_deleted',
['model' => Str::singular(__('models.classes.range'))] ['model' => Str::singular(__('models.classes.range'))]

View file

@ -1,16 +1,20 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Lib\Discount;
use App\Mail\Won\DiscountWonMail; use App\Mail\Won\DiscountWonMail;
use App\Mail\Won\PrizeIsSentMail; use App\Mail\Won\PrizeIsSentMail;
use App\Mail\Won\PrizeWonMail; use App\Mail\Won\PrizeWonMail;
use App\Mail\Won\WinningProofSubmitedMail; use App\Mail\Won\WinningProofSubmitedMail;
use App\Models\Participation;
use App\Models\Prize; use App\Models\Prize;
use App\Models\Settings; use App\Models\Settings;
use App\Lib\Discount; use App\Models\User;
use App\Models\Participation; use Closure;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\Response; use Illuminate\Http\Response;
@ -21,209 +25,216 @@ class Re7Controller extends Controller
{ {
/** /**
* Re7Controller * Re7Controller
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
$this->middleware(function (Request $request, \Closure $next) { $this->middleware(function (Request $request, Closure $next) {
$this->authorize('fo.showRe7'); $this->authorize('fo.showRe7');
return $next($request); return $next($request);
}); });
} }
/** /**
* Show Home page * Show Home page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function home(): View public function home(): View
{ {
return view('theme::pages.home'); /** @var view-string $view */
$view = 'theme::pages.home';
return view($view);
} }
/** /**
* Show Teaser page * Show Teaser page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function teaser(): View public function teaser(): View
{ {
return view('theme::pages.teaser'); /** @var view-string $view */
$view = 'theme::pages.teaser';
return view($view);
} }
/** /**
* Show Teaser page * Show Teaser page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function endGame(): View public function endGame(): View
{ {
return view('theme::pages.end-game'); /** @var view-string $view */
$view = 'theme::pages.end-game';
return view($view);
} }
/** /**
* Show code input page * Show code input page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function participate(): View public function participate(): View
{ {
return view('theme::pages.participation.participate'); /** @var view-string $view */
$view = 'theme::pages.participation.participate';
return view($view);
} }
/** /**
* Show code input page * Show code input page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function participationExhausted(): View public function participationExhausted(): View
{ {
return view('theme::pages.participation.participation-exhausted'); /** @var view-string $view */
$view = 'theme::pages.participation.participation-exhausted';
return view($view);
} }
// * -- Participation // * -- Participation
/** /**
* Show prize page * Show prize page
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/ */
public function prize(Prize $prize): View public function prize(Prize $prize): View
{ {
// * Fake participation model // * Fake participation model
$participationModel = new Participation(); $participationModel = new Participation();
$participationModel = 9999999; $participationModel = 9999999;
return view('theme::pages.participation.won', [
'prizeModel' => $prize, /** @var view-string $view */
'participationModel' => $participationModel $view = 'theme::pages.participation.won';
return view($view, [
'prizeModel' => $prize,
'participationModel' => $participationModel,
]); ]);
} }
/** /**
* Show discount page * Show discount page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function discount(): View public function discount(): View
{ {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
/** @var \App\Lib\Discount */ /** @var Discount */
$discount = Arr::get( $discount = Arr::get(
$settings->web_coupons, $settings->web_coupons,
$this->getRandomWeightedElementIndex($settings->web_coupons) $this->getRandomWeightedElementIndex($settings->web_coupons)
); );
if (!$settings->discounts_activated) { if (! $settings->discounts_activated) {
return abort(404); return abort(404);
} }
$mesBrDiscount = new Discount(); $mesBrDiscount = new Discount();
return view('theme::pages.participation.won', compact('discount', 'mesBrDiscount'));
/** @var view-string $view */
$view = 'theme::pages.participation.won';
return view($view, ['discount' => $discount, 'mesBrDiscount' => $mesBrDiscount]);
} }
/** /**
* Show loose page * Show loose page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function loose(): View public function loose(): View
{ {
return view('theme::pages.participation.loose'); /** @var view-string $view */
$view = 'theme::pages.home';
return view($view);
} }
// * -- END Participation // * -- END Participation
/** /**
* View won prize mail * View won prize mail
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/ */
public function mailPrize(Prize $prize): View public function mailPrize(Prize $prize): View
{ {
return view('back::pages.re7-preview-mail', [ /** @var view-string $view */
$view = 'back::pages.re7-preview-mail';
return view($view, [
'render_url' => route('re7.mail-print', [ 'render_url' => route('re7.mail-print', [
'type' => 'prize', 'type' => 'prize',
'prize' => $prize 'prize' => $prize,
]) ]),
]); ]);
} }
/** /**
* View won discount mail * View won discount mail
*
* @return \Illuminate\Contracts\View\View
*/ */
public function mailDiscount(): View public function mailDiscount(): View
{ {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
if (!$settings->discounts_activated) { if (! $settings->discounts_activated) {
return abort(404); return abort(404);
} }
return view('back::pages.re7-preview-mail', [ /** @var view-string $view */
$view = 'back::pages.re7-preview-mail';
return view($view, [
'render_url' => route('re7.mail-print', [ 'render_url' => route('re7.mail-print', [
'type' => 'discount' 'type' => 'discount',
]) ]),
]); ]);
} }
/** /**
* View proof mail received mail * View proof mail received mail
*
* @return \Illuminate\Contracts\View\View
*/ */
public function mailProofReceived(): View public function mailProofReceived(): View
{ {
return view('back::pages.re7-preview-mail', [ /** @var view-string $view */
$view = 'back::pages.re7-preview-mail';
return view($view, [
'render_url' => route('re7.mail-print', [ 'render_url' => route('re7.mail-print', [
'type' => 'proof' 'type' => 'proof',
]) ]),
]); ]);
} }
/** /**
* View sent prize mail * View sent prize mail
*
* @return \Illuminate\Contracts\View\View
*/ */
public function mailPrizeSent(): View public function mailPrizeSent(): View
{ {
return view('back::pages.re7-preview-mail', [ /** @var view-string $view */
$view = 'back::pages.re7-preview-mail';
return view($view, [
'render_url' => route('re7.mail-print', [ 'render_url' => route('re7.mail-print', [
'type' => 'prize-sent' 'type' => 'prize-sent',
]) ]),
]); ]);
} }
/** /**
* Print a specific mail * Print a specific mail
*
* @param string $type
* @param \App\Models\Prize|null $prize
* @return \Illuminate\Http\Response
*/ */
public function mailPrint(string $type, Prize|null $prize = null): Response public function mailPrint(string $type, ?Prize $prize = null): Response
{ {
/** @var \App\Models\User */ /** @var User */
$user = auth('backend')->user(); $user = auth('backend')->user();
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
switch ($type) { switch ($type) {
case 'discount': case 'discount':
/** @var \App\Lib\Discount */ /** @var Discount */
$discount = Arr::get( $discount = Arr::get(
$settings->web_coupons, $settings->web_coupons,
$this->getRandomWeightedElementIndex($settings->web_coupons) $this->getRandomWeightedElementIndex($settings->web_coupons)
); );
return response((new DiscountWonMail( return response(new DiscountWonMail(
new Address( new Address(
$user->email, $user->email,
$user->first_name $user->first_name
@ -232,9 +243,9 @@ class Re7Controller extends Controller
$discount, $discount,
config('app.url'), config('app.url'),
$settings->company, $settings->company,
))->render()); )->render());
case 'prize': case 'prize':
return response((new PrizeWonMail( return response(new PrizeWonMail(
new Address( new Address(
$user->email, $user->email,
$user->first_name $user->first_name
@ -243,27 +254,27 @@ class Re7Controller extends Controller
$prize, $prize,
config('app.url'), config('app.url'),
$settings->company, $settings->company,
))->render()); )->render());
case 'prize-sent': case 'prize-sent':
return response((new PrizeIsSentMail( return response(new PrizeIsSentMail(
new Address( new Address(
$user->email, $user->email,
$user->first_name $user->first_name
), ),
$settings->name, $settings->name,
$settings->company, $settings->company,
))->render()); )->render());
case 'proof': case 'proof':
return response((new WinningProofSubmitedMail( return response(new WinningProofSubmitedMail(
new Address( new Address(
$user->email, $user->email,
$user->first_name $user->first_name
), ),
$settings->name, $settings->name,
$settings->company, $settings->company,
))->render()); )->render());
default: default:
return response('', 404); return response('', 404);
} //end switch } // end switch
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -7,9 +9,14 @@ use App\Http\Requests\Bo\Settings\UpdateSettingsRequest;
use App\Lib\Discount; use App\Lib\Discount;
use App\Lib\Helpers\FileStorageHelper; use App\Lib\Helpers\FileStorageHelper;
use App\Models\Settings; use App\Models\Settings;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use RuntimeException;
use TypeError;
class SettingsController extends Controller class SettingsController extends Controller
{ {
@ -23,45 +30,39 @@ class SettingsController extends Controller
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\Settings $settings
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(Settings $settings): \Illuminate\Contracts\View\View public function edit(Settings $settings): View
{ {
return view('back::pages.settings.edit', [ /** @var view-string $view */
'settingsModel' => $settings $view = 'back::pages.settings.edit';
return view($view, [
'settingsModel' => $settings,
]); ]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Settings\UpdateSettingsRequest $request
* @param \App\Models\Settings $settings
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update(UpdateSettingsRequest $request, Settings $settings): \Illuminate\Http\RedirectResponse public function update(UpdateSettingsRequest $request, Settings $settings): RedirectResponse
{ {
return DB::transaction(function () use ($request, $settings) { return DB::transaction(function () use ($request, $settings) {
$data = $request->validated(); $data = $request->validated();
// * Assign parsed Web coupon objects // * Assign parsed Web coupon objects
Arr::set($data, 'web_coupons', collect(Arr::get($data, 'web_coupons'))->filter() Arr::set($data, 'web_coupons', collect(Arr::get($data, 'web_coupons'))->filter()
->map(fn (array $coupon) => $this->parseWebCoupon($coupon)) ->map(fn (array $coupon): ?\App\Lib\Discount => $this->parseWebCoupon($coupon))
->filter()->all()); ->filter()->all());
$settings->fill($data); $settings->fill($data);
if ($settings->saveOrFail()) { if ($settings->saveOrFail()) {
return redirect() return to_route('bo.settings.edit', $settings)
->route('bo.settings.edit', $settings)
->with('success', trans( ->with('success', trans(
'crud.messages.has_been_updated', 'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.settings'))] ['model' => Str::singular(__('models.classes.settings'))]
)); ));
} }
return redirect()
->back() return back()
->with('error', trans( ->with('error', trans(
'crud.messages.cannot_be_updated', 'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.settings'))] ['model' => Str::singular(__('models.classes.settings'))]
@ -69,18 +70,16 @@ class SettingsController extends Controller
}); });
} }
/** /**
* Try to parse webCoupon * Try to parse webCoupon
* *
* @param array<string,string> $coupon * @param array<string,string> $coupon
* @return \App\Lib\Discount|null
*/ */
private function parseWebCoupon(array $coupon): Discount | null private function parseWebCoupon(array $coupon): ?Discount
{ {
/** @var \Illuminate\Http\UploadedFile|string */ /** @var UploadedFile|string */
$picture = Arr::get($coupon, 'picture'); $picture = Arr::get($coupon, 'picture');
if (is_object($picture) and ($picture instanceof \Illuminate\Http\UploadedFile)) { if ($picture instanceof UploadedFile) {
$picture = FileStorageHelper::storeFile( $picture = FileStorageHelper::storeFile(
new Settings(), new Settings(),
$picture, $picture,
@ -96,7 +95,7 @@ class SettingsController extends Controller
Arr::get($coupon, 'percentage'), Arr::get($coupon, 'percentage'),
Arr::get($coupon, 'web_coupon_identifiant'), Arr::get($coupon, 'web_coupon_identifiant'),
); );
} catch (\RuntimeException | \TypeError) { } catch (RuntimeException | TypeError) {
return null; return null;
} }
} }

View file

@ -1,10 +1,15 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest; use App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest;
use App\Models\StaticPage; use App\Models\StaticPage;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -21,13 +26,10 @@ class StaticPageController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $query */ /** @var Builder $query */
$query = StaticPage::query(); $query = StaticPage::query();
/** @var string $search Search field */ /** @var string $search Search field */
@ -50,41 +52,44 @@ class StaticPageController extends Controller
/** Custom pagination */ /** Custom pagination */
$staticPageModelList = $this->paginate($query); $staticPageModelList = $this->paginate($query);
return view('back::pages.static_pages.index', compact('staticPageModelList', 'search', 'searchFields')); /** @var view-string $view */
$view = 'back::pages.static_pages.index';
return view($view, [
'staticPageModelList' => $staticPageModelList,
'search' => $search,
'searchFields' => $searchFields,
]);
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\StaticPage $static_page
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(StaticPage $static_page): \Illuminate\Contracts\View\View public function edit(StaticPage $static_page): View
{ {
return view('back::pages.static_pages.edit', ['staticPageModel' => $static_page]); /** @var view-string $view */
$view = 'back::pages.static_pages.edit';
return view($view, ['staticPageModel' => $static_page]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest $request
* @param \App\Models\StaticPage $static_page
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update(UpdateStaticPageRequest $request, StaticPage $static_page): \Illuminate\Http\RedirectResponse public function update(UpdateStaticPageRequest $request, StaticPage $static_page): RedirectResponse
{ {
return DB::transaction(function () use ($request, $static_page) { return DB::transaction(function () use ($request, $static_page) {
$static_page->fill($request->validated()); $static_page->fill($request->validated());
if ($static_page->saveOrFail()) { if ($static_page->saveOrFail()) {
return redirect()->route('bo.static_pages.edit', $static_page) return to_route('bo.static_pages.edit', $static_page)
->with('success', __('crud.messages.has_been_updated', [ ->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1)) 'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1)),
])); ]));
} }
return redirect()->route('bo.static_pages.edit', $static_page)
return to_route('bo.static_pages.edit', $static_page)
->with('error', __('crud.messages.cannot_be_updated', [ ->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1)) 'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1)),
])); ]));
}); });
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Bo; namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -7,6 +9,9 @@ use App\Http\Requests\Bo\Users\StoreUserRequest;
use App\Http\Requests\Bo\Users\UpdateUserRequest; use App\Http\Requests\Bo\Users\UpdateUserRequest;
use App\Models\User; use App\Models\User;
use App\Traits\Controllers\UpdateModelPublished; use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -25,13 +30,10 @@ class UserController extends Controller
/** /**
* Display a listing of the resource. * Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function index(Request $request): \Illuminate\Contracts\View\View public function index(Request $request): View
{ {
/** @var \Illuminate\Database\Eloquent\Builder $userModels */ /** @var Builder $userModels */
$userModels = User::query(); $userModels = User::query();
/** @var string $search Search field */ /** @var string $search Search field */
@ -60,109 +62,101 @@ class UserController extends Controller
/** Custom pagination */ /** Custom pagination */
$userModels = $this->paginate($userModels); $userModels = $this->paginate($userModels);
return view('back::pages.users.index', compact('userModels', 'search', 'searchFields')); /** @var view-string $view */
} $view = 'back::pages.users.index';
return view($view, ['userModels' => $userModels, 'search' => $search, 'searchFields' => $searchFields]);
}
/** /**
* Show the form for creating a new resource. * Show the form for creating a new resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/ */
public function create(User $user): \Illuminate\Contracts\View\View public function create(User $user): View
{ {
return view('back::pages.users.create', ['userModel' => $user]); /** @var view-string $view */
$view = 'back::pages.users.create';
return view($view, ['userModel' => $user]);
} }
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Users\StoreUserRequest $request
* @return \Illuminate\Http\RedirectResponse
*/ */
public function store(StoreUserRequest $request): \Illuminate\Http\RedirectResponse public function store(StoreUserRequest $request): RedirectResponse
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
$user = new User(); $user = new User();
$user->fill($request->validated()); $user->fill($request->validated());
if ($user->saveOrFail()) { if ($user->saveOrFail()) {
return redirect()->route('bo.users.edit', $user) return to_route('bo.users.edit', $user)
->with('success', __('crud.messages.has_been_created', [ ->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.user')) 'model' => Str::ucfirst(__('models.classes.user')),
])); ]));
} }
return redirect()->back()
return back()
->with('error', __('crud.messages.cannot_be_created', [ ->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.user')) 'model' => Str::ucfirst(__('models.classes.user')),
])); ]));
}); });
} }
/** /**
* Show the form for editing the specified resource. * Show the form for editing the specified resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/ */
public function edit(User $user): \Illuminate\Contracts\View\View public function edit(User $user): View
{ {
return view('back::pages.users.edit', ['userModel' => $user]); /** @var view-string $view */
$view = 'back::pages.users.edit';
return view($view, ['userModel' => $user]);
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Users\UpdateUserRequest $request
* @param \App\Models\User $user
* @return \Illuminate\Http\RedirectResponse
*/ */
public function update(UpdateUserRequest $request, User $user): \Illuminate\Http\RedirectResponse public function update(UpdateUserRequest $request, User $user): RedirectResponse
{ {
return DB::transaction(function () use ($request, $user) { return DB::transaction(function () use ($request, $user) {
$user->fill($request->validated()); $user->fill($request->validated());
if ($user->saveOrFail()) { if ($user->saveOrFail()) {
return redirect()->route('bo.users.edit', $user) return to_route('bo.users.edit', $user)
->with('success', __('crud.messages.has_been_updated', [ ->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.user')) 'model' => Str::ucfirst(__('models.classes.user')),
])); ]));
} }
return redirect()->route('bo.users.edit', $user)
return to_route('bo.users.edit', $user)
->with('error', __('crud.messages.cannot_be_updated', [ ->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.user')) 'model' => Str::ucfirst(__('models.classes.user')),
])); ]));
}); });
} }
/** /**
* Remove the specified resource from storage. * Remove the specified resource from storage.
*
* @param \App\Models\User $user
* @return \Illuminate\Http\RedirectResponse
*/ */
public function destroy(User $user): \Illuminate\Http\RedirectResponse public function destroy(User $user): RedirectResponse
{ {
if ($user->deleteOrFail()) { if ($user->deleteOrFail()) {
return redirect()->route('bo.users.index') return to_route('bo.users.index')
->with('success', __('crud.messages.has_been_deleted', [ ->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.user')) 'model' => Str::ucfirst(__('models.classes.user')),
])); ]));
} }
return redirect()->back()
return back()
->with('error', __('crud.messages.cannot_be_deleted', [ ->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.user')) 'model' => Str::ucfirst(__('models.classes.user')),
])); ]));
} }
/** /**
* Duplicate the specified resource. * Duplicate the specified resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/ */
public function duplicate(User $user): \Illuminate\Contracts\View\View public function duplicate(User $user): View
{ {
return $this->create($user->replicate()); return $this->create($user->replicate());
} }

View file

@ -1,13 +1,18 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Enums\Pagination\ItemsPerPaginationEnum; use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Enums\Theme\BootstrapThemeEnum; use App\Enums\Theme\BootstrapThemeEnum;
use App\Lib\Discount;
use App\Lib\Helpers\ToolboxHelper; use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests; use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController; use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
@ -20,7 +25,6 @@ class Controller extends BaseController
use AuthorizesRequests; use AuthorizesRequests;
use ValidatesRequests; use ValidatesRequests;
/** /**
* Utility function for getting random values with weighting. * Utility function for getting random values with weighting.
* Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50) * Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50)
@ -30,8 +34,9 @@ class Controller extends BaseController
* weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66% * weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66%
* chance of being selected. Also note that weights should be integers. * chance of being selected. Also note that weights should be integers.
* *
* @param array<\App\Lib\Discount> $weightedValues * @param array<Discount> $weightedValues
* @return integer The winning key * @return int The winning key
*
* @url https://stackoverflow.com/a/11872928/4355295 * @url https://stackoverflow.com/a/11872928/4355295
*/ */
protected function getRandomWeightedElementIndex(array $weightedValues): int protected function getRandomWeightedElementIndex(array $weightedValues): int
@ -45,13 +50,12 @@ class Controller extends BaseController
return $key; return $key;
} }
} }
return $rand; return $rand;
} }
/** /**
* Gives the redirection path for FO Auth controllers * Gives the redirection path for FO Auth controllers
*
* @return string
*/ */
protected function authLoggedRedirectPath(): string protected function authLoggedRedirectPath(): string
{ {
@ -62,63 +66,57 @@ class Controller extends BaseController
* Build Search query on specified fields * 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 Builder $query The eloquent query builder.
* @param string $search The query string. * @param string $search The query string.
* @param callable $searchQuery The query string. * @param callable $searchQuery The query string.
* @param string ...$fields Either a simple array or a string * @param string ...$fields Either a simple array or a string
* in case of a string the two fields * in case of a string the two fields
* would be concatenate to make the search. * would be concatenate to make the search.
* @return \Illuminate\Database\Eloquent\Builder
*/ */
protected function searchQuery( protected function searchQuery(
Builder $query, Builder $query,
string $search, string $search,
callable $searchQuery = null, ?callable $searchQuery = null,
string ...$fields string ...$fields
): \Illuminate\Database\Eloquent\Builder { ): Builder {
if (count($fields)) { if (count($fields)) {
// Search using words. // Search using words.
$words = explode(' ', $search); $words = explode(' ', $search);
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) { $query->where(function (\Illuminate\Contracts\Database\Query\Builder $query) use ($searchQuery, $words, $search, $fields): void {
foreach ($words as $word) { foreach ($words as $word) {
if (!strlen($word)) { if (! strlen($word)) {
continue; continue;
} }
$this->searchOnFields($query, $word, $fields); $this->searchOnFields($query, $word, $fields);
} //end foreach } // end foreach
// Search whole using whole sentence. // Search whole using whole sentence.
$this->searchOnFields($query, $search, $fields); $this->searchOnFields($query, $search, $fields);
if ($searchQuery) { if ($searchQuery) {
$searchQuery($query); $searchQuery($query);
} }
}); });
} //end if } // end if
return $query; return $query;
} }
/** /**
* 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( private function searchOnFields(
Builder $query, Builder $query,
string $search, string $search,
array $fields array $fields
): \Illuminate\Database\Eloquent\Builder { ): Builder {
$table = $query->getModel()->getTable(); $table = $query->getModel()->getTable();
foreach ($fields as $field) { foreach ($fields as $field) {
$field = str_replace('?', '\?', $field); $field = str_replace('?', '\?', $field);
if (is_array($field)) { if (is_array($field)) {
$query->orWhereRaw( $query->orWhereRaw(
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) { sprintf('UPPER(CONCAT(%s)) LIKE UPPER(?)', collect($field)->map(fn ($q): string => "`$table`.`$q`")->implode(', \' \', ')),
return "`$table`.`$q`";
})->implode(', \' \', ')),
['%' . htmlspecialchars($search) . '%'] ['%' . htmlspecialchars($search) . '%']
); );
return $query; return $query;
} }
// Get models woth enum's label. // Get models woth enum's label.
@ -129,62 +127,56 @@ class Controller extends BaseController
['%' . htmlspecialchars($array->value) . '%'] ['%' . htmlspecialchars($array->value) . '%']
); );
} }
return $query; return $query;
} }
// Get models with relation's name/label. // Get models with relation's name/label.
if (Str::endsWith($field, '_id')) { if (Str::endsWith($field, '_id')) {
$query->orWhereHas(Str::remove('_id', $field), function ($queryRelation) use ($field, $search) { $query->orWhereHas(
if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) { Str::remove('_id', $field),
$queryRelation->where('name', 'like', '%' . $search . '%'); function (\Illuminate\Contracts\Database\Query\Builder $queryRelation) use ($field, $search): void {
} elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) { if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) {
$queryRelation->where('label', 'like', '%' . $search . '%'); $queryRelation->where('name', 'like', '%' . $search . '%');
} elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) {
$queryRelation->where('label', 'like', '%' . $search . '%');
}
} }
}); );
return $query; return $query;
} }
$query->orWhereRaw( $query->orWhereRaw(
"UPPER(`$table`.`$field`) LIKE UPPER(?)", "UPPER(`$table`.`$field`) LIKE UPPER(?)",
['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%'] ['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%']
); );
} //end foreach } // end foreach
return $query; return $query;
} }
/** /**
* Get the search value (on string or Enum). * Get the search value (on string or Enum).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param string $field
* @return string|array
*/ */
protected function getSearchValue(Builder $query, string $search, string $field): string|array protected function getSearchValue(Builder $query, string $search, string $field): string | array
{ {
$cast = $query->getModel()->getCasts()[$field] ?? null; $cast = $query->getModel()->getCasts()[$field] ?? null;
if ( if (
count($query->getModel()->getCasts()) and // Check if the model has casts. count($query->getModel()->getCasts()) && $cast && enum_exists($cast) // Check if the cast is an enum.
$cast and // Check if there is cast about specific field.
enum_exists($cast) // Check if the cast is an enum.
) { ) {
// @phpstan-ignore-next-line // @phpstan-ignore-next-line
$cases = collect($cast::toArray()); $cases = collect($cast::toArray());
$enum = $cases->filter(function ($item) use ($search) { $enum = $cases->filter(fn ($item): int | false => preg_match('/' . Str::lower($search) . '/', Str::lower($item->label)));
return preg_match('/' . Str::lower($search) . '/', Str::lower($item->label));
});
return $enum->toArray(); return $enum->toArray();
} else {
return $search;
} }
return $search;
} }
/** /**
* 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 = []): \Illuminate\Database\Eloquent\Builder protected function sortQuery(Builder $query, array $ignore = []): Builder
{ {
$rName = request()->route()->getName(); $rName = request()->route()->getName();
$table = $query->getModel()->getTable(); $table = $query->getModel()->getTable();
@ -201,8 +193,8 @@ class Controller extends BaseController
Session::remove("$rName.sort_col"); Session::remove("$rName.sort_col");
Session::remove("$rName.sort_way"); Session::remove("$rName.sort_way");
} }
if (request()->sort_col and request()->sort_way and Schema::hasColumn($table, request()->sort_col)) { if (request()->sort_col && request()->sort_way && Schema::hasColumn($table, request()->sort_col)) {
if (!in_array(request()->sort_col, $ignore)) { if (! in_array(request()->sort_col, $ignore)) {
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way); $query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
} }
} elseif (Schema::hasColumn($table, 'order')) { } elseif (Schema::hasColumn($table, 'order')) {
@ -210,59 +202,55 @@ class Controller extends BaseController
Session::put("$rName.sort_col", 'order'); Session::put("$rName.sort_col", 'order');
Session::put("$rName.sort_way", 'asc'); Session::put("$rName.sort_way", 'asc');
} elseif (Schema::hasColumn($table, 'updated_at')) { } elseif (Schema::hasColumn($table, 'updated_at')) {
$query = $query->orderBy('updated_at', 'DESC'); $query = $query->latest('updated_at');
Session::put("$rName.sort_col", 'updated_at'); Session::put("$rName.sort_col", 'updated_at');
Session::put("$rName.sort_way", 'desc'); Session::put("$rName.sort_way", 'desc');
} elseif (Schema::hasColumn($table, 'created_at')) { } elseif (Schema::hasColumn($table, 'created_at')) {
$query = $query->orderBy('created_at', 'DESC'); $query = $query->latest();
Session::put("$rName.sort_col", 'created_at'); Session::put("$rName.sort_col", 'created_at');
Session::put("$rName.sort_way", 'desc'); Session::put("$rName.sort_way", 'desc');
} }
return $query; return $query;
} }
/** /**
* Customize pagination with cache or config (default). * Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/ */
protected function paginate( protected function paginate(
Builder $query, Builder $query,
ItemsPerPaginationEnum $pagination = null ?ItemsPerPaginationEnum $pagination = null
): \Illuminate\Contracts\Pagination\LengthAwarePaginator { ): LengthAwarePaginator {
$currentRoutePath = Str::slug(request()->route()->getName()); $currentRoutePath = Str::slug(request()->route()->getName());
$cacheKey = "pagination.{$currentRoutePath}"; $cacheKey = "pagination.{$currentRoutePath}";
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve; $pagination ??= ItemsPerPaginationEnum::twelve;
$pagination = ToolboxHelper::getValidatedEnum( $pagination = ToolboxHelper::getValidatedEnum(
Cache::get($cacheKey, $pagination->value()), Cache::get($cacheKey, $pagination->value()),
'pagination', 'pagination',
'\App\Enums\Pagination\ItemsPerPaginationEnum', ItemsPerPaginationEnum::class,
); );
if (Cache::get($cacheKey) !== $pagination) { if (Cache::get($cacheKey) !== $pagination) {
Cache::put($cacheKey, $pagination); Cache::put($cacheKey, $pagination);
} }
$query = $query->paginate($pagination); $query = $query->paginate($pagination);
return $query; return $query;
} }
/** /**
* Set the bootstrap theme or light (default). * Set the bootstrap theme or light (default).
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/ */
protected function setTheme(Request $request): \Illuminate\Http\RedirectResponse protected function setTheme(Request $request): RedirectResponse
{ {
$theme = ToolboxHelper::getValidatedEnum( $theme = ToolboxHelper::getValidatedEnum(
$request->theme ?: Cache::get('theme', BootstrapThemeEnum::light->value()), $request->theme ?: Cache::get('theme', BootstrapThemeEnum::light->value()),
'theme', 'theme',
'\App\Http\Controllers\BootstrapThemeEnum', '\App\Http\Controllers\BootstrapThemeEnum',
); );
if (Cache::get("theme") !== $theme) { if (Cache::get('theme') !== $theme) {
Cache::put("theme", $theme); Cache::put('theme', $theme);
} }
return redirect()->back()->with('success', __('crud.messages.theme_updated'));
return back()->with('success', __('crud.messages.theme_updated'));
} }
} }

View file

@ -1,20 +1,21 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo\Auth; namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ConfirmsPasswords; use Illuminate\Foundation\Auth\ConfirmsPasswords;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
class ConfirmPasswordController extends Controller class ConfirmPasswordController extends Controller
{ {
use ConfirmsPasswords; use ConfirmsPasswords;
/** /**
* Create a new controller instance. * Create a new controller instance.
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -23,21 +24,21 @@ class ConfirmPasswordController extends Controller
/** /**
* Display the password confirmation view. * Display the password confirmation view.
*
* @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse
*/ */
public function showConfirmForm(): \Illuminate\View\View|\Illuminate\Http\RedirectResponse public function showConfirmForm(): View | RedirectResponse
{ {
if (Auth::guard('frontend')->check()) { if (Auth::guard('frontend')->check()) {
return \response()->redirectToRoute('home'); return \response()->redirectToRoute('home');
} }
return view('theme::auth.passwords.confirm');
/** @var view-string $view */
$view = 'theme::auth.passwords.confirm';
return view($view);
} }
/** /**
* Get the post register / login redirect path. * Get the post register / login redirect path.
*
* @return string
*/ */
public function redirectPath(): string public function redirectPath(): string
{ {

View file

@ -1,11 +1,14 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo\Auth; namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\PasswordBroker; use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails; use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Password;
@ -16,16 +19,13 @@ class ForgotPasswordController extends Controller
/** /**
* Message to user * Message to user
* *
* @var string *
* @phpcs:disable Generic.Files.LineLength.TooLong * @phpcs:disable Generic.Files.LineLength.TooLong
*/ */
private $message = 'front.messages.password_reset_email_sent'; private string $message = 'front.messages.password_reset_email_sent';
//phpcs:enable // phpcs:enable
/** /**
* Create a new controller instance. * Create a new controller instance.
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -39,13 +39,14 @@ class ForgotPasswordController extends Controller
*/ */
public function showLinkRequestForm(): View public function showLinkRequestForm(): View
{ {
return view('theme::auth.passwords.email'); /** @var view-string $view */
$view = 'theme::auth.passwords.email';
return view($view);
} }
/** /**
* Get the broker to be used during password reset. * Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/ */
public function broker(): PasswordBroker public function broker(): PasswordBroker
{ {
@ -56,7 +57,7 @@ class ForgotPasswordController extends Controller
* Get the response for a successful password reset link. * Get the response for a successful password reset link.
* ! Dans les deux cas on ne divulgue pas si un user existe. * ! Dans les deux cas on ne divulgue pas si un user existe.
* *
* @return \Illuminate\Http\RedirectResponse * @return RedirectResponse
*/ */
protected function sendResetLinkResponse() protected function sendResetLinkResponse()
{ {
@ -67,9 +68,7 @@ class ForgotPasswordController extends Controller
* Get the response for a failed password reset link. * Get the response for a failed password reset link.
* ! Dans les deux cas on ne divulgue pas si un user existe. * ! Dans les deux cas on ne divulgue pas si un user existe.
* *
* @param \Illuminate\Http\Request $request * @return RedirectResponse
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/ */
protected function sendResetLinkFailedResponse(Request $request, string $response) protected function sendResetLinkFailedResponse(Request $request, string $response)
{ {
@ -77,6 +76,7 @@ class ForgotPasswordController extends Controller
if ($response === 'passwords.user') { if ($response === 'passwords.user') {
return back()->with('info', trans($this->message)); return back()->with('info', trans($this->message));
} }
// * Renvoyer les autres message d'erreur style throttle. // * Renvoyer les autres message d'erreur style throttle.
return back() return back()
->withInput($request->only('email')) ->withInput($request->only('email'))

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo\Auth; namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -28,8 +30,6 @@ class LoginController extends Controller
/** /**
* Create a new controller instance. * Create a new controller instance.
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -38,8 +38,6 @@ class LoginController extends Controller
/** /**
* Get the guard to be used during authentication. * Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/ */
protected function guard(): StatefulGuard protected function guard(): StatefulGuard
{ {
@ -53,15 +51,15 @@ class LoginController extends Controller
*/ */
public function showLoginForm(): View public function showLoginForm(): View
{ {
return view('theme::auth.login'); /** @var view-string $view */
$view = 'theme::auth.login';
return view($view);
} }
/** /**
* Check if the authenticated user can access the game * Check if the authenticated user can access the game
* *
* @param \Illuminate\Http\Request $request
* @param \App\Models\Customer $user
* @return void
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing, Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed * @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing, Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
*/ */
protected function authenticated(Request $request, Customer $user): void protected function authenticated(Request $request, Customer $user): void
@ -69,11 +67,11 @@ class LoginController extends Controller
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */ /** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $user->api_data; $crmCustomer = $user->api_data;
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
$countries = $settings->authorized_countries; $countries = $settings->authorized_countries;
if (count($countries) && !in_array($crmCustomer->country->value, $countries)) { if (count($countries) && ! in_array($crmCustomer->country->value, $countries)) {
$this->logout($request); $this->logout($request);
Session::flash('info', trans('front.messages.game_not_available_in_country')); Session::flash('info', trans('front.messages.game_not_available_in_country'));
} else { } else {
@ -86,8 +84,6 @@ class LoginController extends Controller
/** /**
* The user has logged out of the application. * The user has logged out of the application.
* *
* @param \Illuminate\Http\Request $request
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/ */
protected function loggedOut(Request $request): void protected function loggedOut(Request $request): void
@ -97,8 +93,6 @@ class LoginController extends Controller
/** /**
* Get the post register / login redirect path. * Get the post register / login redirect path.
*
* @return string
*/ */
public function redirectPath(): string public function redirectPath(): string
{ {

View file

@ -1,10 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo\Auth; namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\CreateAccountRequest; use App\Http\Requests\Fo\Customers\CreateAccountRequest;
use App\Models\Customer; use App\Models\Customer;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\Validation\Validator as ValidationValidator; use Illuminate\Contracts\Validation\Validator as ValidationValidator;
use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
@ -14,10 +17,12 @@ use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException as IlluminateValidationException; use Illuminate\Validation\ValidationException as IlluminateValidationException;
use Illuminate\View\View;
use LDC\LDCCRM\Enums\ApiError; use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException; use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException; use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
use LDC\LDCCRM\Models\Customer as CrmCustomer; use LDC\LDCCRM\Models\Customer as CrmCustomer;
use RuntimeException;
class RegisterController extends Controller class RegisterController extends Controller
{ {
@ -25,8 +30,6 @@ class RegisterController extends Controller
/** /**
* Create a new controller instance. * Create a new controller instance.
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -36,7 +39,7 @@ class RegisterController extends Controller
/** /**
* Get the guard to be used during authentication. * Get the guard to be used during authentication.
* *
* @return \Illuminate\Contracts\Auth\StatefulGuard * @return StatefulGuard
*/ */
protected function guard() protected function guard()
{ {
@ -46,19 +49,21 @@ class RegisterController extends Controller
/** /**
* Show the application registration form. * Show the application registration form.
* *
* @return \Illuminate\View\View * @return View
*/ */
public function showRegistrationForm() public function showRegistrationForm()
{ {
return view('theme::auth.register'); /** @var view-string $view */
$view = 'theme::auth.register';
return view($view);
} }
/** /**
* Get a validator for an incoming registration request. * Get a validator for an incoming registration request.
* *
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
* @throws IlluminateValidationException If data is incorrect. * @throws IlluminateValidationException If data is incorrect.
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/ */
protected function validator(array $data): ValidationValidator protected function validator(array $data): ValidationValidator
@ -66,6 +71,7 @@ class RegisterController extends Controller
// phpcs:enable // phpcs:enable
$request = CreateAccountRequest::capture(); $request = CreateAccountRequest::capture();
$request->prepareForValidation(); $request->prepareForValidation();
return Validator::make( return Validator::make(
$request->validationData(), $request->validationData(),
$request->rules(), $request->rules(),
@ -77,10 +83,8 @@ class RegisterController extends Controller
/** /**
* Create a new user instance after a valid registration. * Create a new user instance after a valid registration.
* *
* @param array $data
* @return \App\Models\Customer
* @throws IlluminateValidationException If there are validation errors. * @throws IlluminateValidationException If there are validation errors.
* @throws \RuntimeException On unhandled api Error. * @throws RuntimeException On unhandled api Error.
*/ */
protected function create(array $data): Customer protected function create(array $data): Customer
{ {
@ -107,7 +111,7 @@ class RegisterController extends Controller
// * Enregistrement du Hashcode // * Enregistrement du Hashcode
/** @var \App\Models\Customer $customer */ /** @var \App\Models\Customer $customer */
$customer = Customer::withTrashed()->firstOrNew([ $customer = Customer::withTrashed()->firstOrNew([
'email' => $data['email'] 'email' => $data['email'],
]); ]);
$customer->fill(Arr::only($data, $customer->getFillable())); $customer->fill(Arr::only($data, $customer->getFillable()));
$customer->hashcode = $crmCustomer->hashcode; $customer->hashcode = $crmCustomer->hashcode;
@ -127,30 +131,28 @@ class RegisterController extends Controller
} catch (ApiErrorException $e) { } catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::customerExists)) { if ($e->getApiError()->equals(ApiError::customerExists)) {
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => trans('front.messages.email_already_in_use') 'error' => trans('front.messages.email_already_in_use'),
]); ]);
} }
if ($e->getApiError()->equals(ApiError::validationFailed)) { if ($e->getApiError()->equals(ApiError::validationFailed)) {
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => $e->getMessage() 'error' => $e->getMessage(),
]); ]);
} }
report($e); report($e);
Log::error(__CLASS__ . '::create reached end of possibilities.'); Log::error(self::class . '::create reached end of possibilities.');
throw new \RuntimeException(__CLASS__ . '::create reached end of possibilities.'); throw new RuntimeException(self::class . '::create reached end of possibilities.', $e->getCode(), $e);
} catch (CrmValidationException $e) { } catch (CrmValidationException $e) {
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => $e->getMessage() 'error' => $e->getMessage(),
]); ]);
} //end try } // end try
} }
/** /**
* Get the post register / login redirect path. * Get the post register / login redirect path.
*
* @return string
*/ */
public function redirectPath() public function redirectPath(): string
{ {
return $this->authLoggedRedirectPath(); return $this->authLoggedRedirectPath();
} }

View file

@ -1,20 +1,26 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo\Auth; namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Customer;
use Illuminate\Auth\Events\PasswordReset; use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Contracts\Auth\PasswordBroker; use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Contracts\Auth\StatefulGuard; use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use LDC\LDCCRM\CrmApiV4;
use LDC\LDCCRM\Exceptions\ApiErrorException; use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException; use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException as ExceptionsValidationException; use LDC\LDCCRM\Exceptions\ValidationException as ExceptionsValidationException;
use TypeError;
class ResetPasswordController extends Controller class ResetPasswordController extends Controller
{ {
@ -22,8 +28,6 @@ class ResetPasswordController extends Controller
/** /**
* Create a new controller instance. * Create a new controller instance.
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -34,8 +38,6 @@ class ResetPasswordController extends Controller
/** /**
* Get the guard to be used during authentication. * Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/ */
protected function guard(): StatefulGuard protected function guard(): StatefulGuard
{ {
@ -46,15 +48,15 @@ class ResetPasswordController extends Controller
* Display the password reset view for the given token. * Display the password reset view for the given token.
* *
* If no token is present, display the link request form. * If no token is present, display the link request form.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/ */
public function showResetForm(Request $request): \Illuminate\Contracts\View\View public function showResetForm(Request $request): View
{ {
$token = $request->route()->parameter('token'); $token = $request->route()->parameter('token');
return view('theme::auth.passwords.reset')->with( /** @var view-string $view */
$view = 'theme::auth.passwords.reset';
return view($view)->with(
['token' => $token, 'email' => $request->email] ['token' => $token, 'email' => $request->email]
); );
} }
@ -62,32 +64,29 @@ class ResetPasswordController extends Controller
/** /**
* Reset the given user's password. * Reset the given user's password.
* *
* @param \App\Models\Customer $customer
* @param string $password
* @return void
* @throws \Illuminate\Validation\ValidationException If something goes wrong. * @throws \Illuminate\Validation\ValidationException If something goes wrong.
*/ */
protected function resetPassword(\App\Models\Customer $customer, string $password): void protected function resetPassword(Customer $customer, string $password): void
{ {
/** @var \LDC\LDCCRM\CrmApiV4 */ /** @var CrmApiV4 */
$crm = app('crmAPI'); $crm = resolve('crmAPI');
try { try {
if (!($crmCustomer = $crm->getCustomer($customer->hashcode))) { if (! ($crmCustomer = $crm->getCustomer($customer->hashcode))) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'error' => trans('Changement impossible, compte inexistant.') 'error' => trans('Changement impossible, compte inexistant.'),
]); ]);
} }
$crmCustomer->password = $password; $crmCustomer->password = $password;
$crmCustomer->save(); $crmCustomer->save();
} catch (\TypeError | CrmException | ApiErrorException $e) { } catch (TypeError | CrmException | ApiErrorException $e) {
report($e); report($e);
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'error' => trans('front.messages.unexpected_error_try_later') 'error' => trans('front.messages.unexpected_error_try_later'),
]); ]);
} catch (ExceptionsValidationException $e) { } catch (ExceptionsValidationException $e) {
throw ValidationException::withMessages([ throw ValidationException::withMessages([
'password' => $e->getMessage() 'password' => $e->getMessage(),
]); ]);
} }
@ -102,8 +101,6 @@ class ResetPasswordController extends Controller
/** /**
* Get the post register / login redirect path. * Get the post register / login redirect path.
*
* @return string
*/ */
public function redirectPath(): string public function redirectPath(): string
{ {
@ -112,8 +109,6 @@ class ResetPasswordController extends Controller
/** /**
* Get the broker to be used during password reset. * Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/ */
public function broker(): PasswordBroker public function broker(): PasswordBroker
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo\Auth; namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -8,6 +10,7 @@ use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\Events\Verified; use Illuminate\Auth\Events\Verified;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\VerifiesEmails; use Illuminate\Foundation\Auth\VerifiesEmails;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -23,8 +26,6 @@ class VerificationController extends Controller
/** /**
* Create a new controller instance. * Create a new controller instance.
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -35,15 +36,15 @@ class VerificationController extends Controller
/** /**
* Show the email verification notice. * Show the email verification notice.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View
*/ */
public function show(Request $request): RedirectResponse|View public function show(Request $request): RedirectResponse | View
{ {
/** @var view-string $view */
$view = 'theme::auth.verify';
return $request->user('frontend')->hasVerifiedEmail() return $request->user('frontend')->hasVerifiedEmail()
? redirect($this->redirectPath()) ? redirect($this->redirectPath())
: view('theme::auth.verify'); : view($view);
} }
/** /**
@ -51,22 +52,16 @@ class VerificationController extends Controller
* *
* ! Overloaded trait * ! Overloaded trait
* *
* @param \Illuminate\Http\Request $request * @throws AuthorizationException If request is invalid.
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException If request is invalid.
*/ */
public function verify(Request $request): JsonResponse|RedirectResponse public function verify(Request $request): JsonResponse | RedirectResponse
{ {
/** @var \App\Models\Customer */ /** @var Customer */
$user = Customer::findOrFail($request->route('id')); $user = Customer::findOrFail($request->route('id'));
if (!hash_equals((string) $request->route('id'), (string) $user->getKey())) { throw_unless(hash_equals((string) $request->route('id'), (string) $user->getKey()), AuthorizationException::class);
throw new AuthorizationException();
}
if (!hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification()))) { throw_unless(hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification())), AuthorizationException::class);
throw new AuthorizationException();
}
if ($user->hasVerifiedEmail()) { if ($user->hasVerifiedEmail()) {
return $request->wantsJson() return $request->wantsJson()
@ -74,7 +69,7 @@ class VerificationController extends Controller
: redirect($this->redirectPath()); : redirect($this->redirectPath());
} }
DB::transaction(function () use ($user) { DB::transaction(function () use ($user): void {
if ($user->markEmailAsVerified()) { if ($user->markEmailAsVerified()) {
$this->setCrmCustomerAsVerified($user); $this->setCrmCustomerAsVerified($user);
event(new Verified($user)); event(new Verified($user));
@ -93,8 +88,8 @@ class VerificationController extends Controller
/** /**
* The user has been verified. * The user has been verified.
* *
* @param \Illuminate\Http\Request $request
* @return mixed * @return mixed
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass * @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/ */
protected function verified(Request $request) protected function verified(Request $request)
@ -107,8 +102,6 @@ class VerificationController extends Controller
/** /**
* Get the post register / login redirect path. * Get the post register / login redirect path.
*
* @return string
*/ */
public function redirectPath(): string public function redirectPath(): string
{ {
@ -118,9 +111,7 @@ class VerificationController extends Controller
/** /**
* Set Crm Customer active * Set Crm Customer active
* *
* @param \App\Models\Customer $customer * @throws HttpResponseException In case of failure.
* @return void
* @throws \Illuminate\Http\Exceptions\HttpResponseException In case of failure.
*/ */
private function setCrmCustomerAsVerified(Customer $customer): void private function setCrmCustomerAsVerified(Customer $customer): void
{ {
@ -137,12 +128,11 @@ class VerificationController extends Controller
if ($e->getApiError()->equals(ApiError::customerAlreadyActive)) { if ($e->getApiError()->equals(ApiError::customerAlreadyActive)) {
// * Ignore this and continue, as it may be activated via another CRM service. // * Ignore this and continue, as it may be activated via another CRM service.
return; return;
} else {
report($e);
redirect($this->redirectPath())->with('error', trans(
'front.messages.unexpected_error_try_later'
))->throwResponse();
} }
report($e);
redirect($this->redirectPath())->with('error', trans(
'front.messages.unexpected_error_try_later'
))->throwResponse();
} }
} }
} }

View file

@ -1,11 +1,16 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo; namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\VerifyInformationsRequest;
use App\Http\Requests\Fo\Customers\SingleOptinRequest; use App\Http\Requests\Fo\Customers\SingleOptinRequest;
use App\Http\Requests\Fo\Customers\VerifyInformationsRequest;
use App\Models\Customer;
use App\Models\Participation;
use App\Repositories\Fo\CrmRepository; use App\Repositories\Fo\CrmRepository;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Contracts\Database\Eloquent\Builder; use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@ -19,16 +24,15 @@ use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException; use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException; use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
use LDC\LDCCRM\Models\SingleOptin; use LDC\LDCCRM\Models\SingleOptin;
use LDC\MESBR\MesBr;
use Illuminate\Http\Response;
class CustomerController extends Controller class CustomerController extends Controller
{ {
/** @var \App\Repositories\Fo\CrmRepository $crmRepository */ private readonly CrmRepository $crmRepository;
private $crmRepository;
/** /**
* ParticipateController * ParticipateController
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -37,26 +41,24 @@ class CustomerController extends Controller
/** /**
* Verify profil info form * Verify profil info form
*
* @return \Illuminate\Contracts\View\View
*/ */
public function profilVerify(): View public function profilVerify(): View
{ {
// ! Pas de Gate/Policies Ici volontairement, vu avec Vincent // ! Pas de Gate/Policies Ici volontairement, vu avec Vincent
/** @var Customer */
/** @var \App\Models\Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
$crmCustomer = $customer->api_data; $crmCustomer = $customer->api_data;
return view('theme::auth.profil-verify', compact('crmCustomer')); /** @var view-string $view */
$view = 'theme::auth.profil-verify';
return view($view, ['crmCustomer' => $crmCustomer]);
} }
/** /**
* Profile verify * Profile verify
* *
* @param \App\Http\Requests\Fo\Customers\VerifyInformationsRequest $request
* @return \Illuminate\Http\RedirectResponse
* @throws IlluminateValidationException In case of any error validation or within the crm. * @throws IlluminateValidationException In case of any error validation or within the crm.
*/ */
public function profilVerifySubmit(VerifyInformationsRequest $request): RedirectResponse public function profilVerifySubmit(VerifyInformationsRequest $request): RedirectResponse
@ -65,7 +67,7 @@ class CustomerController extends Controller
try { try {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
// * --- FILL AND SAVE CUSTOMER // * --- FILL AND SAVE CUSTOMER
/** @var \App\Models\Customer */ /** @var Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
// * Màj, on ne mets pas à jour le mdp ou l'adresse email, juste les champs pour mesBr // * Màj, on ne mets pas à jour le mdp ou l'adresse email, juste les champs pour mesBr
@ -89,86 +91,93 @@ class CustomerController extends Controller
} catch (CrmValidationException $e) { } catch (CrmValidationException $e) {
// * Local Validation failure // * Local Validation failure
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => $e->getMessage() 'error' => $e->getMessage(),
]); ]);
} catch (ApiErrorException $e) { } catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::validationFailed)) { if ($e->getApiError()->equals(ApiError::validationFailed)) {
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => $e->getMessage() 'error' => $e->getMessage(),
]); ]);
} }
report($e); report($e);
Log::error(__CLASS__ . '::update reached end of possibilities.'); Log::error(self::class . '::update reached end of possibilities.');
} catch (CrmException $e) { } catch (CrmException $e) {
report($e); report($e);
Log::error(__CLASS__ . '::error occured on calling crm.'); Log::error(self::class . '::error occured on calling crm.');
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => trans('Une erreur est survenue, veuillez réssayer plus tard') 'error' => trans('Une erreur est survenue, veuillez réssayer plus tard'),
]); ]);
} //end try } // end try
return back()->withInput()->withErrors(trans('front.messages.unexpected_error_try_later')); return back()->withInput()->withErrors(trans('front.messages.unexpected_error_try_later'));
} }
/** /**
* View all customer winnig participations * View all customer winnig participations
*
* @return \Illuminate\Contracts\View\View
*/ */
public function viewWinningParticipations(): View public function viewWinningParticipations(): View
{ {
$this->authorize('viewWonList', \App\Models\Participation::class); $this->authorize('viewWonList', Participation::class);
/** @var \App\Models\Customer */ /** @var Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
// * Recupération des informations CRM // * Recupération des informations CRM
/** @var \LDC\LDCCRM\Models\Customer*/ /** @var \LDC\LDCCRM\Models\Customer */
$crmUser = $this->crmRepository->fetchCrmCustomer($customer); $crmUser = $this->crmRepository->fetchCrmCustomer($customer);
/** @var \LDC\MESBR\MesBr */ /** @var MesBr $mesBr */
$mesBr = app('mesBrAPI'); $mesBr = resolve('mesBrAPI');
/** @var array{coupons?: list<array{quantity: int|string, printed: int|string}>} $brBasket */
$brBasket = $mesBr->getBasket($crmUser); $brBasket = $mesBr->getBasket($crmUser);
if (!empty($brBasket['coupons'])) {
$canPrint = (collect($brBasket['coupons'])->sum('quantity') - collect($brBasket['coupons'])->sum('printed')); if (! empty($brBasket['coupons'])) {
$coupons = collect($brBasket['coupons']);
$canPrint = $coupons->sum('quantity') - $coupons->sum('printed');
} }
$winningList = $customer->participations() $winningList = $customer->participations()
->with(['instantWin'])->where(function (Builder $q) { ->with(['instantWin'])->where(function (Builder $q): void {
$q->whereHas('instantWin'); $q->whereHas('instantWin');
})->get(); })->get();
return view('theme::pages.participation.winning-participations', [ /** @var view-string $view */
$view = 'theme::pages.participation.winning-participations';
return view($view, [
'winningList' => $winningList, 'winningList' => $winningList,
'canPrint' => $canPrint ?? null 'canPrint' => $canPrint ?? null,
]); ]);
} }
/** /**
* Call the CRM to add a single optin * Call the CRM to add a single optin
* *
* @param \App\Http\Requests\Fo\Customers\SingleOptinRequest $request
* @return string|array|null|\Illuminate\Contracts\Translation\Translator
* @throws \LDC\LDCCRM\Exceptions\ValidationException ValidationException. * @throws \LDC\LDCCRM\Exceptions\ValidationException ValidationException.
* @throws \Illuminate\Contracts\Container\BindingResolutionException BindingResolutionException. * @throws BindingResolutionException BindingResolutionException.
*/ */
public function addOptIn(SingleOptinRequest $request) public function addOptIn(SingleOptinRequest $request): Response | string
{ {
try { try {
SingleOptin::setStatus($request->email, true); SingleOptin::setStatus($request->email, true);
return \response(trans("front.messages.newsletter_subscription_success"), 200);
return \response(trans('front.messages.newsletter_subscription_success'), 200);
} catch (ApiErrorException $e) { } catch (ApiErrorException $e) {
// Il y a deja un customerInfo avec optin a true pour ce customer. // Il y a deja un customerInfo avec optin a true pour ce customer.
if ($e->getApiError()->equals(ApiError::singleOptinCustomerExists)) { if ($e->getApiError()->equals(ApiError::singleOptinCustomerExists)) {
return trans('front.messages.newsletter_email_already_subscribed'); return trans('front.messages.newsletter_email_already_subscribed');
} }
if (app()->bound('sentry')) { if (app()->bound('sentry')) {
app('sentry')->captureException($e); resolve('sentry')->captureException($e);
} }
return \response(trans('front.messages.unexpected_error_try_later'), 404); return \response(trans('front.messages.unexpected_error_try_later'), 404);
} catch (CrmException $e) { } catch (CrmException $e) {
if (app()->bound('sentry')) { if (app()->bound('sentry')) {
app('sentry')->captureException($e); resolve('sentry')->captureException($e);
} }
return \response(trans('front.messages.unexpected_error_try_later'), 404); return \response(trans('front.messages.unexpected_error_try_later'), 404);
} }
} }

View file

@ -1,34 +1,37 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo; namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\Customer; use App\Models\Customer;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use LDC\MESBR\MesBr;
use RuntimeException;
class DiscountController extends Controller class DiscountController extends Controller
{ {
/** /**
* Fetch PDF BR. * Fetch PDF BR.
*
* @return \Illuminate\Http\RedirectResponse
*/ */
public function fetchBrPdf(): \Illuminate\Http\RedirectResponse public function fetchBrPdf(): RedirectResponse
{ {
/** @var \App\Models\Customer $customer */ /** @var Customer $customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
$pdf = $this->fetchBrPdfFromMesBr($customer); $pdf = $this->fetchBrPdfFromMesBr($customer);
if ($pdf === null) { if ($pdf === null) {
\back()->withErrors([ \back()->withErrors([
'coupon' => trans('front.messages.pdf_download_error') 'coupon' => trans('front.messages.pdf_download_error'),
])->throwResponse(); ])->throwResponse();
} }
return redirect()->to( return redirect()->to(
sprintf( sprintf(
"%s/basket/%s/print", '%s/basket/%s/print',
config('mesbr.endpoint'), config('mesbr.endpoint'),
$pdf $pdf
) )
@ -38,53 +41,54 @@ class DiscountController extends Controller
/** /**
* Fetch coupons PDF. * Fetch coupons PDF.
* *
* @param \App\Models\Customer $customer
* @return string|null
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/ */
private function fetchBrPdfFromMesBr(Customer $customer): ?string private function fetchBrPdfFromMesBr(Customer $customer): ?string
{ {
// phpcs:enable // phpcs:enable
// * Phase - 1 Recupération des informations CRM // * Phase - 1 Recupération des informations CRM
/** @var \App\Models\Customer $customer */ /** @var Customer $customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */ /** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->api_data; $crmCustomer = $customer->api_data;
try { try {
/** @var \LDC\MESBR\MesBr */ /** @var MesBr */
$mesBr = app('mesBrAPI'); $mesBr = resolve('mesBrAPI');
$result = $mesBr->getBasket($crmCustomer); $result = $mesBr->getBasket($crmCustomer);
$result = \is_object($result) ? (array) $result : $result; $result = \is_object($result) ? (array) $result : $result;
if (Arr::get($result, 'status') !== "OK") { if (Arr::get($result, 'status') !== 'OK') {
report(new \RuntimeException( report(new RuntimeException(
\sprintf( \sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s', 'MESBR : Une erreur est survenue. Code : %s Message : %s',
Arr::get($result, 'code') ?? 'Undefined', Arr::get($result, 'code') ?? 'Undefined',
Arr::get($result, 'message') ?? 'Undefined', Arr::get($result, 'message') ?? 'Undefined',
) )
)); ));
return null; return null;
} }
// * La réponse est conforme ? // * La réponse est conforme ?
$needed_fields = ['uuid']; $needed_fields = ['uuid'];
if (!Arr::has($result, $needed_fields)) { if (! Arr::has($result, $needed_fields)) {
report(new \RuntimeException(sprintf( report(new RuntimeException(sprintf(
'MESBR : Il manques des champs dans la réponse, attendu => %s, donné => %s', 'MESBR : Il manques des champs dans la réponse, attendu => %s, donné => %s',
json_encode($needed_fields), json_encode($needed_fields),
json_encode($result), json_encode($result),
))); )));
return null; return null;
} }
// * Tout est OK // * Tout est OK
return Arr::get($result, 'uuid') ?: null; return Arr::get($result, 'uuid') ?: null;
} catch (\RuntimeException $e) { } catch (RuntimeException $e) {
report($e); report($e);
// * Retenter ? // * Retenter ?
return null; return null;
} //end try } // end try
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo; namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -7,89 +9,100 @@ use App\Models\FaqQuestion;
use App\Models\FaqTheme; use App\Models\FaqTheme;
use App\Models\Range; use App\Models\Range;
use App\Models\Settings; use App\Models\Settings;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
class HomeController extends Controller class HomeController extends Controller
{ {
/** /**
* Display home page * Display home page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function home(): \Illuminate\Contracts\View\View public function home(): View
{ {
if (!Gate::check('fo.gameEnabled')) { if (! Gate::check('fo.gameEnabled')) {
\session()->flash('warning', trans('front.messages.game_missing_required_elements')); \session()->flash('warning', trans('front.messages.game_missing_required_elements'));
} }
return view('theme::pages.home');
/** @var view-string $view */
$view = 'theme::pages.home';
return view($view);
} }
/** /**
* Display products with game stickers * Display products with game stickers
* *
* @return \Illuminate\Contracts\View\View
* @phpcs:ignore Squiz.Commenting.FunctionComment.EmptyThrows * @phpcs:ignore Squiz.Commenting.FunctionComment.EmptyThrows
* @throws \Illuminate\Http\Exceptions\HttpResponseException *
* @throws HttpResponseException
*/ */
public function showProducts(): \Illuminate\Contracts\View\View public function showProducts(): View
{ {
// ? Show product list authorized ? // ? Show product list authorized ?
if (!Gate::allows('fo.showProducts')) { if (! Gate::allows('fo.showProducts')) {
\response()->redirectToRoute('fo.homepage')->throwResponse(); \response()->redirectToRoute('fo.homepage')->throwResponse();
} }
$rangeModelList = Range::query()->with('products')->orderBy('id')->get(); $rangeModelList = Range::query()->with('products')->orderBy('id')->get();
return view('theme::pages.products', compact('rangeModelList'));
/** @var view-string $view */
$view = 'theme::pages.products';
return view($view, ['rangeModelList' => $rangeModelList]);
} }
/** /**
* Display Faq page * Display Faq page
*
* @return \Illuminate\Contracts\View\View
*/ */
public function showFaq(): \Illuminate\Contracts\View\View public function showFaq(): View
{ {
// ? Show QA authorized ? // ? Show QA authorized ?
if (!Gate::allows('fo.showFaq')) { if (! Gate::allows('fo.showFaq')) {
\response()->redirectToRoute('fo.homepage')->throwResponse(); \response()->redirectToRoute('fo.homepage')->throwResponse();
} }
$questionModelList = FaqQuestion::getCached(); $questionModelList = FaqQuestion::getCached();
$themeModelList = FaqTheme::getCached(); $themeModelList = FaqTheme::getCached();
return view('theme::pages.faq', compact( /** @var view-string $view */
'questionModelList', $view = 'theme::pages.faq';
'themeModelList'
)); return view($view, ['questionModelList' => $questionModelList, 'themeModelList' => $themeModelList]);
} }
/** /**
* Show the teaser page if it is activated * Show the teaser page if it is activated
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/ */
public function teaser(): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse public function teaser(): View | RedirectResponse
{ {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
if (!$settings->should_display_teaser_page) { if (! $settings->should_display_teaser_page) {
return redirect()->route('fo.homepage'); return to_route('fo.homepage');
} }
return view('theme::pages.teaser');
/** @var view-string $view */
$view = 'theme::pages.teaser';
return view($view);
} }
/** /**
* Show the end game page if it is activated * Show the end game page if it is activated
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/ */
public function endGame(): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse public function endGame(): View | RedirectResponse
{ {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
if (!$settings->should_display_end_page) { if (! $settings->should_display_end_page) {
return redirect()->route('fo.homepage'); return to_route('fo.homepage');
} }
return view('theme::pages.end-game');
/** @var view-string $view */
$view = 'theme::pages.end-game';
return view($view);
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo; namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
@ -7,13 +9,18 @@ use App\Http\Requests\Fo\Customers\CreateAccountRequest;
use App\Http\Requests\Fo\ParticipateRequest; use App\Http\Requests\Fo\ParticipateRequest;
use App\Lib\Discount as LibDiscount; use App\Lib\Discount as LibDiscount;
use App\Mail\Won\PrizeWonAdminMail; use App\Mail\Won\PrizeWonAdminMail;
use App\Models\Code;
use App\Models\Customer; use App\Models\Customer;
use App\Models\InstantWin; use App\Models\InstantWin;
use App\Models\Participation; use App\Models\Participation;
use App\Models\Product;
use App\Models\Settings; use App\Models\Settings;
use App\Notifications\Customer\HasWonDiscount; use App\Notifications\Customer\HasWonDiscount;
use App\Notifications\Customer\HasWonInstantWin; use App\Notifications\Customer\HasWonInstantWin;
use App\Repositories\Fo\CrmRepository; use App\Repositories\Fo\CrmRepository;
use Exception;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@ -22,15 +29,15 @@ use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use LDC\MESBR\MesBr;
use RuntimeException;
class ParticipateController extends Controller class ParticipateController extends Controller
{ {
/** @var \App\Repositories\Fo\CrmRepository $crmRepository */ private readonly CrmRepository $crmRepository;
private $crmRepository;
/** /**
* ParticipateController * ParticipateController
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -38,53 +45,55 @@ class ParticipateController extends Controller
$this->middleware(function (Request $request, callable $next) { $this->middleware(function (Request $request, callable $next) {
$this->authorize('fo.gameEnabled'); $this->authorize('fo.gameEnabled');
$this->assertUserHasEnoughInfoToUseMesBr(); $this->assertUserHasEnoughInfoToUseMesBr();
return $next($request); return $next($request);
}); });
} }
/** /**
* Display participation form * Display participation form
*
* @return \Illuminate\Contracts\View\View
*/ */
public function participate(): \Illuminate\Contracts\View\View public function participate(): View
{ {
if (!Gate::check('participate', Participation::class)) { if (! Gate::check('participate', Participation::class)) {
return view('theme::pages.participation.participation-exhausted'); /** @var view-string $view */
$view = 'theme::pages.participation.participation-exhausted';
return view($view);
} }
return view('theme::pages.participation.participate');
/** @var view-string $view */
$view = 'theme::pages.participation.participate';
return view($view);
} }
/** /**
* Participate Method * Participate Method
*
* @param \App\Http\Requests\Fo\ParticipateRequest $request
* @return \Illuminate\Contracts\View\View
*/ */
public function participateSubmit(ParticipateRequest $request): \Illuminate\Contracts\View\View public function participateSubmit(ParticipateRequest $request): View
{ {
return DB::transaction(function () use ($request) { return DB::transaction(function () use ($request) {
// ! On verrouille le ou les instants gagnant // ! On verrouille le ou les instants gagnant
/** @var \App\Models\InstantWin|null */ /** @var InstantWin|null */
$instantWin = InstantWin::query()->lockForUpdate() $instantWin = InstantWin::query()->lockForUpdate()
->where('available_at', '<', now()) ->where('available_at', '<', now())
->whereDoesntHave('participation') ->whereDoesntHave('participation')
->get()->sortBy('available_at')->first(); ->get()->sortBy('available_at')->first();
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
/** @var \App\Models\Customer */ /** @var Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
/** @var \App\Models\Code|null */ /** @var Code|null */
$code = $request->post('unique_code'); $code = $request->post('unique_code');
/** @var \App\Models\Product|null */ /** @var Product|null */
$product = $request->post('product'); $product = $request->post('product');
// * Make new participation // * Make new participation
$participation = Participation::makeNew( $participation = Participation::makeNew(
app('clientIp'), resolve('clientIp'),
$customer, $customer,
$code, $code,
$product $product
@ -92,19 +101,21 @@ class ParticipateController extends Controller
// * Si l'utilisateur actuel à déjà gagné un gros lot, pas d'instant gagnant. // * Si l'utilisateur actuel à déjà gagné un gros lot, pas d'instant gagnant.
// ? As-t-on un instant gagnant ? // ? As-t-on un instant gagnant ?
if (Participation::customerAlreadyHasAnInstantWin($customer) or !$instantWin) { if (Participation::customerAlreadyHasAnInstantWin($customer) || ! $instantWin) {
// * --- DISCOUNT WIN --- // * --- DISCOUNT WIN ---
// ? Les BR sont activés ? // ? Les BR sont activés ?
if (!$settings->discounts_activated) { if (! $settings->discounts_activated) {
// * On enregistre la participation du joueur. // * On enregistre la participation du joueur.
$participation->save(); $participation->save();
// * Pas de coupons disponibles, le joueur à perdu /** @var view-string $view Pas de coupons disponibles, le joueur à perdu. */
return view('theme::pages.participation.loose'); $view = 'theme::pages.participation.loose';
return view($view);
} }
// * Le joueur à gagné un coupon. // * Le joueur à gagné un coupon.
/** @var \App\Lib\Discount Coupon aléatoire en fonction du ratio de chaque coupon */ /** @var \App\Lib\Discount Coupon aléatoire en fonction du ratio de chaque coupon */
$discount = Arr::get( $discount = Arr::get(
$settings->web_coupons, $settings->web_coupons,
$this->getRandomWeightedElementIndex($settings->web_coupons) $this->getRandomWeightedElementIndex($settings->web_coupons)
@ -124,8 +135,11 @@ class ParticipateController extends Controller
$crmCustomer->firstname $crmCustomer->firstname
)); ));
return view('theme::pages.participation.won', compact('discount')); /** @var view-string $view */
} //end if $view = 'theme::pages.participation.won';
return view($view, ['discount' => $discount]);
} // end if
// * --- INSTANT WIN --- // * --- INSTANT WIN ---
// * On enregistre la participation du joueur. // * On enregistre la participation du joueur.
@ -148,43 +162,42 @@ class ParticipateController extends Controller
$participation->created_at, $participation->created_at,
$instantWin->prize->label, $instantWin->prize->label,
$crmCustomer, $crmCustomer,
optional($code)->code_unique, $code?->code_unique,
optional($product)->ean_code $product?->ean_code
)); ));
return view('theme::pages.participation.won', [ /** @var view-string $view */
'prizeModel' => $instantWin->prize, $view = 'theme::pages.participation.won';
return view($view, [
'prizeModel' => $instantWin->prize,
'participationModel' => $participation, 'participationModel' => $participation,
'crmCustomer' => $crmCustomer 'crmCustomer' => $crmCustomer,
]); ]);
}); });
} }
// * --- PRIVATE METHODS --- // * --- PRIVATE METHODS ---
/** /**
* Try Call CRM + MesBr to setup discount * Try Call CRM + MesBr to setup discount
* *
* @param \App\Models\Customer $customer * @throws HttpResponseException If Any error occurs.
* @param LibDiscount $discount *
* @return void * @phpcs:disable Generic.Metrics.CyclomaticComplexity
* @throws \Illuminate\Http\Exceptions\HttpResponseException If Any error occurs.
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
*/ */
private function setDiscountAsWon(Customer $customer, LibDiscount $discount): void private function setDiscountAsWon(Customer $customer, LibDiscount $discount): void
{ {
// phpcs:enable // phpcs:enable
// * Phase - 1 Recupération des informations CRM // * Phase - 1 Recupération des informations CRM
/** @var \LDC\LDCCRM\Models\Customer*/ /** @var \LDC\LDCCRM\Models\Customer */
$crmUser = $this->crmRepository->fetchCrmCustomer($customer); $crmUser = $this->crmRepository->fetchCrmCustomer($customer);
// * Phase - 2 Mise à jour du coupon dans MesBr // * Phase - 2 Mise à jour du coupon dans MesBr
try { try {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
/** @var \LDC\MESBR\MesBr */ /** @var MesBr */
$mesBr = app('mesBrAPI'); $mesBr = resolve('mesBrAPI');
// * MaJ Customer // * MaJ Customer
/** @var string */ /** @var string */
@ -195,23 +208,24 @@ class ParticipateController extends Controller
* *
* Id est necessaire pour update panier.. * Id est necessaire pour update panier..
*/ */
/** @var object|null $result */
$result = $mesBr->updateCustomer($crmUser->hashcode, $crmUser->email, $crmUser->country->label()); $result = $mesBr->updateCustomer($crmUser->hashcode, $crmUser->email, $crmUser->country->label());
if (optional($result)->status !== "OK") { if ($result?->status !== 'OK') {
throw new \RuntimeException( throw new RuntimeException(
\sprintf( \sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s', 'MESBR : Une erreur est survenue. Code : %s Message : %s',
$result->code ?? 'Undefined', $result->code ?? 'Undefined',
$result->message ?? 'Undefined' $result->message ?? 'Undefined'
) )
); );
} }
/** @var object|null */ /** @var object|null $crm_customer */
$crm_customer = optional($result)->customer; $crm_customer = $result->customer ?? null;
if (empty(optional($crm_customer)->hashcode)) { throw_if(
throw new \RuntimeException( empty($crm_customer->hashcode),
'MESBR : Une erreur est survenue. Update Customer n\'as pas retourné le customer hashcode' RuntimeException::class,
); 'MESBR : Une erreur est survenue. Update Customer n\'as pas retourné le customer hashcode',
} );
return $crm_customer->hashcode; return $crm_customer->hashcode;
}, 100); }, 100);
@ -222,11 +236,11 @@ class ParticipateController extends Controller
strval($discount->web_coupon_identifiant) strval($discount->web_coupon_identifiant)
); );
if (Arr::get($result, 'status') === "OK") { if (Arr::get($result, 'status') === 'OK') {
// * La réponse est conforme ? // * La réponse est conforme ?
$needed_fields = ['ref']; $needed_fields = ['ref'];
if (!Arr::has($result, $needed_fields)) { if (! Arr::has($result, $needed_fields)) {
throw new \RuntimeException(sprintf( throw new RuntimeException(sprintf(
'MESBR : Il manques des champs dans la réponse, attendu => %s, donné => %s', 'MESBR : Il manques des champs dans la réponse, attendu => %s, donné => %s',
json_encode($needed_fields), json_encode($needed_fields),
json_encode($result), json_encode($result),
@ -237,20 +251,18 @@ class ParticipateController extends Controller
return; return;
} }
throw new \RuntimeException( throw new RuntimeException(
\sprintf( \sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s', 'MESBR : Une erreur est survenue. Code : %s Message : %s',
$result["code"] ?? 'Undefined', $result['code'] ?? 'Undefined',
$result["message"] ?? 'Undefined' $result['message'] ?? 'Undefined'
) )
); );
} catch (\RuntimeException $e) { } catch (RuntimeException $e) {
report($e); report($e);
// * Retenter ? // * Retenter ?
back()->with('error', trans('front.messages.unexpected_error_try_later'))->throwResponse(); back()->with('error', trans('front.messages.unexpected_error_try_later'))->throwResponse();
} //end try } // end try
throw new \Error('Should not reach this point');
} }
/** /**
@ -258,35 +270,34 @@ class ParticipateController extends Controller
* Or redirect him to the fill form * Or redirect him to the fill form
* ! Also validates its birth date (because beeing an adult is required to participate) * ! Also validates its birth date (because beeing an adult is required to participate)
* *
* @return void * @throws HttpResponseException If customer data doest not validate
* @throws \Illuminate\Http\Exceptions\HttpResponseException If customer data doest not validate * or and error occurs.
* or and error occurs.
*/ */
private function assertUserHasEnoughInfoToUseMesBr(): void private function assertUserHasEnoughInfoToUseMesBr(): void
{ {
// * Valider que le client dispose des donnés requises pour MesBr // * Valider que le client dispose des donnés requises pour MesBr
try { try {
/** @var \App\Models\Customer */ /** @var Customer */
$authUser = auth('frontend')->user(); $authUser = auth('frontend')->user();
$crmCustomer = $this->crmRepository->fetchCrmCustomer($authUser); $crmCustomer = $this->crmRepository->fetchCrmCustomer($authUser);
// * On valide les données selon les critères du site // * On valide les données selon les critères du site
$rules = collect((new CreateAccountRequest())->rules()); $rules = collect(new CreateAccountRequest()->rules());
Validator::make( Validator::make(
[ [
'birth_date' => $crmCustomer->birth_date, 'birth_date' => $crmCustomer->birth_date,
'address' => $crmCustomer->address, 'address' => $crmCustomer->address,
'postal_code' => $crmCustomer->postcode, 'postal_code' => $crmCustomer->postcode,
'country' => $crmCustomer->country->value, 'country' => $crmCustomer->country->value,
'city' => $crmCustomer->city 'city' => $crmCustomer->city,
], ],
[ [
'birth_date' => $rules->get('birth_date'), 'birth_date' => $rules->get('birth_date'),
'address' => $rules->get('address'), 'address' => $rules->get('address'),
'postal_code' => $rules->get('postal_code'), 'postal_code' => $rules->get('postal_code'),
'country' => $rules->get('country'), 'country' => $rules->get('country'),
'city' => $rules->get('city') 'city' => $rules->get('city'),
] ]
)->validate(); )->validate();
Session::remove('needCrmInfoUpdate'); Session::remove('needCrmInfoUpdate');
@ -295,13 +306,13 @@ class ParticipateController extends Controller
->with('warning', trans( ->with('warning', trans(
'front.messages.profile_incomplete_cannot_play' 'front.messages.profile_incomplete_cannot_play'
))->throwResponse(); ))->throwResponse();
} catch (\Exception $e) { } catch (Exception $e) {
report($e); report($e);
// * Retenter ? // * Retenter ?
response()->redirectToRoute('fo.homepage') response()->redirectToRoute('fo.homepage')
->with('error', trans('front.messages.unexpected_error_try_later')) ->with('error', trans('front.messages.unexpected_error_try_later'))
->throwResponse(); ->throwResponse();
} //end try } // end try
} }
// * --- END PRIVATE METHODS --- // * --- END PRIVATE METHODS ---

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo; namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType; use App\Enums\StaticPageType;
@ -11,47 +13,44 @@ class StaticPageController extends Controller
{ {
/** /**
* Show the rules view * Show the rules view
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/ */
public function reglement() public function reglement(): View
{ {
$staticPageModel = StaticPage::query()->where('type', StaticPageType::rules)->firstOrFail(); $staticPageModel = StaticPage::query()->where('type', StaticPageType::rules)->firstOrFail();
return $this->viewStaticPage($staticPageModel); return $this->viewStaticPage($staticPageModel);
} }
/** /**
* Show the privacy view * Show the privacy view
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/ */
public function charte() public function charte(): View
{ {
$staticPageModel = StaticPage::query()->where('type', StaticPageType::confidentiality)->firstOrFail(); $staticPageModel = StaticPage::query()->where('type', StaticPageType::confidentiality)->firstOrFail();
return $this->viewStaticPage($staticPageModel); return $this->viewStaticPage($staticPageModel);
} }
/** /**
* Show the legals view * Show the legals view
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/ */
public function legals() public function legals(): View
{ {
$staticPageModel = StaticPage::query()->where('type', StaticPageType::legals)->firstOrFail(); $staticPageModel = StaticPage::query()->where('type', StaticPageType::legals)->firstOrFail();
return $this->viewStaticPage($staticPageModel); return $this->viewStaticPage($staticPageModel);
} }
/** /**
* Display a static page * Display a static page
*
* @param \App\Models\StaticPage $staticPageModel
* @return \Illuminate\Contracts\View\View
*/ */
private function viewStaticPage(StaticPage $staticPageModel): View private function viewStaticPage(StaticPage $staticPageModel): View
{ {
return view('theme::pages.static_page', [ /** @var view-string $view */
'staticPageModel' => $staticPageModel $view = 'theme::pages.static_page';
return view($view, [
'staticPageModel' => $staticPageModel,
]); ]);
} }
} }

View file

@ -1,10 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Controllers\Fo; namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\SubmitWinningProofRequest; use App\Http\Requests\Fo\SubmitWinningProofRequest;
use App\Mail\Won\WinningProofAdminMail; use App\Mail\Won\WinningProofAdminMail;
use App\Models\Customer;
use App\Models\Participation; use App\Models\Participation;
use App\Models\Settings; use App\Models\Settings;
use App\Notifications\Customer\HasSubmitedWinProof; use App\Notifications\Customer\HasSubmitedWinProof;
@ -19,42 +22,42 @@ use Illuminate\Validation\ValidationException as IlluminateValidationException;
use LDC\LDCCRM\Enums\ApiError; use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException; use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException; use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
use RuntimeException;
class WinningProofController extends Controller class WinningProofController extends Controller
{ {
/** /**
* WinningProofController * WinningProofController
*
* @return void
*/ */
public function __construct() public function __construct()
{ {
$this->middleware(function (Request $request, callable $next) { $this->middleware(function (Request $request, callable $next) {
$this->authorize('fo.gameEnabled'); $this->authorize('fo.gameEnabled');
$this->authorize('submitProof', $request->route('participation')); $this->authorize('submitProof', $request->route('participation'));
return $next($request); return $next($request);
}); });
} }
/** /**
* Display winning proof form * Display winning proof form
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Contracts\View\View
*/ */
public function displayForm(Participation $participation): View public function displayForm(Participation $participation): View
{ {
$participation->load('instantWin.prize'); $participation->load('instantWin.prize');
/** @var \App\Models\Customer */ /** @var Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
$crmCustomer = $customer->api_data; $crmCustomer = $customer->api_data;
return view('theme::pages.participation.won-form-only', [ /** @var view-string $view */
$view = 'theme::pages.participation.won-form-only';
return view($view, [
'participationModel' => $participation, 'participationModel' => $participation,
'prizeModel' => $participation->instantWin->prize, 'prizeModel' => $participation->instantWin->prize,
'crmCustomer' => $crmCustomer 'crmCustomer' => $crmCustomer,
]); ]);
} }
@ -64,10 +67,8 @@ class WinningProofController extends Controller
* - 2 Save les pictures de preuve de gains * - 2 Save les pictures de preuve de gains
* - 3 Envoi un mail au abonnés (settings notification_emails) avec les infos client * - 3 Envoi un mail au abonnés (settings notification_emails) avec les infos client
* *
* @param \App\Http\Requests\Fo\SubmitWinningProofRequest $request
* @param \App\Models\Participation $participation
* @return \Illuminate\Http\RedirectResponse
* @throws IlluminateValidationException In case of failure. * @throws IlluminateValidationException In case of failure.
*
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber * @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
*/ */
public function displayFormSubmit( public function displayFormSubmit(
@ -80,14 +81,14 @@ class WinningProofController extends Controller
$participation->load([ $participation->load([
'instantWin.prize', 'instantWin.prize',
'code', 'code',
'product' 'product',
]); ]);
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
// * --- PHASE 1 Update Crm Customer --- // * --- PHASE 1 Update Crm Customer ---
/** @var \App\Models\Customer */ /** @var Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
$crmCustomer = $customer->api_data; $crmCustomer = $customer->api_data;
@ -118,7 +119,7 @@ class WinningProofController extends Controller
$participation->fill(Arr::only($request->validated(), [ $participation->fill(Arr::only($request->validated(), [
'barcode', 'barcode',
'ticket', 'ticket',
'sticker' 'sticker',
])); ]));
$participation->save(); $participation->save();
@ -130,8 +131,8 @@ class WinningProofController extends Controller
$participation->created_at, $participation->created_at,
$participation->instantWin->prize->label, $participation->instantWin->prize->label,
$crmCustomer, $crmCustomer,
optional($participation->code)->code_unique, $participation->code?->code_unique,
optional($participation->product)->ean_code, $participation->product?->ean_code,
$participation->sticker, $participation->sticker,
$participation->ticket, $participation->ticket,
$participation->barcode, $participation->barcode,
@ -145,16 +146,16 @@ class WinningProofController extends Controller
} catch (ApiErrorException $e) { } catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::validationFailed)) { if ($e->getApiError()->equals(ApiError::validationFailed)) {
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => $e->getMessage() 'error' => $e->getMessage(),
]); ]);
} }
report($e); report($e);
Log::error(__CLASS__ . '::create reached end of possibilities.'); Log::error(self::class . '::create reached end of possibilities.');
throw new \RuntimeException(__CLASS__ . '::create reached end of possibilities.'); throw new RuntimeException(self::class . '::create reached end of possibilities.', $e->getCode(), $e);
} catch (CrmValidationException $e) { } catch (CrmValidationException $e) {
throw IlluminateValidationException::withMessages([ throw IlluminateValidationException::withMessages([
'error' => $e->getMessage() 'error' => $e->getMessage(),
]); ]);
} //end try } // end try
} }
} }

View file

@ -1,8 +1,37 @@
<?php <?php
declare(strict_types=1);
namespace App\Http; namespace App\Http;
use App\Http\Middleware\Authenticate;
use App\Http\Middleware\CheckGameStatus;
use App\Http\Middleware\CheckThemeVersion;
use App\Http\Middleware\EncryptCookies;
use App\Http\Middleware\HtmlSanitizer;
use App\Http\Middleware\PreventRequestsDuringMaintenance;
use App\Http\Middleware\RedirectIfAuthenticated;
use App\Http\Middleware\SecureHeaders;
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\TrustHosts;
use App\Http\Middleware\TrustProxies;
use App\Http\Middleware\ValidateSignature;
use App\Http\Middleware\VerifyCsrfToken;
use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
use Illuminate\Auth\Middleware\Authorize;
use Illuminate\Auth\Middleware\EnsureEmailIsVerified;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Foundation\Http\Kernel as HttpKernel; use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull;
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
use Illuminate\Http\Middleware\HandleCors;
use Illuminate\Http\Middleware\SetCacheHeaders;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class Kernel extends HttpKernel class Kernel extends HttpKernel
{ {
@ -13,14 +42,15 @@ class Kernel extends HttpKernel
* *
* @var array<int, string> * @var array<int, string>
*/ */
#[\Override]
protected $middleware = [ protected $middleware = [
\App\Http\Middleware\TrustHosts::class, TrustHosts::class,
\App\Http\Middleware\TrustProxies::class, TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class, HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class, PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class, TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, ConvertEmptyStringsToNull::class,
]; ];
/** /**
@ -28,22 +58,23 @@ class Kernel extends HttpKernel
* *
* @var array<string, array<int, string>> * @var array<string, array<int, string>>
*/ */
#[\Override]
protected $middlewareGroups = [ protected $middlewareGroups = [
'web' => [ 'web' => [
'htmlsanitize', 'htmlsanitize',
\App\Http\Middleware\EncryptCookies::class, EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class, StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class, ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class, VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class, SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class, SecureHeaders::class,
\App\Http\Middleware\CheckThemeVersion::class CheckThemeVersion::class,
], ],
'api' => [ 'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api', ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class, SubstituteBindings::class,
], ],
]; ];
@ -54,18 +85,19 @@ class Kernel extends HttpKernel
* *
* @var array<string, string> * @var array<string, string>
*/ */
protected $routeMiddleware = [ #[\Override]
'auth' => \App\Http\Middleware\Authenticate::class, protected $middlewareAliases = [
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'auth' => Authenticate::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class, 'auth.basic' => AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'auth.session' => AuthenticateSession::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class, 'cache.headers' => SetCacheHeaders::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'can' => Authorize::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, 'guest' => RedirectIfAuthenticated::class,
'check-game-status' => \App\Http\Middleware\CheckGameStatus::class, 'password.confirm' => RequirePassword::class,
'signed' => \App\Http\Middleware\ValidateSignature::class, 'check-game-status' => CheckGameStatus::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'signed' => ValidateSignature::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, 'throttle' => ThrottleRequests::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class, 'verified' => EnsureEmailIsVerified::class,
'htmlsanitize' => HtmlSanitizer::class,
]; ];
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware;
@ -10,27 +12,25 @@ class Authenticate extends Middleware
{ {
/** /**
* Get the path the user should be redirected to when they are not authenticated. * Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/ */
protected function redirectTo(Request $request): ?string protected function redirectTo(Request $request): ?string
{ {
$logins = [ $logins = [
'backend' => route('bo.login') 'backend' => route('bo.login'),
]; ];
if (!$request->expectsJson()) { if (! $request->expectsJson()) {
// * Notify must login // * Notify must login
Session::flash('warning', trans('Veuillez vous connecter pour continuer')); Session::flash('warning', trans('Veuillez vous connecter pour continuer'));
foreach ($request->route()->gatherMiddleware() as $middleware) { foreach ($request->route()->gatherMiddleware() as $middleware) {
if (\is_string($middleware) and strpos($middleware, 'auth:') !== false) { if (\is_string($middleware) && str_contains($middleware, 'auth:')) {
$route = $logins[str_replace('auth:', '', $middleware)] ?? null; $route = $logins[str_replace('auth:', '', $middleware)] ?? null;
if ($route) { if ($route) {
return $route; return $route;
} }
} }
} }
return route('fo.login'); return route('fo.login');
} }

View file

@ -1,9 +1,13 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use App\Models\Settings; use App\Models\Settings;
use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use RuntimeException;
class CheckGameStatus class CheckGameStatus
{ {
@ -12,21 +16,20 @@ class CheckGameStatus
* *
* * Check with game settings which page should be shown and redirect if needed. * * Check with game settings which page should be shown and redirect if needed.
* *
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed * @return mixed
* @throws \RuntimeException If tha game has an unhandled state. *
* @throws RuntimeException If tha game has an unhandled state.
*/ */
public function handle(Request $request, \Closure $next) public function handle(Request $request, Closure $next)
{ {
/** @var \App\Models\Settings $settings */ /** @var Settings $settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
return match (true) { return match (true) {
// * End page // * End page
$settings->should_display_end_page => redirect()->route('fo.end'), $settings->should_display_end_page => to_route('fo.end'),
// * Teaser // * Teaser
$settings->should_display_teaser_page => redirect()->route('fo.teaser'), $settings->should_display_teaser_page => to_route('fo.teaser'),
// * Game is ended // * Game is ended
$settings->game_has_ended => redirect($settings->redirect_close), $settings->game_has_ended => redirect($settings->redirect_close),
// * Game has started // * Game has started

View file

@ -1,12 +1,15 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -15,27 +18,22 @@ class CheckThemeVersion
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
* @param \Illuminate\Http\Request $request * @param Closure(Request):Response $next
* @param \Closure(\Illuminate\Http\Request):(\Symfony\Component\HttpFoundation\Response) $next *
* @return \Symfony\Component\HttpFoundation\Response
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/ */
public function handle(Request $request, \Closure $next): Response public function handle(Request $request, Closure $next): Response
{ {
// phpcs:enable // phpcs:enable
if (!app()->runningInConsole()) { if (! app()->runningInConsole()) {
$appInfos = Cache::remember('composer', 84600, function () { $appInfos = Cache::remember('composer', 84600, fn (): mixed => json_decode(File::get(\app_path('../composer.json'))));
return json_decode(File::get(\app_path('../composer.json')));
});
// * Assert theme version does not diverge more than minor // * Assert theme version does not diverge more than minor
$appVer = Str::of($appInfos->version)->explode('.'); $appVer = Str::of($appInfos->version)->explode('.');
$themeVer = Str::of(config('theme.version'))->explode('.'); $themeVer = Str::of(config('theme.version'))->explode('.');
if ( if (
// * Major check // * Major check
Arr::get($appVer, 0) !== Arr::get($themeVer, 0) or Arr::get($appVer, 0) !== Arr::get($themeVer, 0) || Arr::get($appVer, 1) !== Arr::get($themeVer, 1)
// * Patch check
Arr::get($appVer, 1) !== Arr::get($themeVer, 1)
) { ) {
Session::flash('warning', trans( Session::flash('warning', trans(
'Ce thème n\'a semble t-il pas été vérifié pour cette version du moteur' 'Ce thème n\'a semble t-il pas été vérifié pour cette version du moteur'

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware; use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
@ -11,5 +13,6 @@ class EncryptCookies extends Middleware
* *
* @var array<int, string> * @var array<int, string>
*/ */
#[\Override]
protected $except = []; protected $except = [];
} }

View file

@ -1,8 +1,11 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use App\Lib\Helpers\ToolboxHelper; use App\Lib\Helpers\ToolboxHelper;
use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -10,19 +13,14 @@ class HtmlSanitizer
{ {
/** /**
* Handle an incoming request. * Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|array ...$params
* @return \Symfony\Component\HttpFoundation\Response
*/ */
public function handle(Request $request, \Closure $next, string|array ...$params): Response public function handle(Request $request, Closure $next, string | array ...$params): Response
{ {
$inputs = $request->all(); $inputs = $request->all();
/** @var callable|null */ /** @var callable|null */
$cleanInput = null; $cleanInput = null;
$cleanInput = function ( $cleanInput = function (
null|string|array &$input, null | string | array &$input,
string $name, string $name,
?string $rootName = null ?string $rootName = null
) use ( ) use (
@ -33,14 +31,14 @@ class HtmlSanitizer
if (is_array($input)) { if (is_array($input)) {
array_walk_recursive($input, $cleanInput, $name); array_walk_recursive($input, $cleanInput, $name);
} }
if (!is_string($input)) { if (! is_string($input)) {
return; return;
} }
if (in_array($name, $params) or in_array($rootName ?? $name, $params)) { if (in_array($name, $params) || in_array($rootName ?? $name, $params)) {
// ? Is a json input ? // ? Is a json input ?
if ($isJson = \json_decode($input, true) and is_array($isJson)) { if ($isJson = \json_decode($input, true) and is_array($isJson)) {
// ! Decode each json field recursive. // ! Decode each json field recursive.
\array_walk_recursive($isJson, function (string|array &$item) { \array_walk_recursive($isJson, function (string | array &$item): void {
if (\is_string($item)) { if (\is_string($item)) {
$item = clean($item); $item = clean($item);
$item = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($item); $item = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($item);
@ -56,6 +54,7 @@ class HtmlSanitizer
}; };
\array_walk($inputs, $cleanInput); \array_walk($inputs, $cleanInput);
$request->merge($inputs); $request->merge($inputs);
return $next($request); return $next($request);
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware; use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
@ -11,5 +13,6 @@ class PreventRequestsDuringMaintenance extends Middleware
* *
* @var array<int, string> * @var array<int, string>
*/ */
#[\Override]
protected $except = []; protected $except = [];
} }

View file

@ -1,8 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use RuntimeException;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated class RedirectIfAuthenticated
@ -10,30 +14,31 @@ class RedirectIfAuthenticated
/** /**
* Handle an incoming request. * Handle an incoming request.
* *
* @param \Illuminate\Http\Request $request * @param Closure(Request):Response $next
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next * @param string|null ...$guards
* @param string|null ...$guards *
* @return \Symfony\Component\HttpFoundation\Response * @throws RuntimeException If guard is unhandled.
* @throws \RuntimeException If guard is unhandled. *
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint * @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/ */
public function handle(Request $request, \Closure $next, ...$guards): Response public function handle(Request $request, Closure $next, ...$guards): Response
{ {
// phpcs:enable phpcs:disable // phpcs:enable phpcs:disable
$guards = empty($guards) ? [null] : $guards; $guards = $guards === [] ? [null] : $guards;
foreach ($guards as $guard) { foreach ($guards as $guard) {
if (auth($guard)->check()) { if (auth($guard)->check()) {
switch ($guard) { switch ($guard) {
case 'frontend': case 'frontend':
return \redirect()->route('fo.homepage'); return to_route('fo.homepage');
case 'backend': case 'backend':
if (!$request->routeIs('bo.*')) { if (! $request->routeIs('bo.*')) {
continue 2; continue 2;
} }
return \redirect()->route('bo.dashboard');
return to_route('bo.dashboard');
default: default:
throw new \RuntimeException("Unhandled guard `{$guard}` ."); throw new RuntimeException("Unhandled guard `{$guard}` .");
} }
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Closure; use Closure;
@ -11,7 +13,7 @@ use Symfony\Component\HttpFoundation\Response;
class SecureHeaders class SecureHeaders
{ {
private $unwantedHeaderList = [ private array $unwantedHeaderList = [
'X-Powered-By', 'X-Powered-By',
'Server', 'Server',
]; ];
@ -47,7 +49,7 @@ class SecureHeaders
"connect-src 'self' blob: " . implode(' ', array_filter([ "connect-src 'self' blob: " . implode(' ', array_filter([
$viteDevSocket, $viteDevSocket,
$viteDevOrigin, $viteDevOrigin,
'https://yl.s-web.fr' 'https://yl.s-web.fr',
])) . ';', ])) . ';',
"img-src 'self' data: blob: " . implode(' ', array_filter([ "img-src 'self' data: blob: " . implode(' ', array_filter([
$viteDevOrigin, $viteDevOrigin,
@ -56,11 +58,11 @@ class SecureHeaders
"frame-src 'self' " . implode(' ', [ "frame-src 'self' " . implode(' ', [
'https://www.google.com/recaptcha/', 'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/', 'https://recaptcha.google.com/recaptcha/',
'https://www.google.com/maps/' 'https://www.google.com/maps/',
]) . ';', ]) . ';',
"font-src 'self' data: " . implode(' ', array_filter([ "font-src 'self' data: " . implode(' ', array_filter([
$viteDevOrigin, $viteDevOrigin,
'https://fonts.gstatic.com' // <-- Point-virgule retiré ici 'https://fonts.gstatic.com',
])) . ';', ])) . ';',
"style-src 'self' 'unsafe-inline' " . implode(' ', array_filter([ "style-src 'self' 'unsafe-inline' " . implode(' ', array_filter([
'https://cdnjs.cloudflare.com', 'https://cdnjs.cloudflare.com',
@ -76,7 +78,7 @@ class SecureHeaders
'https://cdnjs.cloudflare.com', 'https://cdnjs.cloudflare.com',
"$scheme://www.google-analytics.com", "$scheme://www.google-analytics.com",
$viteDevOrigin, $viteDevOrigin,
])) . ';' ])) . ';',
]; ];
$response->headers->set( $response->headers->set(

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware; use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
@ -11,6 +13,7 @@ class TrimStrings extends Middleware
* *
* @var array<int, string> * @var array<int, string>
*/ */
#[\Override]
protected $except = [ protected $except = [
'current_password', 'current_password',
'password', 'password',

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware; use Illuminate\Http\Middleware\TrustHosts as Middleware;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware; use Illuminate\Http\Middleware\TrustProxies as Middleware;
@ -12,15 +14,17 @@ class TrustProxies extends Middleware
* *
* @var array<int, string>|string|null * @var array<int, string>|string|null
*/ */
#[\Override]
protected $proxies = '*'; protected $proxies = '*';
/** /**
* The headers that should be used to detect proxies. * The headers that should be used to detect proxies.
* *
* @var integer * @var int
*/ */
#[\Override]
protected $headers = protected $headers =
Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_PROTO |

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware; use Illuminate\Routing\Middleware\ValidateSignature as Middleware;

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Middleware; namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
@ -11,7 +13,8 @@ class VerifyCsrfToken extends Middleware
* *
* @var array<int, string> * @var array<int, string>
*/ */
#[\Override]
protected $except = [ protected $except = [
'/csp-report' '/csp-report',
]; ];
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\BannedIps; namespace App\Http\Requests\Bo\BannedIps;
use App\Models\BannedIp; use App\Models\BannedIp;
@ -11,7 +13,7 @@ class StoreBannedIpRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -20,10 +22,8 @@ class StoreBannedIpRequest extends FormRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
return [ return [
'ip' => 'required|ip|unique:banned_ips,ip', 'ip' => 'required|ip|unique:banned_ips,ip',
@ -32,8 +32,6 @@ class StoreBannedIpRequest extends FormRequest
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {

View file

@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\BannedIps; namespace App\Http\Requests\Bo\BannedIps;
use App\Models\BannedIp;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
@ -10,7 +13,7 @@ class UpdateBannedIpRequest extends StoreBannedIpRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -19,18 +22,17 @@ class UpdateBannedIpRequest extends StoreBannedIpRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
/** @var \App\Models\BannedIp */ /** @var BannedIp */
$ipToBanModel = $this->route('banned_ip'); $ipToBanModel = $this->route('banned_ip');
return [ return [
'ip' => [ 'ip' => [
'required', 'ip', 'required', 'ip',
Rule::unique('banned_ips', 'ip') Rule::unique('banned_ips', 'ip')
->ignore($ipToBanModel->ip, 'ip') ->ignore($ipToBanModel->ip, 'ip'),
], ],
]; ];
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Codes; namespace App\Http\Requests\Bo\Codes;
use App\Models\Code; use App\Models\Code;
@ -11,7 +13,7 @@ class CodeSearchRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -26,7 +28,7 @@ class CodeSearchRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'code' => 'required|string|min:1|max:25' 'code' => 'required|string|min:1|max:25',
]; ];
} }
@ -38,7 +40,7 @@ class CodeSearchRequest extends FormRequest
public function attributes(): array public function attributes(): array
{ {
return [ return [
'code' => trans('code') 'code' => trans('code'),
]; ];
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Codes; namespace App\Http\Requests\Bo\Codes;
use App\Models\Code; use App\Models\Code;
@ -10,31 +12,28 @@ class ExportCodesRequest extends FormRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
return Gate::check('export', Code::class) and $this->json(); return Gate::check('export', Code::class) && $this->json();
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
*
* @return void
*/ */
protected function prepareForValidation(): void protected function prepareForValidation(): void
{ {
$codesAvailableToExport = Code::query()->where('code_exported', false); $codesAvailableToExport = Code::query()->where('code_exported', false);
/** @var integer|null */
$lastExportedId = optional(Code::query()->where('code_exported', true) /** @var int|null $lastExportedId */
->orderBy('id', 'desc')->first())->id ?? null; $lastExportedId = Code::query()->where('code_exported', true)->max('id');
if ($lastExportedId) { if ($lastExportedId) {
$codesAvailableToExport = $codesAvailableToExport $codesAvailableToExport->where('id', '>', $lastExportedId);
->where('id', '>', $lastExportedId);
} }
$this->merge([ $this->merge([
'available_export' => $codesAvailableToExport->count() 'available_export' => $codesAvailableToExport->count(),
]); ]);
} }
@ -46,10 +45,10 @@ class ExportCodesRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'nb_code_tot' => 'required|integer|min:1|lte:available_export', 'nb_code_tot' => 'required|integer|min:1|lte:available_export',
'nb_code_fic' => 'required|integer|min:1', 'nb_code_fic' => 'required|integer|min:1',
'nb_code_ligne' => 'required|integer|min:1', 'nb_code_ligne' => 'required|integer|min:1',
'available_export' => 'required|integer' 'available_export' => 'required|integer',
]; ];
} }
@ -61,10 +60,10 @@ class ExportCodesRequest extends FormRequest
public function attributes(): array public function attributes(): array
{ {
return [ return [
'nb_code_fic' => trans('nombre total de codes à exporter'), 'nb_code_fic' => trans('nombre total de codes à exporter'),
'nb_code_tot' => trans('nombre de codes par fichier'), 'nb_code_tot' => trans('nombre de codes par fichier'),
'nb_code_ligne' => trans('nombre de codes par ligne'), 'nb_code_ligne' => trans('nombre de codes par ligne'),
'available_export' => trans('nombre de codes restant à exporté(s)') 'available_export' => trans('nombre de codes restant à exporté(s)'),
]; ];
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Codes; namespace App\Http\Requests\Bo\Codes;
use App\Models\Code; use App\Models\Code;
@ -10,12 +12,10 @@ class GenerateCodesRequest extends FormRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
return Gate::check('generate', Code::class) and $this->json(); return Gate::check('generate', Code::class) && $this->json();
} }
/** /**
@ -26,7 +26,7 @@ class GenerateCodesRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'quantity' => 'required|integer|min:1' 'quantity' => 'required|integer|min:1',
]; ];
} }
@ -38,7 +38,7 @@ class GenerateCodesRequest extends FormRequest
public function attributes(): array public function attributes(): array
{ {
return [ return [
'quantity' => trans('validation.custom.quantity') 'quantity' => trans('validation.custom.quantity'),
]; ];
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\FaqQuestions; namespace App\Http\Requests\Bo\FaqQuestions;
use App\Models\FaqQuestion; use App\Models\FaqQuestion;
@ -11,7 +13,7 @@ class StoreFaqQuestionRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -20,28 +22,24 @@ class StoreFaqQuestionRequest extends FormRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
return [ return [
'question' => 'required|string|min:3|max:255', 'question' => 'required|string|min:3|max:255',
'answer' => 'required|string|min:3', 'answer' => 'required|string|min:3',
'theme_id' => 'required|exists:faq_themes,id', 'theme_id' => 'required|exists:faq_themes,id',
]; ];
} }
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {
return [ return [
'question' => trans('validation.custom.question'), 'question' => trans('validation.custom.question'),
'answer' => trans('validation.custom.answer'), 'answer' => trans('validation.custom.answer'),
'theme_id' => trans_choice('models.classes.faq-themes', 1), 'theme_id' => trans_choice('models.classes.faq-themes', 1),
]; ];
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\FaqQuestions; namespace App\Http\Requests\Bo\FaqQuestions;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
@ -9,7 +11,7 @@ class UpdateFaqQuestionRequest extends StoreFaqQuestionRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\FaqThemes; namespace App\Http\Requests\Bo\FaqThemes;
use App\Models\FaqTheme; use App\Models\FaqTheme;
@ -11,7 +13,7 @@ class StoreFaqThemeRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -20,20 +22,16 @@ class StoreFaqThemeRequest extends FormRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
return [ return [
'name' => 'required|string|min:3|max:255' 'name' => 'required|string|min:3|max:255',
]; ];
} }
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\FaqThemes; namespace App\Http\Requests\Bo\FaqThemes;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
@ -9,7 +11,7 @@ class UpdateFaqThemeRequest extends StoreFaqThemeRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Prizes; namespace App\Http\Requests\Bo\Prizes;
use App\Models\Prize; use App\Models\Prize;
@ -14,7 +16,7 @@ class StorePrizeRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -23,37 +25,34 @@ class StorePrizeRequest extends FormRequest
/** /**
* Prepare the data for validation. * Prepare the data for validation.
*
* @return void
*/ */
public function prepareForValidation(): void public function prepareForValidation(): void
{ {
$this->merge([ $this->merge([
'pdf_winning_proof' => $this->input('pdf_winning_proof') 'pdf_winning_proof' => $this->input('pdf_winning_proof'),
]); ]);
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
$rules = [ $rules = [
'label' => 'required|string|min:3|max:255', 'label' => 'required|string|min:3|max:255',
'picture_alt' => 'required|string|between:3,255', 'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255', 'picture_title' => 'required|string|between:3,255',
'arg_title' => 'required|string|min:3|max:255', 'arg_title' => 'required|string|min:3|max:255',
'arg_subtitle' => 'nullable|string|min:3|max:255', 'arg_subtitle' => 'nullable|string|min:3|max:255',
'arg_content' => 'required|string|min:15', 'arg_content' => 'required|string|min:15',
'quantity' => 'required|numeric|min:1', 'quantity' => 'required|numeric|min:1',
'pdf_winning_proof' => 'nullable|file|mimes:pdf', 'pdf_winning_proof' => 'nullable|file|mimes:pdf',
]; ];
$min_width = config('theme.models.prizes.picture_min_width'); $min_width = config('theme.models.prizes.picture_min_width');
$min_height = config('theme.models.prizes.picture_min_height'); $min_height = config('theme.models.prizes.picture_min_height');
$max_width = config('theme.models.prizes.picture_max_width'); $max_width = config('theme.models.prizes.picture_max_width');
$max_height = config('theme.models.prizes.picture_max_height'); $max_height = config('theme.models.prizes.picture_max_height');
return \array_merge( return \array_merge(
$rules, $rules,
$this->pictureRules('picture', $min_width, $min_height, $max_width, $max_height), $this->pictureRules('picture', $min_width, $min_height, $max_width, $max_height),
@ -62,20 +61,18 @@ class StorePrizeRequest extends FormRequest
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {
return [ return [
'label' => trans('validation.custom.label'), 'label' => trans('validation.custom.label'),
'picture' => trans('validation.custom.picture'), 'picture' => trans('validation.custom.picture'),
'picture_alt' => trans('validation.custom.picture_alt'), 'picture_alt' => trans('validation.custom.picture_alt'),
'picture_title' => trans('validation.custom.picture_title'), 'picture_title' => trans('validation.custom.picture_title'),
'arg_title' => trans('validation.attributes.title'), 'arg_title' => trans('validation.attributes.title'),
'arg_subtitle' => trans('validation.custom.subtitle'), 'arg_subtitle' => trans('validation.custom.subtitle'),
'arg_content' => trans('validation.attributes.content'), 'arg_content' => trans('validation.attributes.content'),
'quantity' => trans('validation.custom.quantity'), 'quantity' => trans('validation.custom.quantity'),
'pdf_winning_proof' => trans('validation.custom.pdf_winning_proof'), 'pdf_winning_proof' => trans('validation.custom.pdf_winning_proof'),
]; ];
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Prizes; namespace App\Http\Requests\Bo\Prizes;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
@ -9,7 +11,7 @@ class UpdatePrizeRequest extends StorePrizeRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Products; namespace App\Http\Requests\Bo\Products;
use App\Models\Product; use App\Models\Product;
@ -15,7 +17,7 @@ class StoreProductRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -34,13 +36,11 @@ class StoreProductRequest extends FormRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
$rules = [ $rules = [
'name' => 'required|string|min:3|max:255', 'name' => 'required|string|min:3|max:255',
'ean_code' => [ 'ean_code' => [
'required', 'required',
'unique:products,ean_code', 'unique:products,ean_code',
@ -48,10 +48,11 @@ class StoreProductRequest extends FormRequest
], ],
'picture_alt' => 'nullable|required_with:picture|string|between:3,255', 'picture_alt' => 'nullable|required_with:picture|string|between:3,255',
'picture_title' => 'nullable|required_with:picture|string|between:3,255', 'picture_title' => 'nullable|required_with:picture|string|between:3,255',
'range_id' => 'exists:ranges,id' 'range_id' => 'exists:ranges,id',
]; ];
$width = config('theme.models.products.picture_max_width'); $width = config('theme.models.products.picture_max_width');
$height = config('theme.models.products.picture_max_height'); $height = config('theme.models.products.picture_max_height');
return \array_merge( return \array_merge(
$rules, $rules,
$this->pictureRules('picture', $width, $height, $width, $height, true), $this->pictureRules('picture', $width, $height, $width, $height, true),
@ -60,28 +61,26 @@ class StoreProductRequest extends FormRequest
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {
return [ return [
'name' => trans('validation.attributes.name'), 'name' => trans('validation.attributes.name'),
'picture_title' => trans(':field1 :inter1:field2 :inter2:model', [ 'picture_title' => trans(':field1 :inter1:field2 :inter2:model', [
'field1' => Str::ucfirst(trans('validation.custom.attribute_title')), 'field1' => Str::ucfirst(trans('validation.custom.attribute_title')),
'inter1' => trans('validation.custom.of_the.vowel'), 'inter1' => trans('validation.custom.of_the.vowel'),
'field2' => trans('validation.attributes.image'), 'field2' => trans('validation.attributes.image'),
'inter2' => trans('validation.custom.of_the.male'), 'inter2' => trans('validation.custom.of_the.male'),
'model' => trans('models.classes.product'), 'model' => trans('models.classes.product'),
]), ]),
'picture_alt' => trans(':field1 :inter1:field2 :inter2:model', [ 'picture_alt' => trans(':field1 :inter1:field2 :inter2:model', [
'field1' => Str::ucfirst(trans('validation.custom.attribute_alt')), 'field1' => Str::ucfirst(trans('validation.custom.attribute_alt')),
'inter1' => trans('validation.custom.of_the.vowel'), 'inter1' => trans('validation.custom.of_the.vowel'),
'field2' => trans('validation.attributes.image'), 'field2' => trans('validation.attributes.image'),
'inter2' => trans('validation.custom.of_the.male'), 'inter2' => trans('validation.custom.of_the.male'),
'model' => trans('models.classes.product'), 'model' => trans('models.classes.product'),
]), ]),
'picture' => trans('validation.attributes.image'), 'picture' => trans('validation.attributes.image'),
'ean_code' => trans('validation.custom.ean_code'), 'ean_code' => trans('validation.custom.ean_code'),
'range_id' => trans('models.classes.range'), 'range_id' => trans('models.classes.range'),
]; ];

View file

@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Products; namespace App\Http\Requests\Bo\Products;
use App\Models\Product;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
@ -10,7 +13,7 @@ class UpdateProductRequest extends StoreProductRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -19,13 +22,12 @@ class UpdateProductRequest extends StoreProductRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
/** @var \App\Models\Product */ /** @var Product */
$productModel = $this->route('product'); $productModel = $this->route('product');
return array_merge(parent::rules(), [ return array_merge(parent::rules(), [
'ean_code' => [ 'ean_code' => [
'required', 'required',

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Ranges; namespace App\Http\Requests\Bo\Ranges;
use App\Models\Range; use App\Models\Range;
@ -14,7 +16,7 @@ class StoreRangeRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -33,10 +35,8 @@ class StoreRangeRequest extends FormRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
$rules = [ $rules = [
'name' => 'required|string|min:2|max:255', 'name' => 'required|string|min:2|max:255',
@ -45,6 +45,7 @@ class StoreRangeRequest extends FormRequest
]; ];
$width = config('theme.models.ranges.picture_max_width'); $width = config('theme.models.ranges.picture_max_width');
$height = config('theme.models.ranges.picture_max_height'); $height = config('theme.models.ranges.picture_max_height');
return \array_merge( return \array_merge(
$rules, $rules,
$this->pictureRules('picture', $width, $height, $width, $height, false), $this->pictureRules('picture', $width, $height, $width, $height, false),

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Ranges; namespace App\Http\Requests\Bo\Ranges;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
@ -9,7 +11,7 @@ class UpdateRangeRequest extends StoreRangeRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Settings; namespace App\Http\Requests\Bo\Settings;
use App\Enums\CodeType; use App\Enums\CodeType;
@ -8,15 +10,13 @@ use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use LDC\LDCCRM\Enums\Country;
use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\Rules\Enum;
use LDC\LDCCRM\Enums\Country;
class UpdateSettingsRequest extends FormRequest class UpdateSettingsRequest extends FormRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
@ -25,8 +25,6 @@ class UpdateSettingsRequest extends FormRequest
/** /**
* Prepare the data for validation. * Prepare the data for validation.
*
* @return void
*/ */
protected function prepareForValidation(): void protected function prepareForValidation(): void
{ {
@ -54,48 +52,46 @@ class UpdateSettingsRequest extends FormRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules(): array public function rules(): array
{ {
return [ return [
'name' => 'required|string|min:3|max:255', 'name' => 'required|string|min:3|max:255',
'company' => 'required|string|min:3|max:255', 'company' => 'required|string|min:3|max:255',
'start_date' => 'required|date', 'start_date' => 'required|date',
'end_date' => 'required|date', 'end_date' => 'required|date',
'nb_participation_player' => 'required|numeric|min:0', 'nb_participation_player' => 'required|numeric|min:0',
'nb_participation_ip' => 'required|numeric|min:0', 'nb_participation_ip' => 'required|numeric|min:0',
'nb_unique_code_use' => 'required|numeric|min:0', 'nb_unique_code_use' => 'required|numeric|min:0',
'code_type' => ['required', new Enum(CodeType::class)], 'code_type' => ['required', new Enum(CodeType::class)],
'activated_teaser' => 'required|boolean', 'activated_teaser' => 'required|boolean',
'activated_close_page' => 'required|boolean', 'activated_close_page' => 'required|boolean',
'close_page_active_date' => [ 'close_page_active_date' => [
'nullable', 'nullable',
'required_if:activated_close_page,true,1', 'required_if:activated_close_page,true,1',
'date', 'date',
'after_or_equal:end_date' 'after_or_equal:end_date',
], ],
'redirect_close' => 'required|url:http,https', 'redirect_close' => 'required|url:http,https',
'notify_prize_sent' => 'required|boolean', 'notify_prize_sent' => 'required|boolean',
'discounts_activated' => 'required|boolean', 'discounts_activated' => 'required|boolean',
'discounts_site' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255', 'discounts_site' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255',
'discounts_campaign' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255', 'discounts_campaign' => 'nullable|required_if:discounts_activated,true,1|string|min:3|max:255',
'web_coupons' => [ 'web_coupons' => [
'present', 'array', function ($attribute, $value, $fail) { 'present', 'array', function ($attribute, $value, $fail): void {
if ($this->post('discounts_activated') and !collect($value)->filter()->count()) { if ($this->post('discounts_activated') && ! collect($value)->filter()->count()) {
$fail(trans('validation.min.array', [ $fail(trans('validation.min.array', [
'attribute' => Arr::get($this->attributes(), $attribute), 'attribute' => Arr::get($this->attributes(), $attribute),
'min' => 1 'min' => 1,
])); ]));
} }
if (collect($value)->sum('percentage') > 100) { if (collect($value)->sum('percentage') > 100) {
$fail(trans('validation.max.numeric', [ $fail(trans('validation.max.numeric', [
'attribute' => Arr::get($this->attributes(), $attribute), 'attribute' => Arr::get($this->attributes(), $attribute),
'max' => 100 'max' => 100,
])); ]));
} }
} },
], ],
'web_coupons.*' => 'array', 'web_coupons.*' => 'array',
'web_coupons.*.web_coupon_identifiant' => 'required|digits:13', 'web_coupons.*.web_coupon_identifiant' => 'required|digits:13',
@ -103,7 +99,7 @@ class UpdateSettingsRequest extends FormRequest
'web_coupons.*.face_value' => 'required|numeric|min:10|max:300', 'web_coupons.*.face_value' => 'required|numeric|min:10|max:300',
'web_coupons.*.valid_on' => 'required|string|min:3|max:255', 'web_coupons.*.valid_on' => 'required|string|min:3|max:255',
'web_coupons.*.picture' => [ 'web_coupons.*.picture' => [
'required', function (string $attribute, $value, callable $fail) { 'required', function (string $attribute, ?string $value, callable $fail): void {
ToolboxHelper::validatePicture( ToolboxHelper::validatePicture(
$attribute, $attribute,
$value, $value,
@ -112,17 +108,17 @@ class UpdateSettingsRequest extends FormRequest
'maxHeight' => 409, 'maxHeight' => 409,
'maxWith' => 920, 'maxWith' => 920,
'minHeight' => 409, 'minHeight' => 409,
'minWith' => 920 'minWith' => 920,
] ]
); );
} },
], ],
'notification_emails' => 'required|array|min:1', 'notification_emails' => 'required|array|min:1',
'notification_emails.*' => 'email:rfc,spoof,dns', 'notification_emails.*' => 'email:rfc,spoof,dns',
'authorized_countries' => 'sometimes|array|max:' . count(Country::getValidCountries()), 'authorized_countries' => 'sometimes|array|max:' . count(Country::getValidCountries()),
'authorized_countries.*' => [ 'authorized_countries.*' => [
'sometimes', 'sometimes',
Rule::in(Country::getValidCountries()) Rule::in(Country::getValidCountries()),
], ],
'winning_proof' => 'required|boolean', 'winning_proof' => 'required|boolean',
@ -131,8 +127,6 @@ class UpdateSettingsRequest extends FormRequest
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {

View file

@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\StaticPages; namespace App\Http\Requests\Bo\StaticPages;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -11,7 +14,7 @@ class UpdateStaticPageRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -20,20 +23,18 @@ class UpdateStaticPageRequest extends FormRequest
/** /**
* Prepare the data for validation. * Prepare the data for validation.
*
* @return void
*/ */
public function prepareForValidation(): void public function prepareForValidation(): void
{ {
$this->merge([ $this->merge([
'content' => (string) Str::of($this->input('content')) 'content' => (string) Str::of($this->input('content')),
]); ]);
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string> * @return array<string, ValidationRule|array<mixed>|string>
*/ */
public function rules(): array public function rules(): array
{ {
@ -47,8 +48,6 @@ class UpdateStaticPageRequest extends FormRequest
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo; namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
@ -8,8 +10,6 @@ class UpdateChangeOrderRequest extends FormRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
@ -18,20 +18,16 @@ class UpdateChangeOrderRequest extends FormRequest
/** /**
* Prepare the data for validation. * Prepare the data for validation.
*
* @return void
*/ */
protected function prepareForValidation(): void protected function prepareForValidation(): void
{ {
$this->merge([ $this->merge([
'direction' => $this->route('direction') === 'up' ? true : false 'direction' => $this->route('direction') === 'up',
]); ]);
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules(): array public function rules(): array
{ {

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Users; namespace App\Http\Requests\Bo\Users;
use App\Enums\Users\RoleEnum; use App\Enums\Users\RoleEnum;
@ -14,7 +16,7 @@ class StoreUserRequest extends FormRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -29,48 +31,44 @@ class StoreUserRequest extends FormRequest
protected function prepareForValidation() protected function prepareForValidation()
{ {
$this->merge([ $this->merge([
'published' => $this->boolean('published') 'published' => $this->boolean('published'),
]); ]);
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules(): array public function rules(): array
{ {
return [ return [
'first_name' => 'required|string|min:3|max:255', 'first_name' => 'required|string|min:3|max:255',
'last_name' => 'required|string|min:3|max:255', 'last_name' => 'required|string|min:3|max:255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email', 'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email',
'password' => [ 'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8) 'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
->letters() ->letters()
->mixedCase() ->mixedCase()
->numbers() ->numbers()
->symbols() ->symbols()
->uncompromised() ->uncompromised(),
], ],
'role' => ['required', new Enum(RoleEnum::class)], 'role' => ['required', new Enum(RoleEnum::class)],
'published' => 'required|boolean', 'published' => 'required|boolean',
]; ];
} }
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {
return [ return [
'first_name' => trans('validation.attributes.first_name'), 'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'), 'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'), 'email' => trans('validation.attributes.email'),
'password' => trans('validation.attributes.password'), 'password' => trans('validation.attributes.password'),
'role' => trans('validation.attributes.role'), 'role' => trans('validation.attributes.role'),
'published' => trans('validation.custom.publication') 'published' => trans('validation.custom.publication'),
]; ];
} }
} }

View file

@ -1,7 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Bo\Users; namespace App\Http\Requests\Bo\Users;
use App\Models\User;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Password; use Illuminate\Validation\Rules\Password;
@ -11,7 +14,7 @@ class UpdateUserRequest extends StoreUserRequest
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
* *
* @return boolean * @return bool
*/ */
public function authorize() public function authorize()
{ {
@ -25,33 +28,32 @@ class UpdateUserRequest extends StoreUserRequest
*/ */
protected function prepareForValidation() protected function prepareForValidation()
{ {
/** @var \App\Models\User */ /** @var User */
$userModel = $this->route('user'); $userModel = $this->route('user');
$this->merge([ $this->merge([
'published' => $this->boolean('published'), 'published' => $this->boolean('published'),
'password' => $this->input( 'password' => $this->input(
'password', 'password',
) ?? $userModel->password, ) ?? $userModel->password,
'password_confirmation' => $this->input( 'password_confirmation' => $this->input(
'password_confirmation' 'password_confirmation'
) ?? $userModel->password ) ?? $userModel->password,
]); ]);
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules(): array public function rules(): array
{ {
/** @var \App\Models\User */ /** @var User */
$userModel = $this->route('user'); $userModel = $this->route('user');
return array_merge(parent::rules(), [ return array_merge(parent::rules(), [
'email' => [ 'email' => [
'required', 'required',
'email:rfc,strict,dns,spoof,filter', 'email:rfc,strict,dns,spoof,filter',
Rule::unique('users', 'email')->ignore($userModel->getRouteKey()) Rule::unique('users', 'email')->ignore($userModel->getRouteKey()),
], ],
'password' => [ 'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8) 'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
@ -59,7 +61,7 @@ class UpdateUserRequest extends StoreUserRequest
->mixedCase() ->mixedCase()
->numbers() ->numbers()
->symbols() ->symbols()
->uncompromised() ->uncompromised(),
], ],
]); ]);
} }

View file

@ -1,10 +1,14 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Fo\Customers; namespace App\Http\Requests\Fo\Customers;
use App\Models\Customer;
use App\Models\Settings; use App\Models\Settings;
use App\Repositories\Fo\CrmRepository; use App\Repositories\Fo\CrmRepository;
use App\Rules\IsAdult; use App\Rules\IsAdult;
use Closure;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
@ -18,8 +22,6 @@ class CreateAccountRequest extends FormRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
@ -28,35 +30,30 @@ class CreateAccountRequest extends FormRequest
/** /**
* Prepare the data for validation. * Prepare the data for validation.
*
* @return void
*/ */
public function prepareForValidation(): void public function prepareForValidation(): void
{ {
$this->merge([ $this->merge([
'phone' => $this->input('phone'), 'phone' => $this->input('phone'),
'opt_in' => $this->boolean('opt_in'), 'opt_in' => $this->boolean('opt_in'),
'ok_cgv' => $this->boolean('ok_cgv') 'ok_cgv' => $this->boolean('ok_cgv'),
]); ]);
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules(): array public function rules(): array
{ {
/** @var \App\Models\Settings $settings */ /** @var Settings $settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
/** @var \App\Models\Customer|null */ /** @var \App\Models\Customer|null */
$authUser = auth('frontend')->user(); $authUser = auth('frontend')->user();
$countryCode = null;
$countryCode = $this->integer('country') != 0 ? $countryCode = $this->integer('country') != 0 ?
$this->integer('country') : ($authUser ? $this->integer('country') : ($authUser ?
(new CrmRepository())->fetchCrmCustomer($authUser)->country->value : 0); new CrmRepository()->fetchCrmCustomer($authUser)->country->value : 0);
$countryRules = match ($countryCode) { $countryRules = match ($countryCode) {
Country::belgium->value => [ Country::belgium->value => [
'phone' => 'phone:BE', 'phone' => 'phone:BE',
@ -105,13 +102,13 @@ class CreateAccountRequest extends FormRequest
'max:255', 'max:255',
// * Does email already exists in CRM ? // * Does email already exists in CRM ?
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function (string $attribute, $value, \Closure $fail) { function (string $attribute, $value, Closure $fail): void {
if (CrmCustomer::findWithEmail(\strval($value))) { if (CrmCustomer::findWithEmail(\strval($value)) instanceof CrmCustomer) {
$fail('Un compte existe déjà avec cette adresse.'); $fail('Un compte existe déjà avec cette adresse.');
} else { } else {
// ! Si le compte n'existe pas en CRM mais existe en local. // ! Si le compte n'existe pas en CRM mais existe en local.
// ! On le soft-delete pour permettre l'inscription et la restauration propre. // ! On le soft-delete pour permettre l'inscription et la restauration propre.
$localCustomer = \App\Models\Customer::where('email', \strval($value))->first(); $localCustomer = Customer::where('email', \strval($value))->first();
if ($localCustomer) { if ($localCustomer) {
$localCustomer->delete(); $localCustomer->delete();
} }
@ -123,7 +120,7 @@ class CreateAccountRequest extends FormRequest
'required', 'required',
'confirmed', 'confirmed',
Password::min(8)->mixedCase() Password::min(8)->mixedCase()
->numbers()->symbols()->uncompromised() ->numbers()->symbols()->uncompromised(),
], ],
'ok_cgv' => 'accepted', 'ok_cgv' => 'accepted',
'opt_in' => 'required|boolean', 'opt_in' => 'required|boolean',
@ -132,8 +129,6 @@ class CreateAccountRequest extends FormRequest
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {
@ -157,30 +152,28 @@ class CreateAccountRequest extends FormRequest
/** /**
* Get custom messages for validator errors. * Get custom messages for validator errors.
*
* @return array
*/ */
public function messages(): array public function messages(): array
{ {
$messages = [ $messages = [
'postal_code' => trans('validation.postal_code', [ 'postal_code' => trans('validation.postal_code', [
'countries' => \implode(', ', Country::getValidCountriesLabel()), 'countries' => \implode(', ', Country::getValidCountriesLabel()),
'examples' => 72000 'examples' => 72000,
]), ]),
'phone.phone' => trans('validation.phone', [ 'phone.phone' => trans('validation.phone', [
'countries' => \implode(', ', Country::getValidCountriesLabel()) 'countries' => \implode(', ', Country::getValidCountriesLabel()),
]), ]),
'email.unique' => trans('Un compte existe déjà avec cette adresse.'), 'email.unique' => trans('Un compte existe déjà avec cette adresse.'),
'ok_cgv.accepted' => trans('Vous devez accepter les conditions d\'utilisation du site pour continuer.') 'ok_cgv.accepted' => trans('Vous devez accepter les conditions d\'utilisation du site pour continuer.'),
]; ]; // end match
$messages = match ($this->integer('country')) { return match ($this->integer('country')) {
Country::belgium->value => array_merge( Country::belgium->value => array_merge(
$messages, $messages,
[ [
'postal_code' => trans('validation.postal_code', [ 'postal_code' => trans('validation.postal_code', [
'countries' => Country::belgium->label(), 'countries' => Country::belgium->label(),
'examples' => 1000 'examples' => 1000,
]), ]),
] ]
), ),
@ -189,7 +182,7 @@ class CreateAccountRequest extends FormRequest
[ [
'postal_code' => trans('validation.postal_code', [ 'postal_code' => trans('validation.postal_code', [
'countries' => Country::luxembourg->label(), 'countries' => Country::luxembourg->label(),
'examples' => 1111 'examples' => 1111,
]), ]),
] ]
), ),
@ -198,7 +191,7 @@ class CreateAccountRequest extends FormRequest
[ [
'postal_code' => trans('validation.postal_code', [ 'postal_code' => trans('validation.postal_code', [
'countries' => Country::germany->label(), 'countries' => Country::germany->label(),
'examples' => 10115 'examples' => 10115,
]), ]),
] ]
), ),
@ -214,12 +207,10 @@ class CreateAccountRequest extends FormRequest
array_search(Country::germany->label(), Country::getValidCountriesLabel()), array_search(Country::germany->label(), Country::getValidCountriesLabel()),
] ]
)), )),
'examples' => 72000 'examples' => 72000,
]), ]),
] ]
), ),
}; //end match };
return $messages;
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Fo\Customers; namespace App\Http\Requests\Fo\Customers;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
@ -8,20 +10,16 @@ class SingleOptinRequest extends FormRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize() public function authorize(): bool
{ {
return true; return true;
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules() public function rules(): array
{ {
return [ return [
'email' => 'required|email:rfc,dns,spoof', 'email' => 'required|email:rfc,dns,spoof',

View file

@ -1,15 +1,16 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Fo\Customers; namespace App\Http\Requests\Fo\Customers;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
class VerifyInformationsRequest extends CreateAccountRequest class VerifyInformationsRequest extends CreateAccountRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
@ -19,7 +20,7 @@ class VerifyInformationsRequest extends CreateAccountRequest
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string> * @return array<string, ValidationRule|array<mixed>|string>
*/ */
public function rules(): array public function rules(): array
{ {

View file

@ -1,9 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Fo; namespace App\Http\Requests\Fo;
use App\Enums\CodeType; use App\Enums\CodeType;
use App\Models\Code; use App\Models\Code;
use App\Models\Customer;
use App\Models\Participation; use App\Models\Participation;
use App\Models\Product; use App\Models\Product;
use App\Models\Settings; use App\Models\Settings;
@ -19,30 +22,27 @@ class ParticipateRequest extends FormRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
/** @var \App\Models\Customer */ /** @var Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
// * Use rate limiter to prevent brute force sticker code // * Use rate limiter to prevent brute force sticker code
if (RateLimiter::tooManyAttempts("participate:{$customer->id}", $perMinute = 5)) { if (RateLimiter::tooManyAttempts("participate:{$customer->id}", $perMinute = 5)) {
return false; return false;
} }
RateLimiter::hit("participate:{$customer->id}"); RateLimiter::hit("participate:{$customer->id}");
return Gate::check('participate', Participation::class); return Gate::check('participate', Participation::class);
} }
/** /**
* Prepare the data for validation. * Prepare the data for validation.
*
* @return void
*/ */
protected function prepareForValidation(): void protected function prepareForValidation(): void
{ {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
if ($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both)) { if ($settings->code_type->equals(CodeType::unique) || $settings->code_type->equals(CodeType::both)) {
try { try {
$this->merge([ $this->merge([
@ -83,18 +83,16 @@ class ParticipateRequest extends FormRequest
->first(), ->first(),
'game_rules' => $this->boolean('game_rules'), 'game_rules' => $this->boolean('game_rules'),
]); ]);
} //end if } // end if
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
*
* @return array
*/ */
public function rules(): array public function rules(): array
{ {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
$rules = [ $rules = [
'game_rules' => 'required|boolean|accepted', 'game_rules' => 'required|boolean|accepted',
@ -105,17 +103,18 @@ class ParticipateRequest extends FormRequest
$rules = \array_merge($rules, ['unique_code' => [ $rules = \array_merge($rules, ['unique_code' => [
'required', 'required',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function (string $attribute, $value, callable $fail) { function (string $attribute, $value, callable $fail): void {
// ? Le Code unique est resolved ? // ? Le Code unique est resolved ?
if ($value === false or !\is_object($value) or \get_class($value) !== Code::class) { if ($value === false || ! \is_object($value) || $value::class !== Code::class) {
$fail(trans('front.messages.unique_code_invalid')); $fail(trans('front.messages.unique_code_invalid'));
return; return;
} }
/** @var \App\Models\Customer */ /** @var Customer */
$customer = auth('frontend')->user(); $customer = auth('frontend')->user();
/** @var \App\Models\Code $value */ /** @var Code $value */
// * Check si l'utilisateur n'a pas déjà utilisé ce code. // * Check si l'utilisateur n'a pas déjà utilisé ce code.
$query = Participation::query() $query = Participation::query()
->whereHas('code', fn (Builder $q) => $q->where('id', $value->id)) ->whereHas('code', fn (Builder $q) => $q->where('id', $value->id))
@ -123,6 +122,7 @@ class ParticipateRequest extends FormRequest
// ! Une seule participation avec un code donné par joueur // ! Une seule participation avec un code donné par joueur
if ($query->exists()) { if ($query->exists()) {
$fail(trans('front.messages.unique_code_already_used')); $fail(trans('front.messages.unique_code_already_used'));
return; return;
} }
@ -131,37 +131,36 @@ class ParticipateRequest extends FormRequest
if ($value->has_been_used_too_many_times) { if ($value->has_been_used_too_many_times) {
// * Ne pas laisser d'indices, message générique // * Ne pas laisser d'indices, message générique
$fail(trans('front.messages.unique_code_invalid')); $fail(trans('front.messages.unique_code_invalid'));
return; return;
} }
} },
]]); ]]);
} //end if } // end if
// ? Does the game use EAN Codes ? // ? Does the game use EAN Codes ?
if ($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both)) { if ($settings->code_type->equals(CodeType::ean) || $settings->code_type->equals(CodeType::both)) {
$rules = \array_merge($rules, ['ean_code' => [ return \array_merge($rules, ['ean_code' => [
'required', 'required',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) { function ($attribute, $value, $fail): void {
// phpcs:enable // phpcs:enable
/** @var \App\Models\Produt|mixed */ /** @var \App\Models\Produt|mixed */
$product = $this->input('product'); $product = $this->input('product');
if ( if (
empty($product) or !is_object($product) or empty($product) || ! is_object($product) || ! ($product instanceof Model) || ! $product->exists
!($product instanceof Model) or !$product->exists
) { ) {
$fail(trans('front.messages.barcode_invalid')); $fail(trans('front.messages.barcode_invalid'));
} }
} },
]]); ]]);
} }
return $rules; return $rules;
} }
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {

View file

@ -1,9 +1,12 @@
<?php <?php
declare(strict_types=1);
namespace App\Http\Requests\Fo; namespace App\Http\Requests\Fo;
use App\Http\Requests\Fo\Customers\CreateAccountRequest; use App\Http\Requests\Fo\Customers\CreateAccountRequest;
use App\Repositories\Fo\CrmRepository; use App\Repositories\Fo\CrmRepository;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Gate;
use LDC\LDCCRM\Enums\Country; use LDC\LDCCRM\Enums\Country;
@ -12,26 +15,23 @@ class SubmitWinningProofRequest extends CreateAccountRequest
{ {
/** /**
* Determine if the user is authorized to make this request. * Determine if the user is authorized to make this request.
*
* @return boolean
*/ */
public function authorize(): bool public function authorize(): bool
{ {
return auth('frontend')->check() && return auth('frontend')->check() && Gate::check('submitProof', $this->route('participation'));
Gate::check('submitProof', $this->route('participation'));
} }
/** /**
* Get the validation rules that apply to the request. * Get the validation rules that apply to the request.
* *
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string> * @return array<string, ValidationRule|array<mixed>|string>
*/ */
public function rules(): array public function rules(): array
{ {
$authUser = auth('frontend')->user(); $authUser = auth('frontend')->user();
$countryCode = $this->integer('country') != 0 ? $countryCode = $this->integer('country') != 0 ?
$this->integer('country') : ($authUser ? $this->integer('country') : ($authUser ?
(new CrmRepository())->fetchCrmCustomer($authUser)->country->value : 0); new CrmRepository()->fetchCrmCustomer($authUser)->country->value : 0);
$countryRules = match ($countryCode) { $countryRules = match ($countryCode) {
Country::belgium->value => ['phone' => 'phone:BE'], Country::belgium->value => ['phone' => 'phone:BE'],
Country::luxembourg->value => ['phone' => 'phone:LU'], Country::luxembourg->value => ['phone' => 'phone:LU'],
@ -75,14 +75,12 @@ class SubmitWinningProofRequest extends CreateAccountRequest
return collect(['sticker', 'ticket', 'barcode']) return collect(['sticker', 'ticket', 'barcode'])
->filter(fn (string $proof) => config("proofs.$proof")) ->filter(fn (string $proof) => config("proofs.$proof"))
->mapWithKeys(fn (string $proof) => [$proof => $fileRules]) ->mapWithKeys(fn (string $proof): array => [$proof => $fileRules])
->all(); ->all();
} }
/** /**
* Get custom attributes for validator errors. * Get custom attributes for validator errors.
*
* @return array
*/ */
public function attributes(): array public function attributes(): array
{ {
@ -90,7 +88,7 @@ class SubmitWinningProofRequest extends CreateAccountRequest
parent::attributes(), parent::attributes(),
[ [
'sticker' => trans('validation.custom.sticker'), 'sticker' => trans('validation.custom.sticker'),
'ticket' => trans('validation.custom.ticket'), 'ticket' => trans('validation.custom.ticket'),
'barcode' => trans('validation.custom.barcode'), 'barcode' => trans('validation.custom.barcode'),
] ]
); );

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Jobs\DeleteOlds; namespace App\Jobs\DeleteOlds;
use App\Models\Customer; use App\Models\Customer;
@ -12,12 +14,10 @@ class DeleteOldCustomers
{ {
/** /**
* Called when the job will be executed. * Called when the job will be executed.
*
* @return void
*/ */
public function handle() public function handle(): void
{ {
Log::debug(__CLASS__ . ' Removing old customers'); Log::debug(self::class . ' Removing old customers');
Customer::query()->withTrashed()->where('last_connection', '<=', now()->subMonths(36))->forceDelete(); Customer::query()->withTrashed()->where('last_connection', '<=', now()->subMonths(36))->forceDelete();
} }
} }

View file

@ -1,33 +1,32 @@
<?php <?php
declare(strict_types=1);
namespace App\Jobs; namespace App\Jobs;
use App\Mail\Jobs\CodeExportSucceed; use App\Mail\Jobs\CodeExportSucceed;
use App\Mail\Jobs\JobFailed; use App\Mail\Jobs\JobFailed;
use App\Models\Code; use App\Models\Code;
use Illuminate\Bus\Queueable; use Exception;
use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Throwable;
use ZipArchive;
class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique class ProcessExportCodesUniques implements ShouldBeUnique, ShouldQueue
{ {
use Dispatchable;
use InteractsWithQueue;
use Queueable; use Queueable;
use SerializesModels;
/** /**
* The number of seconds the job can run before timing out. * The number of seconds the job can run before timing out.
* *
* 28800 seconds (8 hours) * 28800 seconds (8 hours)
* *
* @var integer * @var int
*/ */
public $timeout = 28800; public $timeout = 28800;
@ -36,15 +35,14 @@ class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique
* *
* @var string * @var string
*/ */
protected $exportFileName = null; protected $exportFileName;
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @param integer $nbCodeFic Nombre de codes par fichiers. * @param int $nbCodeFic Nombre de codes par fichiers.
* @param integer $nbCodeTot Nombre total de codes à exporter. * @param int $nbCodeTot Nombre total de codes à exporter.
* @param integer $nbCodeLigne Nombre de codes par ligne. * @param int $nbCodeLigne Nombre de codes par ligne.
* @return void
*/ */
public function __construct( public function __construct(
protected int $nbCodeFic, protected int $nbCodeFic,
@ -55,12 +53,10 @@ class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique
/** /**
* Execute the job. * Execute the job.
*
* @return void
*/ */
public function handle() public function handle(): void
{ {
DB::transaction(function () { DB::transaction(function (): void {
$this->exportFileName = sprintf( $this->exportFileName = sprintf(
'CodesUniques_%s', 'CodesUniques_%s',
date('dmY_His') date('dmY_His')
@ -76,8 +72,8 @@ class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique
$filesPath = $this->generateCsvFiles($lastExportedCodeId); $filesPath = $this->generateCsvFiles($lastExportedCodeId);
// * Compression des fichiers. // * Compression des fichiers.
$zip = new \ZipArchive(); $zip = new ZipArchive();
if ($zip->open($exportFilePath, \ZipArchive::CREATE) === true) { if ($zip->open($exportFilePath, ZipArchive::CREATE) === true) {
foreach ($filesPath as $filePath) { foreach ($filesPath as $filePath) {
if (File::exists($filePath)) { if (File::exists($filePath)) {
$zip->addFile($filePath, \pathinfo($filePath, \PATHINFO_BASENAME)); $zip->addFile($filePath, \pathinfo($filePath, \PATHINFO_BASENAME));
@ -100,21 +96,22 @@ class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique
$this->nbCodeLigne, $this->nbCodeLigne,
$exportFilePath $exportFilePath
)); ));
} catch (\Exception $e) { } catch (Exception $e) {
// * Clear exported file in case of Failure. // * Clear exported file in case of Failure.
if (File::exists($exportFilePath)) { if (File::exists($exportFilePath)) {
File::delete($exportFilePath); File::delete($exportFilePath);
} }
throw $e; throw $e;
} //end try } // end try
}); });
} }
/** /**
* Generate CSV File using the job parameters * Generate CSV File using the job parameters
* *
* @param integer|null $lastExportedCodeId * @param int|null $lastExportedCodeId
* @return array Array of temp files * @return array Array of temp files
*
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/ */
private function generateCsvFiles($lastExportedCodeId = null): array private function generateCsvFiles($lastExportedCodeId = null): array
@ -152,7 +149,7 @@ class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique
} else { } else {
File::append($tempFilePath, Code::intToSerial($code) . ';'); File::append($tempFilePath, Code::intToSerial($code) . ';');
$currentLineCodes++; $currentLineCodes++;
} //end if } // end if
$totalExportedCodes++; $totalExportedCodes++;
$currentExportedCodes++; $currentExportedCodes++;
@ -175,29 +172,26 @@ class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique
Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]); Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]);
$exportedCodesId = []; $exportedCodesId = [];
} }
} //end if } // end if
} //end foreach } // end foreach
// ? Are there exported codes to save ? // ? Are there exported codes to save ?
if (count($exportedCodesId)) { if (count($exportedCodesId)) {
Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]); Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]);
} }
return $outPaths; return $outPaths;
} }
/** /**
* In case of job failure * In case of job failure
*
* @param \Throwable $exception
* @return void
*/ */
public function failed(\Throwable $exception) public function failed(Throwable $exception): void
{ {
Mail::to(config('mail.admin'))->send( Mail::to(config('mail.admin'))->send(
new JobFailed(__CLASS__, [ new JobFailed(self::class, [
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString() 'trace' => $exception->getTraceAsString(),
]) ])
); );
} }

View file

@ -1,43 +1,39 @@
<?php <?php
declare(strict_types=1);
namespace App\Jobs; namespace App\Jobs;
use App\Mail\Jobs\CodeGenerationSucceed; use App\Mail\Jobs\CodeGenerationSucceed;
use App\Mail\Jobs\JobFailed; use App\Mail\Jobs\JobFailed;
use App\Models\Code; use App\Models\Code;
use App\Models\Settings; use App\Models\Settings;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique; use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Throwable;
class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique class ProcessUniqueCode implements ShouldBeUnique, ShouldQueue
{ {
use Dispatchable;
use InteractsWithQueue;
use Queueable; use Queueable;
use SerializesModels;
/** /**
* The number of seconds the job can run before timing out. * The number of seconds the job can run before timing out.
* *
* 7200 seconds (2 hours) * 7200 seconds (2 hours)
* *
* @var integer * @var int
*/ */
public $timeout = 7200; public $timeout = 7200;
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @param integer $amount Amount of code to generate. * @param int $amount Amount of code to generate.
* @param integer $length Length of each code to generate. * @param int $length Length of each code to generate.
* @return void
*/ */
public function __construct(protected int $amount, protected int $length) public function __construct(protected int $amount, protected int $length)
{ {
@ -45,38 +41,29 @@ class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique
/** /**
* Execute the job. * Execute the job.
*
* @return void
*/ */
public function handle() public function handle(): void
{ {
DB::transaction(function () { DB::transaction(function (): void {
// ! Le premier code commence à 1 // ! Le premier code commence à 1
$last_id = (Code::query()->max('id') ?? 0) + 1; $last_id = (Code::query()->max('id') ?? 0) + 1;
/** @var \App\Models\Settings $settings */ /** @var Settings $settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
$maxSQLPlaceholders = 65535; $maxSQLPlaceholders = 65535;
$insertColumns = collect(DB::getSchemaBuilder() $insertColumns = collect(DB::getSchemaBuilder()
->getColumnListing((new Code())->getTable())) ->getColumnListing(new Code()->getTable()))
->filter(function ($name) { ->reject(fn ($name): bool => in_array($name, ['id', 'code_exported']))->count() + 1;
// * Ignorer les colonnes avec valeurs par défaut sur la table.
return !in_array($name, ['id', 'code_exported']);
})->count() + 1;
/** /**
* Nombre d'insertions max sur un seule requète * Nombre d'insertions max sur un seule requète
*
* @var integer $perPage
*/ */
$perPage = \floor($maxSQLPlaceholders / $insertColumns); $perPage = \floor($maxSQLPlaceholders / $insertColumns);
/** /**
* Nombre de page totale à insérer. * Nombre de page totale à insérer.
*
* @var integer $totalPages
*/ */
$totalPages = \ceil($this->amount / $perPage); $totalPages = \ceil($this->amount / $perPage);
@ -87,7 +74,7 @@ class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique
$insertToGenerate = min($perPage, $this->amount - $totalGeneratedInserts); $insertToGenerate = min($perPage, $this->amount - $totalGeneratedInserts);
do { do {
$inserts[] = [ $inserts[] = [
'id' => $last_id++, 'id' => $last_id++,
'date_creation' => now()->format('Y-m-d'), 'date_creation' => now()->format('Y-m-d'),
]; ];
} while (($totalGeneratedInserts < $this->amount) && --$insertToGenerate); } while (($totalGeneratedInserts < $this->amount) && --$insertToGenerate);
@ -108,16 +95,13 @@ class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique
/** /**
* In case of job failure * In case of job failure
*
* @param \Throwable $exception
* @return void
*/ */
public function failed(\Throwable $exception) public function failed(Throwable $exception): void
{ {
Mail::to(config('mail.admin'))->send( Mail::to(config('mail.admin'))->send(
new JobFailed(__CLASS__, [ new JobFailed(self::class, [
'message' => $exception->getMessage(), 'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString() 'trace' => $exception->getTraceAsString(),
]) ])
); );
} }
@ -130,8 +114,8 @@ class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique
public function middleware(): array public function middleware(): array
{ {
return [ return [
(new WithoutOverlapping('code_generation')) new WithoutOverlapping('code_generation')
->releaseAfter(60) ->releaseAfter(60),
]; ];
} }
} }

View file

@ -1,24 +1,30 @@
<?php <?php
declare(strict_types=1);
namespace App\Lib; namespace App\Lib;
use Illuminate\Contracts\Support\Arrayable; use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable; use Illuminate\Contracts\Support\Jsonable;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use JsonSerializable;
use ReturnTypeWillChange;
use RuntimeException;
use stdClass;
use TypeError;
/** /**
* Ligne de Coupon stocké dans settings * Ligne de Coupon stocké dans settings
* *
* @property integer $web_coupon_identifiant EAN 13 format (Mais ne représente pas un EAN 13), * @property int $web_coupon_identifiant EAN 13 format (Mais ne représente pas un EAN 13),
* Format utilisé dans MesBr. * Format utilisé dans MesBr.
* @property integer $percentage Pourcentage de chance de gagner le lot nombre de 1 à 100. * @property int $percentage Pourcentage de chance de gagner le lot nombre de 1 à 100.
* @property integer $face_value Valeur faciale en centimes. * @property int $face_value Valeur faciale en centimes.
* @property string $valid_on Text valable sur.. (duplicat de ce qui est inscrit dans MesBr) * @property string $valid_on Text valable sur.. (duplicat de ce qui est inscrit dans MesBr)
* Pour affichage sur la page de gain est dans le mail. * Pour affichage sur la page de gain est dans le mail.
* @property string $picture Image locale du Coupon. * @property string $picture Image locale du Coupon.
* @package App\Lib\Inputs
*/ */
class Discount extends \stdClass implements Jsonable, Arrayable, \JsonSerializable class Discount extends stdClass implements Arrayable, Jsonable, JsonSerializable
{ {
/** /**
* Properties list * Properties list
@ -36,33 +42,31 @@ class Discount extends \stdClass implements Jsonable, Arrayable, \JsonSerializab
/** /**
* Get property * Get property
* *
* @param string $name *
* @return mixed * @throws TypeError If prop is undefined.
* @throws \TypeError If prop is undefined.
*/ */
public function __get(string $name) public function __get(string $name): mixed
{ {
if ( if (
in_array($name, self::$propertiesList) in_array($name, self::$propertiesList)
) { ) {
return $this->{$name}; return $this->{$name};
} }
throw new \TypeError("undefined property {$name}"); throw new TypeError("undefined property {$name}");
} }
/** /**
* Make and instance * Make and instance
* *
* @param string $picture Image locale du Coupon. * @param string $picture Image locale du Coupon.
* @param string $valid_on Text valable sur.. (duplicat de ce qui est inscrit dans MesBr) * @param string $valid_on Text valable sur.. (duplicat de ce qui est inscrit dans MesBr)
* Pour affichage sur la page de gain est dans le mail. * Pour affichage sur la page de gain est dans le mail.
* @param integer $face_value Valeur faciale en centimes. * @param int $face_value Valeur faciale en centimes.
* @param integer $percentage Pourcentage de chance de gagner le lot nombre de 1 à 100. * @param int $percentage Pourcentage de chance de gagner le lot nombre de 1 à 100.
* @param integer $web_coupon_identifiant EAN 13 format (Mais ne représente pas un EAN 13), * @param int $web_coupon_identifiant EAN 13 format (Mais ne représente pas un EAN 13),
* format utilisé dans MesBr. * format utilisé dans MesBr.
* *
* @return \App\Lib\Discount * @throws RuntimeException If $type or $data is wrong.
* @throws \RuntimeException If $type or $data is wrong.
*/ */
public static function make( public static function make(
string $picture, string $picture,
@ -78,54 +82,42 @@ class Discount extends \stdClass implements Jsonable, Arrayable, \JsonSerializab
$ipTypeData->valid_on = $valid_on; $ipTypeData->valid_on = $valid_on;
$ipTypeData->picture = $picture; $ipTypeData->picture = $picture;
self::assertIsInputTypeData($ipTypeData); self::assertIsInputTypeData($ipTypeData);
return $ipTypeData; return $ipTypeData;
} }
/** /**
* Confirm $data is of type InputTypeData * Confirm $data is of type InputTypeData
* *
* @param \App\Lib\Discount $data *
* @return void * @throws RuntimeException If $data is wrong.
* @throws \RuntimeException If $data is wrong.
*/ */
public static function assertIsInputTypeData(Discount $data) public static function assertIsInputTypeData(Discount $data): void
{ {
if (!\is_int($data->web_coupon_identifiant)) { throw_unless(\is_int($data->web_coupon_identifiant), RuntimeException::class, "InputTypeData 'web_coupon_identifiant' should be an integer");
throw new \RuntimeException("InputTypeData 'web_coupon_identifiant' should be an integer"); throw_unless(\is_int($data->percentage), RuntimeException::class, "InputTypeData 'percentage' should be an integer");
} throw_unless(\is_int($data->face_value), RuntimeException::class, "InputTypeData 'face_value' should be an integer");
if (!\is_int($data->percentage)) { throw_unless(\is_string($data->valid_on), RuntimeException::class, "InputTypeData 'valid_on' should be a string");
throw new \RuntimeException("InputTypeData 'percentage' should be an integer"); throw_unless(\is_string($data->picture), RuntimeException::class, "InputTypeData 'picture' should be a string");
}
if (!\is_int($data->face_value)) {
throw new \RuntimeException("InputTypeData 'face_value' should be an integer");
}
if (!\is_string($data->valid_on)) {
throw new \RuntimeException("InputTypeData 'valid_on' should be a string");
}
if (!\is_string($data->picture)) {
throw new \RuntimeException("InputTypeData 'picture' should be a string");
}
} }
// * TRANSFORMERS // * TRANSFORMERS
/** /**
* Get the instance as an array. * Get the instance as an array.
*
* @return array
*/ */
public function toArray(): array public function toArray(): array
{ {
return collect(Arr::mapWithKeys(self::$propertiesList, fn (string $property) => [ return collect(Arr::mapWithKeys(self::$propertiesList, fn (string $property): array => [
$property => $this->{$property} $property => $this->{$property},
]))->filter()->all(); ]))->filter()->all();
} }
/** /**
* Convert the object to its JSON representation. * Convert the object to its JSON representation.
* *
* @param integer $options * @param int $options
* @return string *
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing * @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
*/ */
public function toJson($options = 0): string public function toJson($options = 0): string
@ -139,7 +131,7 @@ class Discount extends \stdClass implements Jsonable, Arrayable, \JsonSerializab
* *
* @return array * @return array
*/ */
#[\ReturnTypeWillChange] #[ReturnTypeWillChange]
public function jsonSerialize() public function jsonSerialize()
{ {
return $this->toArray(); return $this->toArray();

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Lib\Helpers; namespace App\Lib\Helpers;
use Illuminate\Container\Container; use Illuminate\Container\Container;
@ -11,10 +13,6 @@ class CollectionHelper
{ {
/** /**
* Paginate a collection * Paginate a collection
*
* @param \Illuminate\Support\Collection $results
* @param integer $pageSize
* @return \Illuminate\Pagination\LengthAwarePaginator
*/ */
public static function paginate(Collection $results, int $pageSize): LengthAwarePaginator public static function paginate(Collection $results, int $pageSize): LengthAwarePaginator
{ {
@ -25,18 +23,13 @@ class CollectionHelper
/** /**
* Paginate a collection * Paginate a collection
*
* @param \Illuminate\Support\Collection $results
* @param integer $pageSize
* @param integer $page
* @return \Illuminate\Pagination\LengthAwarePaginator
*/ */
public static function paginateManually(Collection $results, int $pageSize, int $page = 1): LengthAwarePaginator public static function paginateManually(Collection $results, int $pageSize, int $page = 1): LengthAwarePaginator
{ {
$total = $results->count(); $total = $results->count();
return self::paginator($results->forPage($page, $pageSize), $total, $pageSize, $page, [ return self::paginator($results->forPage($page, $pageSize), $total, $pageSize, $page, [
'path' => Paginator::resolveCurrentPath(), 'path' => Paginator::resolveCurrentPath(),
'pageName' => 'page', 'pageName' => 'page',
]); ]);
} }
@ -44,21 +37,16 @@ class CollectionHelper
/** /**
* Create a new length-aware paginator instance. * Create a new length-aware paginator instance.
* *
* @param \Illuminate\Support\Collection $items * @return LengthAwarePaginator
* @param integer $total
* @param integer $perPage
* @param integer $currentPage
* @param array $options
* @return \Illuminate\Pagination\LengthAwarePaginator
*/ */
protected static function paginator(Collection $items, int $total, int $perPage, int $currentPage, array $options) protected static function paginator(Collection $items, int $total, int $perPage, int $currentPage, array $options)
{ {
return Container::getInstance()->makeWith(LengthAwarePaginator::class, [ return Container::getInstance()->makeWith(LengthAwarePaginator::class, [
'items' => $items, 'items' => $items,
'total' => $total, 'total' => $total,
'perPage' => $perPage, 'perPage' => $perPage,
'currentPage' => $currentPage, 'currentPage' => $currentPage,
'options' => $options 'options' => $options,
]); ]);
} }
} }

View file

@ -1,55 +1,56 @@
<?php <?php
declare(strict_types=1);
namespace App\Lib\Helpers; namespace App\Lib\Helpers;
use Sentry\Event;
use Sentry\ExceptionDataBag;
use Sentry\Frame;
use Sentry\Stacktrace;
use Throwable;
class ExceptionsHelper class ExceptionsHelper
{ {
/** /**
* Handle sentry event * Handle sentry event
*
* @param \Sentry\Event $event
* @return null|\Sentry\Event
*/ */
public static function sentryEventHandler(\Sentry\Event $event): ?\Sentry\Event public static function sentryEventHandler(Event $event): ?Event
{ {
// Remove unwanted reports. // Remove unwanted reports.
$event->setModules([]); $event->setModules([]);
$event->setOsContext(null); $event->setOsContext(null);
/** @var \Sentry\ExceptionDataBag[] */ /** @var ExceptionDataBag[] */
$exceptionBag = []; $exceptionBag = [];
foreach ($event->getExceptions() as $exceptionDataBag) { foreach ($event->getExceptions() as $exceptionDataBag) {
$exceptionBag[] = self::getAnonymizedSentryException($exceptionDataBag); $exceptionBag[] = self::getAnonymizedSentryException($exceptionDataBag);
} }
$event->setExceptions($exceptionBag); $event->setExceptions($exceptionBag);
return $event; return $event;
} }
/** /**
* Anonimize Sentry Exception bag * Anonimize Sentry Exception bag
*
* @param \Sentry\ExceptionDataBag $exceptionDataBag
* @return \Sentry\ExceptionDataBag
*/ */
private static function getAnonymizedSentryException( private static function getAnonymizedSentryException(
\Sentry\ExceptionDataBag $exceptionDataBag ExceptionDataBag $exceptionDataBag
): \Sentry\ExceptionDataBag { ): ExceptionDataBag {
$exceptionDataBag->setValue(self::getAnonymizedMessage($exceptionDataBag->getValue())); $exceptionDataBag->setValue(self::getAnonymizedMessage($exceptionDataBag->getValue()));
if ($stackTrace = $exceptionDataBag->getStacktrace()) { if (($stackTrace = $exceptionDataBag->getStacktrace()) instanceof Stacktrace) {
$exceptionDataBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace)); $exceptionDataBag->setStacktrace(self::getAnonymizedSentryStackTrace($stackTrace));
} }
return $exceptionDataBag; return $exceptionDataBag;
} }
/** /**
* Anonymise sentry stacktrace * Anonymise sentry stacktrace
*
* @param \Sentry\Stacktrace $stackTrace
* @return \Sentry\Stacktrace
*/ */
private static function getAnonymizedSentryStackTrace(\Sentry\Stacktrace $stackTrace): \Sentry\Stacktrace private static function getAnonymizedSentryStackTrace(Stacktrace $stackTrace): Stacktrace
{ {
/** @var \Sentry\Frame[] */ /** @var Frame[] */
$frames = \array_reverse($stackTrace->getFrames(), true); $frames = \array_reverse($stackTrace->getFrames(), true);
$i = \count($frames) - 1; $i = \count($frames) - 1;
// * Remove all but keep first (index.php). // * Remove all but keep first (index.php).
@ -66,13 +67,14 @@ class ExceptionsHelper
$stackTrace->removeFrame(count($stackTrace->getFrames()) - 1); $stackTrace->removeFrame(count($stackTrace->getFrames()) - 1);
} }
} }
return $stackTrace; return $stackTrace;
} }
/** /**
* Get anonymized sentry params * Get anonymized sentry params
* *
* @param mixed[] $params * @param mixed[] $params
* @return string[] * @return string[]
*/ */
private static function getAnonymizedSentryParams(array $params): array private static function getAnonymizedSentryParams(array $params): array
@ -86,46 +88,44 @@ class ExceptionsHelper
case $type === 'lambda': case $type === 'lambda':
case $type === 'object': case $type === 'object':
continue 2; continue 2;
case $type === 'array' and \is_array($param): case $type === 'array' && \is_array($param):
$param = sprintf('Array(%d)', \count($param)); $param = sprintf('Array(%d)', \count($param));
continue 2; continue 2;
default: default:
if (\is_numeric($param)) { if (\is_numeric($param)) {
$param = \ucfirst($type); $param = \ucfirst($type);
continue 2; continue 2;
} }
if (\is_string($param)) { if (\is_string($param)) {
$param = sprintf('String(%d)', \strlen($param)); $param = sprintf('String(%d)', \strlen($param));
continue 2; continue 2;
} }
} //end switch } // end switch
} //end foreach } // end foreach
return $params; return $params;
} }
/** /**
* Anonymize at least Sql queries * Anonymize at least Sql queries
*
* @param string $message
* @return string
*/ */
public static function getAnonymizedMessage(string $message): string public static function getAnonymizedMessage(string $message): string
{ {
$message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message; $message = \preg_replace('/^(.*)( \(SQL:.*\))$/', '$1', $message) ?? $message;
if ($sqlPos = \strpos($message, 'to use near')) { if ($sqlPos = \strpos($message, 'to use near')) {
$message = \mb_strcut($message, 0, $sqlPos); return \mb_strcut($message, 0, $sqlPos);
} }
return $message; return $message;
} }
/** /**
* Anonymize stack trace * Anonymize stack trace
*
* @param \Throwable $e
* @return string
*/ */
public static function getAnonymizedStackTrace(\Throwable $e): string public static function getAnonymizedStackTrace(Throwable $e): string
{ {
$projectPath = \realpath(\sprintf('%s/../', \app_path())); $projectPath = \realpath(\sprintf('%s/../', \app_path()));
$traceStack = $e->getTrace(); $traceStack = $e->getTrace();
@ -152,17 +152,15 @@ class ExceptionsHelper
} }
$traceStr .= ")\n"; $traceStr .= ")\n";
} }
} //end foreach } // end foreach
$maxStackStage++; $maxStackStage++;
$traceStr .= "#$maxStackStage {main}"; $traceStr .= "#$maxStackStage {main}";
return $traceStr; return $traceStr;
} }
/** /**
* Handle Args * Handle Args
*
* @param array $stack
* @return string
*/ */
public static function handleArgs(array $stack): string public static function handleArgs(array $stack): string
{ {
@ -170,21 +168,15 @@ class ExceptionsHelper
$argsCount = count($stack['args']); $argsCount = count($stack['args']);
for ($i = 0; $i < $argsCount; $i++) { for ($i = 0; $i < $argsCount; $i++) {
$type = \gettype($stack['args'][$i]); $type = \gettype($stack['args'][$i]);
switch ($type) { match ($type) {
case 'object': 'object' => $traceArgs .= sprintf('Object(%s)', $stack['args'][$i]::class),
$traceArgs .= sprintf('Object(%s)', \get_class($stack['args'][$i])); 'array' => $traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i])),
break; 'string' => $traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i])),
case 'array': default => $traceArgs .= \gettype($stack['args'][$i]),
$traceArgs .= sprintf('Array(%d)', \count($stack['args'][$i])); };
break;
case 'string':
$traceArgs .= sprintf('String(%d)', \strlen($stack['args'][$i]));
break;
default:
$traceArgs .= \gettype($stack['args'][$i]);
}
$traceArgs .= ($i !== ($argsCount - 1) ? ',' : ''); $traceArgs .= ($i !== ($argsCount - 1) ? ',' : '');
} }
return $traceArgs; return $traceArgs;
} }
} }

View file

@ -1,11 +1,17 @@
<?php <?php
declare(strict_types=1);
namespace App\Lib\Helpers; namespace App\Lib\Helpers;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use RuntimeException;
use SplFileInfo;
/** /**
* This class is used to handle files in LDC Laravel stack * This class is used to handle files in LDC Laravel stack
@ -33,28 +39,25 @@ class FileStorageHelper
/** /**
* Remove all files from string attribute html that are stored in public * Remove all files from string attribute html that are stored in public
* *
* @param string $publicStorageFilePath The file path from public directory. * @param string $publicStorageFilePath The file path from public directory.
* @return string The local storage path. * @return string The local storage path.
*/ */
public static function getFileFullPath(string $publicStorageFilePath): string public static function getFileFullPath(string $publicStorageFilePath): string
{ {
/** @var \Illuminate\Filesystem\FilesystemAdapter */ /** @var FilesystemAdapter */
$disk = Storage::disk('public'); $disk = Storage::disk('public');
$path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/')); $path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/'));
return $disk->path($path); return $disk->path($path);
} }
/** /**
* Destroy the file associate with the attribute. * Destroy the file associate with the attribute.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $attribute
* @return void
*/ */
public static function removeFile(Model $model, string $attribute): void public static function removeFile(Model $model, string $attribute): void
{ {
$path = \ltrim(\urldecode($model->{$attribute}), '/'); $path = \ltrim(\urldecode($model->{$attribute}), '/');
if (\strpos($path, 'storage') !== 0) { if (! str_starts_with($path, 'storage')) {
return; return;
} }
$path = ToolboxHelper::mbReplace('storage/', '', $path); $path = ToolboxHelper::mbReplace('storage/', '', $path);
@ -65,20 +68,15 @@ class FileStorageHelper
/** /**
* Destroy the file associate with the attribute old value (before updated to DB). * Destroy the file associate with the attribute old value (before updated to DB).
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $attribute
* @param boolean $onlyIfUpdated
* @return void
*/ */
public static function removeOldFile(Model $model, string $attribute, bool $onlyIfUpdated = true): void public static function removeOldFile(Model $model, string $attribute, bool $onlyIfUpdated = true): void
{ {
// * Ignore If removal has to be done only if the field has changed and it actually hasnt. // * Ignore If removal has to be done only if the field has changed and it actually hasnt.
if ($onlyIfUpdated and $model->{$attribute} === ToolboxHelper::getOriginal($model, $attribute)) { if ($onlyIfUpdated && $model->{$attribute} === ToolboxHelper::getOriginal($model, $attribute)) {
return; return;
} }
$originalPath = \ltrim(\urldecode(ToolboxHelper::getOriginal($model, $attribute)), '/'); $originalPath = \ltrim(\urldecode(ToolboxHelper::getOriginal($model, $attribute)), '/');
if (\strpos($originalPath, 'storage') !== 0) { if (! str_starts_with($originalPath, 'storage')) {
return; return;
} }
$originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath); $originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath);
@ -92,37 +90,39 @@ class FileStorageHelper
* *
* You can use filename param to overload file name. * You can use filename param to overload file name.
* *
* @param \Illuminate\Database\Eloquent\Model $model The model that will hold the picture. * @param Model $model The model that will hold the picture.
* @param \Illuminate\Http\UploadedFile|\SplFileInfo|string $file The file, will just return itself is string. * @param UploadedFile|SplFileInfo|string $file The file, will just return itself is string.
* @param boolean $slugify Force filename slugification. * @param bool $slugify Force filename slugification.
* @param string|null $filename This string may be used to store the file. * @param string|null $filename This string may be used to store the file.
* @param boolean $private To store the file in a private space. * @param bool $private To store the file in a private space.
* @return string *
* @throws \RuntimeException If file copy fails. * @throws RuntimeException If file copy fails.
*
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh * @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/ */
public static function storeFile( public static function storeFile(
Model $model, Model $model,
$file, $file,
bool $slugify = false, bool $slugify = false,
string $filename = null, ?string $filename = null,
bool $private = false bool $private = false
): string { ): string {
// phpcs:enable // phpcs:enable
$tableName = $model->getTable(); $tableName = $model->getTable();
if (\is_object($file) and $file instanceof \Illuminate\Http\UploadedFile) { if ($file instanceof UploadedFile) {
/** @var \Illuminate\Http\UploadedFile $file */ /** @var UploadedFile $file */
$filename = $filename ?: $file->getClientOriginalName(); $filename = $filename ?: $file->getClientOriginalName();
$filename = $slugify ? self::slugifyFileName($filename) : $filename; $filename = $slugify ? self::slugifyFileName($filename) : $filename;
$newPath = self::prepareMoveFile($tableName, $filename, $private); $newPath = self::prepareMoveFile($tableName, $filename, $private);
$newPath = str_replace(storage_path('app'), '', $newPath); $newPath = str_replace(storage_path('app'), '', $newPath);
if (!$private) { if (! $private) {
// * Store public file. // * Store public file.
$file->storePubliclyAs( $file->storePubliclyAs(
pathinfo($newPath, \PATHINFO_DIRNAME), pathinfo($newPath, \PATHINFO_DIRNAME),
pathinfo($newPath, \PATHINFO_BASENAME) pathinfo($newPath, \PATHINFO_BASENAME)
); );
return 'storage' . str_replace('/public', '', $newPath); return 'storage' . str_replace('/public', '', $newPath);
} }
@ -132,23 +132,25 @@ class FileStorageHelper
); );
return str_replace('/private/', '', $newPath); return str_replace('/private/', '', $newPath);
} //end if } // end if
if (\is_object($file) and $file instanceof \SplFileInfo) { if ($file instanceof SplFileInfo) {
/** @var \SplFileInfo $file */ /** @var SplFileInfo $file */
$filename = $filename ?: $file->getBaseName(); $filename = $filename ?: $file->getBaseName();
$filename = $slugify ? self::slugifyFileName($filename) : $filename; $filename = $slugify ? self::slugifyFileName($filename) : $filename;
$newPath = self::prepareMoveFile($tableName, $filename, $private); $newPath = self::prepareMoveFile($tableName, $filename, $private);
if (!File::copy($file->getRealPath(), $newPath)) { if (! File::copy($file->getRealPath(), $newPath)) {
throw new \RuntimeException(sprintf( throw new RuntimeException(sprintf(
'Failed to copy file %s to %s', 'Failed to copy file %s to %s',
$file->getRealPath(), $file->getRealPath(),
$newPath $newPath
)); ));
} }
return $private ? return $private ?
\ltrim(str_replace(\storage_path('app/private'), '/storage/app/private', $newPath), '/') : \ltrim(str_replace(\storage_path('app/private'), '/storage/app/private', $newPath), '/') :
\ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/'); \ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/');
} }
return strval($file); return strval($file);
} }
@ -157,22 +159,22 @@ class FileStorageHelper
* If oldHtmlString is passed, a diff is done with new and old files * If oldHtmlString is passed, a diff is done with new and old files
* to keep only files that are present in the new html string. * to keep only files that are present in the new html string.
* *
* @param \Illuminate\Database\Eloquent\Model $model The model that will hold the picture. * @param Model $model The model that will hold the picture.
* @param string $htmlString The html string to parse and modify files. * @param string $htmlString The html string to parse and modify files.
* @param string|null $oldHtmlString The old html string with alreadyParsed * @param string|null $oldHtmlString The old html string with alreadyParsed
* files using storeModelFilesFromHtml. * files using storeModelFilesFromHtml.
* @return string Html with files pathes replaced using storage strategy. * @return string Html with files pathes replaced using storage strategy.
*/ */
public static function storeModelFilesFromHtml( public static function storeModelFilesFromHtml(
Model $model, Model $model,
string $htmlString, string $htmlString,
string $oldHtmlString = null ?string $oldHtmlString = null
): string { ): string {
\collect(self::onlyUploadedFilesWithFileManager(self::findFilesInHtml($htmlString))) \collect(self::onlyUploadedFilesWithFileManager(self::findFilesInHtml($htmlString)))
->each(function (string $path) use ($model, &$htmlString) { ->each(function (string $path) use ($model, &$htmlString): void {
/** @var \Illuminate\Filesystem\FilesystemAdapter */ /** @var FilesystemAdapter */
$disk = Storage::disk('uploads'); $disk = Storage::disk('uploads');
$fileInfo = new \SplFileInfo($disk->path( $fileInfo = new SplFileInfo($disk->path(
ToolboxHelper::mbReplace('/uploads/', '', \urldecode($path)) ToolboxHelper::mbReplace('/uploads/', '', \urldecode($path))
)); ));
$htmlString = ToolboxHelper::mbReplace( $htmlString = ToolboxHelper::mbReplace(
@ -193,9 +195,8 @@ class FileStorageHelper
* Remove all old files from string attribute html that are stored in public that * Remove all old files from string attribute html that are stored in public that
* are nove present anymore on new html string * are nove present anymore on new html string
* *
* @param string $newHtmlString The html string with alreadyParsed files using storeModelFilesFromHtml. * @param string $newHtmlString The html string with alreadyParsed files using storeModelFilesFromHtml.
* @param string $oldHtmlString The old html string with alreadyParsed files using storeModelFilesFromHtml. * @param string $oldHtmlString The old html string with alreadyParsed files using storeModelFilesFromHtml.
* @return void
*/ */
public static function removeOldModelFilesFromHtml(string $newHtmlString, string $oldHtmlString): void public static function removeOldModelFilesFromHtml(string $newHtmlString, string $oldHtmlString): void
{ {
@ -206,16 +207,15 @@ class FileStorageHelper
* Remove all old files that are stored in public that * Remove all old files that are stored in public that
* are not present anymore on new array files * are not present anymore on new array files
* *
* @param string[] $oldFiles The old array files. * @param string[] $oldFiles The old array files.
* @param string[] $newFiles The new array files. * @param string[] $newFiles The new array files.
* @return void
*/ */
public static function removeOldFiles(array $oldFiles, array $newFiles): void public static function removeOldFiles(array $oldFiles, array $newFiles): void
{ {
$oldFiles = \collect(self::onlyUploadedFilesWithFileStorageHelper($oldFiles)); $oldFiles = \collect(self::onlyUploadedFilesWithFileStorageHelper($oldFiles));
$filesToKeep = \collect(self::onlyUploadedFilesWithFileStorageHelper($newFiles)); $filesToKeep = \collect(self::onlyUploadedFilesWithFileStorageHelper($newFiles));
$filesToRemove = $oldFiles->diff($filesToKeep); $filesToRemove = $oldFiles->diff($filesToKeep);
$filesToRemove->each(function (string $publicStorageFilePath) { $filesToRemove->each(function (string $publicStorageFilePath): void {
$path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/')); $path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/'));
if (Storage::disk('public')->exists($path)) { if (Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path); Storage::disk('public')->delete($path);
@ -226,13 +226,12 @@ class FileStorageHelper
/** /**
* Remove all files from string attribute html that are stored in public * Remove all files from string attribute html that are stored in public
* *
* @param string $htmlString The html string with alreadyParsed files using storeModelFilesFromHtml. * @param string $htmlString The html string with alreadyParsed files using storeModelFilesFromHtml.
* @return void
*/ */
public static function removeModelFilesOfHtml(string $htmlString): void public static function removeModelFilesOfHtml(string $htmlString): void
{ {
$filesToRemove = \collect(self::onlyUploadedFilesWithFileStorageHelper(self::findFilesInHtml($htmlString))); $filesToRemove = \collect(self::onlyUploadedFilesWithFileStorageHelper(self::findFilesInHtml($htmlString)));
$filesToRemove->each(function (string $publicStorageFilePath) { $filesToRemove->each(function (string $publicStorageFilePath): void {
$path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/')); $path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/'));
if (Storage::disk('public')->exists($path)) { if (Storage::disk('public')->exists($path)) {
Storage::disk('public')->delete($path); Storage::disk('public')->delete($path);
@ -242,13 +241,11 @@ class FileStorageHelper
/** /**
* Find all occurence of files in html * Find all occurence of files in html
*
* @param string $html
* @return array
*/ */
public static function findFilesInHtml(string $html): array public static function findFilesInHtml(string $html): array
{ {
preg_match_all('/href="(?P<href>.*)"|src="(?P<src>.*)"|url="(?P<url>.*)"/Um', $html, $m, PREG_SET_ORDER, 0); preg_match_all('/href="(?P<href>.*)"|src="(?P<src>.*)"|url="(?P<url>.*)"/Um', $html, $m, PREG_SET_ORDER, 0);
return collect($m)->pluck('href')->filter() return collect($m)->pluck('href')->filter()
->concat(collect($m)->pluck('url')->filter()) ->concat(collect($m)->pluck('url')->filter())
->concat(collect($m)->pluck('src')->filter()) ->concat(collect($m)->pluck('src')->filter())
@ -258,19 +255,18 @@ class FileStorageHelper
/** /**
* Filter array to get only uploaded files that exists * Filter array to get only uploaded files that exists
* on disk 'public' (using helper storage strategy) * on disk 'public' (using helper storage strategy)
*
* @param array $urls
* @return array
*/ */
public static function onlyUploadedFilesWithFileStorageHelper(array $urls): array public static function onlyUploadedFilesWithFileStorageHelper(array $urls): array
{ {
return \collect($urls)->unique() return \collect($urls)->unique()
// * Filter to get only /uploads and using app url or empty // * Filter to get only /uploads and using app url or empty
->filter(function (string $url) { ->filter(function (string $url): bool {
$path = \parse_url($url, \PHP_URL_PATH); $path = \parse_url($url, \PHP_URL_PATH);
return \strpos(\ltrim($path, '/'), 'storage/modelfiles') === 0;
return str_starts_with(\ltrim($path, '/'), 'storage/modelfiles');
})->filter(function (string $url) { })->filter(function (string $url) {
$path = \parse_url($url, \PHP_URL_PATH); $path = \parse_url($url, \PHP_URL_PATH);
return Storage::disk('public') return Storage::disk('public')
->exists(ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($path), '/'))); ->exists(ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($path), '/')));
})->all(); })->all();
@ -279,21 +275,19 @@ class FileStorageHelper
/** /**
* Filter array to get only uploaded files that exists * Filter array to get only uploaded files that exists
* on disk 'uploads' * on disk 'uploads'
*
* @param array $urls
* @return array
*/ */
private static function onlyUploadedFilesWithFileManager(array $urls): array private static function onlyUploadedFilesWithFileManager(array $urls): array
{ {
return \collect($urls)->unique() return \collect($urls)->unique()
// * Filter to get only /uploads and using app url or empty // * Filter to get only /uploads and using app url or empty
->filter(function (string $url) { ->filter(function (string $url): bool {
$host = \parse_url($url, \PHP_URL_HOST); $host = \parse_url($url, \PHP_URL_HOST);
$path = \parse_url($url, \PHP_URL_PATH); $path = \parse_url($url, \PHP_URL_PATH);
return ($host === \parse_url(config('app.url'), PHP_URL_HOST) or \is_null($host)) and
\strpos(\ltrim($path, '/'), 'uploads') === 0; return ($host === \parse_url(config('app.url'), PHP_URL_HOST) || \is_null($host)) && str_starts_with(\ltrim($path, '/'), 'uploads');
})->filter(function (string $url) { })->filter(function (string $url) {
$path = \parse_url($url, \PHP_URL_PATH); $path = \parse_url($url, \PHP_URL_PATH);
return Storage::disk('uploads') return Storage::disk('uploads')
->exists(ToolboxHelper::mbReplace('uploads/', '', \ltrim(\urldecode($path), '/'))); ->exists(ToolboxHelper::mbReplace('uploads/', '', \ltrim(\urldecode($path), '/')));
})->all(); })->all();
@ -303,10 +297,9 @@ class FileStorageHelper
* Prepare storage path and return * Prepare storage path and return
* the file full path * the file full path
* *
* @param string $tableName Model table name. * @param string $tableName Model table name.
* @param string $filename The basename for the file. * @param string $filename The basename for the file.
* @param boolean $private May the file be stored in a private dir. * @param bool $private May the file be stored in a private dir.
* @return string
*/ */
private static function prepareMoveFile(string $tableName, string $filename, bool $private = false): string private static function prepareMoveFile(string $tableName, string $filename, bool $private = false): string
{ {
@ -325,6 +318,7 @@ class FileStorageHelper
$filename, $filename,
); );
$filename = self::getFileUniqueName($folderPath, $filename); $filename = self::getFileUniqueName($folderPath, $filename);
return "{$folderPath}/{$filename}"; return "{$folderPath}/{$filename}";
} }
@ -332,11 +326,6 @@ class FileStorageHelper
* Create sub folders to store files using pattern * Create sub folders to store files using pattern
* storage/modelfiles/posts/10/04/19/5f * storage/modelfiles/posts/10/04/19/5f
* year/month/day/md5 first 2 letters * year/month/day/md5 first 2 letters
*
* @param string $storage_dir
* @param string $table
* @param string $filename
* @return string
*/ */
private static function getStoragePath(string $storage_dir, string $table, string $filename): string private static function getStoragePath(string $storage_dir, string $table, string $filename): string
{ {
@ -350,40 +339,34 @@ class FileStorageHelper
gmdate('d'), gmdate('d'),
substr($filenameHash, 0, 2) substr($filenameHash, 0, 2)
)); ));
if (!File::exists($pathFolder)) { if (! File::exists($pathFolder)) {
File::makeDirectory($pathFolder, 0755, true); File::makeDirectory($pathFolder, 0755, true);
} }
return $pathFolder; return $pathFolder;
} }
/** /**
* Get unique filename if the path is already occupied * Get unique filename if the path is already occupied
* *
* @param string $folderPath * @throws RuntimeException If a unique filename cannot be obtained.
* @param string $filename
* @return string
* @throws \RuntimeException If a unique filename cannot be obtained.
*/ */
private static function getFileUniqueName(string $folderPath, string $filename): string private static function getFileUniqueName(string $folderPath, string $filename): string
{ {
$attempts = 0; $attempts = 0;
$maxAttempts = 20; $maxAttempts = 20;
while (File::exists("{$folderPath}/{$filename}")) { while (File::exists("{$folderPath}/{$filename}")) {
if ($attempts++ >= $maxAttempts) { throw_if($attempts++ >= $maxAttempts, RuntimeException::class, "Failed to have a unique filename for {$folderPath}/{$filename}");
throw new \RuntimeException("Failed to have a unique filename for {$folderPath}/{$filename}");
}
$filename = pathinfo($filename, \PATHINFO_FILENAME) . $filename = pathinfo($filename, \PATHINFO_FILENAME) .
\uniqid('-') . '.' . \uniqid('-') . '.' .
pathinfo($filename, \PATHINFO_EXTENSION); pathinfo($filename, \PATHINFO_EXTENSION);
} }
return $filename; return $filename;
} }
/** /**
* Slugify the filename * Slugify the filename
*
* @param string $filename
* @return string
*/ */
public static function slugifyFileName(string $filename): string public static function slugifyFileName(string $filename): string
{ {
@ -393,8 +376,8 @@ class FileStorageHelper
/** /**
* Sanitize File name for uploaded files * Sanitize File name for uploaded files
* *
* @param string $filename The original uploaded client file name. * @param string $filename The original uploaded client file name.
* @param boolean $beautify Beautify the filename. * @param bool $beautify Beautify the filename.
* @return string The sanitized file name. * @return string The sanitized file name.
*/ */
public static function sanitizeFileName(string $filename, bool $beautify = true): string public static function sanitizeFileName(string $filename, bool $beautify = true): string
@ -417,7 +400,7 @@ class FileStorageHelper
$filename = Str::of($filename)->ltrim('.-'); $filename = Str::of($filename)->ltrim('.-');
// * Optional beautification // * Optional beautification
if ($beautify) { if ($beautify) {
$filename = self::beautifyFilename($filename); $filename = self::beautifyFilename($filename->value());
} }
// * Maximize filename length to 255 bytes http://serverfault.com/a/9548/44086 // * Maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
/** @var string */ /** @var string */
@ -426,20 +409,18 @@ class FileStorageHelper
$filename = pathinfo($filename, \PATHINFO_FILENAME); $filename = pathinfo($filename, \PATHINFO_FILENAME);
$encoding = mb_detect_encoding($filename); $encoding = mb_detect_encoding($filename);
$encoding = $encoding ? $encoding : null; $encoding = $encoding ?: null;
return \mb_strcut( return \mb_strcut(
$filename, $filename,
0, 0,
255 - (!empty($extension) ? strlen($extension) + 1 : 0), 255 - (! empty($extension) ? strlen($extension) + 1 : 0),
$encoding $encoding
) . (!empty($extension) ? ".$extension" : ''); ) . (! empty($extension) ? ".$extension" : '');
} }
/** /**
* Beautify a filename * Beautify a filename
*
* @param string $filename
* @return string
*/ */
private static function beautifyFilename(string $filename): string private static function beautifyFilename(string $filename): string
{ {
@ -450,18 +431,19 @@ class FileStorageHelper
// * "file___name.zip" becomes 'file-name.zip' // * "file___name.zip" becomes 'file-name.zip'
'/_+/', '/_+/',
// * "file---name.zip" becomes 'file-name.zip' // * "file---name.zip" becomes 'file-name.zip'
'/-+/' '/-+/',
], '-', $filename); ], '-', $filename);
$filename = preg_replace([ $filename = preg_replace([
// * "file--.--.-.--name.zip" becomes "file.name.zip" // * "file--.--.-.--name.zip" becomes "file.name.zip"
'/-*\.-*/', '/-*\.-*/',
// * "file...name..zip" becomes "file.name.zip" // * "file...name..zip" becomes "file.name.zip"
'/\.{2,}/' '/\.{2,}/',
], '.', $filename); ], '.', $filename);
// Lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625 . // Lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625 .
$filename = mb_strtolower($filename, mb_detect_encoding($filename)); $filename = mb_strtolower($filename, mb_detect_encoding($filename));
// * ".file-name.-" becomes "file-name" // * ".file-name.-" becomes "file-name"
$filename = trim($filename, '.-'); $filename = trim($filename, '.-');
return $filename; return $filename;
} }
} }

View file

@ -1,75 +1,84 @@
<?php <?php
declare(strict_types=1);
namespace App\Lib\Helpers; namespace App\Lib\Helpers;
use App\Models\Settings; use App\Models\Settings;
use Carbon\Carbon; use Exception;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Filesystem\FilesystemAdapter;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Str; use Illuminate\Process\Exceptions\ProcessTimedOutException;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule; use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use RuntimeException;
use Symfony\Component\Finder\SplFileInfo; use Symfony\Component\Finder\SplFileInfo;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Process\Exception\InvalidArgumentException;
use Symfony\Component\Process\Exception\LogicException;
class ToolboxHelper class ToolboxHelper
{ {
/** /**
* Run a terminal command and gives the output * Run a terminal command and gives the output.
* *
* @param array|string $command The command to execute on terminal. * @param array|string $command The command to execute on terminal.
* @param boolean $throw Wether to throw en esception on failure or not. * @param bool $throw Wether to throw en esception on failure or not.
* @return string|false Return false on failure only if $throw is false. * @return string|false Return false on failure only if $throw is false.
*
* @phpcs:disable Squiz.Commenting.FunctionComment.EmptyThrows * @phpcs:disable Squiz.Commenting.FunctionComment.EmptyThrows
*
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber * @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
* @throws \Illuminate\Process\Exceptions\ProcessTimedOutException *
* @throws \Symfony\Component\Process\Exception\LogicException * @throws ProcessTimedOutException
* @throws \Symfony\Component\Process\Exception\InvalidArgumentException * @throws LogicException
* @throws InvalidArgumentException
* @throws \Symfony\Component\Process\Exception\RuntimeException * @throws \Symfony\Component\Process\Exception\RuntimeException
* @throws \LogicException * @throws \LogicException
* @throws \RuntimeException If the command fails. * @throws RuntimeException If the command fails.
*/ */
public static function runConsoleCommand(array|string $command, bool $throw = true): string | false public static function runConsoleCommand(array | string $command, bool $throw = true): string | false
{ {
// phpcs:enable // phpcs:enable
$process = \Illuminate\Support\Facades\Process::command($command)->run(); $process = Process::command($command)->run();
if ($process->failed()) { if ($process->failed()) {
$code = $process->exitCode(); $code = $process->exitCode();
$ouput = $process->errorOutput(); $ouput = $process->errorOutput();
if ($throw) { throw_if($throw, RuntimeException::class, "Failed to run command exit code {$code} with output \n{$ouput}");
throw new \RuntimeException("Failed to run command exit code {$code} with output \n{$ouput}");
}
return false; return false;
} }
return $process->output(); return $process->output();
} }
/** /**
* Assert game is not oppened * Assert game is not oppened.
* *
* @return void * @throws RuntimeException If game has started.
* @throws \RuntimeException If game has started.
*/ */
public static function assertGameIsNotOppened(): void public static function assertGameIsNotOppened(): void
{ {
/** @var \App\Models\Settings */ /** @var Settings */
$settings = app(Settings::class); $settings = resolve(Settings::class);
if ($settings->game_is_open) { throw_if($settings->game_is_open, RuntimeException::class, 'Cannot delete prize while game is oppened');
throw new \RuntimeException('Cannot delete prize while game is oppened');
}
} }
/** /**
* @param array<int|string, mixed> $array1 * @param array<int|string, mixed> $array1
* @param array<int|string, mixed> $array2 * @param array<int|string, mixed> $array2
*
* @return array<int|string, mixed> * @return array<int|string, mixed>
*/ */
public static function arrayMergeRecursiveDistinct(array &$array1, array &$array2): array public static function arrayMergeRecursiveDistinct(array &$array1, array &$array2): array
@ -87,89 +96,69 @@ class ToolboxHelper
} }
/** /**
* Validate recaptcha * Validate recaptcha.
*
* @param \Illuminate\Http\Request $request
* @param boolean $isV3
* @return void
*/ */
public static function validateRecaptcha(Request $request, bool $isV3 = true) public static function validateRecaptcha(Request $request, bool $isV3 = true): void
{ {
Validator::validate($request->all(), [ Validator::validate($request->all(), [
'g-recaptcha-response' => [ 'g-recaptcha-response' => [
'required', 'string', 'required', 'string',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed // @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) use ($isV3) { function ($attribute, $value, $fail) use ($isV3): void {
try { try {
$response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [ $response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
'secret' => config('services.google.recaptcha.private'), 'secret' => config('services.google.recaptcha.private'),
'response' => \strval($value), 'response' => \strval($value),
'remoteip' => app('clientIp') 'remoteip' => resolve('clientIp'),
]); ]);
if ($e = $response->toException()) { throw_if($e = $response->toException(), $e);
throw $e;
}
if ($isV3) { if ($isV3) {
if ( throw_if(! $response->json('success')
!$response->json('success') or || ! $response->json('challenge_ts')
!$response->json('challenge_ts') or || Date::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5))
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or || $response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST)
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST) or || ! $response->json('score'), RuntimeException::class);
!$response->json('score')
) {
throw new \RuntimeException();
}
if (\floatval($response->json('score')) < 0.8) { if (\floatval($response->json('score')) < 0.8) {
$fail(trans('recaptcha.not_enough_score')); $fail(trans('recaptcha.not_enough_score'));
} }
return; return;
} }
if ( throw_if(! $response->json('success')
!$response->json('success') or || ! $response->json('challenge_ts')
!$response->json('challenge_ts') or || Date::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5))
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or || $response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST), RuntimeException::class);
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST) } catch (Exception) {
) {
throw new \RuntimeException();
}
} catch (\Exception $e) {
$fail(trans('recaptcha.error')); $fail(trans('recaptcha.error'));
} //end try } // end try
} },
] ],
], [], ['g-recaptcha-response' => 'reCaptcha']); ], [], ['g-recaptcha-response' => 'reCaptcha']);
} }
/** /**
* Strip scheme (https) on oembed urls * Strip scheme (https) on oembed urls.
* Only embed allowed are for MP4 self hosted videos right now * Only embed allowed are for MP4 self hosted videos right now
* *
* https is force by ckeditor MediaElements which natively supports * https is force by ckeditor MediaElements which natively supports
* only cloud hosted medias (youtube, spotify etc..) * only cloud hosted medias (youtube, spotify etc..)
*
* @param string $input
* @return string
*/ */
public static function stripCkEditorSchemesOnMediaInputs(string $input): string public static function stripCkEditorSchemesOnMediaInputs(string $input): string
{ {
$input = \preg_replace('/(?<=<oembed url=")(https:\/\/)(?=.*?">)/m', '', $input); return \preg_replace('/(?<=<oembed url=")(https:\/\/)(?=.*?">)/m', '', $input);
return $input;
} }
/** /**
* Assert That no Back Office url is present on txt * Assert That no Back Office url is present on txt.
* *
* @param string $field * @throws ValidationException If url is a bo url.
* @param string $text
* @return string
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
*/ */
public static function assertNoInternalBoUrls(string $field, string $text): string public static function assertNoInternalBoUrls(string $field, string $text): string
{ {
$routes = Route::getRoutes(); $routes = Route::getRoutes();
// * Url replace and email replace. // * Url replace and email replace.
$text = preg_replace_callback( $text = preg_replace_callback(
// phpcs:ignore Generic.Files.LineLength.TooLong // @phpcs:ignore Generic.Files.LineLength.TooLong
'/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/', '/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
function ($matches) use ($routes, $field) { function ($matches) use ($routes, $field) {
$match = collect($matches)->first() ?? ''; $match = collect($matches)->first() ?? '';
@ -178,34 +167,33 @@ class ToolboxHelper
$request = Request::create($url); $request = Request::create($url);
// If a route is match then it do not throws exception. // If a route is match then it do not throws exception.
if ( if (
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) && \parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST)
trim($routes->match($request)->getPrefix() ?? '', '/') === 'bo' && trim($routes->match($request)->getPrefix() ?? '', '/') === 'bo'
) { ) {
// Removes the matched url. // Removes the matched url.
throw ValidationException::withMessages([ throw ValidationException::withMessages([
$field => trans( $field => trans(
'crud.libs.toolbox.assertNoInternalBoUrls', 'crud.libs.toolbox.assertNoInternalBoUrls',
['attribute' => $field] ['attribute' => $field]
) ),
]); ]);
} }
return $match; return $match;
} catch (NotFoundHttpException $e) { } catch (NotFoundHttpException) {
return $match; return $match;
} }
}, },
$text $text
); );
return $text; return $text;
} }
/** /**
* Assert That only external url is present on txt * Assert That only external url is present on txt.
* *
* @param string $field * @throws ValidationException If url is a bo url.
* @param string $text
* @return string
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
*/ */
public static function assertNoInternalUrls(string $field, string $text): string public static function assertNoInternalUrls(string $field, string $text): string
{ {
@ -213,7 +201,7 @@ class ToolboxHelper
$routes = Route::getRoutes(); $routes = Route::getRoutes();
// * Url replace and email replace. // * Url replace and email replace.
$text = preg_replace_callback( $text = preg_replace_callback(
// phpcs:ignore Generic.Files.LineLength.TooLong // @phpcs:ignore Generic.Files.LineLength.TooLong
'/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/', '/(?P<url>(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/',
function ($matches) use ($routes, $field) { function ($matches) use ($routes, $field) {
$match = collect($matches)->first() ?? ''; $match = collect($matches)->first() ?? '';
@ -229,35 +217,31 @@ class ToolboxHelper
$field => trans( $field => trans(
'crud.libs.toolbox.assertNoInternalUrls', 'crud.libs.toolbox.assertNoInternalUrls',
['attribute' => $field, 'appname' => config('app.name')] ['attribute' => $field, 'appname' => config('app.name')]
) ),
]); ]);
} }
return $match; return $match;
} catch (NotFoundHttpException $e) { } catch (NotFoundHttpException) {
return $match; return $match;
} }
}, },
$text $text
); );
return $text; return $text;
} }
/** /**
* Mutltibyte string replace * Mutltibyte string replace.
*
* @param array|string $search
* @param array|string $replace
* @param array|string $subject
* @param integer $count
* @return array|string|false
*/ */
public static function mbReplace( public static function mbReplace(
array|string $search, array | string $search,
array|string $replace, array | string $replace,
array|string $subject, array | string $subject,
int &$count = 0 int &$count = 0
): array|string|false { ): array | string | false {
if (!is_array($search) && is_array($replace)) { if (! is_array($search) && is_array($replace)) {
return false; return false;
} }
if (is_array($subject)) { if (is_array($subject)) {
@ -266,7 +250,7 @@ class ToolboxHelper
$string = &self::mbReplace($search, $replace, $string, $count); $string = &self::mbReplace($search, $replace, $string, $count);
} }
} elseif (is_array($search)) { } elseif (is_array($search)) {
if (!is_array($replace)) { if (! is_array($replace)) {
foreach ($search as &$string) { foreach ($search as &$string) {
$subject = self::mbReplace($string, $replace, $subject, $count); $subject = self::mbReplace($string, $replace, $subject, $count);
} }
@ -280,37 +264,37 @@ class ToolboxHelper
} }
} else { } else {
$parts = mb_split(preg_quote($search), $subject); $parts = mb_split(preg_quote($search), $subject);
// @phpstan-ignore-next-line
$count = count($parts) - 1; $count = count($parts) - 1;
$subject = implode($replace, $parts); $subject = implode($replace, $parts);
} //end if } // end if
return $subject; return $subject;
} }
/** /**
* Asserts that the field is unique * Asserts that the field is unique.
* *
* @param string $table *
* @param string $field * @throws ValidationException If the field is not unique.
* @param string|integer|float|boolean|null|array $value
* @param integer|null $id
* @param callable|null $query
* @return void
* @throws \Illuminate\Validation\ValidationException If the field is not unique.
*/ */
public static function assertFieldIsUnique( public static function assertFieldIsUnique(
string $table, string $table,
string $field, string $field,
string|int|float|bool|null|array $value, string | int | float | bool | null | array $value,
?int $id = null, ?int $id = null,
?callable $query = null ?callable $query = null
) { ): void {
if (!$query) { if (! $query) {
Validator::make([$field => $value], [ Validator::make([$field => $value], [
self::mbReplace('.', '\.', $field) => Rule::unique($table, $field)->ignore($id), self::mbReplace('.', '\.', $field) => Rule::unique($table, $field)->ignore($id),
], [ ], [
self::mbReplace('.', '\.', "{$field}.unique") self::mbReplace('.', '\.', "{$field}.unique") => trans('crud.libs.toolbox.assertFieldIsUnique', [
=> trans('crud.libs.toolbox.assertFieldIsUnique', ['attribute' => $field, 'value' => $value]) 'attribute' => $field,
'value' => $value,
]),
])->validate(); ])->validate();
return; return;
} }
Validator::make([$field => $value], [ Validator::make([$field => $value], [
@ -319,16 +303,12 @@ class ToolboxHelper
$field . '.unique' => trans( $field . '.unique' => trans(
'crud.libs.toolbox.assertFieldIsUnique', 'crud.libs.toolbox.assertFieldIsUnique',
['attribute' => $field, 'value' => $value] ['attribute' => $field, 'value' => $value]
) ),
])->validate(); ])->validate();
} }
/** /**
* Get original polyfill with autotranslate behavior * Get original polyfill with autotranslate behavior.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $attribute
* @return mixed
*/ */
public static function getOriginal(Model $model, string $attribute): mixed public static function getOriginal(Model $model, string $attribute): mixed
{ {
@ -342,17 +322,12 @@ class ToolboxHelper
$model->useFallbackLocale() $model->useFallbackLocale()
); );
} }
return $model->getOriginal($attribute); return $model->getOriginal($attribute);
} }
/** /**
* Dynamic model query * Dynamic model query.
*
* @param string $defaultLocale
* @param \Illuminate\Database\Eloquent\Builder $q
* @param string $column
* @param string $value
* @return \Illuminate\Database\Eloquent\Builder
*/ */
public static function queryColumnWithLocales( public static function queryColumnWithLocales(
string $defaultLocale, string $defaultLocale,
@ -362,17 +337,17 @@ class ToolboxHelper
): Builder { ): Builder {
$currentLocale = app()->currentLocale(); $currentLocale = app()->currentLocale();
$locales = collect(config('app.locales')) $locales = collect(config('app.locales'))
->mapWithKeys(fn (string $locale) => [$locale => $locale]); ->mapWithKeys(fn (string $locale): array => [$locale => $locale]);
if (!$locales->has($defaultLocale)) { if (! $locales->has($defaultLocale)) {
// * Remove default locale. // * Remove default locale.
$locales->pull($defaultLocale); $locales->pull($defaultLocale);
} }
if (!$locales->has($currentLocale)) { if (! $locales->has($currentLocale)) {
// * Remove current locale. // * Remove current locale.
$locales->pull($currentLocale); $locales->pull($currentLocale);
} }
return $q->where(function (Builder $q) use ($defaultLocale, $currentLocale, $locales, $column, $value) { return $q->where(function (Builder $q) use ($defaultLocale, $currentLocale, $locales, $column, $value): void {
// * Query default locale // * Query default locale
$q->whereRaw("JSON_EXTRACT({$column}, '$.{$defaultLocale}') = '{$value}'"); $q->whereRaw("JSON_EXTRACT({$column}, '$.{$defaultLocale}') = '{$value}'");
if ($defaultLocale !== $currentLocale) { if ($defaultLocale !== $currentLocale) {
@ -387,12 +362,7 @@ class ToolboxHelper
} }
/** /**
* Generates an array from a dot separated string * Generates an array from a dot separated string.
*
* @param string $path
* @param mixed $value
* @param string $separator
* @return array
*/ */
public static function dotStringToNestedArray(string $path, mixed $value, string $separator = '.'): array public static function dotStringToNestedArray(string $path, mixed $value, string $separator = '.'): array
{ {
@ -402,85 +372,77 @@ class ToolboxHelper
foreach ($keys as $key) { foreach ($keys as $key) {
$arr = [$key => $arr]; $arr = [$key => $arr];
} }
return $arr; return $arr;
} }
/** /**
* Convert deeply an object to array * Convert deeply an object to array.
* *
* @param mixed $input * @throws RuntimeException If $input var is a scalar value.
* @return array
* @throws \RuntimeException If $input var is a scalar value.
*/ */
public static function objectToArray(mixed $input): array public static function objectToArray(mixed $input): array
{ {
// Only process if it's an object or array being passed to the function. // Only process if it's an object or array being passed to the function.
if (!(is_object($input) or is_array($input))) { if (!is_object($input) && !is_array($input)) {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input)); throw new RuntimeException('Cannot handle $input of type ' . \gettype($input));
} }
$ret = (array) $input; $ret = (array) $input;
foreach ($ret as &$item) { foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar. // Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ? $item = (is_object($item) || is_array($item)) ?
self::objectToArray($item) : $item; self::objectToArray($item) : $item;
} }
return $ret; return $ret;
} }
/** /**
* Convert deeply an array to object * Convert deeply an array to object.
* *
* @param mixed $input * @throws RuntimeException If $input var is a scalar value.
* @return object
* @throws \RuntimeException If $input var is a scalar value.
*/ */
public static function arrayToObject(mixed $input): object public static function arrayToObject(mixed $input): object
{ {
// Only process if it's an object or array being passed to the function. // Only process if it's an object or array being passed to the function.
if (!(is_object($input) or is_array($input))) { if (!is_object($input) && !is_array($input)) {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input)); throw new RuntimeException('Cannot handle $input of type ' . \gettype($input));
} }
$ret = (object) $input; $ret = (object) $input;
foreach ($ret as &$item) { foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar. // Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ? $item = (is_object($item) || is_array($item)) ?
self::arrayToObject($item) : $item; self::arrayToObject($item) : $item;
} }
return $ret; return $ret;
} }
/** /**
* Validate picture * Validate picture.
*
* @param string $attribute
* @param string|null $value
* @param callable $fail
* @param object $sizes
* @return void
*/ */
public static function validatePicture(string $attribute, string|null $value, callable $fail, object $sizes) public static function validatePicture(string $attribute, ?string $value, callable $fail, object $sizes): void
{ {
if (!($useStorage = Storage::exists($value)) and !File::exists($value)) { if (! ($useStorage = Storage::exists($value)) && ! File::exists($value)) {
$fail(trans(':attribute n\'existe pas dans le système de fichier', [ $fail(trans(':attribute n\'existe pas dans le système de fichier', [
'attribute' => $attribute 'attribute' => $attribute,
])); ]));
return; return;
} }
/** @var string */
$fileFullPath = self::getUploadedFileFullPath($value, $useStorage); $fileFullPath = self::getUploadedFileFullPath($value, $useStorage);
if ( if (
!\in_array(self::getUploadedFileMimeType($value, $useStorage), [ ! \in_array(self::getUploadedFileMimeType($value, $useStorage), [
'image/jpeg', 'image/jpg', 'image/png', 'image/gif' 'image/jpeg', 'image/jpg', 'image/png', 'image/gif',
]) ])
) { ) {
$fail(trans(':attribute doit être une image de type \'image/jpeg,image/jpg,image/png,image/gif\'', [ $fail(trans(':attribute doit être une image de type \'image/jpeg,image/jpg,image/png,image/gif\'', [
'attribute' => $attribute 'attribute' => $attribute,
])); ]));
} }
if (!($dimensions = \getimagesize($fileFullPath))) { if (! ($dimensions = \getimagesize($fileFullPath))) {
$fail(trans(':attribute impossible de valider les dimensions', [ $fail(trans(':attribute impossible de valider les dimensions', [
'attribute' => $attribute 'attribute' => $attribute,
])); ]));
} else { } else {
$width = intval($dimensions[0]); $width = intval($dimensions[0]);
@ -499,60 +461,52 @@ class ToolboxHelper
$yMessage = trans(":attribute la hauteur doit être de {$sizes->minHeight}px"); $yMessage = trans(":attribute la hauteur doit être de {$sizes->minHeight}px");
} }
if (($width < $sizes->minWith) or ($width > $sizes->maxWith)) { if ($width < $sizes->minWith || $width > $sizes->maxWith) {
$fail($xMessage); $fail($xMessage);
} }
if (($height < $sizes->minHeight) or ($height > $sizes->maxHeight)) { if ($height < $sizes->minHeight || $height > $sizes->maxHeight) {
$fail($yMessage); $fail($yMessage);
} }
} //end if } // end if
} }
/** /**
* Get the file full path * Get the file full path.
*
* @param string $value
* @param boolean $useStorage
* @return string
*/ */
private static function getUploadedFileFullPath(string $value, bool $useStorage): string private static function getUploadedFileFullPath(string $value, bool $useStorage): string
{ {
/** @var \Illuminate\Filesystem\FilesystemAdapter */ /** @var FilesystemAdapter */
$disk = Storage::disk(); $disk = Storage::disk();
return !$useStorage ? (realpath($value) ?: $value) : $disk->path($value);
return ! $useStorage ? (realpath($value) ?: $value) : $disk->path($value);
} }
/** /**
* Get the file mimetype * Get the file mimetype.
* *
* @param string $value
* @param boolean $useStorage
* @return string|false * @return string|false
*/ */
private static function getUploadedFileMimeType(string $value, bool $useStorage) private static function getUploadedFileMimeType(string $value, bool $useStorage)
{ {
return !$useStorage ? File::mimeType($value) : Storage::mimeType($value); return ! $useStorage ? File::mimeType($value) : Storage::mimeType($value);
} }
/** /**
* Fetch class list from a specific namespace * Fetch class list from a specific namespace.
* *
* @param string $namespace
* @return array<string> An array of classe namespaces * @return array<string> An array of classe namespaces
*/ */
public static function getClassesUsingNamespace(string $namespace): array public static function getClassesUsingNamespace(string $namespace): array
{ {
return collect(File::files(\app_path(Str::of($namespace)->replaceFirst('\App', '')))) return collect(File::files(\app_path(Str::of($namespace)->replaceFirst('\App', '')->value())))
->filter(fn (SplFileInfo $splFileInfo) => $splFileInfo->getExtension() === 'php') ->filter(fn (SplFileInfo $splFileInfo): bool => $splFileInfo->getExtension() === 'php')
->map(fn (SplFileInfo $splFileInfo) => Str::of($splFileInfo->getBasename()) ->map(fn (SplFileInfo $splFileInfo) => Str::of($splFileInfo->getBasename())
->replace('.' . $splFileInfo->getExtension(), '')->prepend("{$namespace}\\"))->all(); ->replace('.' . $splFileInfo->getExtension(), '')->prepend("{$namespace}\\")->value())->all();
} }
/** /**
* Get all classes from a specific namespace that implement a specific interface * Get all classes from a specific namespace that implement a specific interface.
* *
* @param string $namespace
* @param string $interface
* @return array<string> An array of classe namespaces * @return array<string> An array of classe namespaces
*/ */
public static function getClassesUsingInterface(string $namespace, string $interface): array public static function getClassesUsingInterface(string $namespace, string $interface): array
@ -560,17 +514,12 @@ class ToolboxHelper
return Arr::where( return Arr::where(
static::getClassesUsingNamespace($namespace), static::getClassesUsingNamespace($namespace),
fn (string $class_namespace) => \collect(class_implements($class_namespace, true))->filter() fn (string $class_namespace) => \collect(class_implements($class_namespace, true))->filter()
->contains(fn (string $class_interface) => $class_interface === $interface) ->contains(fn (string $class_interface): bool => $class_interface === $interface)
); );
} }
/** /**
* Get validated pagination from request. * Get validated pagination from request.
*
* @param integer $default
* @param string $field
* @param string $enumPath
* @return integer
*/ */
public static function getValidatedEnum(int $default, string $field, string $enumPath): int public static function getValidatedEnum(int $default, string $field, string $enumPath): int
{ {
@ -579,7 +528,7 @@ class ToolboxHelper
[$field => \request()->get($field)], [$field => \request()->get($field)],
[$field => new Enum($enumPath)] [$field => new Enum($enumPath)]
)->validated()[$field]); )->validated()[$field]);
} catch (ValidationException $e) { } catch (ValidationException) {
return $default; return $default;
} }
} }

View file

@ -7,17 +7,8 @@ use LDC\LDCCRM\Models\Customer;
class MesBr class MesBr
{ {
private $endpoint; public function __construct(private $endpoint, private $refCamp, private $refSite)
private $refCamp;
private $refSite;
public function __construct($endpoint, $refCamp, $refSite)
{ {
$this->endpoint = $endpoint;
$this->refCamp = $refCamp;
$this->refSite = $refSite;
} }
public function createCustomer(string $hashcode, string $email, string $country) public function createCustomer(string $hashcode, string $email, string $country)
@ -43,70 +34,63 @@ class MesBr
public function printCoupons(string $hashcode, array $coupons) public function printCoupons(string $hashcode, array $coupons)
{ {
return $this->callApi("GET", '/coupons/print', [ return $this->callApi("GET", '/coupons/print', [
'refcamp' => $this->refCamp, 'refcamp' => $this->refCamp,
'hashcode' => $hashcode, 'hashcode' => $hashcode,
'coupons' => implode(",", $coupons) 'coupons' => implode(",", $coupons)
]); ]);
} }
private function callApi($method, $url, $data = [], $path = '/api/v1') private function callApi(string $method, string $url, array $data = [], string $path = '/api/v1')
{ {
$url = $this->endpoint . $path . $url; $url = $this->endpoint . $path . $url;
$curl = curl_init(); $curl = curl_init();
switch ($method) { switch ($method) {
case "POST": case "POST":
curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POST, true);
if ($data) { if ($data !== []) {
curl_setopt($curl, CURLOPT_POSTFIELDS, $data); curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
} }
break; break;
case "PUT": case "PUT":
case "DELETE": case "DELETE":
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
if ($data) { if ($data !== []) {
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data)); curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
} }
break; break;
default: default:
if ($data) { if ($data !== []) {
$url = sprintf("%s?%s", $url, http_build_query($data)); $url = sprintf("%s?%s", $url, http_build_query($data));
} }
} }
curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_COOKIESESSION, true); curl_setopt($curl, CURLOPT_COOKIESESSION, true);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($curl); $result = curl_exec($curl);
if (!curl_errno($curl)) { if (!curl_errno($curl)) {
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if (!in_array($http_code, [200, 201])) { if (!in_array($http_code, [200, 201])) {
return (object)[ return (object)[
'status' => 'KO', 'status' => 'KO',
'code' => 500, 'code' => 500,
'message' => 'Connection to API failed - Code HTTP : ' . $http_code 'message' => 'Connection to API failed - Code HTTP : ' . $http_code
]; ];
} }
} else { } else {
return (object)[ return (object)[
'status' => 'KO', 'status' => 'KO',
'code' => 500, 'code' => 500,
'message' => 'Connection to API failed : ' . curl_error($curl) 'message' => 'Connection to API failed : ' . curl_error($curl)
]; ];
} }
curl_close($curl);
return json_decode($result); return json_decode($result);
} }
// * Added METHODS // * Added METHODS
/** . /** .
* Update panier * Update panier
* *
* @param \LDC\LDCCRM\Models\Customer $crm_customer * @return array<string,int|string>
* @param string $crm_customer_hashcode
* @param string $ref_site
* @param string $coupon_id
* @param integer $quantity
* @return array<string,string>
* 'status' => 'OK', * 'status' => 'OK',
* 'link' => URL, * 'link' => URL,
* 'verif' => URL, * 'verif' => URL,
@ -120,16 +104,16 @@ class MesBr
int $quantity = 1 int $quantity = 1
): array { ): array {
$res = $this->callAPI("GET", "/updatebasket", [ $res = $this->callAPI("GET", "/updatebasket", [
'refsite' => $ref_site, 'refsite' => $ref_site,
'refcamp' => $this->refCamp, 'refcamp' => $this->refCamp,
'refadherent' => $crm_customer_hashcode, 'refadherent' => $crm_customer_hashcode,
'nom' => $crm_customer->lastname, 'nom' => $crm_customer->lastname,
'prenom' => $crm_customer->firstname, 'prenom' => $crm_customer->firstname,
'email' => $crm_customer->email, 'email' => $crm_customer->email,
'code_postal' => $crm_customer->postcode, 'code_postal' => $crm_customer->postcode,
'commune' => $crm_customer->city, 'commune' => $crm_customer->city,
'pays' => $this->getCountry($crm_customer->country), 'pays' => $this->getCountry($crm_customer->country),
'panier' => "({$quantity}){$coupon_id}" 'panier' => "({$quantity}){$coupon_id}"
], ''); ], '');
if (!$res) { if (!$res) {
@ -148,10 +132,7 @@ class MesBr
/** . /** .
* Update basket * Update basket
* *
* @param \LDC\LDCCRM\Models\Customer $crm_customer * @return array<string,int|string>
* @param string $coupon_id
* @param integer $quantity
* @return array<string,string>
* 'status' => 'OK', * 'status' => 'OK',
* 'ref' => 'Basket REF' * 'ref' => 'Basket REF'
*/ */
@ -161,10 +142,10 @@ class MesBr
int $quantity = 1 int $quantity = 1
): array { ): array {
$res = $this->callAPI("GET", "/customer/basket/update", [ $res = $this->callAPI("GET", "/customer/basket/update", [
'refsite' => $this->refSite, 'refsite' => $this->refSite,
'refcamp' => $this->refCamp, 'refcamp' => $this->refCamp,
'customer_hashcode' => $crm_customer->hashcode, 'customer_hashcode' => $crm_customer->hashcode,
'panier' => "({$quantity}){$coupon_id}" 'panier' => "({$quantity}){$coupon_id}"
]); ]);
if (!$res) { if (!$res) {
@ -181,15 +162,14 @@ class MesBr
/** /**
* Get customer basket * Get customer basket
* *
* @param \LDC\LDCCRM\Models\Customer $crm_customer * @return array<string,int|string>
* @return array
*/ */
public function getBasket( public function getBasket(
Customer $crm_customer, Customer $crm_customer,
): array { ): array {
$res = $this->callApi("GET", '/customer/basket', [ $res = $this->callApi("GET", '/customer/basket', [
'refcamp' => $this->refCamp, 'refcamp' => $this->refCamp,
'hashcode' => $crm_customer->hashcode 'hashcode' => $crm_customer->hashcode
]); ]);
@ -208,15 +188,12 @@ class MesBr
/** /**
* Translate country to MesBr Country * Translate country to MesBr Country
*
* @param \LDC\LDCCRM\Enums\Country $country
* @return inteager
*/ */
private function getCountry(Country $country): int private function getCountry(Country $country): int
{ {
return match ($country) { return match ($country) {
Country::belgium() => 56, Country::belgium => 56,
Country::luxembourg() => 442, Country::luxembourg => 442,
// * France // * France
default => 250 default => 250
}; };

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Mail\Jobs; namespace App\Mail\Jobs;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -17,11 +19,10 @@ class CodeExportSucceed extends Mailable
/** /**
* Create a new message instance. * Create a new message instance.
* *
* @param integer $nbCodeFic Nombre de codes par fichiers. * @param int $nbCodeFic Nombre de codes par fichiers.
* @param integer $nbCodeTot Nombre total de codes à exporter. * @param int $nbCodeTot Nombre total de codes à exporter.
* @param integer $nbCodeLigne Nombre de codes par ligne. * @param int $nbCodeLigne Nombre de codes par ligne.
* @param string $archive_path Nom de l'archive crée. * @param string $archive_path Nom de l'archive crée.
* @return void
*/ */
public function __construct( public function __construct(
protected int $nbCodeFic, protected int $nbCodeFic,
@ -33,10 +34,8 @@ class CodeExportSucceed extends Mailable
/** /**
* Get the message envelope. * Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/ */
public function envelope(): \Illuminate\Mail\Mailables\Envelope public function envelope(): Envelope
{ {
return new Envelope( return new Envelope(
to: [new Address(\config('mail.admin'))], to: [new Address(\config('mail.admin'))],
@ -46,18 +45,16 @@ class CodeExportSucceed extends Mailable
/** /**
* Get the message content definition. * Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/ */
public function content(): \Illuminate\Mail\Mailables\Content public function content(): Content
{ {
return new Content( return new Content(
markdown: 'mails.jobs.code-unique-export-succeed', markdown: 'mails.jobs.code-unique-export-succeed',
with: [ with: [
'archive_path' => $this->archive_path, 'archive_path' => $this->archive_path,
'nbCodeFic' => $this->nbCodeFic, 'nbCodeFic' => $this->nbCodeFic,
'nbCodeTot' => $this->nbCodeTot, 'nbCodeTot' => $this->nbCodeTot,
'nbCodeLigne' => $this->nbCodeLigne 'nbCodeLigne' => $this->nbCodeLigne,
], ],
); );
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Mail\Jobs; namespace App\Mail\Jobs;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -17,9 +19,8 @@ class CodeGenerationSucceed extends Mailable
/** /**
* Create a new job instance. * Create a new job instance.
* *
* @param integer $amount Amount of code to generate. * @param int $amount Amount of code to generate.
* @param string $gameName The game name. * @param string $gameName The game name.
* @return void
*/ */
public function __construct(private int $amount, private string $gameName) public function __construct(private int $amount, private string $gameName)
{ {
@ -27,10 +28,8 @@ class CodeGenerationSucceed extends Mailable
/** /**
* Get the message envelope. * Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/ */
public function envelope(): \Illuminate\Mail\Mailables\Envelope public function envelope(): Envelope
{ {
return new Envelope( return new Envelope(
to: [new Address(\config('mail.admin'))], to: [new Address(\config('mail.admin'))],
@ -40,16 +39,14 @@ class CodeGenerationSucceed extends Mailable
/** /**
* Get the message content definition. * Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/ */
public function content(): \Illuminate\Mail\Mailables\Content public function content(): Content
{ {
return new Content( return new Content(
markdown: 'mails.jobs.code-generation-succeed', markdown: 'mails.jobs.code-generation-succeed',
with: [ with: [
'quantity' => $this->amount, 'quantity' => $this->amount,
'gameName' => $this->gameName 'gameName' => $this->gameName,
], ],
); );
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Mail\Jobs; namespace App\Mail\Jobs;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
@ -16,10 +18,6 @@ class JobFailed extends Mailable
/** /**
* Create a new message instance. * Create a new message instance.
*
* @param string $job
* @param array $exception
* @return void
*/ */
public function __construct(public string $job, public array $exception) public function __construct(public string $job, public array $exception)
{ {
@ -27,10 +25,8 @@ class JobFailed extends Mailable
/** /**
* Get the message envelope. * Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/ */
public function envelope(): \Illuminate\Mail\Mailables\Envelope public function envelope(): Envelope
{ {
return new Envelope( return new Envelope(
to: [new Address(\config('mail.admin'))], to: [new Address(\config('mail.admin'))],
@ -40,16 +36,14 @@ class JobFailed extends Mailable
/** /**
* Get the message content definition. * Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/ */
public function content(): \Illuminate\Mail\Mailables\Content public function content(): Content
{ {
return new Content( return new Content(
markdown: 'mails.jobs.failure', markdown: 'mails.jobs.failure',
with: [ with: [
'job' => $this->job, 'job' => $this->job,
'exception' => $this->exception 'exception' => $this->exception,
], ],
); );
} }

View file

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types=1);
namespace App\Mail; namespace App\Mail;
use App\Models\Settings; use App\Models\Settings;
@ -14,24 +16,12 @@ class PasswordReset extends Mailable implements ShouldQueue
use Queueable; use Queueable;
use SerializesModels; use SerializesModels;
/** @var \App\Models\User */
protected $user;
/** @var \App\Models\Settings */
protected $settings;
/** /**
* Create a new message instance. * Create a new message instance.
* *
* @param \App\Models\User $user
* @param \App\Models\Settings $settings
*
* @return void
*/ */
public function __construct(User $user, Settings $settings) public function __construct(protected User $user, protected Settings $settings)
{ {
$this->user = $user;
$this->settings = $settings;
} }
/** /**
@ -41,8 +31,11 @@ class PasswordReset extends Mailable implements ShouldQueue
*/ */
public function build() public function build()
{ {
/** @var view-string $view */
$view = 'front.basic.password-email';
return $this->subject('Changement de votre mot de passe') return $this->subject('Changement de votre mot de passe')
->view('front.basic.password-email') ->view($view)
->with(['user' => $this->user, 'settings' => $this->settings]); ->with(['user' => $this->user, 'settings' => $this->settings]);
} }
} }

View file

@ -1,9 +1,10 @@
<?php <?php
declare(strict_types=1);
namespace App\Mail\Won; namespace App\Mail\Won;
use App\Lib\Discount; use App\Lib\Discount;
use App\Models\Settings;
use Illuminate\Bus\Queueable; use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable; use Illuminate\Mail\Mailable;
@ -21,13 +22,11 @@ class DiscountWonMail extends Mailable implements ShouldQueue
/** /**
* Create a new message instance. * Create a new message instance.
* *
* @param \Illuminate\Mail\Mailables\Address $recipient The winner. * @param Address $recipient The winner.
* @param string $game_name The game name. * @param string $game_name The game name.
* @param \App\Lib\Discount $discount Amount of the won discount in cents. * @param Discount $discount Amount of the won discount in cents.
* @param string $print_url Url to print the discount. * @param string $print_url Url to print the discount.
* @param string $company The company name. * @param string $company The company name.
*
* @return void
*/ */
public function __construct( public function __construct(
protected Address $recipient, protected Address $recipient,
@ -35,40 +34,41 @@ class DiscountWonMail extends Mailable implements ShouldQueue
protected Discount $discount, protected Discount $discount,
protected string $print_url, protected string $print_url,
protected string $company protected string $company
) {} ) {
}
/** /**
* Get the message envelope. * Get the message envelope.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/ */
public function envelope(): \Illuminate\Mail\Mailables\Envelope public function envelope(): Envelope
{ {
return new Envelope( return new Envelope(
to: [$this->recipient], to: [$this->recipient],
subject: trans('front.mails.subjects.game_winning', [ subject: trans('front.mails.subjects.game_winning', [
'game_name' => $this->game_name 'game_name' => $this->game_name,
]), ]),
); );
} }
/** /**
* Get the message content definition. * Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/ */
public function content(): \Illuminate\Mail\Mailables\Content public function content(): Content
{ {
// * Fix for async usage // * Fix for async usage
View::addNamespace('theme', resource_path('theme/mounted/views')); View::addNamespace('theme', resource_path('theme/mounted/views'));
/** @var view-string $view */
$view = 'theme::mails.won-discount';
return new Content( return new Content(
markdown: 'theme::mails.won-discount', markdown: $view,
with: [ with: [
'first_name' => $this->recipient->name ?? $this->recipient->address, 'first_name' => $this->recipient->name ?? $this->recipient->address,
'game_name' => $this->game_name, 'game_name' => $this->game_name,
'discount' => $this->discount, 'discount' => $this->discount,
'print_url' => $this->print_url, 'print_url' => $this->print_url,
'company' => $this->company, 'company' => $this->company,
], ],
); );
} }

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