add CRUD Products - #5

This commit is contained in:
Alexis Gousseau 2023-06-21 10:24:56 +00:00 • committed by Vincent BRIDIER
parent 151e24d775
commit 7e8c4e7efd
223 changed files with 20732 additions and 3259 deletions

View file

@ -16,3 +16,6 @@ indent_size = 2
[docker-compose.yml]
indent_size = 4
[*.vue]
indent_size = 2

55
.eslintrc.json Normal file
View 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"]
]
}
}
}
}

1
.gitignore vendored
View file

@ -17,3 +17,4 @@ yarn-error.log
/.fleet
/.idea
/.vscode
.stylelintcache

41
.stylelintrc.json Normal file
View 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"
]
}
]
}
}

View file

@ -1,66 +1,9 @@
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400" alt="Laravel Logo"></a></p>
# StartCoQ
<p align="center">
<a href="https://github.com/laravel/framework/actions"><img src="https://github.com/laravel/framework/workflows/tests/badge.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
</p>
## About Laravel
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Learning Laravel
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch.
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 2000 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Laravel Sponsors
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
### Premium Partners
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Cubet Techno Labs](https://cubettech.com)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[Many](https://www.many.co.uk)**
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
- **[DevSquad](https://devsquad.com)**
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
- **[OP.GG](https://op.gg)**
- **[WebReinvent](https://webreinvent.com/?utm_source=laravel&utm_medium=github&utm_campaign=patreon-sponsors)**
- **[Lendio](https://lendio.com)**
## Contributing
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
## License
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
## Installation
```
cp .env.example .env
php artisan key:generate
php artisan storage:link
php artisan migrate [--seed]
```

View file

@ -20,7 +20,7 @@ class Kernel extends ConsoleKernel
*/
protected function commands(): void
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}

View 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',
];
}

View 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',
];
}

View file

@ -1,40 +0,0 @@
<?php
namespace App\Enums\Ingredients;
enum IngredientFamilyEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::alcool => __('Alcool'),
self::flavor => __('Arôme'),
self::naturalCasings => __('Boyaux Naturels'),
self::groceryStore => __('Épicerie'),
self::spicesHerbs => __('Épices et aromates'),
self::flourBreadcrumbs => __('Farine / Chapelure'),
self::fruitsVegetables => __('Fruits et légumes'),
self::aromaticMixMarinade => __('Mix aromatique / Marinade'),
self::functionalMixSimpleIngredients => __('Mix fonctionnel / ingredients simples'),
self::meatProducts => __('Produits carnés'),
self::sauces => __('Sauces'),
self::dairyProducts => __('Produits laitiers'),
};
}
}

View file

@ -1,42 +0,0 @@
<?php
namespace App\Enums\Ingredients;
enum IngredientFunctionEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::dye => __('Colorant'),
self::antiOxidant => __('Anti-oxidant'),
self::starch => __('Amidon'),
self::curator => __('Conservateur'),
self::freezingAgent => __('Gelifiant'),
self::sweetener => __('Edulcorant'),
self::stabilizer => __('Stabilisateur'),
self::emulsifier => __('Emulsifiant'),
self::thickener => __('Epaississant'),
self::sugar => __('Sucre'),
self::salt => __('Sel'),
self::fiber => __('Fibre'),
self::protein => __('Protéine'),
};
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace App\Enums\Ingredients;
enum IngredientOriginEnum : int {
case magazine = 1;
case photo = 2;
case web = 3;
case mail = 4;
case salon = 5;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::magazine => __('Magazine'),
self::photo => __('Photo de rayon'),
self::web => __('Web'),
self::mail => __('Fournisseur/Mail'),
self::salon => __('Salon')
};
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace App\Enums\Ingredients;
enum IngredientPackagingEnum : int {
case fees = 1;
case pasteurizedSterilized = 2;
case frozen = 3;
case bagDehydrated = 4;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::fees => __('Frais'),
self::pasteurizedSterilized => __('Pasteurisé / Stérilisé'),
self::frozen => __('Surgelé'),
self::bagDehydrated => __('Sac / déshydraté'),
};
}
}

View 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'
];
}

View 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é',
];
}

View file

@ -2,41 +2,42 @@
namespace App\Enums;
enum KeywordCategoryEnum : string {
case ingredientPleasure = "ingredient_pleasure";
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";
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::ingredientPleasure => __('IngredientPleasure'),
self::ingredientPracticality => __('IngredientPracticality'),
self::ingredientHealth => __('IngredientHealth'),
self::ingredienEthics => __('IngredientEthics'),
self::productPleasure => __('ProductPleasure'),
self::productShape => __('ProductShape'),
self::productPracticality => __('ProductPracticality'),
self::productHealth => __('ProductHealth'),
self::productEthics => __('ProductEthics'),
self::packagingPleasure => __('PackagingPleasure'),
self::packagingPracticality => __('PackagingPracticality'),
self::packagingEthics => __('PackagingEthics'),
self::packagingDesign => __('PackagingDesign'),
};
}
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',
];
}

View 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'
];
}

View 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'
];
}

View 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'
];
}

View file

@ -1,36 +0,0 @@
<?php
namespace App\Enums\Packaging;
enum PackagingBenefitEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::resistance => __('Résistance'),
self::recyclability => __('Recyclabilité'),
self::presentation => __('Présentation'),
self::openingClosing => __('Ouverture/Fermeture'),
self::toaster => __('Grille pain'),
self::traditionalOven => __('Four Traditionnel'),
self::microwaveOven => __('Four micro-onde'),
self::communication => __('Communication'),
self::barbecue => __('Barbecue'),
self::others => __('Autres')
};
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace App\Enums\Packaging;
enum PackagingMaterialEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::plactic => __('Plastique'),
self::paper => __('Carton/Papier'),
self::wood => __('Bois/Osier'),
self::aluminium => __('Aluminium'),
self::inox => __('Acier/Inox'),
self::glass => __('Verre'),
self::ceramic => __('Céramique/Terre cuite/Grès'),
self::materials => __('Multi Matériaux'),
self::others => __('Autres')
};
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace App\Enums\Packaging;
enum PackagingOriginEnum : int {
case magazine = 1;
case photo = 2;
case web = 3;
case mail = 4;
case salon = 5;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::magazine => __('Magazine'),
self::photo => __('Photo de rayon'),
self::web => __('Web'),
self::mail => __('Fournisseur/Mail'),
self::salon => __('Salon')
};
}
}

View file

@ -1,36 +0,0 @@
<?php
namespace App\Enums\Packaging;
enum PackagingTypeEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::box => __('Boîte/Barquette/Bol'),
self::bag => __('Sac/Sachet'),
self::jar => __('Pot'),
self::bottle => __('Bouteille/Gobelet/Flacon'),
self::film => __('Film'),
self::sleeves => __('Sleeves'),
self::blister => __('Tube/Blister/Feuille'),
self::sheathCase => __('Étui fourreau'),
self::tag => __('Étiquettes'),
self::others => __('Autres')
};
}
}

View file

@ -1,20 +0,0 @@
<?php
namespace App\Enums\Packaging;
enum PackagingWatchTypeEnum : int {
case product = 1;
case packaging = 2;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::product => __('Produit Fini'),
self::packaging => __('Emballage'),
};
}
}

View 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'
];
}

View 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',
];
}

View 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'
];
}

View 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'
];
}

View 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'
];
}

View 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'
];
}

View file

@ -1,30 +0,0 @@
<?php
namespace App\Enums\Process;
enum ProcessBenefitEnum : int {
case rateGain = 1;
case capacityGain = 2;
case efficiencyGain = 3;
case laborGain = 4;
case packagingGain = 5;
case energyGain = 6;
case others = 7;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::rateGain => __('Gain Cadence'),
self::capacityGain => __('Gain capacité'),
self::efficiencyGain => __('Gain rendement'),
self::laborGain => __('Gain main d\'oeuvre'),
self::packagingGain => __('Conso Emballage'),
self::energyGain => __('Conso Energie'),
self::others => __('Autres')
};
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace App\Enums\Process;
enum ProcessMeatPreparationEnum : int {
case cutting = 1;
case mixture = 2;
case grinding = 3;
case nibbling = 4;
case churning = 5;
case injection = 6;
case others = 7;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::cutting => __('Cutterage'),
self::mixture => __('Mélange'),
self::grinding => __('Broyage'),
self::nibbling => __('Grignotage'),
self::churning => __('Barattage'),
self::injection => __('Injection'),
self::others => __('Autres')
};
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace App\Enums\Process;
enum ProcessOriginEnum : int {
case magazine = 1;
case photo = 2;
case web = 3;
case mail = 4;
case salon = 5;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::magazine => __('Magazine'),
self::photo => __('Photo de rayon'),
self::web => __('Web'),
self::mail => __('Fournisseur/Mail'),
self::salon => __('Salon')
};
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace App\Enums\Process;
enum ProcessPrimaryPackingEnum : int {
case sealing = 1;
case thermoforming = 2;
case verticalBagging = 3;
case horizontalBagging = 4;
case skin = 5;
case others = 6;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::sealing => __('Operculage'),
self::thermoforming => __('Thermoformage'),
self::verticalBagging => __('Ensachage Vertical'),
self::horizontalBagging => __('Ensachage Horizontal'),
self::skin => __('Skin'),
self::others => __('Autres')
};
}
}

View file

@ -1,48 +0,0 @@
<?php
namespace App\Enums\Process;
enum ProcessTypeEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::slaughter => __('Abbattage/Découpe'),
self::beef => __('Préparation de viande'),
self::slicing => __('Tranchage/Émincage'),
self::cook => __('Cuisson'),
self::freezing => __('Refroidissement/Surgélation'),
self::forming => __('Formage/Embossage/Portionnage'),
self::mainPackaging => __('Conditionnement primaire'),
self::secondPackaging => __('Conditionnement secondaire'),
self::calibring => __('Pesage/Calibrage'),
self::inspection => __('Contrôles/Inspection'),
self::identification => __('Identification (Marqueurs/Jets d\'encres)'),
self::labeling => __('Étiquetage'),
self::conveyance => __('Convoyage/Transitique'),
self::logistics => __('Logistiques'),
self::robotization => __('Robotisation'),
self::others => __('Autres')
};
}
}

View file

@ -1,22 +0,0 @@
<?php
namespace App\Enums\Process;
enum ProcessWatchTypeEnum : int {
case product = 1;
case packaging = 2;
case process = 3;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::product => __('Produit Fini'),
self::packaging => __('Emballage'),
self::process => __('Process'),
};
}
}

View 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'
];
}

View 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',
];
}

View 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 = 5;
case others = 6;
/**
* 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',
];
}

View 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',
];
}

View 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',
];
}

View 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',
];
}

View 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',
];
}

View file

@ -1,28 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductCertificationEnum : int {
case bioAB = 1;
case labelRouge = 2;
case certifiedCQ = 3;
case standard = 4;
case freeRange = 5;
case others = 6;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::bioAB => __('Bio AB ou Européen'),
self::labelRouge => __('Label Rouge'),
self::certifiedCQ => __('Certifié CQ'),
self::standard => __('Standard'),
self::freeRange => __('Free Range'),
self::others => __('Autres'),
};
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductClaimEnum : int {
case productOfYear = 1;
case omega3 = 2;
case withoutOgm = 3;
case withoutGluten = 4;
case withoutAdditives = 5;
case christmas = 6;
case vegetarian = 7;
case lightweight = 8;
case halal = 9;
case highVitamins = 10;
case others = 11;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::productOfYear => __('Produit de l\'année'),
self::omega3 => __('Bleu Blanc Coeur, source d\'Omega 3'),
self::withoutOgm => __('Sans OGM'),
self::withoutGluten => __('Sans gluten'),
self::withoutAdditives => __('Sans additif, sans colorant, sans conservateur, 100% simple, sans polyphosphate, sans sel nitrité'),
self::withoutAntibiotics => __('Sans Antibiotique'),
self::vegetarian => __('Vegan'),
self::lightweight => __('Allégé'),
self::halal => __('Halal, Casher'),
self::highVitamins => __('Riche en protéines, fibres, calcium… '),
self::others => __('Autres'),
};
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductDistributionNetworkEnum : int {
case gms = 1;
case freezer = 2;
case restauration = 3;
case product = 4;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::gms => __('GMS / Hard discount / Commerce de détail'),
self::freezer => __('Home service/ Freezer Center'),
self::restauration => __('Restauration'),
self::product => __('Produit pour industrie agro-alimentaire'),
};
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductGeoAreaEnum : int {
case french = 1;
case european = 2;
case asia = 3;
case america = 4;
case africa = 5;
case oceania = 6;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::french => __('France'),
self::european => __('Europe'),
self::asia => __('Asie'),
self::america => __('Amérique'),
self::africa => __('Afrique'),
self::oceania => __('Océanie'),
};
}
}

View file

@ -1,30 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductOriginEnum : int {
case frenchPreparation = 1;
case frenchProduct = 2;
case igp = 3;
case regionalOrigins = 4;
case aoc = 5;
case ue = 6;
case others = 7;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::frenchPreparation => __('Né, élevé et préparé en France'),
self::frenchProduct => __('Produit en France'),
self::igp => __('IGP'),
self::regionalOrigins => __('Origines régionales'),
self::aoc => __('AOC'),
self::ue => __('UE'),
self::others => __('Autres'),
};
}
}

View file

@ -1,42 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductRangeEnum : int {
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 = 11;
case frozenSpecific = 12;
case others = 13;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::crus => __('Crus'),
self::elaboratedRaw => __('Élaborés crus'),
self::culinaryAids => __('Aides culinaires'),
self::cooked => __('Cuits'),
self::smoked => __('Fumés, confits ou séchés'),
self::ocokingSauce => __('Cuisine en sauce'),
self::breaded => __('Panés'),
self::rawMeat => __('Charcuterie crue'),
self::cookedMeats => __('Charcuterie cuite'),
self::farcis => __('Farcis'),
self::plant => __('Végétal'),
self::frozenSpecific => __('Spécifique surgelé'),
self::others => __('Autres'),
};
}
}

View file

@ -1,34 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductRayEnum : int {
case butchery = 1;
case poultry = 2;
case freshlyPackaged = 3;
case roastery = 4;
case caterer = 5;
case creamery = 6;
case groceryStore = 7;
case frozen = 8;
case others = 9;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self:: butchery => __('Boucherie'),
self::poultry => __('Volaille'),
self::freshlyPackaged => __('Frais emballé'),
self::roastery => __('Rôtisserie'),
self::caterer => __('Traiteur'),
self::creamery => __('Crémerie'),
self::groceryStore => __('Épicerie'),
self::frozen => __('Surgelé'),
self::others => __('Autres'),
};
}
}

View file

@ -1,29 +0,0 @@
<?php
namespace App\Enums\Products;
enum ProductSourceEnum : int {
case magazin = 1;
case web = 2;
case press = 3;
case tradeShow = 4;
case customerLounge = 5;
case consumerShow = 6;
case catalogArgumentary = 7;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::magazin => __('Magasin'),
self::web => __('Web'),
self::press => __('Presse'),
self::tradeShow => __('Salon Professionnel'),
self::customerLounge => __('Salon Clients'),
self::consumerShow => __('Salon Grand Public'),
self::catalogArgumentary => __('Catalogue Argumentaire'),
};
}
}

View 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 = 11;
case frozenSpecific = 12;
case others = 13;
/**
* 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',
];
}

View 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 = 6;
case groceryStore = 7;
case frozen = 8;
case others = 9;
/**
* 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',
];
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Enums\Products;
use App\Traits\Enums\BaseEnum;
enum SourceEnum : int
{
use BaseEnum;
case catalogArgumentary = 1;
case magazin = 2;
case press = 3;
case showConsumer = 4;
case showCustomer = 5;
case showTrade = 6;
case web = 7;
/**
* 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',
];
}

View 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',
];
}

View 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',
];
}

View 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',
];
}

View 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',
];
}

View file

@ -1,22 +0,0 @@
<?php
namespace App\Enums\Promotions;
enum PromotionAngleEnum : int {
case brand = 1;
case eco = 2;
case fun = 3;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::brand => __('Démarche de la marque'),
self::eco => __('Eco'),
self::fun => __('Fun'),
};
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace App\Enums\Promotions;
enum PromotionDistributionNetworkEnum : int {
case gms = 1;
case hd = 2;
case rhd = 3;
case export = 4;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::gms => __('GMS'),
self::hd => __('HD'),
self::rhd => __('RHD'),
self::export => __('Export'),
};
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace App\Enums\Promotions;
enum PromotionEventEnum : int {
case birthday = 1;
case summer = 2;
case filmLicense = 3;
case christmas = 4;
case celebrityPartnership = 5;
case sport = 6;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::birthday => __('Anniversaire'),
self::summer => __('Été'),
self::filmLicense => __('Licence film / Dessin animé'),
self::christmas => __('Noël'),
self::celebrityPartnership => __('Partenariat célébrité'),
self::sport => __('Sport'),
};
}
}

View file

@ -1,32 +0,0 @@
<?php
namespace App\Enums\Promotions;
enum PromotionLabelDistributionNetworkEnum : int {
case allDistributions = 1;
case lidle = 2;
case aldi = 3;
case erteco = 4;
case netto = 5;
case leaderPrice = 6;
case noLvl2 = 7;
case others = 8;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::allDistributions => __('Toutes les enseignes'),
self::lidle => __('LIDL'),
self::aldi => __('ALDI'),
self::erteco => __('ERTECO'),
self::netto => __('NETTO'),
self::leaderPrice => __('LEADER PRICE'),
self::noLvl2 => __('Pas de niveau 2'),
self::others => __('Autres'),
};
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace App\Enums\Promotions;
enum PromotionSourceEnum : int {
case fieldTour = 1;
case headFeedback = 2;
case web = 3;
case press = 4;
case events = 5;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::fieldTour => __('Tournée terrain'),
self::headFeedback => __('Remontée chef Secteur'),
self::web => __('Pige web'),
self::press => __('Pige presse'),
self::events => __('Salon / Évenementiel'),
};
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace App\Enums\Promotions;
enum PromotionTargetEnum : int {
case women = 1;
case man = 2;
case junior = 3;
case senior = 4;
case allTargets = 5;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::women => __('Femme'),
self::man => __('Homme'),
self::junior => __('Junior'),
self::senior => __('Senior'),
self::allTargets => __('Toutes cibles'),
};
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace App\Enums\Promotions;
enum PromotionTypeEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::animation => __('Animation'),
self::reduction => __('Réduction'),
self::fidelity => __('Fidélité'),
self::consoGames => __('Jeux Conso'),
self::promotionalFormats => __('Formats promo'),
self::flyer => __('OP Trade / Prospectus'),
self::coBranding => __('Co-branding'),
self::merchandising => __('PLV / Merchandising'),
self::goodies => __('Primes / Goodies'),
self::promotionalMaterials => __('Argumentaires Promo'),
self::ephemeralProducts => __('Produits éphèmères'),
};
}
}

View 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',
];
}

View 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',
];
}

View 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',
];
}

View 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',
];
}

View 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',
];
}

View file

@ -1,42 +0,0 @@
<?php
namespace App\Enums\Surveys;
enum SurveyCustomerEnum : int {
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;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::francoscopie => __('Francoscopie'),
self::packaging => __('Emaballages'),
self::innovation => __('Innovation'),
self::snacking => __('Snacking'),
self::vegetarian => __('Vegan'),
self::christmas => __('Noel'),
self::barbecue => __('Barbecue'),
self::sustainableDevelopment => __('Développement durable'),
self::labelRouge => __('Label Rouge'),
self::bio => __('BIO'),
self::certified => __('Certifié'),
self::bbc => __('BBC'),
self::others => __('Autres'),
};
}
}

View file

@ -1,24 +0,0 @@
<?php
namespace App\Enums\Surveys;
enum SurveyDistributionNetworkEnum : int {
case gms = 1;
case freezer = 2;
case restauration = 3;
case product = 4;
public function label(): string {
return static::getLabel($this);
}
public static function getLabel(self $value): string {
return match ($value) {
self::gms => __('GMS / Hard discount / Commerce de détail'),
self::freezer => __('Home service/ Freezer Center'),
self::restauration => __('Restauration'),
self::product => __('Produit pour industrie agro-alimentaire'),
};
}
}

View file

@ -13,8 +13,7 @@ class Handler extends ExceptionHandler
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [
//
];
];
/**
* A list of the exception types that are not reported.
@ -22,8 +21,7 @@ class Handler extends ExceptionHandler
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [
//
];
];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
@ -42,7 +40,6 @@ class Handler extends ExceptionHandler
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
}

View file

@ -2,21 +2,124 @@
namespace App\Http\Controllers;
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\Schema;
use Illuminate\Support\Facades\Session;
class Controller extends BaseController
{
use AuthorizesRequests, ValidatesRequests;
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
/**
* Show the application's homepage.
* Build Search query on specified fields
* splitting search into words or using whole sentence
*
* @return Illuminate\Contracts\View\View
* @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
*/
public function index(): \Illuminate\Contracts\View\View
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
{
return view('pages.home');
$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;
}
}

View 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');
}
}

View file

@ -0,0 +1,430 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
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\Facades\DB;
use Illuminate\Support\Str;
class ProductController extends Controller
{
use HasDocuments;
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $brands */
$brands = Brand::get();
/** @var \Illuminate\Database\Eloquent\Builder $products */
$products = Product::query();
$this->requestFilters($products, $request);
$this->sortQuery($products);
$products = $products->paginate(10);
/** @var array $printMultiProduct Array of id */
$printMultiProduct = session('printMultiProduct') ?? [];
/** @var \Illuminate\Database\Eloquent\Builder $brands */
$brands = Brand::get();
/** @var \Illuminate\Database\Eloquent\Builder $keyword */
$keywords = Keyword::where('keyword_cat', 'LIKE', 'product_%')->get();
return view('pages.products.index', [
'products' => $products,
'brands' => $brands,
'keywords' => $keywords,
'printMultiProduct' => $printMultiProduct,
'queryParameters' => $request->all(),
]);
}
/**
* Show the form for creating a new resource.
*
* @param \Illuminate\Database\Eloquent\Builder $products
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
*/
private function requestFilters(Builder $products, Request $request): \Illuminate\Database\Eloquent\Builder
{
//phpcs:enable
foreach ($request->all() as $key => $queryParameter) {
if (request()->sort_col === null && request()->sort_way === null && request()->rst === null) {
$products->when(
isset($queryParameter) && $queryParameter != 0,
function ($q) use ($queryParameter, $key) {
if ($key === "name") {
$q->where($key, 'LIKE', '%' . $queryParameter . '%');
} elseif ($key === "claims" || $key === "origins") {
foreach ($queryParameter as $item) {
$q->where($key, 'LIKE', '%{"id":"' . $item['value'] . '"}%');
}
} elseif ($key === "keywords") {
foreach ($queryParameter as $keyword) {
$q->whereHas('productKeywords', function ($q) use ($keyword, $key) {
$q->where($key . '.id', intval($keyword['value']));
});
}
} elseif ($key === "date_before" || $key === "date_after") {
if ($key === "date_before") {
$q->whereDate('created_at', '>=', $queryParameter);
} else {
$q->whereDate('created_at', '<=', $queryParameter);
}
} else {
$q->where($key, $queryParameter);
} //end if
}
);
} //end if
} //end foreach
return $products;
}
/**
* Show the form for creating a new resource.
*
* @param App\Models\Product $productModel
* @return \Illuminate\Contracts\View\View
* phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function create(Product $productModel): \Illuminate\Contracts\View\View
{
//phpcs:enable
$brands = Brand::get();
$productsFair = Product::pluck('fair');
$productsShop = Product::pluck('shop');
$productsCatalog = Product::pluck('catalogue');
$keywords = Keyword::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'
));
}
/**
* 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::get();
$productsFair = Product::pluck('fair');
$productsShop = Product::pluck('shop');
$productsCatalog = Product::pluck('catalogue');
$commentModel = $product->productComments;
$keywords = Keyword::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)
{
return $this->create($product->replicate());
}
/**
* 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->GENCODE) &&
$request->GENCODE !== null &&
ctype_digit($request->GENCODE) &&
strlen($request->GENCODE) > 0
) {
if (
isset($request->ID) &&
$request->ID !== null &&
intval($request->GENCODE) === Product::where('id', $request->ID)->firstOrFail()->gencod
) {
$result = null;
} else {
$result = in_array(intval($request->GENCODE), Product::pluck('gencod')->toArray()) ?
Product::orderBy('created_at', 'desc')->where('gencod', intval($request->GENCODE))->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()
{
/** @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');
}
}

View file

@ -36,11 +36,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class
],
];

View file

@ -12,6 +12,5 @@ class EncryptCookies extends Middleware
* @var array<int, string>
*/
protected $except = [
//
];
];
}

View file

@ -12,6 +12,5 @@ class PreventRequestsDuringMaintenance extends Middleware
* @var array<int, string>
*/
protected $except = [
//
];
];
}

View file

@ -13,7 +13,7 @@ class RedirectIfAuthenticated
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{

View 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);
}
}
}

View file

@ -17,7 +17,7 @@ class TrustProxies extends Middleware
/**
* The headers that should be used to detect proxies.
*
* @var int
* @var integer
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |

View file

@ -12,6 +12,5 @@ class VerifyCsrfToken extends Middleware
* @var array<int, string>
*/
protected $except = [
//
];
];
}

View file

@ -0,0 +1,159 @@
<?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\HasDocument;
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 HasDocument;
/**
* 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 : [],
]);
$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|required_if:source,' . SourceEnum::showConsumer->value . ',' . SourceEnum::showCustomer->value . ',' . SourceEnum::showTrade->value,
'shop' => 'nullable|string|min:3|max:255|required_if:source,' . SourceEnum::magazin->value,
'catalogue' => 'nullable|string|min:3|max:255|required_if:source,' . SourceEnum::catalogArgumentary->value,
'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' => 'nullable|array',
'origins.*' => 'present|array',
'origins.*.id' => [new Enum(OriginEnum::class)],
'claims' => 'nullable|array',
'claims.*' => 'present|array',
'claims.*.id' => [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|integer',
'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', false),
);
}
/**
* 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)'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Products;
class UpdateProductRequest extends StoreProductRequest
{
}

View file

@ -0,0 +1,150 @@
<?php
namespace App\Http\Resources;
use App\Enums\Products\CertificationEnum;
use App\Enums\Products\ClaimEnum;
use App\Enums\Products\OriginEnum;
use App\Models\Brand;
use App\Models\Product;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
/** @var \App\Models\Product */
private $product;
/** @var array */
private static $skeleton = [
'id' => null,
'user_id' => null,
'brand_id' => null,
'name' => null,
'saw_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,
'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)
{
foreach (CertificationEnum::toArray() as $enumValue) {
if ($this->product->certification->value === $enumValue->value) {
$certification = $enumValue->label;
}
}
if (count($this->product->origins)) {
$origins = "";
foreach ($this->product->origins as $originValue) {
foreach (OriginEnum::toArray() as $enumValue) {
if (intval($originValue['id']) === $enumValue->value) {
$origins = $origins . $enumValue->label . ", ";
}
}
}
}
if (count($this->product->claims)) {
$claims = "";
foreach ($this->product->claims as $originValue) {
foreach (ClaimEnum::toArray() as $enumValue) {
if (intval($originValue['id']) === $enumValue->value) {
if ($enumValue->value === ClaimEnum::others->value) {
$claims = $claims . $enumValue->label . ": " . $this->product->specified_claim . ", ";
} else {
$claims = $claims . $enumValue->label . ", ";
}
}
}
}
}
foreach ($this->product->productComments as $productComment) {
$productComment->date = Carbon::createFromFormat(
'Y-m-d H:i:s',
$productComment->created_at
)->format('d/m/Y');
}
if (count($this->product->productKeywords)) {
$productKeywords = "";
foreach ($this->product->productKeywords as $productKeyword) {
$productKeywords = $productKeywords . $productKeyword->label . ", ";
}
}
return array_intersect_key(
[
'id' => $this->product->id,
'user_id' => User::where('id', $this->product->user_id)->firstOrFail(),
'brand_id' => Brand::where('id', $this->product->brand_id)->firstOrFail(),
'name' => $this->product->name,
'saw_at' => Carbon::createFromFormat('Y-m-d H:i:s', $this->product->saw_at)->format('d/m/Y'),
'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' => $certification,
'origins' => $origins ?? null,
'claims' => $claims ?? null,
'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,
'productComments' => $this->product->productComments,
'productKeywords' => $productKeywords ?? null,
],
$this->getSkeleton()
);
}
/**
* Returns the resource skeletton
*
* @return array
*/
public static function getSkeleton(): array
{
return self::$skeleton;
}
}

View file

@ -0,0 +1,77 @@
<?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.
*
* @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}");
}
}

View 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;
}
}

View file

@ -0,0 +1,291 @@
<?php
namespace App\Lib\Helpers;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
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;
}
/**
* Generates an array from a dot separated string
*
* @param string $path
* @param mixed $value
* @param string $separator
* @return array
*/
public static function dotStringToNestedArray(string $path, $value, string $separator = '.'): array
{
$arr = $value;
$keys = array_reverse(explode($separator, $path));
foreach ($keys as $key) {
$arr = [$key => $arr];
}
return $arr;
}
/**
* Generate url using laravel route passing our group query if present
* This is used to keep ou group tracking
* Curd-policies is only used for root users as it cannot keep track of our group
* and validate things
*
* @param array|string $name
* @param mixed $parameters
* @param boolean $absolute
* @return string
* @throws \ErrorException If an object given as parameter is not a Model.
*/
public static function routeWithGroup($name, $parameters = [], bool $absolute = true): string
{
// * Handle if parameter if is an object.
if (\is_object($parameters)) {
$parents = \class_parents($parameters);
if ($parents === false or !\in_array(Model::class, $parents)) {
throw new \ErrorException(\sprintf(
'%s only support object of type %s',
__FUNCTION__,
Model::class
));
}
/** @var \Illuminate\Database\Eloquent\Model $model */
$model = $parameters;
$parameters = [Str::singular($model->getTable()) => $model];
}
// * Handle if parameter isnt and array like slug or else.
if (!is_array($parameters)) {
// * Best effort, result is not garantee.
$parameters = [$parameters];
}
// * Pass our group.
if ($group = Session::get('activeGroup', request()->group)) {
$parameters = array_merge($parameters, ['group' => $group]);
}
// * Call laravel route helper.
return route($name, $parameters, $absolute);
}
/**
* Mutltibyte string replace
* @param mixed $search
* @param mixed $replace
* @param mixed $subject
* @param integer $count
* @return mixed
*/
public static function mbReplace($search, $replace, $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,
$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) == []);
}
}

View file

@ -23,7 +23,7 @@ class Brand extends Model
* @var array
*/
protected $fillable = [
'brand',
'name',
'filter',
'created_at',
'updated_at'

View file

@ -2,8 +2,8 @@
namespace App\Models;
use App\Enums\Ingredients\IngredientOriginEnum;
use App\Enums\Ingredients\IngredientFamilyEnum;
use App\Enums\Ingredients\OriginEnum;
use App\Enums\Ingredients\FamilyEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -12,9 +12,9 @@ use Illuminate\Database\Eloquent\Model;
* @property integer $user_id
* @property string $name
* @property string $supplier
* @property boolean $origin
* @property enum $origin
* @property boolean $is_sample
* @property boolean $family
* @property enum $family
* @property string $packing
* @property string $function
* @property string $observation
@ -59,9 +59,9 @@ class Ingredient extends Model
* @var array
*/
protected $casts = [
'origin' => IngredientOriginEnum::class,
'origin' => OriginEnum::class,
'is_sample' => 'boolean',
'family' => IngredientFamilyEnum::class,
'family' => FamilyEnum::class,
'packing' => 'json',
'function' => 'json',
'documents' => 'json',

View file

@ -41,4 +41,14 @@ class Keyword extends Model
{
return $this->hasMany('App\Models\Keywordable');
}
/**
* MorphToMany products relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function products()
{
return $this->morphToMany(Product::class, 'keywordable');
}
}

View file

@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Model;
* @property integer $id
* @property integer $keyword_id
* @property integer $keywordable_id
* @property string $model
* @property string $keywordable_type
* @property string $created_at
* @property string $updated_at
* @property Keyword $keyword
@ -23,8 +23,8 @@ class Keywordable extends Model
*/
protected $fillable = [
'keyword_id',
'model',
'keywordable_id',
'keywordable_type',
'created_at',
'updated_at'
];

View file

@ -2,11 +2,11 @@
namespace App\Models;
use App\Enums\Packaging\PackagingOriginEnum;
use App\Enums\Packaging\PackagingWatchTypeEnum;
use App\Enums\Packaging\PackagingTypeEnum;
use App\Enums\Packaging\PackagingMaterialEnum;
use App\Enums\Packaging\PackagingBenefitEnum;
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;
@ -66,12 +66,12 @@ class Packaging extends Model
* @var array
*/
protected $casts = [
'watch_type' => PackagingWatchTypeEnum::class,
'origin' => PackagingOriginEnum::class,
'watch_type' => WatchTypeEnum::class,
'origin' => OriginEnum::class,
'is_sample' => 'boolean',
'type_packaging' => PackagingTypeEnum::class,
'material' => PackagingMaterialEnum::class,
'benefit' => PackagingBenefitEnum::class,
'type_packaging' => TypeEnum::class,
'material' => MaterialEnum::class,
'benefit' => BenefitEnum::class,
'picture' => 'json',
'documents' => 'json',
];

View file

@ -2,9 +2,9 @@
namespace App\Models;
use App\Enums\Process\ProcessWatchTypeEnum;
use App\Enums\Process\ProcessOriginEnum;
use App\Enums\Process\ProcessBenefitEnum;
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;
@ -63,11 +63,11 @@ class Process extends Model
* @var array
*/
protected $casts = [
'watch_type' => ProcessWatchTypeEnum::class,
'origin' => ProcessOriginEnum::class,
'watch_type' => WatchTypeEnum::class,
'origin' => OriginEnum::class,
'is_test_machine' => 'boolean',
'is_quotation' => 'boolean',
'profit' => ProcessBenefitEnum::class,
'profit' => BenefitEnum::class,
'picture' => 'json',
'documents' => 'json',
'created_at' => 'datetime:Y-m-d',

View file

@ -2,14 +2,20 @@
namespace App\Models;
use App\Enums\Products\ProductSourceEnum;
use App\Enums\Products\ProductCertificationEnum;
use App\Enums\Products\ProductDistributionNetworkEnum;
use App\Enums\Products\ProductRayEnum;
use App\Enums\Products\ProductRangeEnum;
use App\Enums\Products\ProductGeoAreaEnum;
use Illuminate\Database\Eloquent\Model;
use App\Enums\Products\CertificationEnum;
use App\Enums\Products\DistributionNetworkEnum;
use App\Enums\Products\GeoAreaEnum;
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\Facades\Storage;
use Mccarlosen\LaravelMpdf\LaravelMpdf;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* @property integer $id
@ -17,23 +23,23 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
* @property integer $brand_id
* @property string $name
* @property string $brand
* @property string $entry_date
* @property boolean $source
* @property string $saw_at
* @property enum $source
* @property string $fair
* @property string $shop
* @property string $catalogue
* @property string $gencod
* @property string $picture
* @property string $docs
* @property array $docs
* @property string $url
* @property boolean $certification
* @property string $origins
* @property string $claims
* @property enum $certification
* @property object $origins
* @property object $claims
* @property string $specified_claim
* @property boolean $distribution_network
* @property boolean $ray
* @property boolean $product_range
* @property boolean $geo area
* @property enum $distribution_network
* @property enum $ray
* @property enum $product_range
* @property enum $geo area
* @property integer $weight
* @property integer $pvc
* @property string $comment
@ -57,13 +63,15 @@ class Product extends Model
'brand_id',
'name',
'brand',
'entry_date',
'saw_at',
'source',
'fair',
'shop',
'catalogue',
'gencod',
'picture',
'picture_alt',
'picture_title',
'docs',
'url',
'certification',
@ -73,7 +81,7 @@ class Product extends Model
'distribution_network',
'ray',
'product_range',
'geo area',
'geo_area',
'weight',
'pvc',
'comment',
@ -90,22 +98,249 @@ class Product extends Model
* @var array
*/
protected $casts = [
'source' => ProductSourceEnum::class,
'source' => SourceEnum::class,
'docs' => 'json',
'certification' => ProductCertificationEnum::class,
'certification' => CertificationEnum::class,
'origins' => 'json',
'claims' => 'json',
'distribution_network' => ProductDistributionNetworkEnum::class,
'ray' => ProductRayEnum::class,
'product_range' => ProductRangeEnum::class,
'geo_area' => ProductGeoAreaEnum::class,
'distribution_network' => DistributionNetworkEnum::class,
'ray' => RayEnum::class,
'product_range' => RangeEnum::class,
'geo_area' => GeoAreaEnum::class,
'is_tasted' => 'boolean',
'entry_date' => 'datetime:Y-m-d',
'saw_at' => 'datetime:Y-m-d',
'created_at' => 'datetime:Y-m-d',
'updated_at' => 'datetime:Y-m-d',
'deleted_at' => 'datetime:Y-m-d',
];
// * 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);
$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)
{
foreach ($model->claims as $claim) {
$check = (intval($claim['id']) === \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
* @return void
*/
public static function deleteOldDocuments(self $model)
{
foreach ($model->getOriginal('docs') as $document) {
$originalPath = \ltrim(\urldecode($document), '/');
if (\strpos($originalPath, 'storage') !== 0) {
return;
}
$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 $printMultiProduct
* @param string $title
* @return \Mccarlosen\LaravelMpdf\LaravelMpdf
*/
public static function generateMultipleProductPdf(array $printMultiProduct, string $title)
{
/** @var \Mccarlosen\LaravelMpdf\LaravelMpdf */
$pdf = null;
// Loop all views.
foreach ($printMultiProduct as $productId) {
$product = Product::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' => \json_decode(\json_encode(new ProductResource($product)))],
));
}
}
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();
}
// * RELATIONS
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
@ -129,4 +364,14 @@ class Product extends Model
{
return $this->belongsTo('App\Models\Brand');
}
/**
* MorphToMany keywords relation.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function productKeywords()
{
return $this->morphToMany(Keyword::class, 'keywordable');
}
}

View file

@ -2,10 +2,10 @@
namespace App\Models;
use App\Enums\Promotions\PromotionSourceEnum;
use App\Enums\Promotions\PromotionTargetEnum;
use App\Enums\Promotions\PromotionAngleEnum;
use App\Enums\Promotions\PromotionEventEnum;
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;
@ -71,10 +71,10 @@ class Promo extends Model
* @var array
*/
protected $casts = [
'source' => PromotionSourceEnum::class,
'target' => PromotionTargetEnum::class,
'angle' => PromotionAngleEnum::class,
'event' => PromotionEventEnum::class,
'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',

View file

@ -2,7 +2,7 @@
namespace App\Models;
use App\Enums\Promotions\PromotionDistributionNetworkEnum;
use App\Enums\Promotions\DistributionNetworkEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -34,7 +34,7 @@ class PromoDistributionNetwork extends Model
* @var array
*/
protected $casts = [
'name' => PromotionDistributionNetworkEnum::class
'name' => DistributionNetworkEnum::class
];
/**

View file

@ -2,7 +2,7 @@
namespace App\Models;
use App\Enums\Promotions\PromotionTypeEnum;
use App\Enums\Promotions\TypeEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -21,7 +21,7 @@ class PromoType extends Model
/**
* Indicates if the IDs are auto-incrementing.
*
* @var bool
* @var boolean
*/
public $incrementing = false;
@ -41,7 +41,7 @@ class PromoType extends Model
* @var array
*/
protected $casts = [
'value' => PromotionTypeEnum::class
'value' => TypeEnum::class
];
/**

View file

@ -2,7 +2,7 @@
namespace App\Models;
use App\Enums\Surveys\SurveyCustomerEnum;
use App\Enums\Surveys\CustomerEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -54,7 +54,7 @@ class Survey extends Model
'documents' => 'json',
'is_distributor' => 'boolean',
'is_customer' => 'boolean',
'type' => SurveyCustomerEnum::class,
'type' => CustomerEnum::class,
'created_at' => 'datetime:Y-m-d',
'updated_at' => 'datetime:Y-m-d',
'deleted_at' => 'datetime:Y-m-d',

View file

@ -2,23 +2,39 @@
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
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
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'));
}
}

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