Compare commits
85 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
317577fb9b | ||
|
|
2a2490b302 | ||
|
|
53524b96d0 | ||
|
|
e7fe9d920b | ||
|
|
6e678b88c9 | ||
|
|
9c5cef6d99 | ||
|
|
0ab895e7ac | ||
|
|
06301a44fa | ||
|
|
4274b21eea | ||
|
|
dec444160b | ||
|
|
4dfa2d0412 | ||
|
|
a230697de0 | ||
|
|
2a9e109855 | ||
|
|
e19964fc89 | ||
|
|
edad0b4279 | ||
|
|
d70135b6af | ||
|
|
adae4da02f | ||
|
|
2f7289f531 | ||
|
|
6b85d63f5e | ||
|
|
5c5b34f33e | ||
|
|
ca47520a74 | ||
|
|
af3a63e36f | ||
|
|
21cc60457d | ||
|
|
36703bc9ac | ||
|
|
8a99cf98fc | ||
|
|
a4ed3dbb0f | ||
|
|
dae0c6271a | ||
|
|
98762d68a8 | ||
|
|
c450d71bed | ||
|
|
99f7545595 | ||
|
|
a0ddeda5a9 | ||
|
|
771978412a | ||
|
|
f43ea7c9ff | ||
|
|
c88583d727 | ||
|
|
6aab7d4eff | ||
|
|
0681bf8f0a | ||
|
|
923c3e0f6a | ||
|
|
8e48fc5ea1 | ||
|
|
615263f233 | ||
|
|
16f4af41fd | ||
|
|
d51f78a8db | ||
|
|
25b2e42f14 | ||
|
|
b9f51a1a5d | ||
|
|
e381de5e9c | ||
|
|
042e78583e | ||
|
|
e6796029ec | ||
|
|
39fc13dfe2 | ||
|
|
686bf307a2 | ||
|
|
549f874abd | ||
|
|
17ac0aaa5c | ||
|
|
f5367815e1 | ||
|
|
0d1f135616 | ||
|
|
817ffa68fc | ||
|
|
a31512216c | ||
|
|
cc24e547d2 | ||
|
|
16fe3773a9 | ||
|
|
8ce9d5734b | ||
|
|
6de828bc31 | ||
|
|
fe3eb7a955 | ||
|
|
c0bce88e75 | ||
|
|
61e900ee32 | ||
|
|
f44d9d7849 | ||
|
|
ecaec2823f | ||
|
|
22b598a11a | ||
|
|
d9c132ad25 | ||
|
|
634dbe832e | ||
|
|
f539cdd99a | ||
|
|
52dda4e346 | ||
|
|
8a3058e7bc | ||
|
|
97623ce240 | ||
|
|
7e8c4e7efd | ||
|
|
151e24d775 | ||
|
|
254a14a3f8 | ||
|
|
b600a890aa | ||
|
|
5d7c58f4cf | ||
|
|
91b8732434 | ||
|
|
0cc60c62d4 | ||
|
|
ae78c6d1fe | ||
|
|
95c9dd10d5 | ||
|
|
f7f14c6283 | ||
|
|
d454c83fec | ||
|
|
c9d988e64b | ||
|
|
00c809b8a0 | ||
|
|
c3831006e0 | ||
|
|
73412cd632 |
272 changed files with 37858 additions and 0 deletions
21
.editorconfig
Normal file
21
.editorconfig
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
indent_size = 4
|
||||
|
||||
[*.vue]
|
||||
indent_size = 2
|
||||
58
.env.example
Normal file
58
.env.example
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=mailpit
|
||||
MAIL_PORT=1025
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_HOST=
|
||||
PUSHER_PORT=443
|
||||
PUSHER_SCHEME=https
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
VITE_PUSHER_HOST="${PUSHER_HOST}"
|
||||
VITE_PUSHER_PORT="${PUSHER_PORT}"
|
||||
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
|
||||
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
55
.eslintrc.json
Normal file
55
.eslintrc.json
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:vue/vue3-strongly-recommended",
|
||||
"@vue/eslint-config-typescript"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.vue"],
|
||||
"rules": {
|
||||
"no-undef": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.vue"],
|
||||
"rules": {
|
||||
"indent": "off"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parser": "vue-eslint-parser",
|
||||
"parserOptions": {
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["vue", "@typescript-eslint"],
|
||||
"ignorePatterns": ["webpack.mix.js", "**/vendor/*.js", "**/vendor/**/*.js"],
|
||||
"rules": {
|
||||
"indent": ["error", 4, { "SwitchCase": 1 }],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double"],
|
||||
"semi": ["error", "always"],
|
||||
"vue/script-indent": ["error", 2, { "baseIndent": 0, "switchCase": 1 }],
|
||||
"vue/one-component-per-file": "off"
|
||||
},
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
"extensions": [".js", ".vue", ".ts", ".d.ts"]
|
||||
},
|
||||
"alias": {
|
||||
"extensions": [".vue", ".js", ".ts", ".scss", ".d.ts"],
|
||||
"map": [
|
||||
["~", "./resources/assets"],
|
||||
["@", "./node_modules"]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
* text=auto eol=lf
|
||||
|
||||
*.blade.php diff=html
|
||||
*.css diff=css
|
||||
*.html diff=html
|
||||
*.md diff=markdown
|
||||
*.php diff=php
|
||||
|
||||
/.github export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
.styleci.yml export-ignore
|
||||
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/.phpunit.cache
|
||||
/node_modules
|
||||
/public/build
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/vendor
|
||||
.env
|
||||
.env.backup
|
||||
.env.production
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
auth.json
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
.phpunit.result.cache
|
||||
/.fleet
|
||||
/.idea
|
||||
/.vscode
|
||||
.stylelintcache
|
||||
6
.gitlab/issue_templates/Bug.md
Normal file
6
.gitlab/issue_templates/Bug.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
## Comportement constaté
|
||||
|
||||
## Comportement attendu
|
||||
|
||||
## Solution envisagée## Étapes pour la reproduction du bug
|
||||
* step
|
||||
5
.gitlab/issue_templates/Feature.md
Normal file
5
.gitlab/issue_templates/Feature.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
## Présentation du besoin
|
||||
|
||||
## Implémentation
|
||||
|
||||
## Maquettes, documentations...
|
||||
41
.stylelintrc.json
Normal file
41
.stylelintrc.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"extends": [
|
||||
"stylelint-config-standard-scss",
|
||||
"stylelint-config-sass-guidelines",
|
||||
"stylelint-config-idiomatic-order"
|
||||
],
|
||||
"plugins": ["stylelint-scss"],
|
||||
"files": ["**/*.css", "**/*.scss"],
|
||||
"ignoreFiles": [
|
||||
"**/vendor/*.css",
|
||||
"**/vendor/**/*.css",
|
||||
"**/vendor/*.scss",
|
||||
"**/vendor/**/*.scss",
|
||||
"**/vendor/*.sass",
|
||||
"**/vendor/**/*.sass",
|
||||
"**/vendor/*.less",
|
||||
"**/vendor/**/*.less",
|
||||
"**/vendor/*.vue",
|
||||
"**/vendor/**/*.vue"
|
||||
],
|
||||
"customSyntax": "postcss-scss",
|
||||
"rules": {
|
||||
"indentation": 4,
|
||||
"string-quotes": "double",
|
||||
"selector-max-id": null,
|
||||
"color-no-invalid-hex": true,
|
||||
"order/properties-alphabetical-order": null,
|
||||
"max-nesting-depth": 5,
|
||||
"selector-max-compound-selectors": 5,
|
||||
"scss/double-slash-comment-empty-line-before": [
|
||||
"always",
|
||||
{
|
||||
"ignore": [
|
||||
"between-comments",
|
||||
"stylelint-commands",
|
||||
"inside-block"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
22
.vscode/settings.json
vendored
Normal file
22
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#90431a",
|
||||
"activityBar.background": "#90431a",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#1da24b",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"sash.hoverBorder": "#90431a",
|
||||
"statusBar.background": "#652f12",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#90431a",
|
||||
"statusBarItem.remoteBackground": "#652f12",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#652f12",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#652f1299",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
"peacock.remoteColor": "#652f12"
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# StartCoQ
|
||||
|
||||
## Installation
|
||||
```
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
php artisan storage:link
|
||||
php artisan migrate [--seed]
|
||||
```
|
||||
31
app/Console/Kernel.php
Normal file
31
app/Console/Kernel.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands(): void
|
||||
{
|
||||
$this->load(__DIR__ . '/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
41
app/Enums/Ingredients/FamilyEnum.php
Normal file
41
app/Enums/Ingredients/FamilyEnum.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Ingredients;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum FamilyEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case alcool = 1;
|
||||
case flavor = 2;
|
||||
case naturalCasings = 3;
|
||||
case groceryStore = 4;
|
||||
case spicesHerbs = 5;
|
||||
case flourBreadcrumbs = 6;
|
||||
case fruitsVegetables = 7;
|
||||
case aromaticMixMarinade = 8;
|
||||
case functionalMixSimpleIngredients = 9;
|
||||
case meatProducts = 10;
|
||||
case sauces = 11;
|
||||
case dairyProducts = 12;
|
||||
|
||||
/**
|
||||
* Custom added families definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::alcool->name => 'Alcool',
|
||||
self::flavor->name => 'Arôme',
|
||||
self::naturalCasings->name => 'Boyaux Naturels',
|
||||
self::groceryStore->name => 'Épicerie',
|
||||
self::spicesHerbs->name => 'Épices et aromates',
|
||||
self::flourBreadcrumbs->name => 'Farine / Chapelure',
|
||||
self::fruitsVegetables->name => 'Fruits et légumes',
|
||||
self::aromaticMixMarinade->name => 'Mix aromatique / Marinade',
|
||||
self::functionalMixSimpleIngredients->name => 'Mix fonctionnel / ingredients simples',
|
||||
self::meatProducts->name => 'Produits carnés',
|
||||
self::sauces->name => 'Sauces',
|
||||
self::dairyProducts->name => 'Produits laitiers',
|
||||
];
|
||||
}
|
||||
43
app/Enums/Ingredients/FunctionEnum.php
Normal file
43
app/Enums/Ingredients/FunctionEnum.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Ingredients;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum FunctionEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case dye = 1;
|
||||
case antiOxidant = 2;
|
||||
case starch = 3;
|
||||
case curator = 4;
|
||||
case freezingAgent = 5;
|
||||
case sweetener = 6;
|
||||
case stabilizer = 7;
|
||||
case emulsifier = 8;
|
||||
case thickener = 9;
|
||||
case sugar = 10;
|
||||
case salt = 11;
|
||||
case fiber = 12;
|
||||
case protein = 13;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::dye->name => 'Colorant',
|
||||
self::antiOxidant => 'Anti-oxidant',
|
||||
self::starch->name => 'Amidon',
|
||||
self::curator->name => 'Conservateur',
|
||||
self::freezingAgent->name => 'Gelifiant',
|
||||
self::sweetener->name => 'Edulcorant',
|
||||
self::stabilizer->name => 'Stabilisateur',
|
||||
self::emulsifier->name => 'Emulsifiant',
|
||||
self::thickener->name => 'Epaississant',
|
||||
self::sugar->name => 'Sucre',
|
||||
self::salt->name => 'Sel',
|
||||
self::fiber->name => 'Fibre',
|
||||
self::protein->name => 'Protéine',
|
||||
];
|
||||
}
|
||||
27
app/Enums/Ingredients/OriginEnum.php
Normal file
27
app/Enums/Ingredients/OriginEnum.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Ingredients;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum OriginEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case magazine = 1;
|
||||
case photo = 2;
|
||||
case web = 3;
|
||||
case mail = 4;
|
||||
case salon = 5;
|
||||
|
||||
/**
|
||||
* Custom added origins->name definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::magazine->name => 'Magazine',
|
||||
self::photo->name => 'Photo de rayon',
|
||||
self::web->name => 'Web',
|
||||
self::mail->name => 'Fournisseur/Mail',
|
||||
self::salon->name => 'Salon'
|
||||
];
|
||||
}
|
||||
25
app/Enums/Ingredients/PackagingEnum.php
Normal file
25
app/Enums/Ingredients/PackagingEnum.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Ingredients;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum PackagingEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case fees = 1;
|
||||
case pasteurizedSterilized = 2;
|
||||
case frozen = 3;
|
||||
case bagDehydrated = 4;
|
||||
|
||||
/**
|
||||
* Custom added packagings definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::fees->name => 'Frais',
|
||||
self::pasteurizedSterilized->name => 'Pasteurisé / Stérilisé',
|
||||
self::frozen->name => 'Surgelé',
|
||||
self::bagDehydrated->name => 'Sac / déshydraté',
|
||||
];
|
||||
}
|
||||
43
app/Enums/KeywordCategoryEnum.php
Normal file
43
app/Enums/KeywordCategoryEnum.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum KeywordCategoryEnum : string
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case ingredientPleasure = "ingredient_pleasure";
|
||||
case ingredientPracticality = "ingredient_practicality";
|
||||
case ingredientHealth = "ingredient_health";
|
||||
case ingredienEthics = "ingredient_ethics";
|
||||
case productPleasure = "product_pleasure";
|
||||
case productShape = "product_shape";
|
||||
case productPracticality = "product_practicality";
|
||||
case productHealth = "product_health";
|
||||
case productEthics = "product_ethics";
|
||||
case packagingPleasure = "packaging_pleasure";
|
||||
case packagingPracticality = "packaging_practicality";
|
||||
case packagingEthics = "packaging_ethics";
|
||||
case packagingDesign = "packaging_design";
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::ingredientPleasure->name => 'IngredientPleasure',
|
||||
self::ingredientPracticality->name => 'IngredientPracticality',
|
||||
self::ingredientHealth->name => 'IngredientHealth',
|
||||
self::ingredienEthics->name => 'IngredientEthics',
|
||||
self::productPleasure->name => 'ProductPleasure',
|
||||
self::productShape->name => 'ProductShape',
|
||||
self::productPracticality->name => 'ProductPracticality',
|
||||
self::productHealth->name => 'ProductHealth',
|
||||
self::productEthics->name => 'ProductEthics',
|
||||
self::packagingPleasure->name => 'PackagingPleasure',
|
||||
self::packagingPracticality->name => 'PackagingPracticality',
|
||||
self::packagingEthics->name => 'PackagingEthics',
|
||||
self::packagingDesign->name => 'PackagingDesign',
|
||||
];
|
||||
}
|
||||
37
app/Enums/Packaging/BenefitEnum.php
Normal file
37
app/Enums/Packaging/BenefitEnum.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Packaging;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum BenefitEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case resistance = 1;
|
||||
case recyclability = 2;
|
||||
case presentation = 3;
|
||||
case openingClosing = 4;
|
||||
case toaster = 5;
|
||||
case traditionalOven = 6;
|
||||
case microwaveOven = 7;
|
||||
case communication = 8;
|
||||
case barbecue = 9;
|
||||
case others = 10;
|
||||
|
||||
/**
|
||||
* Custom added benefits definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::resistance->name => 'Résistance',
|
||||
self::recyclability->name => 'Recyclabilité',
|
||||
self::presentation->name => 'Présentation',
|
||||
self::openingClosing->name => 'Ouverture/Fermeture',
|
||||
self::toaster->name => 'Grille pain',
|
||||
self::traditionalOven->name => 'Four Traditionnel',
|
||||
self::microwaveOven->name => 'Four micro-onde',
|
||||
self::communication->name => 'Communication',
|
||||
self::barbecue->name => 'Barbecue',
|
||||
self::others->name => 'Autres'
|
||||
];
|
||||
}
|
||||
35
app/Enums/Packaging/MaterialEnum.php
Normal file
35
app/Enums/Packaging/MaterialEnum.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Packaging;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum MaterialEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case plactic = 1;
|
||||
case paper = 2;
|
||||
case wood = 3;
|
||||
case aluminium = 4;
|
||||
case inox = 5;
|
||||
case glass = 6;
|
||||
case ceramic = 7;
|
||||
case materials = 8;
|
||||
case others = 9;
|
||||
|
||||
/**
|
||||
* Custom added materials definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::plactic->name => 'Plastique',
|
||||
self::paper->name => 'Carton/Papier',
|
||||
self::wood->name => 'Bois/Osier',
|
||||
self::aluminium->name => 'Aluminium',
|
||||
self::inox->name => 'Acier/Inox',
|
||||
self::glass->name => 'Verre',
|
||||
self::ceramic->name => 'Céramique/Terre cuite/Grès',
|
||||
self::materials->name => 'Multi Matériaux',
|
||||
self::others->name => 'Autres'
|
||||
];
|
||||
}
|
||||
27
app/Enums/Packaging/OriginEnum.php
Normal file
27
app/Enums/Packaging/OriginEnum.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Packaging;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum OriginEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case magazine = 1;
|
||||
case photo = 2;
|
||||
case web = 3;
|
||||
case mail = 4;
|
||||
case salon = 5;
|
||||
|
||||
/**
|
||||
* Custom added origins definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::magazine->name => 'Magazine',
|
||||
self::photo->name => 'Photo de rayon',
|
||||
self::web->name => 'Web',
|
||||
self::mail->name => 'Fournisseur/Mail',
|
||||
self::salon->name => 'Salon'
|
||||
];
|
||||
}
|
||||
37
app/Enums/Packaging/TypeEnum.php
Normal file
37
app/Enums/Packaging/TypeEnum.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Packaging;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum TypeEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case box = 1;
|
||||
case bag = 2;
|
||||
case jar = 3;
|
||||
case bottle = 4;
|
||||
case film = 5;
|
||||
case sleeves = 6;
|
||||
case blister = 7;
|
||||
case sheathCase = 8;
|
||||
case tag = 9;
|
||||
case others = 10;
|
||||
|
||||
/**
|
||||
* Custom added types definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::box->name => 'Boîte/Barquette/Bol',
|
||||
self::bag->name => 'Sac/Sachet',
|
||||
self::jar->name => 'Pot',
|
||||
self::bottle->name => 'Bouteille/Gobelet/Flacon',
|
||||
self::film->name => 'Film',
|
||||
self::sleeves->name => 'Sleeves',
|
||||
self::blister->name => 'Tube/Blister/Feuille',
|
||||
self::sheathCase->name => 'Étui fourreau',
|
||||
self::tag->name => 'Étiquettes',
|
||||
self::others->name => 'Autres'
|
||||
];
|
||||
}
|
||||
21
app/Enums/Packaging/WatchTypeEnum.php
Normal file
21
app/Enums/Packaging/WatchTypeEnum.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Packaging;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum WatchTypeEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case product = 1;
|
||||
case packaging = 2;
|
||||
|
||||
/**
|
||||
* Custom added watch types definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::product->name => 'Produit Fini',
|
||||
self::packaging->name => 'Emballage',
|
||||
];
|
||||
}
|
||||
16
app/Enums/Pagination/ItemsPerPaginationEnum.php
Normal file
16
app/Enums/Pagination/ItemsPerPaginationEnum.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Pagination;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum ItemsPerPaginationEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case five = 5;
|
||||
case twelve = 12;
|
||||
case twentyfive = 25;
|
||||
case fifty = 50;
|
||||
case onehundred = 100;
|
||||
}
|
||||
31
app/Enums/Process/BenefitEnum.php
Normal file
31
app/Enums/Process/BenefitEnum.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Process;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum BenefitEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case rateGain = 1;
|
||||
case capacityGain = 2;
|
||||
case efficiencyGain = 3;
|
||||
case laborGain = 4;
|
||||
case packagingGain = 5;
|
||||
case energyGain = 6;
|
||||
case others = 7;
|
||||
|
||||
/**
|
||||
* Custom added benefits definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::rateGain->name => 'Gain Cadence',
|
||||
self::capacityGain->name => 'Gain capacité',
|
||||
self::efficiencyGain->name => 'Gain rendement',
|
||||
self::laborGain->name => 'Gain main d\'oeuvre',
|
||||
self::packagingGain->name => 'Conso Emballage',
|
||||
self::energyGain->name => 'Conso Energie',
|
||||
self::others->name => 'Autres'
|
||||
];
|
||||
}
|
||||
31
app/Enums/Process/MeatPreparationEnum.php
Normal file
31
app/Enums/Process/MeatPreparationEnum.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Process;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum MeatPreparationEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case cutting = 1;
|
||||
case mixture = 2;
|
||||
case grinding = 3;
|
||||
case nibbling = 4;
|
||||
case churning = 5;
|
||||
case injection = 6;
|
||||
case others = 7;
|
||||
|
||||
/**
|
||||
* Custom added meat preparations definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::cutting->name => 'Cutterage',
|
||||
self::mixture->name => 'Mélange',
|
||||
self::grinding->name => 'Broyage',
|
||||
self::nibbling->name => 'Grignotage',
|
||||
self::churning->name => 'Barattage',
|
||||
self::injection->name => 'Injection',
|
||||
self::others->name => 'Autres'
|
||||
];
|
||||
}
|
||||
27
app/Enums/Process/OriginEnum.php
Normal file
27
app/Enums/Process/OriginEnum.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Process;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum OriginEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case magazine = 1;
|
||||
case photo = 2;
|
||||
case web = 3;
|
||||
case mail = 4;
|
||||
case salon = 5;
|
||||
|
||||
/**
|
||||
* Custom added origins definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::magazine->name => 'Magazine',
|
||||
self::photo->name => 'Photo de rayon',
|
||||
self::web->name => 'Web',
|
||||
self::mail->name => 'Fournisseur/Mail',
|
||||
self::salon->name => 'Salon'
|
||||
];
|
||||
}
|
||||
29
app/Enums/Process/PrimaryPackingEnum.php
Normal file
29
app/Enums/Process/PrimaryPackingEnum.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Process;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum PrimaryPackingEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case sealing = 1;
|
||||
case thermoforming = 2;
|
||||
case verticalBagging = 3;
|
||||
case horizontalBagging = 4;
|
||||
case skin = 5;
|
||||
case others = 6;
|
||||
|
||||
/**
|
||||
* Custom added primary packagings definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::sealing->name => 'Operculage',
|
||||
self::thermoforming->name => 'Thermoformage',
|
||||
self::verticalBagging->name => 'Ensachage Vertical',
|
||||
self::horizontalBagging->name => 'Ensachage Horizontal',
|
||||
self::skin->name => 'Skin',
|
||||
self::others->name => 'Autres'
|
||||
];
|
||||
}
|
||||
49
app/Enums/Process/TypeEnum.php
Normal file
49
app/Enums/Process/TypeEnum.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Process;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum TypeEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case slaughter = 1;
|
||||
case beef = 2;
|
||||
case slicing = 3;
|
||||
case cook = 4;
|
||||
case freezing = 5;
|
||||
case forming = 6;
|
||||
case mainPackaging = 7;
|
||||
case secondPackaging = 8;
|
||||
case calibring = 9;
|
||||
case inspection = 10;
|
||||
case identification = 11;
|
||||
case labeling = 12;
|
||||
case conveyance = 13;
|
||||
case logistics = 14;
|
||||
case robotization = 15;
|
||||
case others = 16;
|
||||
|
||||
/**
|
||||
* Custom added types definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::slaughter->name => 'Abbattage/Découpe',
|
||||
self::beef->name => 'Préparation de viande',
|
||||
self::slicing->name => 'Tranchage/Émincage',
|
||||
self::cook->name => 'Cuisson',
|
||||
self::freezing->name => 'Refroidissement/Surgélation',
|
||||
self::forming->name => 'Formage/Embossage/Portionnage',
|
||||
self::mainPackaging->name => 'Conditionnement primaire',
|
||||
self::secondPackaging->name => 'Conditionnement secondaire',
|
||||
self::calibring->name => 'Pesage/Calibrage',
|
||||
self::inspection->name => 'Contrôles/Inspection',
|
||||
self::identification->name => 'Identification (Marqueurs/Jets d\'encres',
|
||||
self::labeling->name => 'Étiquetage',
|
||||
self::conveyance->name => 'Convoyage/Transitique',
|
||||
self::logistics->name => 'Logistiques',
|
||||
self::robotization->name => 'Robotisation',
|
||||
self::others->name => 'Autres'
|
||||
];
|
||||
}
|
||||
23
app/Enums/Process/WatchTypeEnum.php
Normal file
23
app/Enums/Process/WatchTypeEnum.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Process;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum WatchTypeEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case product = 1;
|
||||
case packaging = 2;
|
||||
case process = 3;
|
||||
|
||||
/**
|
||||
* Custom added watch types definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::product->name => 'Produit Fini',
|
||||
self::packaging->name => 'Emballage',
|
||||
self::process->name => 'Process',
|
||||
];
|
||||
}
|
||||
29
app/Enums/Products/CertificationEnum.php
Normal file
29
app/Enums/Products/CertificationEnum.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum CertificationEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case bioAB = 1;
|
||||
case labelRouge = 2;
|
||||
case certifiedCQ = 3;
|
||||
case standard = 4;
|
||||
case freeRange = 6;
|
||||
case others = 5;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::bioAB->name => 'Bio AB ou Européen',
|
||||
self::labelRouge->name => 'Label Rouge',
|
||||
self::certifiedCQ->name => 'Certifié CQ',
|
||||
self::standard->name => 'Standard',
|
||||
self::freeRange->name => 'Free Range',
|
||||
self::others->name => 'Autres',
|
||||
];
|
||||
}
|
||||
40
app/Enums/Products/ClaimEnum.php
Normal file
40
app/Enums/Products/ClaimEnum.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum ClaimEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case productOfYear = 1;
|
||||
case omega3 = 2;
|
||||
case withoutOgm = 3;
|
||||
case withoutGluten = 4;
|
||||
case withoutAdditives = 5;
|
||||
case withoutAntibiotics = 6;
|
||||
case vegetarian = 7;
|
||||
case lightweight = 8;
|
||||
case halal = 9;
|
||||
case highVitamins = 10;
|
||||
case others = 99;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::productOfYear->name => 'Produit de l\'année',
|
||||
self::omega3->name => 'Bleu Blanc Coeur, source d\'Omega 3',
|
||||
self::withoutOgm->name => 'Sans OGM',
|
||||
self::withoutGluten->name => 'Sans gluten',
|
||||
self::withoutAdditives->name => 'Sans additif, sans colorant, sans conservateur,
|
||||
100% simple, sans polyphosphate, sans sel nitrité',
|
||||
self::withoutAntibiotics->name => 'Sans Antibiotique',
|
||||
self::vegetarian->name => 'Vegan',
|
||||
self::lightweight->name => 'Allégé',
|
||||
self::halal->name => 'Halal, Casher',
|
||||
self::highVitamins->name => 'Riche en protéines, fibres, calcium…',
|
||||
self::others->name => 'Autres',
|
||||
];
|
||||
}
|
||||
25
app/Enums/Products/DistributionNetworkEnum.php
Normal file
25
app/Enums/Products/DistributionNetworkEnum.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum DistributionNetworkEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case gms = 1;
|
||||
case freezer = 2;
|
||||
case restauration = 3;
|
||||
case product = 4;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::gms->name => 'GMS / Hard discount / Commerce de détail',
|
||||
self::freezer->name => 'Home service/ Freezer Center',
|
||||
self::restauration->name => 'Restauration',
|
||||
self::product->name => 'Produit pour industrie agro-alimentaire',
|
||||
];
|
||||
}
|
||||
29
app/Enums/Products/GeoAreaEnum.php
Normal file
29
app/Enums/Products/GeoAreaEnum.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum GeoAreaEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case french = 1;
|
||||
case european = 2;
|
||||
case asia = 3;
|
||||
case america = 4;
|
||||
case africa = 5;
|
||||
case oceania = 6;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::french->name => 'France',
|
||||
self::european->name => 'Europe',
|
||||
self::asia->name => 'Asie',
|
||||
self::america->name => 'Amérique',
|
||||
self::africa->name => 'Afrique',
|
||||
self::oceania->name => 'Océanie',
|
||||
];
|
||||
}
|
||||
31
app/Enums/Products/OriginEnum.php
Normal file
31
app/Enums/Products/OriginEnum.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum OriginEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case frenchPreparation = 1;
|
||||
case frenchProduct = 2;
|
||||
case igp = 3;
|
||||
case regionalOrigins = 4;
|
||||
case aoc = 5;
|
||||
case ue = 6;
|
||||
case others = 99;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::frenchPreparation->name => 'Né, élevé et préparé en France',
|
||||
self::frenchProduct->name => 'Produit en France',
|
||||
self::igp->name => 'IGP',
|
||||
self::regionalOrigins->name => 'Origines régionales',
|
||||
self::aoc->name => 'AOC',
|
||||
self::ue->name => 'UE',
|
||||
self::others->name => 'Autres',
|
||||
];
|
||||
}
|
||||
43
app/Enums/Products/RangeEnum.php
Normal file
43
app/Enums/Products/RangeEnum.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum RangeEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case crus = 1;
|
||||
case elaboratedRaw = 2;
|
||||
case culinaryAids = 3;
|
||||
case cooked = 4;
|
||||
case smoked = 5;
|
||||
case cookingSauce = 6;
|
||||
case breaded = 7;
|
||||
case rawMeat = 8;
|
||||
case cookedMeats = 9;
|
||||
case farcis = 10;
|
||||
case plant = 12;
|
||||
case frozenSpecific = 13;
|
||||
case others = 11;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::crus->name => 'Crus',
|
||||
self::elaboratedRaw->name => 'Élaborés crus',
|
||||
self::culinaryAids->name => 'Aides culinaires',
|
||||
self::cooked->name => 'Cuits',
|
||||
self::smoked->name => 'Fumés, confits ou séchés',
|
||||
self::cookingSauce->name => 'Cuisine en sauce',
|
||||
self::breaded->name => 'Panés',
|
||||
self::rawMeat->name => 'Charcuterie crue',
|
||||
self::cookedMeats->name => 'Charcuterie cuite',
|
||||
self::farcis->name => 'Farcis',
|
||||
self::plant->name => 'Végétal',
|
||||
self::frozenSpecific->name => 'Spécifique surgelé',
|
||||
self::others->name => 'Autres',
|
||||
];
|
||||
}
|
||||
35
app/Enums/Products/RayEnum.php
Normal file
35
app/Enums/Products/RayEnum.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum RayEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case butchery = 1;
|
||||
case poultry = 2;
|
||||
case freshlyPackaged = 3;
|
||||
case roastery = 4;
|
||||
case caterer = 5;
|
||||
case creamery = 7;
|
||||
case groceryStore = 8;
|
||||
case frozen = 9;
|
||||
case others = 10;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::butchery->name => 'Boucherie',
|
||||
self::poultry->name => 'Volaille',
|
||||
self::freshlyPackaged->name => 'Frais emballé',
|
||||
self::roastery->name => 'Rôtisserie',
|
||||
self::caterer->name => 'Traiteur',
|
||||
self::creamery->name => 'Crémerie',
|
||||
self::groceryStore->name => 'Épicerie',
|
||||
self::frozen->name => 'Surgelé',
|
||||
self::others->name => 'Autres',
|
||||
];
|
||||
}
|
||||
38
app/Enums/Products/SourceEnum.php
Normal file
38
app/Enums/Products/SourceEnum.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum SourceEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
// * Catalogue argumentaire
|
||||
case catalogArgumentary = 7;
|
||||
// * Magasin
|
||||
case magazin = 1;
|
||||
// * Presse
|
||||
case press = 3;
|
||||
// * Salon Grand Public
|
||||
case showConsumer = 6;
|
||||
// * Salon Clients
|
||||
case showCustomer = 5;
|
||||
// * Salon Professionnel
|
||||
case showTrade = 4;
|
||||
// * Web
|
||||
case web = 2;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::catalogArgumentary->name => 'Catalogue Argumentaire',
|
||||
self::magazin->name => 'Magasin',
|
||||
self::press->name => 'Presse',
|
||||
self::showConsumer->name => 'Salon Grand Public',
|
||||
self::showCustomer->name => 'Salon Clients',
|
||||
self::showTrade->name => 'Salon Professionnel',
|
||||
self::web->name => 'Web',
|
||||
];
|
||||
}
|
||||
23
app/Enums/Promotions/AngleEnum.php
Normal file
23
app/Enums/Promotions/AngleEnum.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Promotions;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum AngleEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case brand = 1;
|
||||
case eco = 2;
|
||||
case fun = 3;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::brand->name => 'Démarche de la marque',
|
||||
self::eco->name => 'Eco',
|
||||
self::fun->name => 'Fun',
|
||||
];
|
||||
}
|
||||
25
app/Enums/Promotions/DistributionNetworkEnum.php
Normal file
25
app/Enums/Promotions/DistributionNetworkEnum.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Promotions;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum DistributionNetworkEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case gms = 1;
|
||||
case hd = 2;
|
||||
case rhd = 3;
|
||||
case export = 4;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::gms->name => 'GMS',
|
||||
self::hd->name => 'HD',
|
||||
self::rhd->name => 'RHD',
|
||||
self::export->name => 'Export',
|
||||
];
|
||||
}
|
||||
29
app/Enums/Promotions/EventEnum.php
Normal file
29
app/Enums/Promotions/EventEnum.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Promotions;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum EventEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case birthday = 1;
|
||||
case summer = 2;
|
||||
case filmLicense = 3;
|
||||
case christmas = 4;
|
||||
case celebrityPartnership = 5;
|
||||
case sport = 6;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::birthday->name => 'Anniversaire',
|
||||
self::summer->name => 'Été',
|
||||
self::filmLicense->name => 'Licence film / Dessin animé',
|
||||
self::christmas->name => 'Noël',
|
||||
self::celebrityPartnership->name => 'Partenariat célébrité',
|
||||
self::sport->name => 'Sport',
|
||||
];
|
||||
}
|
||||
33
app/Enums/Promotions/LabelDistributionNetworkEnum.php
Normal file
33
app/Enums/Promotions/LabelDistributionNetworkEnum.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Promotions;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum LabelDistributionNetworkEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case allDistributions = 1;
|
||||
case lidle = 2;
|
||||
case aldi = 3;
|
||||
case erteco = 4;
|
||||
case netto = 5;
|
||||
case leaderPrice = 6;
|
||||
case noLvl2 = 7;
|
||||
case others = 8;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::allDistributions->name => 'Toutes les enseignes',
|
||||
self::lidle->name => 'LIDL',
|
||||
self::aldi->name => 'ALDI',
|
||||
self::erteco->name => 'ERTECO',
|
||||
self::netto->name => 'NETTO',
|
||||
self::leaderPrice->name => 'LEADER PRICE',
|
||||
self::noLvl2->name => 'Pas de niveau 2',
|
||||
self::others->name => 'Autres',
|
||||
];
|
||||
}
|
||||
27
app/Enums/Promotions/SourceEnum.php
Normal file
27
app/Enums/Promotions/SourceEnum.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Promotions;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum SourceEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case fieldTour = 1;
|
||||
case headFeedback = 2;
|
||||
case web = 3;
|
||||
case press = 4;
|
||||
case events = 5;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::fieldTour->name => 'Tournée terrain',
|
||||
self::headFeedback->name => 'Remontée chef Secteur',
|
||||
self::web->name => 'Pige web',
|
||||
self::press->name => 'Pige presse',
|
||||
self::events->name => 'Salon / Évenementiel',
|
||||
];
|
||||
}
|
||||
27
app/Enums/Promotions/TargetEnum.php
Normal file
27
app/Enums/Promotions/TargetEnum.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Promotions;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum TargetEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case women = 1;
|
||||
case man = 2;
|
||||
case junior = 3;
|
||||
case senior = 4;
|
||||
case allTargets = 5;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::women->name => 'Femme',
|
||||
self::man->name => 'Homme',
|
||||
self::junior->name => 'Junior',
|
||||
self::senior->name => 'Senior',
|
||||
self::allTargets->name => 'Toutes cibles',
|
||||
];
|
||||
}
|
||||
39
app/Enums/Promotions/TypeEnum.php
Normal file
39
app/Enums/Promotions/TypeEnum.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Promotions;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum TypeEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case animation = 1;
|
||||
case reduction = 2;
|
||||
case fidelity = 3;
|
||||
case consoGames = 4;
|
||||
case promotionalFormats = 5;
|
||||
case flyer = 6;
|
||||
case coBranding = 7;
|
||||
case merchandising = 8;
|
||||
case goodies = 9;
|
||||
case promotionalMaterials = 10;
|
||||
case ephemeralProducts = 11;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::animation->name => 'Animation',
|
||||
self::reduction->name => 'Réduction',
|
||||
self::fidelity->name => 'Fidélité',
|
||||
self::consoGames->name => 'Jeux Conso',
|
||||
self::promotionalFormats->name => 'Formats promo',
|
||||
self::flyer->name => 'OP Trade / Prospectus',
|
||||
self::coBranding->name => 'Co-branding',
|
||||
self::merchandising->name => 'PLV / Merchandising',
|
||||
self::goodies->name => 'Primes / Goodies',
|
||||
self::promotionalMaterials->name => 'Argumentaires Promo',
|
||||
self::ephemeralProducts->name => 'Produits éphèmères',
|
||||
];
|
||||
}
|
||||
43
app/Enums/Surveys/CustomerEnum.php
Normal file
43
app/Enums/Surveys/CustomerEnum.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Surveys;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum CustomerEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case francoscopie = 1;
|
||||
case packaging = 2;
|
||||
case innovation = 3;
|
||||
case snacking = 4;
|
||||
case vegetarian = 5;
|
||||
case christmas = 6;
|
||||
case barbecue = 7;
|
||||
case sustainableDevelopment = 8;
|
||||
case labelRouge = 9;
|
||||
case bio = 10;
|
||||
case certified = 11;
|
||||
case bbc = 12;
|
||||
case others = 13;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::francoscopie->name => 'Francoscopie',
|
||||
self::packaging->name => 'Emaballages',
|
||||
self::innovation->name => 'Innovation',
|
||||
self::snacking->name => 'Snacking',
|
||||
self::vegetarian->name => 'Vegan',
|
||||
self::christmas->name => 'Noel',
|
||||
self::barbecue->name => 'Barbecue',
|
||||
self::sustainableDevelopment->name => 'Développement durable',
|
||||
self::labelRouge->name => 'Label Rouge',
|
||||
self::bio->name => 'BIO',
|
||||
self::certified->name => 'Certifié',
|
||||
self::bbc->name => 'BBC',
|
||||
self::others->name => 'Autres',
|
||||
];
|
||||
}
|
||||
25
app/Enums/Surveys/DistributionNetworkEnum.php
Normal file
25
app/Enums/Surveys/DistributionNetworkEnum.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Surveys;
|
||||
|
||||
use App\Traits\Enums\BaseEnum;
|
||||
|
||||
enum DistributionNetworkEnum : int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case gms = 1;
|
||||
case freezer = 2;
|
||||
case restauration = 3;
|
||||
case product = 4;
|
||||
|
||||
/**
|
||||
* Custom added functions definition
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::gms->name => 'GMS / Hard discount / Commerce de détail',
|
||||
self::freezer->name => 'Home service/ Freezer Center',
|
||||
self::restauration->name => 'Restauration',
|
||||
self::product->name => 'Produit pour industrie agro-alimentaire',
|
||||
];
|
||||
}
|
||||
45
app/Exceptions/Handler.php
Normal file
45
app/Exceptions/Handler.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of exception types with their corresponding custom log levels.
|
||||
*
|
||||
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
|
||||
*/
|
||||
protected $levels = [];
|
||||
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array<int, class-string<\Throwable>>
|
||||
*/
|
||||
protected $dontReport = [];
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed to the session on validation exceptions.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register the exception handling callbacks for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->reportable(function (Throwable $e) {
|
||||
});
|
||||
}
|
||||
}
|
||||
29
app/Http/Controllers/BrandController.php
Normal file
29
app/Http/Controllers/BrandController.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Brands\StoreBrandRequest;
|
||||
use App\Models\Brand;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BrandController extends Controller
|
||||
{
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Brands\StoreBrandRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function store(StoreBrandRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$brand = new Brand();
|
||||
$brand->fill($request->validated());
|
||||
if ($brand->saveOrFail()) {
|
||||
return \response()->json(['status' => true, 'brand' => $brand]);
|
||||
}
|
||||
return \response()->json(['status' => false, 'message' => __('La marque n\'a pas pu être créée')]);
|
||||
});
|
||||
}
|
||||
}
|
||||
152
app/Http/Controllers/Controller.php
Normal file
152
app/Http/Controllers/Controller.php
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\Pagination\ItemsPerPaginationEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
use DispatchesJobs;
|
||||
use ValidatesRequests;
|
||||
|
||||
/**
|
||||
* Build Search query on specified fields
|
||||
* splitting search into words or using whole sentence
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
|
||||
* @param string $search The query string.
|
||||
* @param callable $searchQuery The query string.
|
||||
* @param string ...$fields Either a simple array or a string
|
||||
* in case of a string the two fields
|
||||
* would be concatenate to make the search.
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function searchQuery(
|
||||
Builder $query,
|
||||
string $search,
|
||||
callable $searchQuery = null,
|
||||
string ...$fields
|
||||
): Builder {
|
||||
if (count($fields)) {
|
||||
// Search using words.
|
||||
$words = explode(' ', $search);
|
||||
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
|
||||
foreach ($words as $word) {
|
||||
if (!strlen($word)) {
|
||||
continue;
|
||||
}
|
||||
$this->searchOnFields($query, $word, $fields);
|
||||
} //end foreach
|
||||
// Search whole using whole sentence.
|
||||
$this->searchOnFields($query, $search, $fields);
|
||||
if ($searchQuery) {
|
||||
$searchQuery($query);
|
||||
}
|
||||
});
|
||||
} //end if
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build query search on all fields
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param string $search
|
||||
* @param array $fields
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
private function searchOnFields(Builder $query, string $search, array $fields): Builder
|
||||
{
|
||||
$table = $query->getModel()->getTable();
|
||||
foreach ($fields as $f) {
|
||||
$f = str_replace('?', '\?', $f);
|
||||
if (is_array($f)) {
|
||||
$query->orWhereRaw(
|
||||
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($f)->map(function ($q) use ($table) {
|
||||
return "`$table`.`$q`";
|
||||
})->implode(', \' \', ')),
|
||||
['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%']
|
||||
);
|
||||
} else {
|
||||
$query->orWhereRaw(
|
||||
"UPPER(`$table`.`$f`) LIKE UPPER(?)",
|
||||
['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%']
|
||||
);
|
||||
}
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort columns with a query
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $ignore
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
protected function sortQuery(Builder $query, array $ignore = []): Builder
|
||||
{
|
||||
$rName = request()->route()->getName();
|
||||
$table = $query->getModel()->getTable();
|
||||
if (request()->sort_col) {
|
||||
Session::put("$rName.sort_col", request()->sort_col);
|
||||
Session::put("$rName.sorted", true);
|
||||
}
|
||||
if (request()->sort_way) {
|
||||
Session::put("$rName.sort_way", request()->sort_way);
|
||||
Session::put("$rName.sorted", true);
|
||||
}
|
||||
if (request()->rst) {
|
||||
Session::remove("$rName.sorted");
|
||||
Session::remove("$rName.sort_col");
|
||||
Session::remove("$rName.sort_way");
|
||||
}
|
||||
if (request()->sort_col and request()->sort_way and Schema::hasColumn($table, request()->sort_col)) {
|
||||
if (
|
||||
!isset($ignore) or
|
||||
(isset($ignore) and !in_array(request()->sort_col, $ignore))
|
||||
) {
|
||||
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
|
||||
}
|
||||
} elseif (Schema::hasColumn($table, 'order')) {
|
||||
$query = $query->orderBy('order', 'ASC');
|
||||
Session::put("$rName.sort_col", 'order');
|
||||
Session::put("$rName.sort_way", 'asc');
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(Builder $query, ItemsPerPaginationEnum $pagination = null): LengthAwarePaginator
|
||||
{
|
||||
$currentRoutePath = Str::slug(request()->route()->getName());
|
||||
$cacheKey = "pagination.{$currentRoutePath}";
|
||||
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve->value;
|
||||
$pagination = ToolboxHelper::getValidatedPaginate(
|
||||
Cache::get($cacheKey, $pagination)
|
||||
);
|
||||
if (Cache::get($cacheKey) !== $pagination) {
|
||||
Cache::put($cacheKey, $pagination);
|
||||
}
|
||||
$query = $query->paginate($pagination);
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
18
app/Http/Controllers/DashboardController.php
Normal file
18
app/Http/Controllers/DashboardController.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show products list.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show(): \Illuminate\Contracts\View\View
|
||||
{
|
||||
return view('pages.dashboard');
|
||||
}
|
||||
}
|
||||
451
app/Http/Controllers/ProductController.php
Normal file
451
app/Http/Controllers/ProductController.php
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Products\IndexFilterRequest;
|
||||
use App\Http\Requests\Products\StoreProductRequest;
|
||||
use App\Http\Requests\Products\UpdateProductRequest;
|
||||
use App\Jobs\ExportProduct;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Keyword;
|
||||
use App\Models\Product;
|
||||
use App\Traits\Controllers\HasDocuments;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
use HasDocuments;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Products\IndexFilterRequest $request
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index(IndexFilterRequest $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $brands */
|
||||
$brandsQuery = Brand::query();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $productsQuery */
|
||||
$productsQuery = Product::query()->orderBy('created_at', 'desc');
|
||||
|
||||
$this->requestFilters($request, $productsQuery);
|
||||
|
||||
$this->sortQuery($productsQuery);
|
||||
|
||||
$products = $this->paginate($productsQuery);
|
||||
|
||||
/** @var array $printMultiProduct Array of id */
|
||||
$printMultiProduct = session('printMultiProduct') ?? [];
|
||||
|
||||
$brands = $brandsQuery->get();
|
||||
|
||||
$keywords = Keyword::query()->where('keyword_cat', 'LIKE', 'product_%')->get();
|
||||
|
||||
return view('pages.products.index', [
|
||||
'products' => $products,
|
||||
'brands' => $brands,
|
||||
'keywords' => $keywords,
|
||||
'printMultiProduct' => $printMultiProduct
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param App\Models\Product $productModel
|
||||
* @param Illuminate\Support\Collection $commentModel
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function create(Product $productModel, Collection $commentModel): \Illuminate\Contracts\View\View
|
||||
{
|
||||
//phpcs:enable
|
||||
$brands = Brand::all();
|
||||
$productsFair = Product::query()->pluck('fair');
|
||||
$productsShop = Product::query()->pluck('shop');
|
||||
$productsCatalog = Product::query()->pluck('catalogue');
|
||||
$keywords = Keyword::query()->where('keyword_cat', 'product_pleasure')
|
||||
->orWhere('keyword_cat', 'product_shape')
|
||||
->orWhere('keyword_cat', 'product_practicality')
|
||||
->orWhere('keyword_cat', 'product_health')
|
||||
->orWhere('keyword_cat', 'product_ethics')
|
||||
->get();
|
||||
|
||||
return view('pages.products.create', compact(
|
||||
'productModel',
|
||||
'brands',
|
||||
'productsFair',
|
||||
'productsShop',
|
||||
'productsCatalog',
|
||||
'keywords',
|
||||
'commentModel'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Products\StoreProductRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(StoreProductRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$product = new Product();
|
||||
$product->fill($request->validated());
|
||||
$this->storeDocuments($request, $product);
|
||||
if ($product->saveOrFail()) {
|
||||
$this->updateCommentsModel($request, $product);
|
||||
$product->productKeywords()->sync(collect($request->productKeywords)->pluck('id'));
|
||||
return redirect()
|
||||
->route('products.edit', $product)
|
||||
->with('success', trans(
|
||||
':model a été créé',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été créé',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Product $product)
|
||||
{
|
||||
$brands = Brand::all();
|
||||
$productsFair = Product::query()->pluck('fair');
|
||||
$productsShop = Product::query()->pluck('shop');
|
||||
$productsCatalog = Product::query()->pluck('catalogue');
|
||||
$commentModel = $product->productComments;
|
||||
$keywords = Keyword::query()->where('keyword_cat', 'product_pleasure')
|
||||
->orWhere('keyword_cat', 'product_shape')
|
||||
->orWhere('keyword_cat', 'product_practicality')
|
||||
->orWhere('keyword_cat', 'product_health')
|
||||
->orWhere('keyword_cat', 'product_ethics')
|
||||
->get();
|
||||
|
||||
return view('pages.products.edit', [
|
||||
'productModel' => $product,
|
||||
'brands' => $brands,
|
||||
'productsFair' => $productsFair,
|
||||
'productsShop' => $productsShop,
|
||||
'productsCatalog' => $productsCatalog,
|
||||
'commentModel' => $commentModel,
|
||||
'keywords' => $keywords,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Products\UpdateProductRequest $request
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(UpdateProductRequest $request, Product $product)
|
||||
{
|
||||
return DB::transaction(function () use ($request, $product) {
|
||||
$product->fill($request->validated());
|
||||
$this->storeDocuments($request, $product);
|
||||
$this->updateCommentsModel($request, $product);
|
||||
$product->productKeywords()->sync(collect($request->productKeywords)->pluck('id'));
|
||||
if ($product->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('products.edit', $product)
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Product $product)
|
||||
{
|
||||
$this->deleteCommentsModel($product);
|
||||
if ($product->deleteOrFail()) {
|
||||
return redirect()
|
||||
->route('products.index')
|
||||
->with('warning', trans(
|
||||
':model a bien été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update comments model.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Models\Product $product
|
||||
* @return void
|
||||
*/
|
||||
public function updateCommentsModel(Request $request, Product $product)
|
||||
{
|
||||
$this->deleteCommentsModel($product);
|
||||
if ($request->evaluation) {
|
||||
foreach ($request->evaluation as $key => $value) {
|
||||
$product->productComments()->create([
|
||||
'evaluation' => $request->evaluation[$key],
|
||||
'aspect' => $request->aspect[$key] ?? null,
|
||||
'taste' => $request->taste[$key] ?? null,
|
||||
'odor' => $request->odor[$key] ?? null,
|
||||
'texture' => $request->texture[$key] ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete comments model.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function deleteCommentsModel(Product $product)
|
||||
{
|
||||
foreach ($product->productComments as $comment) {
|
||||
if (!$comment->deleteOrFail()) {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('error', trans(
|
||||
':model n\'a pas été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function duplicate(Product $product)
|
||||
{
|
||||
$duplicatedProduct = $product->replicate();
|
||||
foreach ($product->productComments as $comment) {
|
||||
$duplicatedComments[] = $comment->replicate();
|
||||
}
|
||||
return $this->create($duplicatedProduct, collect($duplicatedComments ?? null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a boolean if the gencode is already used or not.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function checkGencode(Request $request)
|
||||
{
|
||||
if (
|
||||
isset($request->GENCOD) &&
|
||||
$request->GENCOD !== null &&
|
||||
ctype_digit($request->GENCOD) &&
|
||||
strlen($request->GENCOD) > 0
|
||||
) {
|
||||
if (
|
||||
isset($request->ID) &&
|
||||
$request->ID !== null &&
|
||||
intval($request->GENCOD) === Product::where('id', $request->ID)->firstOrFail()->gencod
|
||||
) {
|
||||
$result = null;
|
||||
} else {
|
||||
$result = in_array(intval($request->GENCOD), Product::pluck('gencod')->toArray()) ?
|
||||
Product::orderBy('created_at', 'desc')->where('gencod', intval($request->GENCOD))->first() : null;
|
||||
}
|
||||
} else {
|
||||
$result = null;
|
||||
}
|
||||
|
||||
return response()->json($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print product commercial sheet.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param integer $id
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function printProductSheet(Request $request = null, int $id = null)
|
||||
{
|
||||
$productId = $id ?? intval($request->id);
|
||||
$product = Product::where('id', $productId)->firstOrFail();
|
||||
$title = trans('Fiche produit : :name - :appname', [
|
||||
'name' => $product->name,
|
||||
'appname' => \config('app.name', 'Startcoq')
|
||||
]);
|
||||
$filename = trans('fiche-product-:slug-:date', [
|
||||
'slug' => Str::slug($product->name),
|
||||
'date' => \now()->format('Y-m-d')
|
||||
]);
|
||||
return Product::generateProductPdf($product, $title)->stream("{$filename}.pdf");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print multi product commercial sheet
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function printMultiProductSheet()
|
||||
{
|
||||
// FIXME: This should not use session.
|
||||
/** @var array printMultiProduct */
|
||||
$printMultiProduct = session('printMultiProduct') ?? [];
|
||||
asort($printMultiProduct);
|
||||
if (count($printMultiProduct)) {
|
||||
$title = trans('Fiches produits - :appname', [
|
||||
'appname' => \config('app.name', 'Startcoq')
|
||||
]);
|
||||
Product::generateMultipleProductPdf($printMultiProduct, $title)->stream();
|
||||
} else {
|
||||
return redirect()
|
||||
->back()
|
||||
->with('warning', trans('Veuillez sélectionner des produits'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print product commercial sheet
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
public function printMultiProductUpdate(Request $request)
|
||||
{
|
||||
/** @var integer Id of the actual product */
|
||||
$productId = intval($request->ID);
|
||||
/** @var array Array of id products */
|
||||
$printMultiProduct = session('printMultiProduct') ?? [];
|
||||
|
||||
if (in_array($productId, $printMultiProduct)) {
|
||||
array_splice($printMultiProduct, array_search($productId, $printMultiProduct), 1);
|
||||
$result = false;
|
||||
} else {
|
||||
$printMultiProduct[] = $productId;
|
||||
$result = true;
|
||||
}
|
||||
session(['printMultiProduct' => $printMultiProduct]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the selection,
|
||||
* destroy the specified session.
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function printMultiProductDestroy()
|
||||
{
|
||||
session()->forget('printMultiProduct');
|
||||
|
||||
return redirect()
|
||||
->back()
|
||||
->with('success', trans('Sélection des :model supprimée', ['model' => trans('models.classes.products')]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a .zip file.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return void
|
||||
*/
|
||||
public function download(Request $request)
|
||||
{
|
||||
\dispatch(new ExportProduct($request->id))->onConnection('database');
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies filters from products.index
|
||||
*
|
||||
* @param \App\Http\Requests\Products\IndexFilterRequest $request
|
||||
* @param \Illuminate\Database\Eloquent\Builder $productsQuery
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
|
||||
*/
|
||||
private function requestFilters(
|
||||
IndexFilterRequest $request,
|
||||
Builder $productsQuery
|
||||
): \Illuminate\Database\Eloquent\Builder {
|
||||
//phpcs:enable
|
||||
foreach ($request->validated() as $parameterName => $queryParameter) {
|
||||
$productsQuery->when(
|
||||
!empty($queryParameter),
|
||||
function (Builder $q) use ($queryParameter, $parameterName) {
|
||||
switch ($parameterName) {
|
||||
// * String type parameters (Approx search)
|
||||
case 'name':
|
||||
case 'shop':
|
||||
case 'fair':
|
||||
case 'catalogue':
|
||||
case 'specified_claim':
|
||||
$q->where($parameterName, 'LIKE', "%{$queryParameter}%");
|
||||
break;
|
||||
// * Number type parameters (Approx search)
|
||||
case 'gencode':
|
||||
$q->where($parameterName, \intval($queryParameter));
|
||||
break;
|
||||
// * Date type parameters
|
||||
case 'date_before':
|
||||
$q->whereDate('created_at', '<=', $queryParameter);
|
||||
break;
|
||||
case 'date_after':
|
||||
$q->whereDate('created_at', '>=', $queryParameter);
|
||||
break;
|
||||
// * Enums Array
|
||||
case 'claims':
|
||||
case 'origins':
|
||||
$q->whereJsonContains($parameterName, \collect($queryParameter)
|
||||
->map(fn ($enumValue) => \intval($enumValue)));
|
||||
break;
|
||||
// * RelationShips parameters
|
||||
case 'keywords':
|
||||
$q->whereHas('productKeywords', function (Builder $subQ) use ($queryParameter) {
|
||||
$subQ->whereIn('keywords.id', $queryParameter);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
$q->where($parameterName, $queryParameter);
|
||||
} //end switch
|
||||
}
|
||||
);
|
||||
} //end foreach
|
||||
return $productsQuery;
|
||||
}
|
||||
}
|
||||
62
app/Http/Kernel.php
Normal file
62
app/Http/Kernel.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array<int, class-string|string>
|
||||
*/
|
||||
protected $middleware = [
|
||||
\App\Http\Middleware\TrustHosts::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
\Illuminate\Http\Middleware\HandleCors::class,
|
||||
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array<string, array<int, class-string|string>>
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\SecureHeaders::class
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's middleware aliases.
|
||||
*
|
||||
* Aliases may be used to conveniently assign middleware to routes and groups.
|
||||
*
|
||||
* @var array<string, class-string|string>
|
||||
*/
|
||||
protected $middlewareAliases = [
|
||||
'auth' => \App\Http\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
|
||||
'signed' => \App\Http\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
];
|
||||
}
|
||||
20
app/Http/Middleware/Authenticate.php
Normal file
20
app/Http/Middleware/Authenticate.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
return $request->expectsJson() ? null : route('login');
|
||||
}
|
||||
}
|
||||
16
app/Http/Middleware/EncryptCookies.php
Normal file
16
app/Http/Middleware/EncryptCookies.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
];
|
||||
}
|
||||
16
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
16
app/Http/Middleware/PreventRequestsDuringMaintenance.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
|
||||
|
||||
class PreventRequestsDuringMaintenance extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be reachable while maintenance mode is enabled.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
];
|
||||
}
|
||||
34
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
34
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request):(\Symfony\Component\HttpFoundation\Response) $next
|
||||
* @param string ...$guards
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
|
||||
*/
|
||||
public function handle(Request $request, \Closure $next, string ...$guards): Response
|
||||
{
|
||||
// phpcs:enable
|
||||
$guards = empty($guards) ? [null] : $guards;
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect(RouteServiceProvider::HOME);
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
105
app/Http/Middleware/SecureHeaders.php
Normal file
105
app/Http/Middleware/SecureHeaders.php
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\View;
|
||||
use Illuminate\Support\Facades\Vite;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class SecureHeaders
|
||||
{
|
||||
/**
|
||||
* Enumerate headers which you do not want in your application's responses.
|
||||
* Great starting point would be to go check out @Scott_Helme's:
|
||||
* https://securityheaders.com/
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $unwantedHeaderList = [
|
||||
'X-Powered-By',
|
||||
'Server',
|
||||
];
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
// phpcs:enable
|
||||
View::share('nonce', $nonce = Vite::useCspNonce());
|
||||
// * For VueJS dev tool to execute
|
||||
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-{$nonce}' 'strict-dynamic'";
|
||||
$scheme = config('app.env') === 'local' ? 'http' : 'https';
|
||||
$reportUri = Route::has('cspReportUri') ?
|
||||
\sprintf(' report-uri %s ;', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
|
||||
$this->removeUnwantedHeaders($this->unwantedHeaderList);
|
||||
$response = $next($request);
|
||||
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
|
||||
$response->headers->set('X-Content-Type-Options', 'nosniff');
|
||||
$response->headers->set('X-XSS-Protection', '1; mode=block');
|
||||
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
|
||||
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
|
||||
$cspPolicies = [
|
||||
"default-src 'none';",
|
||||
"manifest-src 'self';",
|
||||
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
|
||||
"connect-src 'self' blob:;",
|
||||
"img-src * data: blob:;",
|
||||
'frame-src ' . implode(' ', [
|
||||
'https://www.google.com/recaptcha/',
|
||||
'https://recaptcha.google.com/recaptcha/',
|
||||
'https://www.google.com/maps/',
|
||||
'blob:'
|
||||
]) . ';',
|
||||
"object-src 'self' data:;",
|
||||
"font-src 'self' data: https://fonts.gstatic.com;",
|
||||
// ! Using CKEditor will must have unsafe-inline style
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
"style-src 'self' 'unsafe-inline' " . \implode(' ', [
|
||||
'https://cdnjs.cloudflare.com',
|
||||
// * File Manager
|
||||
'https://fonts.googleapis.com',
|
||||
'https://cdn.jsdelivr.net',
|
||||
'https://use.fontawesome.com',
|
||||
'https://fonts.googleapis.com'
|
||||
]) . ';',
|
||||
// ! If you dont use ckeditor use this
|
||||
// "style-src 'self' 'nonce-$nonce';",
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
"script-src 'self' {$jsDev} " . \implode(' ', [
|
||||
'https: blob:',
|
||||
'https://www.googletagmanager.com',
|
||||
'https://cdnjs.cloudflare.com',
|
||||
"$scheme://www.google-analytics.com",
|
||||
]) . ';'
|
||||
];
|
||||
|
||||
$response->headers->set(
|
||||
'Content-Security-Policy',
|
||||
implode(' ', $cspPolicies) . $reportUri
|
||||
);
|
||||
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unwanted headers
|
||||
*
|
||||
* @param array $headerList
|
||||
* @return void
|
||||
*/
|
||||
private function removeUnwantedHeaders(array $headerList)
|
||||
{
|
||||
foreach ($headerList as $header) {
|
||||
header_remove($header);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
app/Http/Middleware/TrimStrings.php
Normal file
19
app/Http/Middleware/TrimStrings.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
'current_password',
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
20
app/Http/Middleware/TrustHosts.php
Normal file
20
app/Http/Middleware/TrustHosts.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustHosts as Middleware;
|
||||
|
||||
class TrustHosts extends Middleware
|
||||
{
|
||||
/**
|
||||
* Get the host patterns that should be trusted.
|
||||
*
|
||||
* @return array<int, string|null>
|
||||
*/
|
||||
public function hosts(): array
|
||||
{
|
||||
return [
|
||||
$this->allSubdomainsOfApplicationUrl(),
|
||||
];
|
||||
}
|
||||
}
|
||||
28
app/Http/Middleware/TrustProxies.php
Normal file
28
app/Http/Middleware/TrustProxies.php
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The trusted proxies for this application.
|
||||
*
|
||||
* @var array<int, string>|string|null
|
||||
*/
|
||||
protected $proxies;
|
||||
|
||||
/**
|
||||
* The headers that should be used to detect proxies.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $headers =
|
||||
Request::HEADER_X_FORWARDED_FOR |
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
22
app/Http/Middleware/ValidateSignature.php
Normal file
22
app/Http/Middleware/ValidateSignature.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
|
||||
|
||||
class ValidateSignature extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the query string parameters that should be ignored.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*
|
||||
* 'fbclid',
|
||||
* 'utm_campaign',
|
||||
* 'utm_content',
|
||||
* 'utm_medium',
|
||||
* 'utm_source',
|
||||
* 'utm_term',
|
||||
*/
|
||||
protected $except = [];
|
||||
}
|
||||
16
app/Http/Middleware/VerifyCsrfToken.php
Normal file
16
app/Http/Middleware/VerifyCsrfToken.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $except = [
|
||||
];
|
||||
}
|
||||
42
app/Http/Requests/Brands/StoreBrandRequest.php
Normal file
42
app/Http/Requests/Brands/StoreBrandRequest.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Brands;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreBrandRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|min:3|max:255',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'name' => trans('nom de la marque'),
|
||||
];
|
||||
}
|
||||
}
|
||||
60
app/Http/Requests/Products/IndexFilterRequest.php
Normal file
60
app/Http/Requests/Products/IndexFilterRequest.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Products;
|
||||
|
||||
use App\Enums\Products\CertificationEnum;
|
||||
use App\Enums\Products\ClaimEnum;
|
||||
use App\Enums\Products\DistributionNetworkEnum;
|
||||
use App\Enums\Products\GeoAreaEnum;
|
||||
use App\Enums\Products\OriginEnum;
|
||||
use App\Enums\Products\RangeEnum;
|
||||
use App\Enums\Products\RayEnum;
|
||||
use App\Enums\Products\SourceEnum;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
|
||||
class IndexFilterRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'nullable|string',
|
||||
'brand_id' => 'nullable|exists:brands,id',
|
||||
'gencod' => 'nullable|string',
|
||||
|
||||
'source' => ['nullable', new Enum(SourceEnum::class)],
|
||||
'shop' => 'nullable|string',
|
||||
'fair' => 'nullable|string',
|
||||
'catalogue' => 'nullable|string',
|
||||
'claims' => 'nullable|array',
|
||||
'claims.*' => ['required', new Enum(ClaimEnum::class)],
|
||||
'specified_claim' => 'nullable|string',
|
||||
'origins' => 'nullable|array',
|
||||
'origins.*' => ['required', new Enum(OriginEnum::class)],
|
||||
'certification' => ['nullable', new Enum(CertificationEnum::class)],
|
||||
'distribution_network' => ['nullable', new Enum(DistributionNetworkEnum::class)],
|
||||
'ray' => ['nullable', new Enum(RayEnum::class)],
|
||||
'product_range' => ['nullable', new Enum(RangeEnum::class)],
|
||||
'geo_area' => ['nullable', new Enum(GeoAreaEnum::class)],
|
||||
'keywords' => 'nullable|array',
|
||||
'keywords.*' => 'required|exists:keywords,id',
|
||||
'date_before' => 'nullable|date:Y-m-d',
|
||||
'date_after' => 'nullable|date:Y-m-d',
|
||||
];
|
||||
}
|
||||
}
|
||||
160
app/Http/Requests/Products/StoreProductRequest.php
Normal file
160
app/Http/Requests/Products/StoreProductRequest.php
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Products;
|
||||
|
||||
use App\Enums\Products\CertificationEnum;
|
||||
use App\Enums\Products\ClaimEnum;
|
||||
use App\Enums\Products\DistributionNetworkEnum;
|
||||
use App\Enums\Products\GeoAreaEnum;
|
||||
use App\Enums\Products\OriginEnum;
|
||||
use App\Enums\Products\RangeEnum;
|
||||
use App\Enums\Products\RayEnum;
|
||||
use App\Enums\Products\SourceEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Traits\Requests\HasDocumentAndPictureList;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class StoreProductRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
use HasDocumentAndPictureList;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'id' => $this->route('product')->id ?? null,
|
||||
'is_tasted' => $this->is_tasted ? true : false,
|
||||
'origins' => $this->origins ? $this->origins : [],
|
||||
'claims' => $this->claims ? $this->claims : [],
|
||||
'picture_alt' => $this->name,
|
||||
'picture_title' => $this->name,
|
||||
'certification' => $this->certification == 0 ? null : $this->certification,
|
||||
]);
|
||||
$this->mergePicture('picture');
|
||||
$this->mergeDocuments('docs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|string|min:3|max:255',
|
||||
'brand_id' => 'required|exists:brands,id',
|
||||
'source' => [new Enum(SourceEnum::class)],
|
||||
'fair' => 'nullable|string|min:3|max:255',
|
||||
'shop' => 'nullable|string|min:3|max:255',
|
||||
'catalogue' => 'nullable|string|min:3|max:255',
|
||||
'gencod' => 'nullable|numeric|min:0',
|
||||
'picture_alt' => 'required|min:3|max:255',
|
||||
'picture_title' => 'required|min:3|max:255',
|
||||
'url' => [
|
||||
'nullable', 'url', function ($attribute, string $value, $fail) {
|
||||
try {
|
||||
ToolboxHelper::assertNoInternalBoUrls($attribute, $value);
|
||||
} catch (ValidationException $e) {
|
||||
$fail(\trans('Impossible d\'utiliser une url du back-office.'));
|
||||
}
|
||||
}
|
||||
],
|
||||
'certification' => ['nullable', new Enum(CertificationEnum::class)],
|
||||
'origins' => 'present|array',
|
||||
'origins.*' => [new Enum(OriginEnum::class)],
|
||||
'claims' => 'present|array',
|
||||
'claims.*' => [new Enum(ClaimEnum::class)],
|
||||
'specified_claim' => 'string|min:3|max:255|required_if:claims,' . ClaimEnum::others->value,
|
||||
'distribution_network' => ['required', new Enum(DistributionNetworkEnum::class)],
|
||||
'ray' => ['required', new Enum(RayEnum::class)],
|
||||
'product_range' => ['required', new Enum(RangeEnum::class)],
|
||||
'geo_area' => ['required', new Enum(GeoAreaEnum::class)],
|
||||
'weight' => 'nullable|integer',
|
||||
'pvc' => 'nullable|decimal:0,2',
|
||||
'comment' => 'nullable|string|min:15',
|
||||
'is_tasted' => 'required|boolean',
|
||||
'tasters_number' => 'nullable|integer',
|
||||
'evaluation' => 'nullable|array',
|
||||
'evaluation.*' => 'sometimes|string|min:3|max:255',
|
||||
'aspect' => 'nullable|array',
|
||||
'aspect.*' => 'nullable|string|min:3|max:255',
|
||||
'taste' => 'nullable|array',
|
||||
'taste.*' => 'nullable|string|min:3|max:255',
|
||||
'odor' => 'nullable|array',
|
||||
'odor.*' => 'nullable|string|min:3|max:255',
|
||||
'texture' => 'nullable|array',
|
||||
'texture.*' => 'nullable|string|min:3|max:255',
|
||||
];
|
||||
return \array_merge(
|
||||
$rules,
|
||||
$this->pictureRules('picture', false, 1200, 900, 1200, 900),
|
||||
$this->documentsRules('docs'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'name' => trans('nom du produit'),
|
||||
'brand_id' => trans('marque du produit'),
|
||||
'source' => trans('source du produit'),
|
||||
'fair' => trans('salon du produit'),
|
||||
'shop' => trans('magazin du produit'),
|
||||
'catalogue' => trans('catalogue du produit'),
|
||||
'gencod' => trans('gencod du produit'),
|
||||
'picture_alt' => trans('attribut alt du produit'),
|
||||
'picture_title' => trans('attribut titre du produit'),
|
||||
'docs' => trans('documents du produit'),
|
||||
'url' => trans('lien du produit'),
|
||||
'certification' => trans('certification'),
|
||||
'origins' => trans('origines du produit'),
|
||||
'origins.*' => trans('origine du produit'),
|
||||
'claims' => trans('allégations du produit'),
|
||||
'claims.*' => trans('allégation du produit'),
|
||||
'specified_claim' => trans('allégation spécifique du produit'),
|
||||
'distribution_network' => trans('réseau de distribution du produit'),
|
||||
'ray' => trans('rayon du produit'),
|
||||
'product_range' => trans('famille du produit'),
|
||||
'geo_area' => trans('zone géographique du produit'),
|
||||
'weight' => trans('poids du produit'),
|
||||
'pvc' => trans('pvc de l\'uvc du produit'),
|
||||
'comment' => trans('commentaire du produit'),
|
||||
'is_tasted' => trans('produit dégusté ?'),
|
||||
'tasters_number' => trans('nombre de dégustateurs du produit'),
|
||||
'evaluation' => trans('évaluations du produit (partie commentaire)'),
|
||||
'evaluation.*' => trans('évaluation du produit (partie commentaire)'),
|
||||
'aspect' => trans('aspects du produit (partie commentaire)'),
|
||||
'aspect.*' => trans('aspect du produit (partie commentaire)'),
|
||||
'taste' => trans('goûts du produit (partie commentaire)'),
|
||||
'taste.*' => trans('goût du produit (partie commentaire)'),
|
||||
'odor' => trans('odeurs du produit (partie commentaire)'),
|
||||
'odor.*' => trans('odeur du produit (partie commentaire)'),
|
||||
'texture' => trans('textures du produit (partie commentaire)'),
|
||||
'texture.*' => trans('texture du produit (partie commentaire)'),
|
||||
];
|
||||
}
|
||||
}
|
||||
7
app/Http/Requests/Products/UpdateProductRequest.php
Normal file
7
app/Http/Requests/Products/UpdateProductRequest.php
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Products;
|
||||
|
||||
class UpdateProductRequest extends StoreProductRequest
|
||||
{
|
||||
}
|
||||
117
app/Http/Resources/ProductResource.php
Normal file
117
app/Http/Resources/ProductResource.php
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Enums\Products\ClaimEnum;
|
||||
use App\Enums\Products\OriginEnum;
|
||||
use App\Models\Keyword;
|
||||
use App\Models\Product;
|
||||
use App\Models\ProductComment;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProductResource extends JsonResource
|
||||
{
|
||||
/** @var \App\Models\Product */
|
||||
private $product;
|
||||
|
||||
/** @var array */
|
||||
private static $skeleton = [
|
||||
'id' => null,
|
||||
'user' => null,
|
||||
'brand' => null,
|
||||
'name' => null,
|
||||
'seen_at' => null,
|
||||
'source' => null,
|
||||
'fair' => null,
|
||||
'shop' => null,
|
||||
'catalogue' => null,
|
||||
'gencod' => null,
|
||||
'weight' => null,
|
||||
'pvc' => null,
|
||||
'picture' => null,
|
||||
'picture_alt' => null,
|
||||
'picture_title' => null,
|
||||
'certification' => null,
|
||||
'origins' => null,
|
||||
'claims' => null,
|
||||
'specified_claim' => null,
|
||||
'distribution_network' => null,
|
||||
'ray' => null,
|
||||
'product_range' => null,
|
||||
'geo_area' => null,
|
||||
'freeComment' => null,
|
||||
'productComments' => null,
|
||||
'productKeywords' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* ProductResource
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Product $product)
|
||||
{
|
||||
$this->product = $product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request|null $request
|
||||
* @return array
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function toArray($request = null)
|
||||
{
|
||||
return array_intersect_key(
|
||||
[
|
||||
'id' => $this->product->id,
|
||||
'user' => (object)$this->product->user->toArray(),
|
||||
'brand' => (object)$this->product->brand->toArray(),
|
||||
'name' => $this->product->name,
|
||||
'seen_at' => $this->product->seen_at->isoFormat('DD\/MM\/YYYY'),
|
||||
'source' => $this->product->source->label(),
|
||||
'fair' => $this->product->fair,
|
||||
'shop' => $this->product->shop,
|
||||
'catalogue' => $this->product->catalogue,
|
||||
'gencod' => $this->product->gencod,
|
||||
'weight' => $this->product->weight,
|
||||
'pvc' => $this->product->pvc,
|
||||
'picture' => $this->product->picture,
|
||||
'picture_alt' => $this->product->picture_alt,
|
||||
'picture_title' => $this->product->picture_title,
|
||||
'certification' => \optional($this->product->certification)->label(),
|
||||
'origins' => $this->product->origins_enum->map(fn (OriginEnum $enum) => $enum->label())->implode(', '),
|
||||
'claims' => $this->product->claims_enum->map(fn (ClaimEnum $enum) => $enum->label())->implode(', '),
|
||||
'specified_claim' => $this->product->specified_claim,
|
||||
'distribution_network' => $this->product->distribution_network->label(),
|
||||
'ray' => $this->product->ray->label(),
|
||||
'product_range' => $this->product->product_range->label(),
|
||||
'geo_area' => $this->product->geo_area->label(),
|
||||
'freeComment' => $this->product->comment,
|
||||
// FIXME: Use dedicaced resources for relations.
|
||||
'productComments' => $this->product->productComments->map(function (ProductComment $comment) {
|
||||
return (object)\array_merge($comment->toArray(), [
|
||||
'created_at' => $comment->created_at->isoFormat('DD\/MM\/YYYY'),
|
||||
'updated_at' => $comment->updated_at->isoFormat('DD\/MM\/YYYY'),
|
||||
]);
|
||||
}),
|
||||
'productKeywords' => $this->product->productKeywords
|
||||
->map(fn (Keyword $keyword) => $keyword->label)
|
||||
->implode(', '),
|
||||
],
|
||||
$this->getSkeleton()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the resource skeletton
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getSkeleton(): array
|
||||
{
|
||||
return self::$skeleton;
|
||||
}
|
||||
}
|
||||
81
app/Jobs/ExportProduct.php
Normal file
81
app/Jobs/ExportProduct.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Product;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ExportProduct implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @param integer $productId
|
||||
* @return void
|
||||
* @throws \Error If file storage config is wrong.
|
||||
* @throws \RuntimeException If the storage disk export is not local file system.
|
||||
*/
|
||||
public function __construct(int $productId)
|
||||
{
|
||||
/** @var \App\Models\Product $product */
|
||||
$product = Product::query()->where('id', intval($productId))->firstOrFail();
|
||||
|
||||
/** @var \Illuminate\Filesystem\FilesystemAdapter */
|
||||
$exportDisk = Storage::disk('export');
|
||||
|
||||
if (!($exportDisk instanceof \Illuminate\Filesystem\FilesystemAdapter)) {
|
||||
throw new \Error('Storage disk export has to be local file system');
|
||||
}
|
||||
|
||||
if (!$exportDisk->directoryExists('export-product')) {
|
||||
$exportDisk->createDirectory('export-product');
|
||||
}
|
||||
|
||||
// * Create zip
|
||||
$zip = new \ZipArchive();
|
||||
$fileName = date('d-m-Y') . '-' . Str::uuid() . '.zip';
|
||||
|
||||
if (!$zip->open($exportDisk->path("export-product/{$fileName}"), \ZipArchive::CREATE) === true) {
|
||||
throw new \RuntimeException("The ZIP file cannot be created.");
|
||||
}
|
||||
|
||||
$dirName = Str::slug(
|
||||
Carbon::parse($product->updated_at)->format('y-m-d') . '-' .
|
||||
$product->user->lastname . '-' . $product->user->id
|
||||
);
|
||||
|
||||
if (!$zip->addEmptyDir($dirName)) {
|
||||
throw new \RuntimeException("The folder cannot be created inside the ZIP.");
|
||||
}
|
||||
|
||||
// * Add files
|
||||
$pdf = Product::generateProductPdf($product, "test");
|
||||
$exportDisk->put('pdf/' . $product->id . '.pdf', $pdf->output());
|
||||
$zip->addFile($exportDisk->path('pdf/' . $product->id . '.pdf'), $dirName . "/" . $product->name . ".pdf");
|
||||
$zip->addFile($product->picture, $dirName . "/photo." . pathinfo($product->picture, PATHINFO_EXTENSION));
|
||||
foreach ($product->docs as $doc) {
|
||||
$zip->addFile($doc, $dirName . "/document." . pathinfo($doc, PATHINFO_EXTENSION));
|
||||
}
|
||||
|
||||
// * Save zip
|
||||
$zip->close();
|
||||
|
||||
if (!$exportDisk->exists("export-product/{$fileName}")) {
|
||||
throw new \RuntimeException("The ZIP file 'export-product/{$fileName}' has not been created.");
|
||||
}
|
||||
|
||||
Product::downloadPrivateExportFile("export-product/{$fileName}");
|
||||
}
|
||||
}
|
||||
425
app/Lib/Helpers/FileStorageHelper.php
Normal file
425
app/Lib/Helpers/FileStorageHelper.php
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Helpers;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* This class is used to handle files in LDC Laravel stack
|
||||
*
|
||||
* * The storage strategy will be using date.
|
||||
* The file name may contain sufix timestamp highresolution time (hrtime or microtime) if is duplicate.
|
||||
* (function() { return time() . ' ' . Str::slug(microtime(true)) . ' ' . hrtime(true); })();
|
||||
*
|
||||
* posts/ - The model table name
|
||||
* 10/ - Year
|
||||
* 04/ - Month
|
||||
* 19/ - Day
|
||||
* 5f/ - First two letters of md5 file
|
||||
* black-cat-under-red-car.png - Slugified file name
|
||||
* black-cat-under-red-car-286071690833400.png - Slugified file name that is duplicated
|
||||
*
|
||||
* The final path will be storage/modelfiles/posts/10/04/19/5f/black-cat-under-red-car-286071690833400.png
|
||||
*
|
||||
* ! No limits of file per folder will be handled.
|
||||
*
|
||||
* @author Munsch Jérémy <jeremy.munsch@ldc.fr>
|
||||
*/
|
||||
class FileStorageHelper
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
$path = \ltrim(\urldecode($model->{$attribute}), '/');
|
||||
if (\strpos($path, 'storage') !== 0) {
|
||||
return;
|
||||
}
|
||||
$path = ToolboxHelper::mbReplace('storage/', '', $path);
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
// * Ignore If removal has to be done only if the field has changed and it actually hasnt.
|
||||
if ($onlyIfUpdated and $model->{$attribute} === $model->getOriginal($attribute)) {
|
||||
return;
|
||||
}
|
||||
$originalPath = \ltrim(\urldecode($model->getOriginal($attribute)), '/');
|
||||
if (\strpos($originalPath, 'storage') !== 0) {
|
||||
return;
|
||||
}
|
||||
$originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath);
|
||||
if (Storage::disk('public')->exists($originalPath)) {
|
||||
Storage::disk('public')->delete($originalPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a file using optimized storage strategy
|
||||
*
|
||||
* You can use filename param to overload file name.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\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 boolean $slugify Force filename slugification.
|
||||
* @param string|null $filename This string may be used to store the file.
|
||||
* @return string
|
||||
* @throws \RuntimeException If file copy fails.
|
||||
*/
|
||||
public static function storeFile(Model $model, $file, bool $slugify = false, string $filename = null): string
|
||||
{
|
||||
$tableName = $model->getTable();
|
||||
if (\is_object($file) and $file instanceof \Illuminate\Http\UploadedFile) {
|
||||
/** @var \Illuminate\Http\UploadedFile $file */
|
||||
$filename = $filename ?: $file->getClientOriginalName();
|
||||
$filename = $slugify ? self::slugifyFileName($filename) : $filename;
|
||||
$newPath = self::prepareMoveFile($tableName, $filename);
|
||||
$newPath = str_replace(storage_path('app'), '', $newPath);
|
||||
$file->storePubliclyAs(
|
||||
pathinfo($newPath, \PATHINFO_DIRNAME),
|
||||
pathinfo($newPath, \PATHINFO_BASENAME)
|
||||
);
|
||||
return 'storage' . str_replace('/public', '', $newPath);
|
||||
}
|
||||
if (\is_object($file) and $file instanceof \SplFileInfo) {
|
||||
/** @var \SplFileInfo $file */
|
||||
$filename = $filename ?: $file->getBaseName();
|
||||
$filename = $slugify ? self::slugifyFileName($filename) : $filename;
|
||||
$newPath = self::prepareMoveFile($tableName, $filename);
|
||||
if (!File::copy($file->getRealPath(), $newPath)) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Failed to copy file %s to %s',
|
||||
$file->getRealPath(),
|
||||
$newPath
|
||||
));
|
||||
}
|
||||
return \ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/');
|
||||
}
|
||||
return strval($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store all files from string attribute html
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model The model that will hold the picture.
|
||||
* @param string $htmlString The html string to parse and modify files.
|
||||
* @param string|null $oldHtmlString The old html string with alreadyParsed
|
||||
* files using storeModelFilesFromHtml.
|
||||
* @return string Html with files pathes replaced using storage strategy.
|
||||
*/
|
||||
public static function storeModelFilesFromHtml(
|
||||
Model $model,
|
||||
string $htmlString,
|
||||
string $oldHtmlString = null
|
||||
): string {
|
||||
\collect(self::onlyUploadedFilesWithFileManager(self::findFilesInHtml($htmlString)))
|
||||
->each(function (string $path) use ($model, &$htmlString) {
|
||||
/** @var \Illuminate\Filesystem\FilesystemAdapter */
|
||||
$disk = Storage::disk('uploads');
|
||||
$fileInfo = new \SplFileInfo($disk->path(
|
||||
ToolboxHelper::mbReplace('/uploads/', '', \urldecode($path))
|
||||
));
|
||||
$htmlString = ToolboxHelper::mbReplace(
|
||||
$path,
|
||||
'/' . self::storeFile($model, $fileInfo, true),
|
||||
$htmlString
|
||||
);
|
||||
});
|
||||
|
||||
if ($oldHtmlString) {
|
||||
self::removeOldModelFilesFromHtml($htmlString, $oldHtmlString);
|
||||
}
|
||||
|
||||
return $htmlString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all old files from string attribute html that are stored in public that
|
||||
* are nove present anymore on new html string
|
||||
*
|
||||
* @param string $newHtmlString The 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
|
||||
{
|
||||
self::removeOldFiles(self::findFilesInHtml($oldHtmlString), self::findFilesInHtml($newHtmlString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all old files that are stored in public that
|
||||
* are not present anymore on new array files
|
||||
*
|
||||
* @param string[] $oldFiles The old array files.
|
||||
* @param string[] $newFiles The new array files.
|
||||
* @return void
|
||||
*/
|
||||
public static function removeOldFiles(array $oldFiles, array $newFiles): void
|
||||
{
|
||||
$oldFiles = \collect(self::onlyUploadedFilesWithFileStorageHelper($oldFiles));
|
||||
$filesToKeep = \collect(self::onlyUploadedFilesWithFileStorageHelper($newFiles));
|
||||
$filesToRemove = $oldFiles->diff($filesToKeep);
|
||||
$filesToRemove->each(function (string $publicStorageFilePath) {
|
||||
$path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/'));
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all files from string attribute html that are stored in public
|
||||
*
|
||||
* @param string $htmlString The html string with alreadyParsed files using storeModelFilesFromHtml.
|
||||
* @return void
|
||||
*/
|
||||
public static function removeModelFilesOfHtml(string $htmlString): void
|
||||
{
|
||||
$filesToRemove = \collect(self::onlyUploadedFilesWithFileStorageHelper(self::findFilesInHtml($htmlString)));
|
||||
$filesToRemove->each(function (string $publicStorageFilePath) {
|
||||
$path = ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($publicStorageFilePath), '/'));
|
||||
if (Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all occurence of files in html
|
||||
* @param string $html
|
||||
* @return 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);
|
||||
return collect($m)->pluck('href')->filter()
|
||||
->concat(collect($m)->pluck('url')->filter())
|
||||
->concat(collect($m)->pluck('src')->filter())
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter array to get only uploaded files that exists
|
||||
* on disk 'public' (using helper storage strategy)
|
||||
*
|
||||
* @param array $urls
|
||||
* @return array
|
||||
*/
|
||||
public static function onlyUploadedFilesWithFileStorageHelper(array $urls): array
|
||||
{
|
||||
return \collect($urls)->unique()
|
||||
// * Filter to get only /uploads and using app url or empty
|
||||
->filter(function (string $url) {
|
||||
$path = \parse_url($url, \PHP_URL_PATH);
|
||||
return \strpos(\ltrim($path, '/'), 'storage/modelfiles') === 0;
|
||||
})->filter(function (string $url) {
|
||||
$path = \parse_url($url, \PHP_URL_PATH);
|
||||
return Storage::disk('public')
|
||||
->exists(ToolboxHelper::mbReplace('storage/', '', \ltrim(\urldecode($path), '/')));
|
||||
})->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter array to get only uploaded files that exists
|
||||
* on disk 'uploads'
|
||||
*
|
||||
* @param array $urls
|
||||
* @return array
|
||||
*/
|
||||
private static function onlyUploadedFilesWithFileManager(array $urls): array
|
||||
{
|
||||
return \collect($urls)->unique()
|
||||
// * Filter to get only /uploads and using app url or empty
|
||||
->filter(function (string $url) {
|
||||
$host = \parse_url($url, \PHP_URL_HOST);
|
||||
$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;
|
||||
})->filter(function (string $url) {
|
||||
$path = \parse_url($url, \PHP_URL_PATH);
|
||||
return Storage::disk('uploads')
|
||||
->exists(ToolboxHelper::mbReplace('uploads/', '', \ltrim(\urldecode($path), '/')));
|
||||
})->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare storage path and return
|
||||
* the file full path
|
||||
*
|
||||
* @param string $tableName Model table name.
|
||||
* @param string $filename The basename for the file.
|
||||
* @return string
|
||||
*/
|
||||
private static function prepareMoveFile(string $tableName, string $filename): string
|
||||
{
|
||||
// * Security filter + beautifier
|
||||
$filename = self::sanitizeFileName($filename);
|
||||
// * Security filter
|
||||
$filename = \filter_var($filename, \FILTER_SANITIZE_STRING);
|
||||
// * Slugify filename
|
||||
$ext = \pathinfo($filename, \PATHINFO_EXTENSION);
|
||||
$filename = Str::slug(\pathinfo($filename, \PATHINFO_FILENAME)) .
|
||||
(\strlen($ext) ? ".{$ext}" : '');
|
||||
|
||||
$folderPath = self::getStoragePath($tableName, $filename);
|
||||
$filename = self::getFileUniqueName($folderPath, $filename);
|
||||
return "{$folderPath}/{$filename}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sub folders to store files using pattern
|
||||
* storage/modelfiles/posts/10/04/19/5f
|
||||
* year/month/day/md5 first 2 letters
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
private static function getStoragePath(string $table, string $filename): string
|
||||
{
|
||||
$filenameHash = \md5($filename);
|
||||
$pathFolder = \storage_path(sprintf(
|
||||
'app/public/modelfiles/%s/%s/%s/%s/%s',
|
||||
$table,
|
||||
gmdate('y'),
|
||||
gmdate('m'),
|
||||
gmdate('d'),
|
||||
substr($filenameHash, 0, 2)
|
||||
));
|
||||
if (!File::exists($pathFolder)) {
|
||||
File::makeDirectory($pathFolder, 0755, true);
|
||||
}
|
||||
return $pathFolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unique filename if the path is already occupied
|
||||
*
|
||||
* @param string $folderPath
|
||||
* @param string $filename
|
||||
* @return string
|
||||
* @throws \RuntimeException If a unique filename cannot be obtained.
|
||||
*/
|
||||
private static function getFileUniqueName(string $folderPath, string $filename): string
|
||||
{
|
||||
$attempts = 0;
|
||||
$maxAttempts = 20;
|
||||
while (File::exists("{$folderPath}/{$filename}")) {
|
||||
if ($attempts++ >= $maxAttempts) {
|
||||
throw new \RuntimeException("Failed to has a unique filename for {$folderPath}/{$filename}");
|
||||
}
|
||||
$filename = pathinfo($filename, \PATHINFO_FILENAME) .
|
||||
\uniqid('-') . '.' .
|
||||
pathinfo($filename, \PATHINFO_EXTENSION);
|
||||
}
|
||||
return $filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Slugify the filename
|
||||
*
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public static function slugifyFileName(string $filename): string
|
||||
{
|
||||
return Str::slug(pathinfo($filename, \PATHINFO_FILENAME)) . '.' . pathinfo($filename, \PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize File name for uploaded files
|
||||
*
|
||||
* @param string $filename The original uploaded client file name.
|
||||
* @param boolean $beautify Beautify the filename.
|
||||
* @return string The sanitized file name.
|
||||
*/
|
||||
public static function sanitizeFileName(string $filename, bool $beautify = true): string
|
||||
{
|
||||
// * Sanitize filename
|
||||
// phpcs:disable Generic.Files.LineLength.TooLong
|
||||
$filename = preg_replace(
|
||||
'~
|
||||
[<>:"/\\\|?*]| # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
|
||||
[\x00-\x1F]| # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
|
||||
[\x7F\xA0\xAD]| # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
|
||||
[#\[\]@!$&\'()+,;=]| # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
|
||||
[{}^\~`] # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
|
||||
~x',
|
||||
'-',
|
||||
$filename
|
||||
);
|
||||
// phpcs:enable
|
||||
// * Avoids '.' '..' or '.hiddenFiles' .
|
||||
$filename = Str::of($filename)->ltrim('.-');
|
||||
// * Optional beautification
|
||||
if ($beautify) {
|
||||
$filename = static::beautifyFilename($filename);
|
||||
}
|
||||
// * Maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
|
||||
/** @var string */
|
||||
$extension = pathinfo($filename, \PATHINFO_EXTENSION);
|
||||
/** @var string */
|
||||
$filename = pathinfo($filename, \PATHINFO_FILENAME);
|
||||
|
||||
$encoding = mb_detect_encoding($filename);
|
||||
$encoding = $encoding ? $encoding : null;
|
||||
return \mb_strcut(
|
||||
$filename,
|
||||
0,
|
||||
255 - (!empty($extension) ? strlen($extension) + 1 : 0),
|
||||
$encoding
|
||||
) . (!empty($extension) ? ".$extension" : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Beautify a filename
|
||||
*
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
private static function beautifyFilename(string $filename): string
|
||||
{
|
||||
// Reduce consecutive characters.
|
||||
$filename = preg_replace([
|
||||
// * "file name.zip" becomes 'file-name.zip'
|
||||
'/ +/',
|
||||
// * "file___name.zip" becomes 'file-name.zip'
|
||||
'/_+/',
|
||||
// * "file---name.zip" becomes 'file-name.zip'
|
||||
'/-+/'
|
||||
], '-', $filename);
|
||||
$filename = preg_replace([
|
||||
// * "file--.--.-.--name.zip" becomes "file.name.zip"
|
||||
'/-*\.-*/',
|
||||
// * "file...name..zip" becomes "file.name.zip"
|
||||
'/\.{2,}/'
|
||||
], '.', $filename);
|
||||
// Lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625 .
|
||||
$filename = mb_strtolower($filename, mb_detect_encoding($filename));
|
||||
// * ".file-name.-" becomes "file-name"
|
||||
$filename = trim($filename, '.-');
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
248
app/Lib/Helpers/ToolboxHelper.php
Normal file
248
app/Lib/Helpers/ToolboxHelper.php
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Helpers;
|
||||
|
||||
use App\Enums\Pagination\ItemsPerPaginationEnum;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Enum;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class ToolboxHelper
|
||||
{
|
||||
/**
|
||||
* Cut string words
|
||||
*
|
||||
* @param string $string
|
||||
* @param integer $max
|
||||
* @param string $hyphen
|
||||
* @return string
|
||||
*/
|
||||
public static function cutWords(string $string, int $max = 20, string $hyphen = '[.]')
|
||||
{
|
||||
$originalString = $string;
|
||||
$tok = strtok($string, ' ');
|
||||
$string = '';
|
||||
while ($tok !== false && mb_strlen($string) < $max) {
|
||||
if (
|
||||
mb_strlen($string) + mb_strlen($tok) <= $max
|
||||
) {
|
||||
$string .= $tok . ' ';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
$tok = strtok(' ');
|
||||
}
|
||||
return trim($string) . (strlen(trim($string)) !== strlen(trim($originalString)) ? $hyphen : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip scheme (https) on oembed urls
|
||||
* Only embed allowed are for MP4 self hosted videos right now
|
||||
*
|
||||
* https is force by ckeditor MediaElements which natively supports
|
||||
* only cloud hosted medias (youtube, spotify etc..)
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
public static function stripCkEditorSchemesOnMediaInputs(string $input): string
|
||||
{
|
||||
$input = \preg_replace('/(?<=<oembed url=")(https:\/\/)(?=.*?">)/m', '', $input);
|
||||
return $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert That no Back Office url is present on txt
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $text
|
||||
* @return string
|
||||
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
|
||||
*/
|
||||
public static function assertNoInternalBoUrls(string $field, string $text): string
|
||||
{
|
||||
$routes = Route::getRoutes();
|
||||
// * Url replace and email replace.
|
||||
$text = preg_replace_callback(
|
||||
// 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@:%_\+.~#?&\/\/=]*))/',
|
||||
function ($matches) use ($routes, $field) {
|
||||
$match = collect($matches)->first() ?? '';
|
||||
$url = $match;
|
||||
try {
|
||||
$request = Request::create($url);
|
||||
// If a route is match then it do not throws exception.
|
||||
if (
|
||||
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) &&
|
||||
trim($routes->match($request)->getPrefix() ?? '', '/') === 'bo'
|
||||
) {
|
||||
// Removes the matched url.
|
||||
throw ValidationException::withMessages([
|
||||
$field => ':attribute : Il est interdit de coller une url du back office dans ce champ'
|
||||
]);
|
||||
}
|
||||
return $match;
|
||||
} catch (NotFoundHttpException $e) {
|
||||
return $match;
|
||||
}
|
||||
},
|
||||
$text
|
||||
);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert That only external url is present on txt
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $text
|
||||
* @return string
|
||||
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
|
||||
*/
|
||||
public static function assertNoInternalUrls(string $field, string $text): string
|
||||
{
|
||||
self::assertNoInternalBoUrls($field, $text);
|
||||
$routes = Route::getRoutes();
|
||||
// * Url replace and email replace.
|
||||
$text = preg_replace_callback(
|
||||
// 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@:%_\+.~#?&\/\/=]*))/',
|
||||
function ($matches) use ($routes, $field) {
|
||||
$match = collect($matches)->first() ?? '';
|
||||
$url = $match;
|
||||
try {
|
||||
$request = Request::create($url);
|
||||
// If a route is match then it do not throws exception.
|
||||
if (
|
||||
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) &&
|
||||
$routes->match($request)
|
||||
) {
|
||||
// Removes the matched url.
|
||||
throw ValidationException::withMessages([
|
||||
$field => sprintf(
|
||||
':attribute : Il est interdit de coller une url de %s dans ce champ',
|
||||
config('app.name')
|
||||
)
|
||||
]);
|
||||
}
|
||||
return $match;
|
||||
} catch (NotFoundHttpException $e) {
|
||||
return $match;
|
||||
}
|
||||
},
|
||||
$text
|
||||
);
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutltibyte string replace
|
||||
* @param string|array $search
|
||||
* @param string|array $replace
|
||||
* @param mixed $subject
|
||||
* @param integer $count
|
||||
* @return mixed
|
||||
*/
|
||||
public static function mbReplace(string|array $search, string|array $replace, mixed $subject, int &$count = 0)
|
||||
{
|
||||
if (!is_array($search) && is_array($replace)) {
|
||||
return false;
|
||||
}
|
||||
if (is_array($subject)) {
|
||||
// Call mb_replace for each single string in $subject .
|
||||
foreach ($subject as &$string) {
|
||||
$string = &self::mbReplace($search, $replace, $string, $count);
|
||||
}
|
||||
} elseif (is_array($search)) {
|
||||
if (!is_array($replace)) {
|
||||
foreach ($search as &$string) {
|
||||
$subject = self::mbReplace($string, $replace, $subject, $count);
|
||||
}
|
||||
} else {
|
||||
$n = max(count($search), count($replace));
|
||||
while ($n--) {
|
||||
$subject = self::mbReplace(current($search), current($replace), $subject, $count);
|
||||
next($search);
|
||||
next($replace);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$parts = mb_split(preg_quote($search), $subject);
|
||||
$count = count($parts) - 1;
|
||||
$subject = implode($replace, $parts);
|
||||
} //end if
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the field is unique
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param mixed $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(
|
||||
string $table,
|
||||
string $field,
|
||||
mixed $value,
|
||||
?int $id = null,
|
||||
?callable $query = null
|
||||
) {
|
||||
if (!$query) {
|
||||
Validator::make([$field => $value], [
|
||||
$field => Rule::unique($table, $field)->ignore($id),
|
||||
], [
|
||||
$field . '.unique' => trans('La valeur :value pour le champ :attribute est déjà utilisé', [
|
||||
'attribute' => $field,
|
||||
'value' => $value
|
||||
])
|
||||
])->validate();
|
||||
return;
|
||||
}
|
||||
Validator::make([$field => $value], [
|
||||
$field => Rule::unique($table)->where($query)->ignore($id),
|
||||
], [
|
||||
$field . '.unique' => trans('La valeur pour le champ :attribute est déjà utilisé', [
|
||||
'attribute' => $field
|
||||
])
|
||||
])->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that two arrays have the same content (1 level only)
|
||||
*
|
||||
* @param array $array1
|
||||
* @param array $array2
|
||||
* @return boolean
|
||||
*/
|
||||
public static function arrayHaveSameContent(array $array1, array $array2): bool
|
||||
{
|
||||
return (array_diff($array1, $array2) == [] && array_diff($array2, $array1) == []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get validated pagination from request
|
||||
*
|
||||
* @param integer $default
|
||||
* @return integer
|
||||
*/
|
||||
public static function getValidatedPaginate(int $default): int
|
||||
{
|
||||
try {
|
||||
return \intval(Validator::make(
|
||||
['pagination' => \request()->get('pagination')],
|
||||
['pagination' => new Enum(ItemsPerPaginationEnum::class)]
|
||||
)->validated()['pagination']);
|
||||
} catch (ValidationException $e) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
app/Models/Brand.php
Normal file
55
app/Models/Brand.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $name
|
||||
* @property string $filter
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property Promo[] $promos
|
||||
* @property Promo[] $promo
|
||||
* @property Product[] $products
|
||||
*/
|
||||
class Brand extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'filter',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function promos()
|
||||
{
|
||||
return $this->belongsToMany('App\Models\Promo', 'promo_brands');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function promo()
|
||||
{
|
||||
return $this->hasMany('App\Models\Promo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function products()
|
||||
{
|
||||
return $this->hasMany('App\Models\Product');
|
||||
}
|
||||
}
|
||||
80
app/Models/Ingredient.php
Normal file
80
app/Models/Ingredient.php
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Ingredients\OriginEnum;
|
||||
use App\Enums\Ingredients\FamilyEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $user_id
|
||||
* @property string $name
|
||||
* @property string $supplier
|
||||
* @property enum $origin
|
||||
* @property boolean $is_sample
|
||||
* @property enum $family
|
||||
* @property string $packing
|
||||
* @property string $function
|
||||
* @property string $observation
|
||||
* @property string $picture
|
||||
* @property string $url
|
||||
* @property string $documents
|
||||
* @property string $fair
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property string $deleted_at
|
||||
* @property User $user
|
||||
*/
|
||||
class Ingredient extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'name',
|
||||
'supplier',
|
||||
'origin',
|
||||
'is_sample',
|
||||
'family',
|
||||
'packing',
|
||||
'function',
|
||||
'observation',
|
||||
'picture',
|
||||
'url',
|
||||
'documents',
|
||||
'fair',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'origin' => OriginEnum::class,
|
||||
'is_sample' => 'boolean',
|
||||
'family' => FamilyEnum::class,
|
||||
'packing' => 'json',
|
||||
'function' => 'json',
|
||||
'documents' => 'json',
|
||||
'created_at' => 'datetime:Y-m-d',
|
||||
'updated_at' => 'datetime:Y-m-d',
|
||||
'deleted_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('App\Models\User');
|
||||
}
|
||||
}
|
||||
59
app/Models/Keyword.php
Normal file
59
app/Models/Keyword.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\KeywordCategoryEnum;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $label
|
||||
* @property \App\Enums\KeywordCategoryEnum $keyword_cat
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
*
|
||||
* * RELATIONSHIPS
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Product> $products
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Keyword> $keywordables
|
||||
*/
|
||||
class Keyword extends Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'label',
|
||||
'keyword_cat',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'keyword_cat' => KeywordCategoryEnum::class
|
||||
];
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function keywordables()
|
||||
{
|
||||
return $this->hasMany(Keywordable::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphToMany products relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function products()
|
||||
{
|
||||
return $this->morphToMany(Product::class, 'keywordable');
|
||||
}
|
||||
}
|
||||
39
app/Models/Keywordable.php
Normal file
39
app/Models/Keywordable.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $keyword_id
|
||||
* @property integer $keywordable_id
|
||||
* @property string $keywordable_type
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property Keyword $keyword
|
||||
*/
|
||||
class Keywordable extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'keyword_id',
|
||||
'keywordable_id',
|
||||
'keywordable_type',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function keyword()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Keyword');
|
||||
}
|
||||
}
|
||||
86
app/Models/Packaging.php
Normal file
86
app/Models/Packaging.php
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Packaging\OriginEnum;
|
||||
use App\Enums\Packaging\WatchTypeEnum;
|
||||
use App\Enums\Packaging\TypeEnum;
|
||||
use App\Enums\Packaging\MaterialEnum;
|
||||
use App\Enums\Packaging\BenefitEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $user_id
|
||||
* @property boolean $watch_type
|
||||
* @property string $supplier
|
||||
* @property string $name
|
||||
* @property string $product_description
|
||||
* @property boolean $origin
|
||||
* @property boolean $is_sample
|
||||
* @property boolean $type_packaging
|
||||
* @property boolean $material
|
||||
* @property boolean $benefit
|
||||
* @property string $observation
|
||||
* @property string $picture
|
||||
* @property string $url
|
||||
* @property string $documents
|
||||
* @property string $fair
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property string $deleted_at
|
||||
* @property User $user
|
||||
*/
|
||||
class Packaging extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'watch_type',
|
||||
'supplier',
|
||||
'name',
|
||||
'product_description',
|
||||
'origin',
|
||||
'is_sample',
|
||||
'type_packaging',
|
||||
'material',
|
||||
'benefit',
|
||||
'observation',
|
||||
'picture',
|
||||
'url',
|
||||
'documents',
|
||||
'fair',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'watch_type' => WatchTypeEnum::class,
|
||||
'origin' => OriginEnum::class,
|
||||
'is_sample' => 'boolean',
|
||||
'type_packaging' => TypeEnum::class,
|
||||
'material' => MaterialEnum::class,
|
||||
'benefit' => BenefitEnum::class,
|
||||
'picture' => 'json',
|
||||
'documents' => 'json',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('App\Models\User');
|
||||
}
|
||||
}
|
||||
93
app/Models/Process.php
Normal file
93
app/Models/Process.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Process\WatchTypeEnum;
|
||||
use App\Enums\Process\OriginEnum;
|
||||
use App\Enums\Process\BenefitEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $user_id
|
||||
* @property boolean $watch_type
|
||||
* @property string $supplier
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
* @property boolean $origin
|
||||
* @property boolean $is_test_machine
|
||||
* @property boolean $is_quotation
|
||||
* @property string $quotation_document
|
||||
* @property boolean $profit
|
||||
* @property string $observation
|
||||
* @property string $picture
|
||||
* @property string $documents
|
||||
* @property string $url
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property string $deleted_at
|
||||
* @property ProcessHasProcessType[] $processHasProcessTypes
|
||||
* @property User $user
|
||||
*/
|
||||
class Process extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'watch_type',
|
||||
'supplier',
|
||||
'name',
|
||||
'description',
|
||||
'origin',
|
||||
'is_test_machine',
|
||||
'is_quotation',
|
||||
'quotation_document',
|
||||
'profit',
|
||||
'observation',
|
||||
'picture',
|
||||
'documents',
|
||||
'url',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'watch_type' => WatchTypeEnum::class,
|
||||
'origin' => OriginEnum::class,
|
||||
'is_test_machine' => 'boolean',
|
||||
'is_quotation' => 'boolean',
|
||||
'profit' => BenefitEnum::class,
|
||||
'picture' => 'json',
|
||||
'documents' => 'json',
|
||||
'created_at' => 'datetime:Y-m-d',
|
||||
'updated_at' => 'datetime:Y-m-d',
|
||||
'deleted_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function processHasProcessTypes()
|
||||
{
|
||||
return $this->hasMany('App\Models\ProcessHasProcessType', 'process_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('App\Models\User');
|
||||
}
|
||||
}
|
||||
52
app/Models/ProcessHasProcessType.php
Normal file
52
app/Models/ProcessHasProcessType.php
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $process_id
|
||||
* @property integer $process_type_id
|
||||
* @property string $value_enum
|
||||
* @property ProcessType $processType
|
||||
* @property Process $process
|
||||
*/
|
||||
class ProcessHasProcessType extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'process_id',
|
||||
'process_type_id',
|
||||
'value_enum'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'value_enum' => 'json'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function processType()
|
||||
{
|
||||
return $this->belongsTo('App\Models\ProcessType');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function process()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Process');
|
||||
}
|
||||
}
|
||||
32
app/Models/ProcessType.php
Normal file
32
app/Models/ProcessType.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property string $name
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property ProcessHasProcessType[] $processHasProcessTypes
|
||||
*/
|
||||
class ProcessType extends Model
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function processHasProcessTypes()
|
||||
{
|
||||
return $this->hasMany('App\Models\ProcessHasProcessType', 'process_type_id');
|
||||
}
|
||||
}
|
||||
452
app/Models/Product.php
Normal file
452
app/Models/Product.php
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Products\CertificationEnum;
|
||||
use App\Enums\Products\ClaimEnum;
|
||||
use App\Enums\Products\DistributionNetworkEnum;
|
||||
use App\Enums\Products\GeoAreaEnum;
|
||||
use App\Enums\Products\OriginEnum;
|
||||
use App\Enums\Products\RangeEnum;
|
||||
use App\Enums\Products\RayEnum;
|
||||
use App\Enums\Products\SourceEnum;
|
||||
use App\Http\Resources\ProductResource;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Mccarlosen\LaravelMpdf\LaravelMpdf;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* FIXME: using migration, do not set nullable props without a real need.
|
||||
*
|
||||
* @property integer $id
|
||||
* @property integer $user_id
|
||||
* @property integer $brand_id
|
||||
* @property string $name
|
||||
* @property \Carbon\Carbon $seen_at
|
||||
* @property \App\Enums\Products\SourceEnum $source
|
||||
* @property string|null $fair
|
||||
* @property string|null $shop
|
||||
* @property string|null $catalogue
|
||||
* @property string|null $gencod
|
||||
* @property string|null $picture
|
||||
* @property string $picture_alt
|
||||
* @property string $picture_title
|
||||
* @property array|null $docs
|
||||
* @property string|null $url
|
||||
* @property \App\Enums\Products\CertificationEnum|null $certification
|
||||
* @property array<integer>|null $origins
|
||||
* @property \Illuminate\Support\Collection<\App\Enums\Products\OriginEnum> $origins_enum
|
||||
* @property array<integer>|null $claims
|
||||
* @property \Illuminate\Support\Collection<\App\Enums\Products\OriginEnum> $claims_enum
|
||||
* @property string|null $specified_claim
|
||||
* @property \App\Enums\Products\DistributionNetworkEnum $distribution_network
|
||||
* @property \App\Enums\Products\RayEnum $ray
|
||||
* @property \App\Enums\Products\RangeEnum $product_range
|
||||
* @property \App\Enums\Products\GeoAreaEnum $geo area
|
||||
* @property integer|null $weight
|
||||
* @property integer|null $pvc
|
||||
* @property string|null $comment
|
||||
* @property boolean $is_tasted
|
||||
* @property integer|null $tasters_number
|
||||
* @property \Carbon\Carbon|null $created_at
|
||||
* @property \Carbon\Carbon|null $updated_at
|
||||
* @property \Carbon\Carbon|null $deleted_at
|
||||
*
|
||||
* * RELATIONSHIPS
|
||||
* @property-read \App\Models\Brand $brand
|
||||
* @property-read \App\Models\User $user
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\ProductComment> $productComments
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Keyword> $productKeywords
|
||||
*/
|
||||
class Product extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'brand_id',
|
||||
'name',
|
||||
// ? FIXME: ?why is there brand here, column is not in DB.
|
||||
'brand',
|
||||
'seen_at',
|
||||
'source',
|
||||
'fair',
|
||||
'shop',
|
||||
'catalogue',
|
||||
'gencod',
|
||||
'picture',
|
||||
'picture_alt',
|
||||
'picture_title',
|
||||
'docs',
|
||||
'url',
|
||||
'certification',
|
||||
'origins',
|
||||
'claims',
|
||||
'specified_claim',
|
||||
'distribution_network',
|
||||
'ray',
|
||||
'product_range',
|
||||
'geo_area',
|
||||
'weight',
|
||||
'pvc',
|
||||
'comment',
|
||||
'is_tasted',
|
||||
'tasters_number',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'source' => SourceEnum::class,
|
||||
'docs' => 'json',
|
||||
'certification' => CertificationEnum::class,
|
||||
'origins' => 'json',
|
||||
'claims' => 'json',
|
||||
'distribution_network' => DistributionNetworkEnum::class,
|
||||
'ray' => RayEnum::class,
|
||||
'product_range' => RangeEnum::class,
|
||||
'geo_area' => GeoAreaEnum::class,
|
||||
'is_tasted' => 'boolean',
|
||||
'seen_at' => 'datetime',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'deleted_at' => 'datetime',
|
||||
];
|
||||
|
||||
// * EVENTS
|
||||
|
||||
/**
|
||||
* The "booted" method of the model.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
self::assertFieldIsUnique('name', $model->name);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
self::updateSource($model);
|
||||
// FIXME: lier le produit à l'utilisateur connecté lors de l'ajout du système de connexion.
|
||||
$model->user_id = 1;
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
self::deleteOldDocuments($model, true);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture, true);
|
||||
self::updateSource($model);
|
||||
self::updateSpecifiedClaim($model);
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
FileStorageHelper::removeOldFile($model, 'picture');
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
self::deleteOldDocuments($model);
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the field is unique.
|
||||
*
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If field already exists.
|
||||
*/
|
||||
private static function assertFieldIsUnique(string $field, string $value, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, $field, $value, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data specified claim.
|
||||
*
|
||||
* @param \App\Models\Product $model
|
||||
* @return void
|
||||
*/
|
||||
private static function updateSpecifiedClaim(Product $model)
|
||||
{
|
||||
$check = false;
|
||||
foreach ($model->claims as $claimValue) {
|
||||
if (!$check) {
|
||||
$check = (intval($claimValue) === \App\Enums\Products\ClaimEnum::others->value) ? true : false;
|
||||
}
|
||||
}
|
||||
$model->specified_claim = ($check) ? $model->specified_claim : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update data source.
|
||||
*
|
||||
* @param self $model
|
||||
* @return void
|
||||
*/
|
||||
private static function updateSource(self $model)
|
||||
{
|
||||
if (
|
||||
$model->source === \App\Enums\Products\SourceEnum::showConsumer ||
|
||||
$model->source === \App\Enums\Products\SourceEnum::showCustomer ||
|
||||
$model->source === \App\Enums\Products\SourceEnum::showTrade
|
||||
) {
|
||||
$model->shop = null;
|
||||
$model->catalogue = null;
|
||||
} elseif ($model->source === \App\Enums\Products\SourceEnum::magazin) {
|
||||
$model->fair = null;
|
||||
$model->catalogue = null;
|
||||
} elseif ($model->source === \App\Enums\Products\SourceEnum::catalogArgumentary) {
|
||||
$model->fair = null;
|
||||
$model->shop = null;
|
||||
} else {
|
||||
$model->fair = null;
|
||||
$model->shop = null;
|
||||
$model->catalogue = null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete old documents.
|
||||
*
|
||||
* @param self $model
|
||||
* @param boolean $onlyIfUpdated
|
||||
* @return void
|
||||
*/
|
||||
public static function deleteOldDocuments(self $model, bool $onlyIfUpdated = false)
|
||||
{
|
||||
$original = \collect($model->getOriginal('docs'))->map(fn ($document) => strval($document));
|
||||
$new = \collect($model->docs)->map(fn ($document) => strval($document))->flip();
|
||||
foreach ($original as $document) {
|
||||
// * Ignore if document is to keep.
|
||||
if ($onlyIfUpdated and $new->has($document)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$originalPath = \ltrim(\urldecode($document), '/');
|
||||
if (\strpos($originalPath, 'storage') !== 0) {
|
||||
continue;
|
||||
}
|
||||
$originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath);
|
||||
if (Storage::disk('public')->exists($originalPath)) {
|
||||
Storage::disk('public')->delete($originalPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate product sheet PDF.
|
||||
*
|
||||
* @param self $model
|
||||
* @param string $title
|
||||
* @return \Mccarlosen\LaravelMpdf\LaravelMpdf
|
||||
*/
|
||||
public static function generateProductPdf(self $model, string $title): LaravelMpdf
|
||||
{
|
||||
/** @var \Mccarlosen\LaravelMpdf\LaravelMpdf */
|
||||
$mpdf = app('laravel-mpdf');
|
||||
/** @var \Mccarlosen\LaravelMpdf\LaravelMpdf */
|
||||
$pdf = $mpdf->loadView(
|
||||
'documents.product',
|
||||
// * Dirty convert to object.
|
||||
[
|
||||
'title' => $title,
|
||||
'product' => \json_decode(\json_encode(new ProductResource($model)))
|
||||
],
|
||||
[],
|
||||
[
|
||||
'mode' => 'utf-8',
|
||||
'format' => 'A4',
|
||||
'default_font_size' => '12',
|
||||
'default_font' => 'sans-serif',
|
||||
'mirrorMargins' => true,
|
||||
'margin_left' => 10,
|
||||
'margin_right' => 10,
|
||||
'margin_top' => 10,
|
||||
'margin_bottom' => 10,
|
||||
'margin_header' => 10,
|
||||
'margin_footer' => 10,
|
||||
'margin_top' => 10,
|
||||
'orientation' => 'L',
|
||||
'title' => $title,
|
||||
'author' => \config('app.name', 'Startcoq'),
|
||||
'watermark' => '',
|
||||
'show_watermark' => false,
|
||||
'watermark_font' => 'sans-serif',
|
||||
'display_mode' => 'fullpage',
|
||||
'watermark_text_alpha' => 0.1,
|
||||
'custom_font_dir' => '',
|
||||
'custom_font_data' => [],
|
||||
'auto_language_detection' => false,
|
||||
'temp_dir' => rtrim(
|
||||
ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(),
|
||||
DIRECTORY_SEPARATOR
|
||||
),
|
||||
'pdfa' => false,
|
||||
'pdfaauto' => false,
|
||||
]
|
||||
);
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a pdf with multiple products.
|
||||
*
|
||||
* @param array<integer> $printMultiProduct
|
||||
* @param string $title
|
||||
* @return \Mccarlosen\LaravelMpdf\LaravelMpdf
|
||||
*/
|
||||
public static function generateMultipleProductPdf(array $printMultiProduct, string $title)
|
||||
{
|
||||
/** @var \Mccarlosen\LaravelMpdf\LaravelMpdf|null */
|
||||
$pdf = null;
|
||||
// * Loop all Model ids.
|
||||
foreach ($printMultiProduct as $productId) {
|
||||
/** @var \App\Models\Product */
|
||||
$product = Product::query()->where('id', $productId)->firstOrFail();
|
||||
// * If not yet defined, set the first page.
|
||||
if (is_null($pdf)) {
|
||||
$pdf = Product::generateProductPdf($product, $title);
|
||||
} else {
|
||||
// * Add another page and add HTML from view to this.
|
||||
$pdf->getMpdf()->AddPage();
|
||||
$pdf->getMpdf()->WriteHTML((string)view(
|
||||
'documents.product-body',
|
||||
['product' => (object)(new ProductResource($product))->toArray()],
|
||||
));
|
||||
}
|
||||
}
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch and stream export file.
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return \Symfony\Component\HttpFoundation\StreamedResponse
|
||||
* @throws \Error If file storage config is wrong.
|
||||
*/
|
||||
public static function downloadPrivateExportFile(string $filepath): StreamedResponse
|
||||
{
|
||||
/** @var \Illuminate\Filesystem\FilesystemAdapter */
|
||||
$disk = Storage::disk('export');
|
||||
|
||||
if (!($disk instanceof \Illuminate\Filesystem\FilesystemAdapter)) {
|
||||
throw new \Error('Le disque de stockage doit être un système de fichiers local');
|
||||
}
|
||||
|
||||
if (!$disk->exists($filepath)) {
|
||||
\abort(400, trans('Le fichier demandé est périmé et n\'existe plus'));
|
||||
}
|
||||
|
||||
return \response()->stream(function () use ($disk, $filepath) {
|
||||
echo $disk->get($filepath);
|
||||
}, 200, [
|
||||
'Cache-Control' => 'public, must-revalidate, max-age=0',
|
||||
'Content-Description' => 'File Transfer',
|
||||
'Content-Type' => $disk->mimeType($filepath),
|
||||
'Expires' => 'Sat, 26 Jul 1997 05:00:00 GMT',
|
||||
'Last-Modified' => gmdate('D, d M Y H:i:s') . ' GMT'
|
||||
])->send();
|
||||
}
|
||||
|
||||
// * GETTERS
|
||||
|
||||
/**
|
||||
* Get origins_enum attribute
|
||||
*
|
||||
* @return \Illuminate\Support\Collection<\App\Enums\Ingredients\OriginEnum>
|
||||
*/
|
||||
public function getOriginsEnumAttribute(): Collection
|
||||
{
|
||||
return \collect($this->getAttribute('origins'))->filter()
|
||||
->map(fn (int $originEnumValue) => OriginEnum::make($originEnumValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set origins_enum attribute
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function setOriginsEnumAttribute(mixed $value): void
|
||||
{
|
||||
$this->setAttribute('origins', \collect($value)->filter()
|
||||
->map(fn ($originEnumValue) => OriginEnum::make($originEnumValue)->value())
|
||||
->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get claims_enum attribute
|
||||
*
|
||||
* @return \Illuminate\Support\Collection<\App\Enums\Ingredients\ClaimEnum>
|
||||
*/
|
||||
public function getClaimsEnumAttribute(): Collection
|
||||
{
|
||||
return \collect($this->getAttribute('claims'))->filter()
|
||||
->map(fn (int $originEnumValue) => ClaimEnum::make($originEnumValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set claims_enum attribute
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function setClaimsEnumAttribute(mixed $value): void
|
||||
{
|
||||
$this->setAttribute('claims', \collect($value)->filter()
|
||||
->map(fn ($claimEnumValue) => OriginEnum::make($claimEnumValue)->value())
|
||||
->all());
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function productComments()
|
||||
{
|
||||
return $this->hasMany(ProductComment::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function brand()
|
||||
{
|
||||
return $this->belongsTo(Brand::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* MorphToMany keywords relation.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function productKeywords()
|
||||
{
|
||||
return $this->morphToMany(Keyword::class, 'keywordable');
|
||||
}
|
||||
}
|
||||
59
app/Models/ProductComment.php
Normal file
59
app/Models/ProductComment.php
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $product_id
|
||||
* @property string $evaluation
|
||||
* @property string|null $aspect
|
||||
* @property string|null $taste
|
||||
* @property string|null $odor
|
||||
* @property string|null $texture
|
||||
* @property \Carbon\Carbon $created_at
|
||||
* @property \Carbon\Carbon $updated_at
|
||||
*
|
||||
* * RELATIONSHIPS
|
||||
* @property-read \App\Models\Product $product
|
||||
*/
|
||||
class ProductComment extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'product_id',
|
||||
'evaluation',
|
||||
'aspect',
|
||||
'taste',
|
||||
'odor',
|
||||
'texture',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function product()
|
||||
{
|
||||
return $this->belongsTo(Product::class);
|
||||
}
|
||||
}
|
||||
123
app/Models/Promo.php
Normal file
123
app/Models/Promo.php
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Promotions\SourceEnum;
|
||||
use App\Enums\Promotions\TargetEnum;
|
||||
use App\Enums\Promotions\AngleEnum;
|
||||
use App\Enums\Promotions\EventEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $user_id
|
||||
* @property integer $promo_type_id
|
||||
* @property integer $promo_distribution_network_id
|
||||
* @property integer $brand_id
|
||||
* @property string $date
|
||||
* @property boolean $source
|
||||
* @property string $shop_name
|
||||
* @property string $shop_city
|
||||
* @property string $sector_chief
|
||||
* @property string $name
|
||||
* @property string $target
|
||||
* @property string $angle
|
||||
* @property string $event
|
||||
* @property string $url
|
||||
* @property string $press
|
||||
* @property string $fair
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property string $deleted_at
|
||||
* @property Brand[] $brands
|
||||
* @property PromoType $promoType
|
||||
* @property PromoDistributionNetwork $promoDistributionNetwork
|
||||
* @property User $user
|
||||
* @property Brand $brand
|
||||
*/
|
||||
class Promo extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'promo_type_id',
|
||||
'promo_distribution_network_id',
|
||||
'brand_id',
|
||||
'date',
|
||||
'source',
|
||||
'shop_name',
|
||||
'shop_city',
|
||||
'sector_chief',
|
||||
'name',
|
||||
'target',
|
||||
'angle',
|
||||
'event',
|
||||
'url',
|
||||
'press',
|
||||
'fair',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'source' => SourceEnum::class,
|
||||
'target' => TargetEnum::class,
|
||||
'angle' => AngleEnum::class,
|
||||
'event' => EventEnum::class,
|
||||
'date' => 'datetime:Y-m-d',
|
||||
'created_at' => 'datetime:Y-m-d',
|
||||
'updated_at' => 'datetime:Y-m-d',
|
||||
'deleted_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function brands()
|
||||
{
|
||||
return $this->belongsToMany('App\Models\Brand', 'promo_brands');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function brand()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Brand');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function promoType()
|
||||
{
|
||||
return $this->belongsTo('App\Models\PromoType');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function promoDistributionNetwork()
|
||||
{
|
||||
return $this->belongsTo('App\Models\PromoDistributionNetwork');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('App\Models\User');
|
||||
}
|
||||
}
|
||||
47
app/Models/PromoDistributionNetwork.php
Normal file
47
app/Models/PromoDistributionNetwork.php
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Promotions\DistributionNetworkEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property boolean $name
|
||||
* @property string $label
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property Promo[] $promos
|
||||
*/
|
||||
class PromoDistributionNetwork extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'label',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'name' => DistributionNetworkEnum::class
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function promos()
|
||||
{
|
||||
return $this->hasMany('App\Models\Promo');
|
||||
}
|
||||
}
|
||||
54
app/Models/PromoType.php
Normal file
54
app/Models/PromoType.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Promotions\TypeEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property boolean $name
|
||||
* @property string $label
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property Promo[] $promos
|
||||
*/
|
||||
class PromoType extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'value',
|
||||
'label',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'value' => TypeEnum::class
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function promos()
|
||||
{
|
||||
return $this->hasMany('App\Models\Promo');
|
||||
}
|
||||
}
|
||||
70
app/Models/Survey.php
Normal file
70
app/Models/Survey.php
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Surveys\CustomerEnum;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* @property integer $user_id
|
||||
* @property string $name
|
||||
* @property string $author
|
||||
* @property string $comment
|
||||
* @property string $date
|
||||
* @property string $documents
|
||||
* @property boolean $is_distributor
|
||||
* @property boolean $is_customer
|
||||
* @property boolean $type
|
||||
* @property string $created_at
|
||||
* @property string $updated_at
|
||||
* @property string $deleted_at
|
||||
* @property User $user
|
||||
*/
|
||||
class Survey extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'name',
|
||||
'author',
|
||||
'comment',
|
||||
'date',
|
||||
'documents',
|
||||
'is_distributor',
|
||||
'is_customer',
|
||||
'type',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
'deleted_at'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'date' => 'datetime:Y-m-d',
|
||||
'documents' => 'json',
|
||||
'is_distributor' => 'boolean',
|
||||
'is_customer' => 'boolean',
|
||||
'type' => CustomerEnum::class,
|
||||
'created_at' => 'datetime:Y-m-d',
|
||||
'updated_at' => 'datetime:Y-m-d',
|
||||
'deleted_at' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('App\Models\User');
|
||||
}
|
||||
}
|
||||
43
app/Models/User.php
Normal file
43
app/Models/User.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasFactory;
|
||||
use Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'email_verified_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
42
app/Providers/AppServiceProvider.php
Normal file
42
app/Providers/AppServiceProvider.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// * LOCAL HTTPS FIX
|
||||
URL::forceScheme('https');
|
||||
|
||||
// * HA PROXY FIX
|
||||
App::singleton('clientIp', function () {
|
||||
if (
|
||||
config('app.env') === 'local'
|
||||
) {
|
||||
return request()->getClientIp();
|
||||
} else {
|
||||
return request()->server('HTTP_X_FORWARDED_FOR') ??
|
||||
request()->getClientIp();
|
||||
}
|
||||
});
|
||||
|
||||
// * LOCAL FORCE eg: for Carbon
|
||||
setlocale(LC_ALL, \sprintf(
|
||||
'%s_%s.UTF-8',
|
||||
config('app.locale'),
|
||||
\strtoupper(config('app.locale'))
|
||||
));
|
||||
Carbon::setLocale(config('app.locale'));
|
||||
}
|
||||
}
|
||||
24
app/Providers/AuthServiceProvider.php
Normal file
24
app/Providers/AuthServiceProvider.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The model to policy mappings for the application.
|
||||
*
|
||||
* @var array<class-string, class-string>
|
||||
*/
|
||||
protected $policies = [];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
38
app/Providers/EventServiceProvider.php
Normal file
38
app/Providers/EventServiceProvider.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event to listener mappings for the application.
|
||||
*
|
||||
* @var array<class-string, array<int, class-string>>
|
||||
*/
|
||||
protected $listen = [
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if events and listeners should be automatically discovered.
|
||||
* @return boolean
|
||||
*/
|
||||
public function shouldDiscoverEvents(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
93
app/Providers/RouteServiceProvider.php
Normal file
93
app/Providers/RouteServiceProvider.php
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The path to the "home" route for your application.
|
||||
*
|
||||
* This is used by Laravel authentication to redirect users after login.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const HOME = '/';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->configureRateLimiting();
|
||||
|
||||
$this->cspReportRoute();
|
||||
|
||||
$this->routes(function () {
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the rate limiters for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(150)->by(optional($request->user())->id ?: app('clientIp'));
|
||||
});
|
||||
RateLimiter::for('web', function (Request $request) {
|
||||
return Limit::perMinute(150)->by(optional($request->user())->id ?: app('clientIp'));
|
||||
});
|
||||
RateLimiter::for('credentials', function (Request $request) {
|
||||
return Limit::perMinute(30)->by(optional($request->user())->id ?: $request->ip());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* * Register CSP report URI
|
||||
*
|
||||
* Put this function inside RouteServiceProvider,
|
||||
* and call it in RouteServiceProvider::boot
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cspReportRoute()
|
||||
{
|
||||
// * CSP REPORT URI, Please add /csp-report to VerifyCsrfToken $except middleware
|
||||
\Illuminate\Support\Facades\Route::post('/csp-report', function () {
|
||||
$isCspRequest = request()->header('Content-Type') === 'application/csp-report';
|
||||
if (!$isCspRequest or !request()->json('csp-report') or !is_array(request()->json('csp-report'))) {
|
||||
abort(401);
|
||||
}
|
||||
// * Flatten if needed
|
||||
$cspReport = collect(request()->json('csp-report'))
|
||||
->flatMap(function ($v, $k) {
|
||||
return [$k => is_array($v) ? collect($v)->flatten()->implode(', ') : $v];
|
||||
})->all();
|
||||
// * Filter array keys
|
||||
$cspReport = collect(array_intersect_key($cspReport, [
|
||||
'blocked-uri' => '', 'document-uri' => '', 'original-policy' => '',
|
||||
'referrer' => '', 'violated-directive' => ''
|
||||
]));
|
||||
\Illuminate\Support\Facades\Log::warning(sprintf(
|
||||
"CSP REPORT for %s : \n %s",
|
||||
$cspReport->get('document-uri'),
|
||||
$cspReport->implode("\n")
|
||||
));
|
||||
})->middleware('throttle:5,2')
|
||||
->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class)
|
||||
->name('cspReportUri');
|
||||
}
|
||||
}
|
||||
78
app/Providers/TranslationServiceProvider.php
Normal file
78
app/Providers/TranslationServiceProvider.php
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
class TranslationServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
foreach ($this->collectLocalesStrings() as $locale) {
|
||||
// * Suported locales
|
||||
Cache::rememberForever(sprintf('translations.%s', $locale), function () use ($locale) {
|
||||
$translations = [
|
||||
'php' => $this->phpTranslations($locale),
|
||||
'json' => $this->jsonTranslations($locale),
|
||||
];
|
||||
return $translations;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather provided langs checking from files
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
private function collectLocalesStrings(): Collection
|
||||
{
|
||||
return collect(File::allFiles(resource_path('lang/')))->flatMap(function (SplFileInfo $file) {
|
||||
if ($file->getRelativePath() and \strpos($file->getRelativePath(), 'vendor') !== 0) {
|
||||
return [$file->getRelativePath() => ''];
|
||||
}
|
||||
})->keys();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather all php translations
|
||||
*
|
||||
* @param string $locale
|
||||
* @return array
|
||||
*/
|
||||
private function phpTranslations(string $locale): array
|
||||
{
|
||||
$path = resource_path("lang/$locale");
|
||||
|
||||
return collect(File::allFiles($path))->flatMap(function ($file) use ($locale) {
|
||||
$translation = $file->getBasename('.php');
|
||||
return [$translation => trans($translation, [], $locale)];
|
||||
})->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gather json translations
|
||||
*
|
||||
* @param string $locale
|
||||
* @return array
|
||||
*/
|
||||
private function jsonTranslations(string $locale): array
|
||||
{
|
||||
$path = resource_path("lang/$locale.json");
|
||||
|
||||
if (is_string($path) && is_readable($path)) {
|
||||
return json_decode(file_get_contents($path), true) ?? [];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
19
app/Providers/ViewServiceProvider.php
Normal file
19
app/Providers/ViewServiceProvider.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class ViewServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Paginator::defaultview('modules.pagination');
|
||||
}
|
||||
}
|
||||
26
app/Traits/Controllers/HasDocuments.php
Normal file
26
app/Traits/Controllers/HasDocuments.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Controllers;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
trait HasDocuments
|
||||
{
|
||||
/**
|
||||
* Save Model Images
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @return void
|
||||
*/
|
||||
private function storeDocuments(Request $request, Model $model): void
|
||||
{
|
||||
$model->docs = [];
|
||||
$model->docs = collect($request->docs)
|
||||
->map(function ($document) use ($model) {
|
||||
return FileStorageHelper::storeFile($model, $document);
|
||||
})->all();
|
||||
}
|
||||
}
|
||||
229
app/Traits/Enums/BaseEnum.php
Normal file
229
app/Traits/Enums/BaseEnum.php
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Enums;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
|
||||
*/
|
||||
trait BaseEnum
|
||||
{
|
||||
// * GENERIC METHODS
|
||||
/**
|
||||
* Get enum value
|
||||
*
|
||||
* @return string|integer
|
||||
*/
|
||||
public function value(): string|int
|
||||
{
|
||||
return self::toValues()[$this->name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enum label
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function label(): string
|
||||
{
|
||||
return self::toLabels()[$this->name];
|
||||
}
|
||||
|
||||
// * STATIC FETCH METHODS
|
||||
|
||||
/**
|
||||
* Get enum labels
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function toLabels(): array
|
||||
{
|
||||
if (empty(self::getCache('labels'))) {
|
||||
$labels = self::getLabelsConstant();
|
||||
self::setCache('labels', \collect(self::cases())
|
||||
->mapWithKeys(function ($enum) use ($labels) {
|
||||
if (!\is_null($labels) and !isset($labels[$enum->name])) {
|
||||
throw new \Error('Missing definition label ' . self::class . " for {$enum->name}");
|
||||
}
|
||||
return [$enum->name => \trans(
|
||||
!\is_null($labels) ? $labels[$enum->name] : Str::ucfirst($enum->name)
|
||||
)];
|
||||
})->all());
|
||||
}
|
||||
return self::getCache('labels');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enum values
|
||||
*
|
||||
* @return array<integer|string>
|
||||
*/
|
||||
public static function toValues(): array
|
||||
{
|
||||
if (empty(self::getCache('values'))) {
|
||||
self::setCache('values', \collect(self::cases())
|
||||
->mapWithKeys(fn ($enum) => [$enum->name => $enum->value])->all());
|
||||
}
|
||||
return self::getCache('values');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enums
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function toArray(): array
|
||||
{
|
||||
if (empty(self::getCache('enums'))) {
|
||||
$definitions = self::getCustomDefinitions();
|
||||
self::setCache('enums', \collect(self::cases())
|
||||
->mapWithKeys(function ($enum) use ($definitions) {
|
||||
$baseArray = [
|
||||
'name' => $enum->name,
|
||||
'label' => $enum->label(),
|
||||
'value' => $enum->value,
|
||||
];
|
||||
foreach ($definitions as $index => $elements) {
|
||||
$index = Str::lower(Str::singular($index));
|
||||
if (!isset($elements[$enum->name])) {
|
||||
throw new \Error("Missing definition {$index} " . self::class . " for {$enum->name}");
|
||||
}
|
||||
$baseArray[$index] = $elements[$enum->name];
|
||||
}
|
||||
return [$enum->name => (object)$baseArray];
|
||||
})->all());
|
||||
}
|
||||
return self::getCache('enums');
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch enum using value
|
||||
*
|
||||
* @param \App\Traits\Enums\BaseEnum|integer|string $value
|
||||
* @return \App\Traits\Enums\BaseEnum|null
|
||||
*/
|
||||
public static function make(self|int|string $value): self|null
|
||||
{
|
||||
return !\is_object($value) ?
|
||||
\collect(self::cases())->keyBy('value')->get($value) : $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if 2 enum are equals
|
||||
*
|
||||
* @param object ...$others
|
||||
* @return boolean
|
||||
* @throws \Error If provided element are not enums taht extends BaseEnum.
|
||||
*/
|
||||
public function equals(object ...$others): bool
|
||||
{
|
||||
foreach ($others as $other) {
|
||||
if (!\in_array(BaseEnum::class, \class_uses_recursive($other))) {
|
||||
throw new \Error(__METHOD__ .
|
||||
' requires $enum to use ' . BaseEnum::class);
|
||||
}
|
||||
if (
|
||||
get_class($this) === get_class($other)
|
||||
&& $this->value === $other->value
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// * CACHE
|
||||
|
||||
/**
|
||||
* Get cache array
|
||||
*
|
||||
* @param string $index
|
||||
* @return array
|
||||
*/
|
||||
private static function getCache(string $index): array
|
||||
{
|
||||
self::initCache();
|
||||
if (!isset($GLOBALS[self::class]['props'][$index])) {
|
||||
$GLOBALS[self::class]['props'][$index] = [];
|
||||
}
|
||||
return $GLOBALS[self::class]['props'][$index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cache array
|
||||
*
|
||||
* @param string $index
|
||||
* @param array $items
|
||||
* @return void
|
||||
*/
|
||||
private static function setCache(string $index, array $items): void
|
||||
{
|
||||
self::initCache();
|
||||
if (!isset($GLOBALS[self::class]['props'][$index])) {
|
||||
$GLOBALS[self::class]['props'][$index] = [];
|
||||
}
|
||||
$GLOBALS[self::class]['props'][$index] = $items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init cache if needed
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function initCache(): void
|
||||
{
|
||||
if (!isset($GLOBALS[self::class])) {
|
||||
$GLOBALS[self::class] = [
|
||||
'props' => [
|
||||
'enums' => [],
|
||||
'values' => [],
|
||||
'labels' => []
|
||||
],
|
||||
'customProps' => null
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// * PRIVATE
|
||||
|
||||
/**
|
||||
* Fetch the LABELS constant if exists
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private static function getLabelsConstant(): array|null
|
||||
{
|
||||
$class_name = self::class;
|
||||
try {
|
||||
$constant_reflex = new \ReflectionClassConstant($class_name, 'LABELS');
|
||||
return $constant_reflex->getValue();
|
||||
} catch (\ReflectionException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom constants
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private static function getCustomDefinitions(): array
|
||||
{
|
||||
// ? Has custom props in cache ?
|
||||
if (\is_null($GLOBALS[self::class]['customProps'])) {
|
||||
$names = \collect(self::cases())->keyBy('name');
|
||||
|
||||
$GLOBALS[self::class]['customProps'] = \collect((new \ReflectionClass(self::class))->getConstants())
|
||||
->filter(function (mixed $constant, string $key) use ($names) {
|
||||
if ($key === 'LABELS' or !\is_array($constant)) {
|
||||
return false;
|
||||
}
|
||||
return \collect($constant)->keys()
|
||||
->filter(fn (string $name) => $names->has($name))->count();
|
||||
})->all();
|
||||
}
|
||||
return $GLOBALS[self::class]['customProps'];
|
||||
}
|
||||
}
|
||||
112
app/Traits/Requests/HasDocumentAndPictureList.php
Normal file
112
app/Traits/Requests/HasDocumentAndPictureList.php
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Requests;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait HasDocumentAndPictureList
|
||||
{
|
||||
/**
|
||||
* Autorised mimtypes
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $autorisedMimTypes = [
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'application/pdf',
|
||||
'application/vnd.ms-powerpoint',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
'application/vnd.ms-excel',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
'text/csv',
|
||||
'text/plain',
|
||||
];
|
||||
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function documentsRules(
|
||||
string $name = 'docs',
|
||||
): array {
|
||||
return [
|
||||
"{$name}" => 'present|array',
|
||||
"{$name}.*" => [
|
||||
'required',
|
||||
function (string $attribute, $value, callable $fail) {
|
||||
return $this->validateDocument($attribute, $value, $fail);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate picture.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param callback $fail
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
*/
|
||||
private function validateDocument(string $attribute, mixed $value, callable $fail)
|
||||
{
|
||||
// phpcs:enable
|
||||
if (!($useStorage = Storage::exists($value)) and !File::exists($value)) {
|
||||
$fail(trans(":attribute n'existe pas dans le système de fichier"));
|
||||
return;
|
||||
}
|
||||
if (!\in_array($this->getUploadedFileMimeType($value, $useStorage), $this->autorisedMimTypes)) {
|
||||
$fail(trans(':attribute doit être un document de type :mimetypes', [
|
||||
'mimetypes' => Str::of($this->autorisedMimTypes)->implode(' ou ')
|
||||
]));
|
||||
}
|
||||
$maxSize = 6 * 1024 * 1024;
|
||||
if (File::size($value) > $maxSize) {
|
||||
$fail(trans(':attribute doit être inférieur à :size Mo', ['size' => $maxSize / (1024 * 1024)]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges docs fields with request data.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergeDocuments(string $name = 'docs'): void
|
||||
{
|
||||
$documents = $this->{$name};
|
||||
$this->merge([$name => $this->{$name} ?? []]);
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
if (is_array($documents)) {
|
||||
foreach ($documents as $key => $documentPath) {
|
||||
// ! Empty document.
|
||||
if ($documentPath === 'false') {
|
||||
// * Empty document and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($documentPath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedDocument = $this->{$name};
|
||||
$changedDocument[$key] = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedDocument
|
||||
]);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
}
|
||||
261
app/Traits/Requests/HasPicture.php
Normal file
261
app/Traits/Requests/HasPicture.php
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Requests;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
trait HasPicture
|
||||
{
|
||||
/**
|
||||
* Minimum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minWith = 100;
|
||||
|
||||
/**
|
||||
* Maximum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxWith = 3840;
|
||||
|
||||
/**
|
||||
* Minimum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minHeight = 100;
|
||||
|
||||
/**
|
||||
* Maximum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxHeight = 2160;
|
||||
|
||||
/**
|
||||
* Image rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param boolean $required
|
||||
* @param integer|null $minWith
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWith
|
||||
* @param integer|null $maxHeight
|
||||
* @return array
|
||||
*/
|
||||
public function pictureRules(
|
||||
string $name = 'picture',
|
||||
bool $required = true,
|
||||
?int $minWith = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWith = null,
|
||||
?int $maxHeight = null
|
||||
): array {
|
||||
$sizes = (object)[
|
||||
'minWith' => $minWith ?? $this->minWith,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWith' => $maxWith ?? $this->maxWith,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight
|
||||
];
|
||||
return [
|
||||
$name => [
|
||||
$required ? 'required' : 'nullable',
|
||||
function (string $attribute, $value, callable $fail) use ($sizes) {
|
||||
return $this->validatePicture($attribute, $value, $fail, $sizes);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param boolean $required
|
||||
* @param integer|null $limitMin
|
||||
* @param integer|null $limitMax
|
||||
* @param integer|null $minWith
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWith
|
||||
* @param integer|null $maxHeight
|
||||
* @return array
|
||||
*/
|
||||
public function picturesRules(
|
||||
string $name = 'pictures',
|
||||
?bool $required = null,
|
||||
?int $limitMin = null,
|
||||
?int $limitMax = null,
|
||||
?int $minWith = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWith = null,
|
||||
?int $maxHeight = null
|
||||
): array {
|
||||
$sizes = (object)[
|
||||
'required' => $required ?? $this->required,
|
||||
'limitMin' => $limitMin ?? $this->limitMin,
|
||||
'limitMax' => $limitMax ?? $this->limitMax,
|
||||
'minWith' => $minWith ?? $this->minWith,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWith' => $maxWith ?? $this->maxWith,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight
|
||||
];
|
||||
return [
|
||||
"{$name}" => ($required ? 'required' : 'nullable') . "|array|between:$limitMin,$limitMax",
|
||||
"{$name}.*" => [
|
||||
($required ? 'required' : 'nullable'),
|
||||
function (string $attribute, $value, callable $fail) use ($sizes) {
|
||||
return $this->validatePicture($attribute, $value, $fail, $sizes);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate picture.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param callback $fail
|
||||
* @param object $sizes
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
*/
|
||||
private function validatePicture(string $attribute, mixed $value, callable $fail, object $sizes)
|
||||
{
|
||||
if (!($useStorage = Storage::exists($value)) and !File::exists($value)) {
|
||||
$fail(trans(":attribute n'existe pas dans le système de fichier"));
|
||||
return;
|
||||
}
|
||||
/** @var string */
|
||||
$fileFullPath = $this->getUploadedFileFullPath($value, $useStorage);
|
||||
if (
|
||||
!\in_array($this->getUploadedFileMimeType($value, $useStorage), [
|
||||
'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'"));
|
||||
}
|
||||
$filesize = \filesize($fileFullPath);
|
||||
$maxSize = 6 * 1024 * 1024;
|
||||
if ($filesize === false || $filesize > $maxSize) {
|
||||
$fail(trans(':attribute doit être inférieur à :size Mo', ['size' => $maxSize / (1024 * 1024)]));
|
||||
}
|
||||
if (!($dimensions = \getimagesize($fileFullPath)) or !isset($dimensions[0]) or !isset($dimensions[1])) {
|
||||
$fail(trans(":attribute impossible de valider les dimensions"));
|
||||
} else {
|
||||
$width = intval($dimensions[0]);
|
||||
$height = intval($dimensions[1]);
|
||||
|
||||
$xMessage = trans(
|
||||
":attribute la largeur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
);
|
||||
if ($sizes->minWith === $sizes->maxWith) {
|
||||
$xMessage = trans(":attribute la largeur doit être de {$sizes->minWith}px");
|
||||
}
|
||||
$yMessage = trans(
|
||||
":attribute la hauteur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
);
|
||||
if ($sizes->minHeight === $sizes->maxHeight) {
|
||||
$yMessage = trans(":attribute la hauteur doit être de {$sizes->minWith}px");
|
||||
}
|
||||
if (($width < $sizes->minWith) or ($width > $sizes->maxWith)) {
|
||||
$fail($xMessage);
|
||||
}
|
||||
if (($height < $sizes->minHeight) or ($height > $sizes->maxHeight)) {
|
||||
$fail($yMessage);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges picture field with request data.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePicture(string $name = 'picture'): void
|
||||
{
|
||||
$picturePath = $this->{$name};
|
||||
if (is_string($picturePath)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedPicture
|
||||
]);
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges pictures fields with request data.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePictures(string $name = 'pictures'): void
|
||||
{
|
||||
$pictures = $this->{$name};
|
||||
if (is_array($pictures)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
foreach ($pictures as $key => $picturePath) {
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = $this->{$name};
|
||||
$changedPicture[$key] = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedPicture
|
||||
]);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file full path.
|
||||
*
|
||||
* @param string $value
|
||||
* @param boolean $useStorage
|
||||
* @return string
|
||||
*/
|
||||
private function getUploadedFileFullPath(string $value, bool $useStorage): string
|
||||
{
|
||||
return !$useStorage ? (realpath($value) ?: $value) : Storage::path($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the file mimetype.
|
||||
*
|
||||
* @param string $value
|
||||
* @param boolean $useStorage
|
||||
* @return string|false
|
||||
*/
|
||||
private function getUploadedFileMimeType(string $value, bool $useStorage)
|
||||
{
|
||||
return !$useStorage ? File::mimeType($value) : Storage::mimeType($value);
|
||||
}
|
||||
}
|
||||
53
artisan
Executable file
53
artisan
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any of our classes manually. It's great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Artisan Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When we run the console application, the current CLI command will be
|
||||
| executed in this console and the response sent back to a terminal
|
||||
| or another output device for the developers. Here goes nothing!
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||
|
||||
$status = $kernel->handle(
|
||||
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||
new Symfony\Component\Console\Output\ConsoleOutput
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running, we will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel->terminate($input, $status);
|
||||
|
||||
exit($status);
|
||||
55
bootstrap/app.php
Normal file
55
bootstrap/app.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Create The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The first thing we will do is create a new Laravel application instance
|
||||
| which serves as the "glue" for all the components of Laravel, and is
|
||||
| the IoC container for the system binding all of the various parts.
|
||||
|
|
||||
*/
|
||||
|
||||
$app = new Illuminate\Foundation\Application(
|
||||
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bind Important Interfaces
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, we need to bind some important interfaces into the container so
|
||||
| we will be able to resolve them when needed. The kernels serve the
|
||||
| incoming requests to this application from both the web and CLI.
|
||||
|
|
||||
*/
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Http\Kernel::class,
|
||||
App\Http\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Console\Kernel::class,
|
||||
App\Console\Kernel::class
|
||||
);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||
App\Exceptions\Handler::class
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Return The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This script returns the application instance. The instance is given to
|
||||
| the calling script so we can separate the building of the instances
|
||||
| from the actual running of the application and sending responses.
|
||||
|
|
||||
*/
|
||||
|
||||
return $app;
|
||||
2
bootstrap/cache/.gitignore
vendored
Normal file
2
bootstrap/cache/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
||||
91
composer.json
Normal file
91
composer.json
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The Laravel Framework.",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"carlos-meneses/laravel-mpdf": "^2.1",
|
||||
"diglactic/laravel-breadcrumbs": "^8.0",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"kwaadpepper/image-resizer": "^2.1",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/tinker": "^2.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"kitloong/laravel-migrations-generator": "^6.8",
|
||||
"krlove/eloquent-model-generator": "dev-l10-compatibility",
|
||||
"laravel-lang/common": "^4.0",
|
||||
"laravel/pint": "^1.0",
|
||||
"laravel/sail": "^1.18",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^7.0",
|
||||
"phpunit/phpunit": "^10.0",
|
||||
"slevomat/coding-standard": "^8.12",
|
||||
"spatie/laravel-ignition": "^2.0",
|
||||
"thedoctor0/laravel-factory-generator": "^1.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/laravel-shift/eloquent-model-generator.git"
|
||||
}
|
||||
],
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"refresh": [
|
||||
"@php artisan optimize:clear",
|
||||
"@php artisan clear-compiled",
|
||||
"@php artisan view:clear",
|
||||
"@php artisan route:clear",
|
||||
"@php artisan config:clear",
|
||||
"@php artisan cache:clear"
|
||||
],
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
],
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": []
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
10313
composer.lock
generated
Normal file
10313
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
216
config/app.php
Normal file
216
config/app.php
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Name
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value is the name of your application. This value is used when the
|
||||
| framework needs to place the application's name in a notification or
|
||||
| any other location as required by the application or its packages.
|
||||
|
|
||||
*/
|
||||
|
||||
'name' => env('APP_NAME', 'Laravel'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Environment
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value determines the "environment" your application is currently
|
||||
| running in. This may determine how you prefer to configure various
|
||||
| services the application utilizes. Set this in your ".env" file.
|
||||
|
|
||||
*/
|
||||
|
||||
'env' => env('APP_ENV', 'production'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Debug Mode
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When your application is in debug mode, detailed error messages with
|
||||
| stack traces will be shown on every error that occurs within your
|
||||
| application. If disabled, a simple generic error page is shown.
|
||||
|
|
||||
*/
|
||||
|
||||
'debug' => (bool)env('APP_DEBUG', false),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application URL
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This URL is used by the console to properly generate URLs when using
|
||||
| the Artisan command line tool. You should set this to the root of
|
||||
| your application so that it is used when running Artisan tasks.
|
||||
|
|
||||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
'asset_url' => env('ASSET_URL', '/'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify the default timezone for your application, which
|
||||
| will be used by the PHP date and date-time functions. We have gone
|
||||
| ahead and set this to a sensible default for you out of the box.
|
||||
|
|
||||
*/
|
||||
|
||||
'timezone' => 'Europe/Paris',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Locale Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The application locale determines the default locale that will be used
|
||||
| by the translation service provider. You are free to set this value
|
||||
| to any of the locales which will be supported by the application.
|
||||
|
|
||||
*/
|
||||
|
||||
'locale' => 'fr',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Fallback Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The fallback locale determines the locale to use when the current one
|
||||
| is not available. You may change the value to correspond to any of
|
||||
| the language folders that are provided through your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'fallback_locale' => 'fr',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Faker Locale
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This locale will be used by the Faker PHP library when generating fake
|
||||
| data for your database seeds. For example, this will be used to get
|
||||
| localized telephone numbers, street address information and more.
|
||||
|
|
||||
*/
|
||||
|
||||
'faker_locale' => 'fr_FR',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Encryption Key
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This key is used by the Illuminate encrypter service and should be set
|
||||
| to a random, 32 character string, otherwise these encrypted strings
|
||||
| will not be safe. Please do this before deploying an application!
|
||||
|
|
||||
*/
|
||||
|
||||
'key' => env('APP_KEY'),
|
||||
|
||||
'cipher' => 'AES-256-CBC',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Maintenance Mode Driver
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These configuration options determine the driver used to determine and
|
||||
| manage Laravel's "maintenance mode" status. The "cache" driver will
|
||||
| allow maintenance mode to be controlled across multiple machines.
|
||||
|
|
||||
| Supported drivers: "file", "cache"
|
||||
|
|
||||
*/
|
||||
|
||||
'maintenance' => [
|
||||
'driver' => 'file',
|
||||
// 'store' 'redis'
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Autoloaded Service Providers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The service providers listed here will be automatically loaded on the
|
||||
| request to your application. Feel free to add your own services to
|
||||
| this array to grant expanded functionality to your applications.
|
||||
|
|
||||
*/
|
||||
|
||||
'providers' => [
|
||||
|
||||
/*
|
||||
* Laravel Framework Service Providers...
|
||||
*/
|
||||
|
||||
Illuminate\Auth\AuthServiceProvider::class,
|
||||
Illuminate\Broadcasting\BroadcastServiceProvider::class,
|
||||
Illuminate\Bus\BusServiceProvider::class,
|
||||
Illuminate\Cache\CacheServiceProvider::class,
|
||||
Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class,
|
||||
Illuminate\Cookie\CookieServiceProvider::class,
|
||||
Illuminate\Database\DatabaseServiceProvider::class,
|
||||
Illuminate\Encryption\EncryptionServiceProvider::class,
|
||||
Illuminate\Filesystem\FilesystemServiceProvider::class,
|
||||
Illuminate\Foundation\Providers\FoundationServiceProvider::class,
|
||||
Illuminate\Hashing\HashServiceProvider::class,
|
||||
Illuminate\Mail\MailServiceProvider::class,
|
||||
Illuminate\Notifications\NotificationServiceProvider::class,
|
||||
Illuminate\Pagination\PaginationServiceProvider::class,
|
||||
Illuminate\Pipeline\PipelineServiceProvider::class,
|
||||
Illuminate\Queue\QueueServiceProvider::class,
|
||||
Illuminate\Redis\RedisServiceProvider::class,
|
||||
Illuminate\Auth\Passwords\PasswordResetServiceProvider::class,
|
||||
Illuminate\Session\SessionServiceProvider::class,
|
||||
Illuminate\Translation\TranslationServiceProvider::class,
|
||||
Illuminate\Validation\ValidationServiceProvider::class,
|
||||
Illuminate\View\ViewServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Package Service Providers...
|
||||
*/
|
||||
|
||||
Krlove\EloquentModelGenerator\Provider\GeneratorServiceProvider::class,
|
||||
|
||||
/*
|
||||
* Application Service Providers...
|
||||
*/
|
||||
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
App\Providers\TranslationServiceProvider::class,
|
||||
App\Providers\ViewServiceProvider::class,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Class Aliases
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This array of class aliases will be registered when this application
|
||||
| is started. However, feel free to register as many as you wish as
|
||||
| the aliases are "lazy" loaded so they don't hinder performance.
|
||||
|
|
||||
*/
|
||||
|
||||
'aliases' => Facade::defaultAliases()->merge([])->toArray(),
|
||||
];
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue