Compare commits
30 commits
<(o_O)>_de
...
sprint_4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0632e5484f | ||
|
|
f06b2aa101 | ||
|
|
2211428f9a | ||
|
|
1717c562f2 | ||
|
|
be5124a4f0 | ||
|
|
5c1e8e5a46 | ||
|
|
306e0fa072 | ||
|
|
afea5d3ab1 | ||
|
|
4a78ce9369 | ||
|
|
b8a4d64b1f | ||
|
|
870672f764 | ||
|
|
3f137e40eb | ||
|
|
6b096945cb | ||
|
|
3f8e194e5c | ||
|
|
1347a58f28 | ||
|
|
48426d3dae | ||
|
|
aa01a88379 | ||
|
|
49ee63a3e4 | ||
|
|
a45821bf8e | ||
|
|
aaba2c5ce1 | ||
|
|
410834c151 | ||
|
|
aadb5c0acc | ||
|
|
67e6892c21 | ||
|
|
ff80b26d9f | ||
|
|
aa3c0adf69 | ||
|
|
7e8ce36ce7 | ||
|
|
2e033368e2 | ||
|
|
a10600adb1 | ||
|
|
74f60c40d8 | ||
|
|
17df77b236 |
|
|
@ -14,7 +14,7 @@ trim_trailing_whitespace = false
|
|||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.{vue}]
|
||||
[*.vue]
|
||||
indent_size = 2
|
||||
|
||||
[docker-compose.yml]
|
||||
|
|
|
|||
41
.eslintrc.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
118
.gitlab-ci.yml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
stages:
|
||||
- Security
|
||||
- CodingStandards
|
||||
- Builds
|
||||
|
||||
composer-security:
|
||||
stage: Security
|
||||
image: $CI_REGISTRY/service-web-lab/ldc-web-images-docker/ldcweb-qa:php7.3-1.0
|
||||
script:
|
||||
- local-php-security-checker --format=json --path=./composer.lock > composer-security-checker-report.json
|
||||
artifacts:
|
||||
when: on_failure
|
||||
expose_as: 'composer-security-checker'
|
||||
paths:
|
||||
- composer-security-checker-report.json
|
||||
allow_failure: true
|
||||
needs: []
|
||||
|
||||
npm-security:
|
||||
stage: Security
|
||||
image: node:17.9.1
|
||||
before_script:
|
||||
- npm install --location=global audit-ci@^6
|
||||
script:
|
||||
- audit-ci -p npm -o json > npm-security-checker-report.json
|
||||
artifacts:
|
||||
when: on_failure
|
||||
expose_as: 'npm-security-checker'
|
||||
paths:
|
||||
- npm-security-checker-report.json
|
||||
allow_failure: true
|
||||
needs: []
|
||||
|
||||
phpcs:
|
||||
stage: CodingStandards
|
||||
image: $CI_REGISTRY/service-web-lab/ldc-web-images-docker/ldcweb-qa:php7.3-1.0
|
||||
before_script:
|
||||
- composer config gitlab-token.gitlab.com $CI_DEPLOY_USER $CI_DEPLOY_PASSWORD
|
||||
- composer install
|
||||
cache:
|
||||
paths:
|
||||
- vendor/
|
||||
script:
|
||||
- vendor/bin/phpcs --report=junit --report-file=phpcs-report.xml
|
||||
artifacts:
|
||||
when: on_failure
|
||||
expose_as: 'phpcs'
|
||||
paths:
|
||||
- phpcs-report.xml
|
||||
reports:
|
||||
junit: phpcs-report.xml
|
||||
allow_failure: false
|
||||
needs:
|
||||
- composer-security
|
||||
- npm-security
|
||||
|
||||
eslint:
|
||||
stage: CodingStandards
|
||||
image: node:17.9.1
|
||||
before_script:
|
||||
- npm install
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
script:
|
||||
- npm run eslint-ci --silent --silent 2>/dev/null 1> eslint-report.xml
|
||||
artifacts:
|
||||
when: on_failure
|
||||
expose_as: 'eslint'
|
||||
paths:
|
||||
- eslint-report.xml
|
||||
reports:
|
||||
junit: eslint-report.xml
|
||||
allow_failure: false
|
||||
needs:
|
||||
- composer-security
|
||||
- npm-security
|
||||
|
||||
stylelint:
|
||||
stage: CodingStandards
|
||||
image: node:17.9.1
|
||||
before_script:
|
||||
- npm install
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
script:
|
||||
- npm run stylelint-ci --silent --silent 2>/dev/null 1> stylelint-report.json
|
||||
artifacts:
|
||||
when: on_failure
|
||||
expose_as: 'stylelint'
|
||||
paths:
|
||||
- stylelint-report.json
|
||||
allow_failure: false
|
||||
needs:
|
||||
- composer-security
|
||||
- npm-security
|
||||
|
||||
front-build:
|
||||
stage: Builds
|
||||
image: node:17.9.1
|
||||
before_script:
|
||||
- npm install
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
script:
|
||||
- npm run prod > front-build-report.json
|
||||
artifacts:
|
||||
when: on_failure
|
||||
expose_as: 'front-build-report'
|
||||
paths:
|
||||
- front-build-report.json
|
||||
allow_failure: false
|
||||
needs:
|
||||
- phpcs
|
||||
- eslint
|
||||
- stylelint
|
||||
39
.stylelintrc.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"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"
|
||||
],
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
24
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#595926",
|
||||
"activityBar.activeBorder": "#439c9c",
|
||||
"activityBar.background": "#595926",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#439c9c",
|
||||
"activityBarBadge.foreground": "#15202b",
|
||||
"sash.hoverBorder": "#595926",
|
||||
"statusBar.background": "#353517",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#595926",
|
||||
"statusBarItem.remoteBackground": "#353517",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#353517",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#35351799",
|
||||
"titleBar.inactiveForeground": "#e7e7e799",
|
||||
"commandCenter.border": "#e7e7e799"
|
||||
},
|
||||
"peacock.remoteColor": "#353517",
|
||||
"vue3snippets.enable-compile-vue-file-on-did-save-code": false
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@ class HtmlColor implements CastsAttributes
|
|||
*/
|
||||
public function get($model, $key, $value, $attributes)
|
||||
{
|
||||
// phpcs:enable
|
||||
return "#" . $this->zeropad(dechex($value), 6);
|
||||
}
|
||||
|
||||
|
|
@ -36,9 +37,14 @@ class HtmlColor implements CastsAttributes
|
|||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return integer
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
|
||||
*/
|
||||
public function set($model, $key, $value, $attributes)
|
||||
{
|
||||
// phpcs:enable
|
||||
return \intval(hexdec(ltrim($value, '#')));
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +55,7 @@ class HtmlColor implements CastsAttributes
|
|||
* @param integer $lim
|
||||
* @return string
|
||||
*/
|
||||
private function zeropad(string $num, int $lim): string
|
||||
protected function zeropad(string $num, int $lim): string
|
||||
{
|
||||
return (strlen($num) >= $lim) ? $num : $this->zeropad("0" . $num, $lim);
|
||||
}
|
||||
|
|
|
|||
62
app/Casts/RgbaColor.php
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace App\Casts;
|
||||
|
||||
/**
|
||||
* Custom cast RGBA Color
|
||||
* https://laravel.com/docs/8.x/eloquent-mutators#custom-casts
|
||||
*/
|
||||
class RgbaColor extends HtmlColor
|
||||
{
|
||||
/**
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
*/
|
||||
public function get($model, $key, $value, $attributes)
|
||||
{
|
||||
// phpcs:enable
|
||||
$intValues = collect(\str_split($this->zeropad(\dechex(\intval($value)), 8), 2))->map(function ($hex) {
|
||||
return intval(\hexdec($hex));
|
||||
});
|
||||
// * Set Transparency as float
|
||||
$hexString = $intValues->push(\round($intValues->pop() / 100, 2))->implode(',');
|
||||
return "rgba($hexString)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return integer
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
*/
|
||||
public function set($model, $key, $value, $attributes)
|
||||
{
|
||||
// phpcs:enable
|
||||
$value = \rtrim(\ltrim(\str_replace(' ', '', \str_replace('rgba', '', \strval($value))), '('), ')');
|
||||
$intValues = collect(\explode(',', $value))->map(function ($val) {
|
||||
return \floatval($val);
|
||||
});
|
||||
// * Set Transparency as int
|
||||
$hexConcat = $intValues->push($intValues->pop() * 100)->map(function ($value) {
|
||||
return $this->zeropad(dechex(\intval($value)), 2);
|
||||
})->implode('');
|
||||
|
||||
return \intval(\hexdec($hexConcat));
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,16 @@
|
|||
|
||||
namespace App\Casts;
|
||||
|
||||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Casts\RgbaColor as CastsRgbaColor;
|
||||
|
||||
//phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
|
||||
//phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
|
||||
class NiceDate implements CastsAttributes
|
||||
/**
|
||||
* Custom cast RGBA Color nullable
|
||||
* https://laravel.com/docs/8.x/eloquent-mutators#custom-casts
|
||||
*/
|
||||
class RgbaColorNullable extends CastsRgbaColor
|
||||
{
|
||||
/**
|
||||
* Return a nice human readable carbon date.
|
||||
* Cast the given value.
|
||||
*
|
||||
* @param Model $model
|
||||
* @param string $key
|
||||
|
|
@ -22,26 +23,24 @@ class NiceDate implements CastsAttributes
|
|||
*/
|
||||
public function get($model, $key, $value, $attributes)
|
||||
{
|
||||
$value = Carbon::parse($value);
|
||||
return $value->isToday() ?
|
||||
trans('Aujourd\'hui à :time', [
|
||||
'time' => $value->isoFormat('HH:mm')
|
||||
]) : $value->isoFormat('YYYY/MM/DD HH:mm');
|
||||
// phpcs:enable
|
||||
return \is_null($value) ? $value : parent::get($model, $key, $value, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a date string to a date time date.
|
||||
* Prepare the given value for storage.
|
||||
*
|
||||
* @param Model $model
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return string
|
||||
* @return integer
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
*/
|
||||
public function set($model, $key, $value, $attributes)
|
||||
{
|
||||
return Carbon::parse($value)->isoFormat('YYYY/MM/DD HH:mm');
|
||||
// phpcs:enable
|
||||
return \is_null($value) ? $value : parent::set($model, $key, $value, $attributes);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace App\Console;
|
||||
|
||||
use App\Jobs\RangesStatusUpdate;
|
||||
use App\Jobs\ProductStatusUpdate;
|
||||
use App\Jobs\SlidersStatusUpdate;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
|
@ -17,6 +19,8 @@ class Kernel extends ConsoleKernel
|
|||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->job(SlidersStatusUpdate::class)->everyMinute();
|
||||
$schedule->job(RangesStatusUpdate::class)->everyMinute();
|
||||
$schedule->job(ProductStatusUpdate::class)->everyMinute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
50
app/Enums/Engagements/EngagementType.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Engagements;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* @method static self renewableEnergies()
|
||||
* @method static self localCereals()
|
||||
* @method static self agroforestry()
|
||||
* @method static self cereals()
|
||||
* @method static self animalWelfare()
|
||||
* @method static self beekeeping()
|
||||
*/
|
||||
final class EngagementType extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'renewableEnergies' => 1,
|
||||
'localCereals' => 2,
|
||||
'agroforestry' => 3,
|
||||
'cereals' => 4,
|
||||
'animalWelfare' => 5,
|
||||
'beekeeping' => 6
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'renewableEnergies' => __('models.classes.engagements.renewableEnergies'),
|
||||
'localCereals' => __('models.classes.engagements.localCereals'),
|
||||
'agroforestry' => __('models.classes.engagements.agroforestry'),
|
||||
'cereals' => __('models.classes.engagements.cereals'),
|
||||
'animalWelfare' => __('models.classes.engagements.animalWelfare'),
|
||||
'beekeeping' => __('models.classes.engagements.beekeeping')
|
||||
];
|
||||
}
|
||||
}
|
||||
48
app/Enums/InputType.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* @method static self boolean()
|
||||
* @method static self image()
|
||||
* @method static self string()
|
||||
* @method static self text()
|
||||
* html autorisé
|
||||
* @method static self url()
|
||||
* @method static self imageArray()
|
||||
* @method static self labelArray()
|
||||
* @method static self dropdownArray()
|
||||
* html autorisé
|
||||
* @method static self miniblocArray()
|
||||
* html autorisé
|
||||
* @method static self videoVimeo()
|
||||
* @method static self number()
|
||||
* @method static self email()
|
||||
*/
|
||||
final class InputType extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'boolean' => 0,
|
||||
'image' => 1,
|
||||
'string' => 2,
|
||||
'text' => 3,
|
||||
'url' => 4,
|
||||
'imageArray' => 5,
|
||||
'labelArray' => 6,
|
||||
'dropdownArray' => 7,
|
||||
'miniblocArray' => 8,
|
||||
'videoVimeo' => 9,
|
||||
'number' => 10,
|
||||
'email' => 11
|
||||
];
|
||||
}
|
||||
}
|
||||
56
app/Enums/Products/ProductLabelEnum.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Products;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self bio()
|
||||
* @method static self lr()
|
||||
* @method static self bea_a()
|
||||
* @method static self bea_b()
|
||||
* @method static self nutriscore_a()
|
||||
* @method static self nutriscore_b()
|
||||
* @method static self planetscore()
|
||||
* @method static self igp()
|
||||
*/
|
||||
final class ProductLabelEnum extends BaseEnum
|
||||
{
|
||||
/**
|
||||
* Enum values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'bio' => 0,
|
||||
'lr' => 1,
|
||||
'bea_a' => 2,
|
||||
'bea_b' => 3,
|
||||
'nutriscore_a' => 4,
|
||||
'nutriscore_b' => 5,
|
||||
'planetscore' => 6,
|
||||
'igp' => 7
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'bio' => trans('Bio'),
|
||||
'lr' => trans('Label Rouge'),
|
||||
'bea_a' => trans('Bea A'),
|
||||
'bea_b' => trans('Bea B'),
|
||||
'nutriscore_a' => trans('Nutriscore A'),
|
||||
'nutriscore_b' => trans('Nutriscore B'),
|
||||
'planetscore' => trans('PlanetScore'),
|
||||
'igp' => trans('IGP')
|
||||
];
|
||||
}
|
||||
}
|
||||
44
app/Enums/Ranges/RangeCategoryEnum.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Ranges;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self poultry()
|
||||
* @method static self egg()
|
||||
* @method static self partners()
|
||||
* @method static self fields()
|
||||
*/
|
||||
final class RangeCategoryEnum extends BaseEnum
|
||||
{
|
||||
/**
|
||||
* Enum values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'poultry' => 0,
|
||||
'egg' => 1,
|
||||
'partners' => 2,
|
||||
'fields' => 3
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'poultry' => trans('🐔 Volaille'),
|
||||
'egg' => trans('🥚 Oeuf'),
|
||||
'partners' => trans('🤝 Produits partenaires'),
|
||||
'fields' => trans('🌳 Plein champs'),
|
||||
];
|
||||
}
|
||||
}
|
||||
44
app/Enums/Recipes/RecipeCategoryEnum.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Recipes;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self stephanie()
|
||||
* @method static self gourmet()
|
||||
* @method static self festive()
|
||||
* @method static self others()
|
||||
*/
|
||||
final class RecipeCategoryEnum extends BaseEnum
|
||||
{
|
||||
/**
|
||||
* Enum values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'stephanie' => 0,
|
||||
'gourmet' => 1,
|
||||
'festive' => 2,
|
||||
'others' => 3
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'stephanie' => trans('Stéphanie Éleveuse de Loué'),
|
||||
'gourmet' => trans('Les recettes gourmandes'),
|
||||
'festive' => trans('Recettes festives'),
|
||||
'others' => trans('Autres'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ use Kwaadpepper\Enum\BaseEnum;
|
|||
* @method static self center()
|
||||
* @method static self right()
|
||||
*/
|
||||
final class SliderPosition extends BaseEnum
|
||||
final class SliderElementPosition extends BaseEnum
|
||||
{
|
||||
/**
|
||||
* Enum values
|
||||
43
app/Http/Controllers/Bo/EngagementsController.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Engagements\UpdateEngagementsRequest;
|
||||
use App\Models\Engagement;
|
||||
|
||||
class EngagementsController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param Engagement $engagement
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function edit(Engagement $engagement): \Illuminate\Contracts\View\View
|
||||
{
|
||||
return view('back.engagements.edit', ['engagement' => $engagement]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param UpdateEngagementsRequest $request
|
||||
* @param Engagement $engagement
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(
|
||||
UpdateEngagementsRequest $request,
|
||||
Engagement $engagement
|
||||
): \Illuminate\Http\RedirectResponse {
|
||||
$engagement->fill(array_replace_recursive(
|
||||
\json_decode(\json_encode($engagement->toArray()), true),
|
||||
$request->validated()
|
||||
));
|
||||
$engagement->saveOrFail();
|
||||
return redirect()
|
||||
->route('bo.engagements.edit', $engagement)
|
||||
->with('success', 'Enregistré !');
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ class NewsController extends Controller
|
|||
public function create()
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$tags = Tag::query()->select(['id', 'name'])->get();
|
||||
$tags = Tag::query()->select(['id', 'name', 'slug'])->get();
|
||||
return view('back.news.create', compact('tags'));
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ class NewsController extends Controller
|
|||
if ($news->saveOrFail()) {
|
||||
$news->tags()->sync(collect($request->tags)->pluck('id'));
|
||||
return redirect()
|
||||
->route('back.news.edit', $news)
|
||||
->route('bo.news.edit', $news)
|
||||
->with('success', trans(
|
||||
':model a été créé',
|
||||
['model' => Str::singular(trans('models.classes.news'))]
|
||||
|
|
@ -98,7 +98,7 @@ class NewsController extends Controller
|
|||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Collection */
|
||||
$tags = Tag::whereNotIn('id', $news->tags()->pluck('id'))
|
||||
->select(['id', 'name'])->get();
|
||||
->select(['id', 'name', 'slug'])->get();
|
||||
|
||||
return view('back.news.edit', [
|
||||
'newsModel' => $news,
|
||||
|
|
|
|||
153
app/Http/Controllers/Bo/ProductController.php
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Products\StoreProductRequest;
|
||||
use App\Http\Requests\Bo\Products\UpdateProductRequest;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use App\Traits\Controllers\UpdateModelStatus;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProductController extends Controller
|
||||
{
|
||||
use UpdateModelStatus;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $products */
|
||||
$products = Product::query();
|
||||
|
||||
if ($request->search) {
|
||||
$this->searchQuery(
|
||||
$products,
|
||||
$request->search,
|
||||
// * Search in tags
|
||||
function ($q) use ($request) {
|
||||
$q->orWhereHas('tags', function ($q) use ($request) {
|
||||
$q->whereRaw(
|
||||
'UPPER(CONCAT(`tags`.`name`)) LIKE UPPER(?)',
|
||||
['%' . filter_var($request->search, FILTER_SANITIZE_STRING) . '%']
|
||||
);
|
||||
});
|
||||
},
|
||||
'title',
|
||||
'description'
|
||||
);
|
||||
}
|
||||
|
||||
$this->sortQuery($products);
|
||||
$searchFields = trans('Titre, Description');
|
||||
|
||||
$products->orderBy('updated_at', 'DESC');
|
||||
$products = $products->paginate();
|
||||
|
||||
return view('back.products.index', compact('products', 'searchFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$ranges = Range::all();
|
||||
return view('back.products.create', compact('ranges'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\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());
|
||||
|
||||
if ($product->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.products.edit', $product)
|
||||
->with('success', trans(
|
||||
':model a été créé',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Product $product)
|
||||
{
|
||||
$ranges = Range::all();
|
||||
return view('back.products.edit', [
|
||||
'productModel' => $product,
|
||||
'ranges' => $ranges,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\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());
|
||||
/** @var \App\Models\IncoNutritionalValue */
|
||||
$nut = $product->incoNutritionalValues()->firstOrNew();
|
||||
$nut->fill($request->validated());
|
||||
$nut->saveOrFail();
|
||||
|
||||
if ($product->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.products.edit', $product)
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Product $product
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Product $product)
|
||||
{
|
||||
if ($product->deleteOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.products.index')
|
||||
->with('warning', trans(
|
||||
':model a bien été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.products'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
}
|
||||
}
|
||||
197
app/Http/Controllers/Bo/RangeController.php
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Ranges\RangeModelTitleRequest;
|
||||
use App\Http\Requests\Bo\Ranges\RangeSearchUriRequest;
|
||||
use App\Http\Requests\Bo\Ranges\StoreRangeRequest;
|
||||
use App\Http\Requests\Bo\Ranges\UpdateRangeRequest;
|
||||
use App\Http\Resources\UriResource;
|
||||
use App\Models\Breeder;
|
||||
use App\Models\Range;
|
||||
use App\Models\Recipe;
|
||||
use App\Traits\Controllers\UpdateModelStatus;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RangeController extends Controller
|
||||
{
|
||||
use UpdateModelStatus;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $ranges */
|
||||
$ranges = Range::query();
|
||||
|
||||
if ($request->search) {
|
||||
$this->searchQuery(
|
||||
$ranges,
|
||||
$request->search,
|
||||
// * Search in tags
|
||||
function ($q) use ($request) {
|
||||
$q->orWhereHas('tags', function ($q) use ($request) {
|
||||
$q->whereRaw(
|
||||
'UPPER(CONCAT(`tags`.`name`)) LIKE UPPER(?)',
|
||||
['%' . filter_var($request->search, FILTER_SANITIZE_STRING) . '%']
|
||||
);
|
||||
});
|
||||
},
|
||||
'title',
|
||||
'description'
|
||||
);
|
||||
}
|
||||
|
||||
$this->sortQuery($ranges);
|
||||
$searchFields = trans('Titre, Description');
|
||||
|
||||
$ranges->orderBy('updated_at', 'DESC');
|
||||
$ranges = $ranges->paginate();
|
||||
|
||||
return view('back.ranges.index', compact('ranges', 'searchFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('back.ranges.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Ranges\StoreRangeRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(StoreRangeRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$range = new Range();
|
||||
$range->fill($request->validated());
|
||||
|
||||
if ($range->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.ranges.edit', $range)
|
||||
->with('success', trans(
|
||||
':model a été créé',
|
||||
['model' => Str::singular(trans('models.classes.ranges'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Range $range
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Range $range)
|
||||
{
|
||||
return view('back.ranges.edit', [
|
||||
'rangeModel' => $range
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Ranges\UpdateRangeRequest $request
|
||||
* @param \App\Models\Range $range
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(UpdateRangeRequest $request, Range $range)
|
||||
{
|
||||
return DB::transaction(function () use ($request, $range) {
|
||||
$range->fill($request->validated());
|
||||
|
||||
if ($range->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.ranges.edit', $range)
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.ranges'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Range $range
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Range $range)
|
||||
{
|
||||
if ($range->deleteOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.ranges.index')
|
||||
->with('warning', trans(
|
||||
':model a bien été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.ranges'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search uris for ranges input links
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Ranges\RangeSearchUriRequest $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
*/
|
||||
public function searchUri(RangeSearchUriRequest $request)
|
||||
{
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Breeder> */
|
||||
$breeders = Breeder::where('firstname', 'LIKE', "%$request->search%")
|
||||
->orWhere('lastname', 'LIKE', "%$request->search%")
|
||||
->orderBy('updated_at')->paginate();
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Recipe> */
|
||||
$recipes = Recipe::where('title', 'LIKE', "%$request->search%")
|
||||
->orWhere('slug', 'LIKE', "%$request->search%")
|
||||
->orderBy('updated_at')->paginate();
|
||||
|
||||
return new UriResource(
|
||||
collect($breeders->all())
|
||||
->merge($recipes->all())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Model title input text uri (from DB value)
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Ranges\RangeModelTitleRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function modelTitle(RangeModelTitleRequest $request)
|
||||
{
|
||||
$title = '';
|
||||
switch ($request->model) {
|
||||
case Breeder::class:
|
||||
/** @var \App\Models\Breeder */
|
||||
$breeder = Breeder::whereId($request->id)->first();
|
||||
$title = \trim(optional($breeder)->lastname . ' ' . optional($breeder)->firstname);
|
||||
break;
|
||||
case Recipe::class:
|
||||
$title = Recipe::whereId($request->id)->first()->title ?? '';
|
||||
break;
|
||||
}
|
||||
return \response()->json($title);
|
||||
}
|
||||
}
|
||||
137
app/Http/Controllers/Bo/RecipeController.php
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Bo;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\Recipes\StoreRecipeRequest;
|
||||
use App\Http\Requests\Bo\Recipes\UpdateRecipeRequest;
|
||||
use App\Models\Recipe;
|
||||
use App\Traits\Controllers\UpdateModelStatus;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RecipeController extends Controller
|
||||
{
|
||||
use UpdateModelStatus;
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
/** @var \Illuminate\Database\Eloquent\Builder $recipes */
|
||||
$recipes = Recipe::query();
|
||||
|
||||
if ($request->search) {
|
||||
$this->searchQuery(
|
||||
$recipes,
|
||||
$request->search,
|
||||
null,
|
||||
'title',
|
||||
'description'
|
||||
);
|
||||
}
|
||||
|
||||
$this->sortQuery($recipes);
|
||||
$searchFields = trans('Titre, Description');
|
||||
|
||||
$recipes->orderBy('updated_at', 'DESC');
|
||||
$recipes = $recipes->paginate();
|
||||
|
||||
return view('back.recipes.index', compact('recipes', 'searchFields'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('back.recipes.create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Recipes\StoreRecipeRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(StoreRecipeRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
$recipe = new Recipe();
|
||||
$recipe->fill($request->validated());
|
||||
|
||||
if ($recipe->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.recipes.edit', $recipe)
|
||||
->with('success', trans(
|
||||
':model a été créé',
|
||||
['model' => Str::singular(trans('models.classes.recipes'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param \App\Models\Recipe $recipe
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit(Recipe $recipe)
|
||||
{
|
||||
return view('back.recipes.edit', [
|
||||
'recipeModel' => $recipe
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Recipes\UpdateRecipeRequest $request
|
||||
* @param \App\Models\Recipe $recipe
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(UpdateRecipeRequest $request, Recipe $recipe)
|
||||
{
|
||||
return DB::transaction(function () use ($request, $recipe) {
|
||||
$recipe->fill($request->validated());
|
||||
|
||||
if ($recipe->saveOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.recipes.edit', $recipe)
|
||||
->with('success', trans(
|
||||
':model a été mis à jour',
|
||||
['model' => Str::singular(trans('models.classes.recipes'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Models\Recipe $recipe
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Recipe $recipe)
|
||||
{
|
||||
if ($recipe->deleteOrFail()) {
|
||||
return redirect()
|
||||
->route('bo.recipes.index')
|
||||
->with('warning', trans(
|
||||
':model a bien été supprimé',
|
||||
['model' => Str::singular(trans('models.classes.recipes'))]
|
||||
));
|
||||
}
|
||||
return back();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,20 +4,19 @@ namespace App\Http\Controllers\Bo;
|
|||
|
||||
use App\Enums\Sliders\SliderType;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Bo\ChangeOrderRequest;
|
||||
use App\Http\Requests\Bo\Sliders\StoreSliderRequest;
|
||||
use App\Http\Requests\Bo\Sliders\UpdateSliderRequest;
|
||||
use App\Models\Slider;
|
||||
use App\Traits\UpdateModelStatus;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use App\Traits\Controllers\UpdateModelStatus;
|
||||
use App\Traits\Models\ChangesModelOrder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\Enum\Exceptions\EnumNotRoutableException;
|
||||
|
||||
class SliderController extends Controller
|
||||
{
|
||||
use ChangesModelOrder;
|
||||
use UpdateModelStatus;
|
||||
|
||||
/**
|
||||
|
|
@ -145,43 +144,4 @@ class SliderController extends Controller
|
|||
}
|
||||
return back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change Item Order
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\ChangeOrderRequest $request
|
||||
* @param \App\Models\Slider $slider
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function changeOrder(ChangeOrderRequest $request, Slider $slider)
|
||||
{
|
||||
try {
|
||||
DB::transaction(function () use ($request, $slider) {
|
||||
$tmp = Slider::query();
|
||||
if ($request->action === 'up') {
|
||||
// * Push Item upward
|
||||
$tmp->where('order', '<', $slider->order)
|
||||
->orderBy('order', 'DESC');
|
||||
} else {
|
||||
// * Push Item downward
|
||||
$tmp->where('order', '>', $slider->order)
|
||||
->orderBy('order', 'ASC');
|
||||
}
|
||||
$tmp->whereType('type', $slider->type);
|
||||
$tmp = $tmp->firstOrFail();
|
||||
$newOrder = $tmp->order;
|
||||
$tmp->order = $slider->order;
|
||||
$slider->order = $newOrder;
|
||||
if (!$slider->saveOrFail() or !$tmp->saveOrFail()) {
|
||||
throw new \ErrorException("Error Processing Request", 1);
|
||||
}
|
||||
});
|
||||
} catch (\ErrorException | ModelNotFoundException $e) {
|
||||
Session::flash('error', trans('Erreur lors de la modification de l\'ordre :message', [
|
||||
'message' => $e->getMessage()
|
||||
]));
|
||||
} //end try
|
||||
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,6 +74,23 @@ class TagController extends Controller
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Bo\Tags\StoreTagRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function jsonStore(StoreTagRequest $request)
|
||||
{
|
||||
return DB::transaction(function () use ($request) {
|
||||
/** @var \App\Models\Tag */
|
||||
$tag = Tag::where('slug', Str::slug($request->name))->firstOrNew();
|
||||
$tag->fill($request->validated());
|
||||
$saved = $tag->saveOrFail();
|
||||
return \response()->json($saved ? $tag->toArray() : [], $saved ? 200 : 500);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
'htmlsanitize',
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
|
|
@ -58,6 +59,6 @@ class Kernel extends HttpKernel
|
|||
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
|
||||
'xss' => \App\Http\Middleware\XssSanitizer::class,
|
||||
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class Authenticate extends Middleware
|
|||
}
|
||||
}
|
||||
}
|
||||
return route('login');
|
||||
return collect($logins)->first() ?? '/';
|
||||
}
|
||||
abort(401);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class XssSanitizer
|
||||
class HtmlSanitizer
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
|
|
@ -18,11 +18,17 @@ class XssSanitizer
|
|||
public function handle(Request $request, \Closure $next, ...$params)
|
||||
{
|
||||
$inputs = $request->all();
|
||||
array_walk_recursive($inputs, function (&$input, $name) use ($params) {
|
||||
/** @var callable|null */
|
||||
$cleanInput = null;
|
||||
$cleanInput = function (&$input, $name, $rootName = null) use ($params, &$cleanInput) {
|
||||
// * Nested element array
|
||||
if (is_array($input)) {
|
||||
array_walk_recursive($input, $cleanInput, $name);
|
||||
}
|
||||
if (!is_string($input)) {
|
||||
return;
|
||||
}
|
||||
if (in_array($name, $params)) {
|
||||
if (in_array($name, $params) or in_array($rootName ?? $name, $params)) {
|
||||
// ? Is a json input ?
|
||||
if ($isJson = \json_decode($input, true) and is_array($isJson)) {
|
||||
// ! Decode each json field recursive.
|
||||
|
|
@ -38,9 +44,9 @@ class XssSanitizer
|
|||
}
|
||||
} else {
|
||||
$input = strip_tags($input);
|
||||
$input = \filter_var($input, FILTER_SANITIZE_STRING);
|
||||
}
|
||||
});
|
||||
};
|
||||
\array_walk($inputs, $cleanInput);
|
||||
$request->merge($inputs);
|
||||
return $next($request);
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ class SecureHeaders
|
|||
$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:;",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Engagements;
|
||||
|
||||
use App\Http\Requests\Bo\UpdateMiniCrudRequest;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UpdateEngagementsRequest extends UpdateMiniCrudRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* The model parameter name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $modelName = 'engagement';
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
$title = strip_tags($this->title);
|
||||
$this->merge(['slug' => Str::slug($title)]);
|
||||
$this->mergePicture('banner');
|
||||
parent::prepareForValidation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return array_merge(
|
||||
[
|
||||
'title' => 'required|min:3|max:255',
|
||||
'title_simple' => 'required|min:3|max:255',
|
||||
'slug' => 'required|min:3|max:255',
|
||||
'seo_title' => 'required|min:53|max:60',
|
||||
'seo_description' => 'required|min:50|max:160',
|
||||
'banner_alt' => 'required|nullable|string|min:6|max:255',
|
||||
'banner_title' => 'required|nullable|string|min:6|max:255'
|
||||
],
|
||||
self::pictureRules('banner', 1920, 340, 1920, 340),
|
||||
parent::rules()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'title' => trans('Titre'),
|
||||
'title_simple' => trans('Titre sans style'),
|
||||
'seo_title' => trans('Titre') . ' seo',
|
||||
'seo_description' => trans('Description') . ' seo',
|
||||
'banner' => trans('Bannière'),
|
||||
'banner_alt' => trans('Texte alternatif de la bannière'),
|
||||
'banner_title' => trans('Titre de la bannière')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Http\Requests\Bo\News;
|
||||
|
||||
use App\Enums\PublishedStatus;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
|
|
@ -12,6 +13,8 @@ use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
|||
*/
|
||||
class StoreNewsRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
|
|
@ -32,6 +35,7 @@ class StoreNewsRequest extends FormRequest
|
|||
$this->merge([
|
||||
'slug' => Str::slug($this->title),
|
||||
]);
|
||||
$this->mergePicture('picture');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -41,13 +45,12 @@ class StoreNewsRequest extends FormRequest
|
|||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
return \array_merge([
|
||||
'title' => 'required|min:6|max:255',
|
||||
'slug' => 'required|max:255',
|
||||
'seo_title' => 'required|min:53|max:60',
|
||||
'seo_title' => 'required|min:45|max:70',
|
||||
'seo_description' => 'required|min:50|max:160',
|
||||
'content' => 'required|string|min:15',
|
||||
'picture' => 'sometimes|image|dimensions:width=300,height=300|mimes:png,jpg,jpeg,gif',
|
||||
'picture_alt' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'picture_title' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'published' => ['required', new EnumIsValidRule(PublishedStatus::class)],
|
||||
|
|
@ -55,7 +58,7 @@ class StoreNewsRequest extends FormRequest
|
|||
'tags.*' => 'required|array',
|
||||
'tags.*.id' => 'required|numeric|exists:tags,id|distinct',
|
||||
'tags.*.name' => 'required|string',
|
||||
];
|
||||
], $this->pictureRules('picture', 300, 300, 300, 300));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
174
app/Http/Requests/Bo/Products/StoreProductRequest.php
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Products;
|
||||
|
||||
use App\Enums\Products\ProductLabelEnum;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
|
||||
/**
|
||||
* Unicity for fields is handled by model events
|
||||
*/
|
||||
class StoreProductRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'slug' => Str::slug($this->title),
|
||||
'status' => $this->status ? true : false,
|
||||
'show_ingredients' => $this->show_ingredients ? true : false,
|
||||
'show_preparation_advices' => $this->show_preparation_advices ? true : false,
|
||||
'show_sales_pitch' => $this->show_sales_pitch ? true : false,
|
||||
'show_allergen_list' => $this->show_allergen_list ? true : false,
|
||||
'show_nutrionnal_values' => $this->show_nutrionnal_values ? true : false,
|
||||
'show_portions' => $this->show_portions ? true : false,
|
||||
'labels' => $this->labels ?? []
|
||||
]);
|
||||
$this->mergePicture();
|
||||
$this->mergePicture('picture_thumb');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return \array_merge(
|
||||
[
|
||||
'title' => 'required|min:6|max:255',
|
||||
'slug' => 'required|max:255',
|
||||
'seo_title' => 'required|min:53|max:60',
|
||||
'seo_description' => 'required|min:50|max:160',
|
||||
'description' => 'required|string|min:15',
|
||||
'picture_alt' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'picture_title' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'picture_thumb_alt' => 'required_with:picture_thumb|nullable|string|min:6|max:255',
|
||||
'picture_thumb_title' => 'required_with:picture_thumb|nullable|string|min:6|max:255',
|
||||
'publish_at' => 'nullable|date_format:Y-m-d\TH:i',
|
||||
'unpublish_at' => 'nullable|date_format:Y-m-d\TH:i',
|
||||
'status' => 'required|boolean',
|
||||
'range_id' => 'required|exists:ranges,id',
|
||||
|
||||
'ref_article' => 'required|numeric|min:0',
|
||||
'ingredients' => 'required|string|min:15',
|
||||
'preparation_advices' => 'required|string|min:15',
|
||||
'sales_pitch' => 'required|string|min:15',
|
||||
'allergen_list' => 'required|string|min:15',
|
||||
'number_portion' => 'required|numeric|min:0',
|
||||
|
||||
'show_ingredients' => 'required|boolean',
|
||||
'show_preparation_advices' => 'required|boolean',
|
||||
'show_sales_pitch' => 'required|boolean',
|
||||
'show_allergen_list' => 'required|boolean',
|
||||
'show_nutrionnal_values' => 'required|boolean',
|
||||
'show_portions' => 'required|boolean',
|
||||
|
||||
'labels' => 'sometimes|array',
|
||||
'labels.*' => 'sometimes|array',
|
||||
'labels.*.value' => ['required', new EnumIsValidRule(ProductLabelEnum::class)],
|
||||
|
||||
'weight' => 'required|numeric',
|
||||
'energy' => 'required|numeric',
|
||||
'energy_kj' => 'required|numeric',
|
||||
'protein' => 'required|numeric',
|
||||
'carbohydrates' => 'required|numeric',
|
||||
'sugar' => 'required|numeric',
|
||||
'fat' => 'required|numeric',
|
||||
'saturated_fatty_acids' => 'required|numeric',
|
||||
'dietary_fiber' => 'required|numeric',
|
||||
'salt' => 'required|numeric',
|
||||
|
||||
'link1' => self::getUrlValidationRules(),
|
||||
'link2' => self::getUrlValidationRules(),
|
||||
],
|
||||
$this->pictureRules('picture', 700, 700, 700, 700),
|
||||
$this->pictureRules('picture_thumb', 300, 300, 300, 300)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'title' => trans('title'),
|
||||
'slug' => trans('title'),
|
||||
'seo_title' => trans('title') . ' seo',
|
||||
'seo_description' => trans('description') . ' seo',
|
||||
'description' => trans('description'),
|
||||
'picture' => trans('illustration'),
|
||||
'picture_alt' => trans('Texte alternatif de l\'illustration'),
|
||||
'picture_title' => trans('Titre de l\'illustration'),
|
||||
'picture_thumb' => trans('vignette'),
|
||||
'picture_thumb_alt' => trans('Texte alternatif de la vignette'),
|
||||
'picture_thumb_title' => trans('Titre de la vignette'),
|
||||
'status' => trans('publié ?'),
|
||||
'publish_at' => trans('date de publication'),
|
||||
'unpublish_at' => trans('date de dépublication'),
|
||||
|
||||
'nutritional' => trans('valeurs nutritionelles'),
|
||||
'nutritional.energy' => trans('énergie'),
|
||||
'nutritional.energy_kj' => trans('énergie kj'),
|
||||
'nutritional.protein' => trans('protéines'),
|
||||
'nutritional.carbohydrates' => trans('glucides'),
|
||||
'nutritional.sugar' => trans('sucres'),
|
||||
'nutritional.fat' => trans('matière grasse'),
|
||||
'nutritional.saturated_fatty_acids' => trans('acides gras saturés'),
|
||||
'nutritional.dietary_fiber' => trans('fibres alimentaires'),
|
||||
'nutritional.salt' => trans('sel'),
|
||||
|
||||
'link1' => trans('lien 1'),
|
||||
'link2' => trans('lien 2'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Url validation rules
|
||||
* Validates an internal url format
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getUrlValidationRules(): array
|
||||
{
|
||||
return ['nullable', function ($attribute, $value, $fail) {
|
||||
$params = collect(\explode('#', \ltrim(\rtrim($value, '#'), '#')));
|
||||
if (
|
||||
!\in_array(
|
||||
$params->get(0),
|
||||
[Breeder::class, Recipe::class]
|
||||
)
|
||||
) {
|
||||
$fail(trans('Invalid internal url'));
|
||||
return;
|
||||
}
|
||||
// Check if model id exists. Param 0 is class, 1 is id.
|
||||
if (!\forward_static_call([$params->get(0), 'find'], $params->get(1))) {
|
||||
$fail(trans("$attribute n\'est plus un lien valide."));
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
10
app/Http/Requests/Bo/Products/UpdateProductRequest.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Products;
|
||||
|
||||
/**
|
||||
* Unicity for fields is handled by model events
|
||||
*/
|
||||
class UpdateProductRequest extends StoreProductRequest
|
||||
{
|
||||
}
|
||||
44
app/Http/Requests/Bo/Ranges/RangeModelTitleRequest.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Ranges;
|
||||
|
||||
use App\Models\Breeder;
|
||||
use App\Models\Recipe;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class RangeModelTitleRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() and $this->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'id' => 'required|numeric',
|
||||
'model' => [
|
||||
\sprintf('in:%s,%s', Breeder::class, Recipe::class),
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed,Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
$table = (new $value())->getTable();
|
||||
Validator::validate(
|
||||
['model' => $this->id],
|
||||
['model' => "required|exists:{$table},id"]
|
||||
);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
31
app/Http/Requests/Bo/Ranges/RangeSearchUriRequest.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Ranges;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class RangeSearchUriRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->check() and $this->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'search' => 'required|string',
|
||||
'page' => 'sometimes|numeric',
|
||||
];
|
||||
}
|
||||
}
|
||||
129
app/Http/Requests/Bo/Ranges/StoreRangeRequest.php
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Ranges;
|
||||
|
||||
use App\Enums\Ranges\RangeCategoryEnum;
|
||||
use App\Models\Breeder;
|
||||
use App\Models\Recipe;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
|
||||
/**
|
||||
* Unicity for fields is handled by model events
|
||||
*/
|
||||
class StoreRangeRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'slug' => Str::slug($this->title),
|
||||
'status' => $this->status ? true : false
|
||||
]);
|
||||
$this->mergePicture();
|
||||
$this->mergePicture('picture_thumb');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return \array_merge(
|
||||
[
|
||||
'title' => 'required|min:6|max:255',
|
||||
'slug' => 'required|max:255',
|
||||
'category' => ['required', new EnumIsValidRule(RangeCategoryEnum::class)],
|
||||
'seo_title' => 'required|min:45|max:70',
|
||||
'seo_description' => 'required|min:50|max:160',
|
||||
'description' => 'required|string|min:15',
|
||||
'argumentation' => 'required|string|min:15',
|
||||
'picture_alt' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'picture_title' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'picture_thumb_alt' => 'required_with:picture_thumb|nullable|string|min:6|max:255',
|
||||
'picture_thumb_title' => 'required_with:picture_thumb|nullable|string|min:6|max:255',
|
||||
'publish_at' => 'nullable|date_format:Y-m-d\TH:i',
|
||||
'unpublish_at' => 'nullable|date_format:Y-m-d\TH:i',
|
||||
'status' => 'required|boolean',
|
||||
'link1' => self::getUrlValidationRules(),
|
||||
'link2' => self::getUrlValidationRules(),
|
||||
],
|
||||
$this->pictureRules('picture', 700, 700, 700, 700),
|
||||
$this->pictureRules('picture_thumb', 300, 300, 300, 300)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'title' => trans('title'),
|
||||
'slug' => trans('title'),
|
||||
'seo_title' => trans('title') . ' seo',
|
||||
'seo_description' => trans('description') . ' seo',
|
||||
'description' => trans('description'),
|
||||
'argumentation' => trans('argumentation'),
|
||||
'picture' => trans('illustration'),
|
||||
'picture_alt' => trans('Texte alternatif de l\'illustration'),
|
||||
'picture_title' => trans('Titre de l\'illustration'),
|
||||
'picture_thumb' => trans('vignette'),
|
||||
'picture_thumb_alt' => trans('Texte alternatif de la vignette'),
|
||||
'picture_thumb_title' => trans('Titre de la vignette'),
|
||||
'status' => trans('publié ?'),
|
||||
'publish_at' => trans('date de publication'),
|
||||
'unpublish_at' => trans('date de dépublication'),
|
||||
'link1' => trans('lien 1'),
|
||||
'link2' => trans('lien 2'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Url validation rules
|
||||
* Validates an internal url format
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getUrlValidationRules(): array
|
||||
{
|
||||
return ['nullable', function ($attribute, $value, $fail) {
|
||||
$params = collect(\explode('#', \ltrim(\rtrim($value, '#'), '#')));
|
||||
if (
|
||||
!\in_array(
|
||||
$params->get(0),
|
||||
[Breeder::class, Recipe::class]
|
||||
)
|
||||
) {
|
||||
$fail(trans('Invalid internal url'));
|
||||
return;
|
||||
}
|
||||
// Check if model id exists. Param 0 is class, 1 is id.
|
||||
if (!\forward_static_call([$params->get(0), 'find'], $params->get(1))) {
|
||||
$fail(trans("$attribute n\'est plus un lien valide."));
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
10
app/Http/Requests/Bo/Ranges/UpdateRangeRequest.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Ranges;
|
||||
|
||||
/**
|
||||
* Unicity for fields is handled by model events
|
||||
*/
|
||||
class UpdateRangeRequest extends StoreRangeRequest
|
||||
{
|
||||
}
|
||||
103
app/Http/Requests/Bo/Recipes/StoreRecipeRequest.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Recipes;
|
||||
|
||||
use App\Enums\Recipes\RecipeCategoryEnum;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
|
||||
/**
|
||||
* Unicity for fields is handled by model events
|
||||
*/
|
||||
class StoreRecipeRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$this->merge([
|
||||
'status' => $this->status ? true : false
|
||||
]);
|
||||
$this->mergePicture('banner');
|
||||
$this->mergePicture('picture');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return \array_merge(
|
||||
[
|
||||
'title' => 'required|min:6|max:255',
|
||||
'seo_title' => 'required|min:53|max:60',
|
||||
'seo_description' => 'required|min:50|max:160',
|
||||
'banner_alt' => 'required_with:banner|nullable|string|min:6|max:255',
|
||||
'banner_title' => 'required_with:banner|nullable|string|min:6|max:255',
|
||||
'category' => ['required', new EnumIsValidRule(RecipeCategoryEnum::class)],
|
||||
'servings' => 'required|numeric|min:0',
|
||||
'preparation' => 'required|string|min:3|max:255',
|
||||
'cooking' => 'required|string|min:3|max:255',
|
||||
'ingredients' => 'required|array',
|
||||
'ingredients.*' => 'required|array',
|
||||
'ingredients.*.value' => 'present|nullable|numeric',
|
||||
'ingredients.*.label' => 'required|string|min:3|max:255',
|
||||
'description' => 'required|string|min:6|max:255',
|
||||
'video' => 'required|string|min:6|max:255',
|
||||
'picture_alt' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'picture_title' => 'required_with:picture|nullable|string|min:6|max:255',
|
||||
'status' => 'required|boolean'
|
||||
],
|
||||
$this->pictureRules('banner', 695, 557, 695, 557),
|
||||
$this->pictureRules('picture', 333, 182, 333, 182)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get custom attributes for validator errors.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'title' => trans('title'),
|
||||
'slug' => trans('title'),
|
||||
'seo_title' => trans('Title') . ' seo',
|
||||
'seo_description' => trans('Description') . ' seo',
|
||||
'banner' => trans('Bannière'),
|
||||
'banner_alt' => trans('Texte alternatif de la bannière'),
|
||||
'banner_title' => trans('Titre de la bannière'),
|
||||
'category' => trans('Catégorie de la recette'),
|
||||
'servings' => trans('Chiffre/nombre de portions de la recette'),
|
||||
'preparation' => trans('Temps de préparation de la recette'),
|
||||
'cooking' => trans('Temps de cuisson de la recette'),
|
||||
'ingredients' => trans('Ingrédients contenus dans la recette'),
|
||||
'ingredients.*.value' => trans('Quantité de l\'ingrédient contenus dans la recette'),
|
||||
'ingredients.*.label' => trans('Libellé de l\'ingrédient contenus dans la recette'),
|
||||
'description' => trans('Description de la recette'),
|
||||
'picture' => trans('Image de la recette'),
|
||||
'picture_alt' => trans('Texte alternatif de la recette'),
|
||||
'picture_title' => trans('Titre de la recette'),
|
||||
'status' => trans('Publié ?')
|
||||
];
|
||||
}
|
||||
}
|
||||
10
app/Http/Requests/Bo/Recipes/UpdateRecipeRequest.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo\Recipes;
|
||||
|
||||
/**
|
||||
* Unicity for fields is handled by model events
|
||||
*/
|
||||
class UpdateRecipeRequest extends StoreRecipeRequest
|
||||
{
|
||||
}
|
||||
|
|
@ -2,12 +2,16 @@
|
|||
|
||||
namespace App\Http\Requests\Bo\Sliders;
|
||||
|
||||
use App\Enums\Sliders\SliderPosition;
|
||||
use App\Enums\Sliders\SliderElementPosition;
|
||||
use App\Enums\Sliders\SliderType;
|
||||
use App\Traits\Requests\HasPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
|
||||
class StoreSliderRequest extends FormRequest
|
||||
{
|
||||
use HasPicture;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
|
|
@ -31,6 +35,7 @@ class StoreSliderRequest extends FormRequest
|
|||
'publish_at' => $this->publish_at ? \str_replace('T', ' ', $this->publish_at) : null,
|
||||
'unpublish_at' => $this->unpublish_at ? \str_replace('T', ' ', $this->unpublish_at) : null,
|
||||
]);
|
||||
$this->mergePicture();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -40,21 +45,31 @@ class StoreSliderRequest extends FormRequest
|
|||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
$rules = [
|
||||
'title' => 'required|min:3|max:255',
|
||||
'title_color' => 'required|color_hex',
|
||||
'title_color_bg' => 'required|color_hex',
|
||||
'position' => ['required', new EnumIsValidRule(SliderPosition::class)],
|
||||
'picture' => 'required|image|dimensions:width=1400,height=630|mimes:png,jpg,jpeg,gif',
|
||||
'title_color' => 'nullable|color_rgba',
|
||||
'title_color_bg' => 'nullable|color_rgba',
|
||||
'subtitle' => 'required|min:3|max:255',
|
||||
'subtitle_color' => 'nullable|color_rgba',
|
||||
'subtitle_color_bg' => 'nullable|color_rgba',
|
||||
'text_position_desktop' => ['required', new EnumIsValidRule(SliderElementPosition::class)],
|
||||
'picture_position_mobile' => ['required', new EnumIsValidRule(SliderElementPosition::class)],
|
||||
'link' => 'required|url',
|
||||
'link_title' => 'required|min:3|max:255',
|
||||
'link_color' => 'required|color_hex',
|
||||
'link_color_bg' => 'required|color_hex',
|
||||
'link_color' => 'nullable|color_rgba',
|
||||
'link_color_hover' => 'nullable|color_rgba',
|
||||
'link_color_bg' => 'nullable|color_rgba',
|
||||
'link_color_bg_hover' => 'nullable|color_rgba',
|
||||
'link_new_tab' => 'required|boolean',
|
||||
'status' => 'required|boolean',
|
||||
'publish_at' => 'nullable|date_format:Y-m-d H:i',
|
||||
'unpublish_at' => 'nullable|date_format:Y-m-d H:i',
|
||||
];
|
||||
|
||||
if (!$this->slider) {
|
||||
$rules['type'] = ['required', new EnumIsValidRule(SliderType::class)];
|
||||
}
|
||||
return \array_merge($rules, $this->pictureRules('picture', 1400, 630, 1400, 630));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,13 +83,15 @@ class StoreSliderRequest extends FormRequest
|
|||
'title' => trans('title'),
|
||||
'title_color' => trans('couleur de titre'),
|
||||
'title_color_bg' => trans('couleur de fond du titre'),
|
||||
'position' => trans('position de centrage de l\'image'),
|
||||
'picture_position_mobile' => trans('position de centrage de l\'image en mobile'),
|
||||
'text_position_desktop' => trans('position de centrage du texte en desktop'),
|
||||
'link' => trans('lien'),
|
||||
'link_title' => trans('titre du lien'),
|
||||
'link_color' => trans('couleur du lien'),
|
||||
'link_color_bg' => trans('couleur de fond du lien'),
|
||||
'link_new_tab' => trans('nouvel onglet'),
|
||||
'status' => trans('publication'),
|
||||
'picture' => trans('image'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ class UpdateSliderRequest extends StoreSliderRequest
|
|||
*/
|
||||
public function rules()
|
||||
{
|
||||
return \array_merge(parent::rules(), [
|
||||
'picture' => 'sometimes|image|dimensions:width=1400,height=630|mimes:png,jpg,jpeg,gif'
|
||||
]);
|
||||
$rules = parent::rules();
|
||||
unset($rules['type']);
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
185
app/Http/Requests/Bo/UpdateMiniCrudRequest.php
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Bo;
|
||||
|
||||
use App\Enums\InputType;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
abstract class UpdateMiniCrudRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* The model parameter name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $modelName = null;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth('backend')->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the data for validation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepareForValidation(): void
|
||||
{
|
||||
foreach ($this->{$this->modelName}->data as $iptName => $input) {
|
||||
$input = \json_decode(\json_encode($input), true);
|
||||
switch ($input['inputType']['value']) {
|
||||
case InputType::boolean()->value:
|
||||
$all = $this->all();
|
||||
$nested = Arr::undot([
|
||||
"data.$iptName.value" =>
|
||||
$this->input("data.$iptName.value") ? true : false
|
||||
]);
|
||||
$all = array_merge_recursive_distinct($all, $nested);
|
||||
$this->merge($all);
|
||||
break;
|
||||
} //end switch
|
||||
} //end foreach
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
// phpcs:enable
|
||||
$rules = [];
|
||||
foreach ($this->{$this->modelName}->data as $iptName => $input) {
|
||||
$input = \json_decode(\json_encode($input), true);
|
||||
switch ($input['inputType']['value']) {
|
||||
case InputType::number()->value:
|
||||
$rules = array_merge($rules, [
|
||||
"data.$iptName.value" => $this->compileValidate('required|numeric', $input)
|
||||
]);
|
||||
break;
|
||||
case InputType::string()->value:
|
||||
$rules = array_merge($rules, [
|
||||
"data.$iptName.value" => $this->compileValidate('required|string|min:1', $input)
|
||||
]);
|
||||
break;
|
||||
case InputType::email()->value:
|
||||
$rules = array_merge($rules, [
|
||||
"data.$iptName.value" =>
|
||||
$this->compileValidate('required|email:rfc,strict,dns,spoof,filter', $input)
|
||||
]);
|
||||
break;
|
||||
case InputType::text()->value:
|
||||
$mergeRules = $this->compileValidate('required|string|min:10', $input);
|
||||
$mergeRules = collect($mergeRules)
|
||||
->map(function ($rule) {
|
||||
if (\strpos($rule, 'max:') !== false) {
|
||||
$max = \intval(str_replace('max:', '', $rule));
|
||||
return function ($attribute, $value, $fail) use ($max) {
|
||||
if (\strlen(\strip_tags($value)) > $max) {
|
||||
$fail("$attribute ne doit pas excéder $max caractères");
|
||||
}
|
||||
};
|
||||
}
|
||||
return $rule;
|
||||
});
|
||||
$rules = array_merge($rules, [
|
||||
"data.$iptName.text" => $mergeRules
|
||||
]);
|
||||
break;
|
||||
case InputType::image()->value:
|
||||
$ipt = $this->data[$iptName];
|
||||
$max = isset($ipt['dimensionsMax']);
|
||||
$dim = collect($ipt['dimensionsMax'] ?? $input['dimensions']);
|
||||
$rules = array_merge($rules, [
|
||||
"data.$iptName.value" => array_merge(
|
||||
$this->compileValidate('sometimes', $input),
|
||||
[function ($attribute, $value, $fail) use ($max, $dim) {
|
||||
try {
|
||||
if (\is_object($value)) {
|
||||
Validator::make(
|
||||
[$attribute => $value],
|
||||
// Replace dots since laravel framework 8.76.
|
||||
[ToolboxHelper::mbReplace('.', '\.', $attribute) => [
|
||||
'file',
|
||||
'mimes:png,jpg,jpeg',
|
||||
sprintf(
|
||||
'dimensions:%s=%d,%s=%d',
|
||||
$max ? 'max_height' : 'height',
|
||||
$dim['hauteur'],
|
||||
$max ? 'max_width' : 'width',
|
||||
$dim['largeur']
|
||||
)
|
||||
]]
|
||||
)->validate();
|
||||
} //end if
|
||||
} catch (ValidationException $e) {
|
||||
$fail(collect($e->errors())->first()[0]);
|
||||
} //end try
|
||||
}]
|
||||
),
|
||||
"data.$iptName.alt" => $this->compileValidate('required|string|min:5', $input),
|
||||
"data.$iptName.title" => $this->compileValidate('required|string|min:5', $input)
|
||||
]);
|
||||
break;
|
||||
case InputType::url()->value:
|
||||
$rules = array_merge($rules, [
|
||||
"data.$iptName.value" => [
|
||||
'required', 'url',
|
||||
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
try {
|
||||
ToolboxHelper::assertNoInternalUrls($attribute, $value);
|
||||
ToolboxHelper::assertNoInternalBoUrls($attribute, $value);
|
||||
} catch (ValidationException $e) {
|
||||
$fail(collect($e->errors())->first()[0]);
|
||||
}
|
||||
}
|
||||
]
|
||||
]);
|
||||
break;
|
||||
case InputType::boolean()->value:
|
||||
$rules = array_merge($rules, [
|
||||
"data.$iptName.value" => $this->compileValidate('boolean', $input)
|
||||
]);
|
||||
break;
|
||||
} //end switch
|
||||
} //end foreach
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param string $inputTypeValidate
|
||||
* @param array $input
|
||||
* @return array
|
||||
*/
|
||||
public function compileValidate(string $inputTypeValidate, array $input): array
|
||||
{
|
||||
if (!isset($input['validate']) or !\is_string($input['validate'])) {
|
||||
return explode('|', $inputTypeValidate);
|
||||
}
|
||||
/** @var array */
|
||||
$inputValidate = explode('|', $input['validate']);
|
||||
$out = explode('|', $inputTypeValidate);
|
||||
foreach ($inputValidate as $validate) {
|
||||
if (!\in_array($validate, $out)) {
|
||||
$out[] = $validate;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
27
app/Http/Resources/BreederUriResource.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Breeder;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class BreederUriResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param mixed $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
/** @var \App\Models\Breeder */
|
||||
$model = $this->resource;
|
||||
return [
|
||||
'class' => Breeder::class,
|
||||
'label' => \trim("{$model->lastname} {$model->firstname}"),
|
||||
'value' => $model->id
|
||||
];
|
||||
}
|
||||
}
|
||||
27
app/Http/Resources/RecipeUriResource.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Recipe;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class RecipeUriResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param mixed $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
/** @var \App\Models\Recipe */
|
||||
$model = $this->resource;
|
||||
return [
|
||||
'class' => Recipe::class,
|
||||
'label' => \trim("{$model->title}"),
|
||||
'value' => $model->id
|
||||
];
|
||||
}
|
||||
}
|
||||
37
app/Http/Resources/UriResource.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Resources;
|
||||
|
||||
use App\Models\Breeder;
|
||||
use App\Models\Recipe;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class UriResource extends ResourceCollection
|
||||
{
|
||||
/**
|
||||
* Transform the resource collection into an array.
|
||||
*
|
||||
* @param mixed $request
|
||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
/** @var \Illuminate\Support\Collection $collection */
|
||||
$collection = $this->resource;
|
||||
return $collection->map(function (Model $model) {
|
||||
switch (\get_class($model)) {
|
||||
case Breeder::class:
|
||||
return new BreederUriResource($model);
|
||||
case Recipe::class:
|
||||
return new RecipeUriResource($model);
|
||||
default:
|
||||
throw new \RuntimeException(sprintf(
|
||||
'Unhandled model %s',
|
||||
\get_class($model)
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
41
app/Jobs/ProductStatusUpdate.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Product;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Update published and visibility attributes reading their
|
||||
* publish_at and unpublish_at attributes.
|
||||
*/
|
||||
class ProductStatusUpdate
|
||||
{
|
||||
/**
|
||||
* Called when the job will be executed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$productsToPublish = Product::where('publish_at', '<=', Carbon::now()->toDateTime())
|
||||
->whereNotNull('publish_at')
|
||||
->whereStatus(false)->get();
|
||||
Log::debug(__CLASS__ . " products to publish " . $productsToPublish->count());
|
||||
foreach ($productsToPublish as $product) {
|
||||
$product->publish_at = null;
|
||||
$product->status = true;
|
||||
$product->saveOrFail();
|
||||
}
|
||||
$productsToUnPublish = Product::where('unpublish_at', '<=', Carbon::now()->toDateTime())
|
||||
->whereNotNull('unpublish_at')
|
||||
->whereStatus(true)->get();
|
||||
Log::debug(__CLASS__ . " products to unpublish " . $productsToPublish->count());
|
||||
foreach ($productsToUnPublish as $product) {
|
||||
$product->unpublish_at = null;
|
||||
$product->status = false;
|
||||
$product->saveOrFail();
|
||||
}
|
||||
}
|
||||
}
|
||||
41
app/Jobs/RangesStatusUpdate.php
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Range;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Update published and visibility attributes reading their
|
||||
* publish_at and unpublish_at attributes.
|
||||
*/
|
||||
class RangesStatusUpdate
|
||||
{
|
||||
/**
|
||||
* Called when the job will be executed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$rangesToPublish = Range::where('publish_at', '<=', Carbon::now()->toDateTime())
|
||||
->whereNotNull('publish_at')
|
||||
->whereStatus(false)->get();
|
||||
Log::debug(__CLASS__ . " ranges to publish " . $rangesToPublish->count());
|
||||
foreach ($rangesToPublish as $range) {
|
||||
$range->publish_at = null;
|
||||
$range->status = true;
|
||||
$range->saveOrFail();
|
||||
}
|
||||
$rangesToUnPublish = Range::where('unpublish_at', '<=', Carbon::now()->toDateTime())
|
||||
->whereNotNull('unpublish_at')
|
||||
->whereStatus(true)->get();
|
||||
Log::debug(__CLASS__ . " ranges to unpublish " . $rangesToPublish->count());
|
||||
foreach ($rangesToUnPublish as $range) {
|
||||
$range->unpublish_at = null;
|
||||
$range->status = false;
|
||||
$range->saveOrFail();
|
||||
}
|
||||
}
|
||||
}
|
||||
410
app/Lib/Helpers/FileStorageHelper.php
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
<?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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib;
|
||||
namespace App\Lib\Helpers;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
|
@ -59,10 +56,7 @@ class ToolboxHelper
|
|||
) {
|
||||
// Removes the matched url.
|
||||
throw ValidationException::withMessages([
|
||||
$field => sprintf(
|
||||
'%s : Il est interdit de coller une url du back office dans ce champ',
|
||||
trans($field)
|
||||
)
|
||||
$field => ' :attribute : Il est interdit de coller une url du back office dans ce champ'
|
||||
]);
|
||||
}
|
||||
return $match;
|
||||
|
|
@ -75,29 +69,6 @@ class ToolboxHelper
|
|||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files path uploaded with ckeditor custom upload plugin (crud-policies)
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $detectExtensions
|
||||
* @return array
|
||||
*/
|
||||
public static function getCkEditorLocalUploadedFiles(
|
||||
string $text,
|
||||
array $detectExtensions = ['png', 'svg', 'jpg', 'gif']
|
||||
): array {
|
||||
$out = [];
|
||||
\preg_match_all(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
'/src="(?P<url>\/storage\/images\/crud-policies\/uploads\/\d{4}-\d{2}-\d{2}\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-[-a-zA-Z0-9@%._\+~#=]{2,256}\.(?:' . implode('|', $detectExtensions) . '))"/',
|
||||
$text,
|
||||
$out
|
||||
);
|
||||
return collect($out['url'] ?? [])->map(function (string $urlEncodedFilePath) {
|
||||
return \urldecode($urlEncodedFilePath);
|
||||
})->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert That only external url is present on txt
|
||||
*
|
||||
|
|
@ -127,8 +98,7 @@ class ToolboxHelper
|
|||
// Removes the matched url.
|
||||
throw ValidationException::withMessages([
|
||||
$field => sprintf(
|
||||
'%s : Il est interdit de coller une url de %s dans ce champ',
|
||||
trans($field),
|
||||
':attribute : Il est interdit de coller une url de %s dans ce champ',
|
||||
config('app.name')
|
||||
)
|
||||
]);
|
||||
|
|
@ -144,113 +114,42 @@ class ToolboxHelper
|
|||
}
|
||||
|
||||
/**
|
||||
* Generates an array from a dot separated string
|
||||
*
|
||||
* @param string $path
|
||||
* @param mixed $value
|
||||
* @param string $separator
|
||||
* @return array
|
||||
* Mutltibyte string replace
|
||||
* @param mixed $search
|
||||
* @param mixed $replace
|
||||
* @param mixed $subject
|
||||
* @param integer $count
|
||||
* @return mixed
|
||||
*/
|
||||
public static function dotStringToNestedArray(string $path, $value, string $separator = '.'): array
|
||||
public static function mbReplace($search, $replace, $subject, int &$count = 0)
|
||||
{
|
||||
$arr = $value;
|
||||
$keys = array_reverse(explode($separator, $path));
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$arr = [$key => $arr];
|
||||
if (!is_array($search) && is_array($replace)) {
|
||||
return false;
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy image
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $attribute
|
||||
* @return void
|
||||
*/
|
||||
public static function removeImage(Model $model, string $attribute)
|
||||
{
|
||||
$path = ltrim($model->{$attribute}, '/');
|
||||
if (File::exists($path) and \strpos($path, 'storage') === 0) {
|
||||
File::delete($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy image
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $attribute
|
||||
* @return void
|
||||
*/
|
||||
public static function removeOldImage(Model $model, string $attribute)
|
||||
{
|
||||
$originalPath = ltrim($model->getOriginal($attribute), '/');
|
||||
if (File::exists($originalPath) and \strpos($originalPath, 'storage') === 0) {
|
||||
File::delete($originalPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an image of different types
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param \Illuminate\Http\UploadedFile|\SplFileInfo|string $image
|
||||
* @return string
|
||||
* @throws \ErrorException If file copy fails.
|
||||
*/
|
||||
public static function storeImage(Model $model, $image): string
|
||||
{
|
||||
$table = $model->getTable();
|
||||
if (\is_object($image) and $image instanceof \Illuminate\Http\UploadedFile) {
|
||||
/** @var \Illuminate\Http\UploadedFile $image */
|
||||
$newPath = self::prepareMoveFile($table, \filter_var(
|
||||
\strip_tags(\urldecode($image->getClientOriginalName())),
|
||||
\FILTER_SANITIZE_STRING
|
||||
));
|
||||
$newPath = str_replace(storage_path('app'), '', $newPath);
|
||||
$image->storePubliclyAs(
|
||||
pathinfo($newPath, \PATHINFO_DIRNAME),
|
||||
pathinfo($newPath, \PATHINFO_BASENAME)
|
||||
);
|
||||
return 'storage' . str_replace('/public', '', $newPath);
|
||||
}
|
||||
if (\is_object($image) and $image instanceof \SplFileInfo) {
|
||||
/** @var \SplFileInfo $image */
|
||||
$newPath = self::prepareMoveFile($table, $image->getBaseName());
|
||||
if (!File::copy($image->getRealPath(), $newPath)) {
|
||||
throw new \ErrorException(sprintf(
|
||||
'Failed to copy file %s to %s',
|
||||
$image->getRealPath(),
|
||||
$newPath
|
||||
));
|
||||
if (is_array($subject)) {
|
||||
// Call mb_replace for each single string in $subject .
|
||||
foreach ($subject as &$string) {
|
||||
$string = &self::mbReplace($search, $replace, $string, $count);
|
||||
}
|
||||
return \ltrim(str_replace(\storage_path('app/public'), '/storage', $newPath), '/');
|
||||
}
|
||||
return strval($image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare storage path and return
|
||||
* the image full path
|
||||
*
|
||||
* @param string $table Model table name.
|
||||
* @param string $filename The basename for the file.
|
||||
* @return string
|
||||
*/
|
||||
private static function prepareMoveFile(string $table, string $filename): string
|
||||
{
|
||||
$pathFolder = \storage_path(sprintf('app/public/images/%s', $table));
|
||||
if (!File::exists($pathFolder)) {
|
||||
File::makeDirectory($pathFolder, 0755, true);
|
||||
}
|
||||
$filename = sprintf('%s-%s', Str::uuid(), $filename);
|
||||
$fullPath = sprintf('%s/%s', $pathFolder, $filename);
|
||||
if (File::exists($fullPath)) {
|
||||
File::delete($fullPath);
|
||||
}
|
||||
return $fullPath;
|
||||
} 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -273,9 +172,10 @@ class ToolboxHelper
|
|||
) {
|
||||
if (!$query) {
|
||||
Validator::make([$field => $value], [
|
||||
$field => Rule::unique($table, $field)->ignore($id),
|
||||
self::mbReplace('.', '\.', $field) => Rule::unique($table, $field)->ignore($id),
|
||||
], [
|
||||
$field . '.unique' => trans('La valeur :value pour le champ :attribute est déjà utilisé', [
|
||||
self::mbReplace('.', '\.', "{$field}.unique")
|
||||
=> trans('La valeur :value pour le champ :attribute est déjà utilisé', [
|
||||
'attribute' => $field,
|
||||
'value' => $value
|
||||
])
|
||||
|
|
@ -291,49 +191,6 @@ class ToolboxHelper
|
|||
])->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate recaptcha V3
|
||||
*
|
||||
* @param Request $request
|
||||
* @return void
|
||||
*/
|
||||
public static function validateRecaptchaV3(Request $request)
|
||||
{
|
||||
Validator::validate($request->all(), [
|
||||
'g-recaptcha-response' => [
|
||||
'required', 'string',
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed, Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
function ($attribute, $value, $fail) {
|
||||
try {
|
||||
$response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
|
||||
'secret' => config('recaptcha.private'),
|
||||
'response' => \strval($value),
|
||||
'remoteip' => app('clientIp')
|
||||
]);
|
||||
if ($e = $response->toException()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (
|
||||
!$response->json('success') or
|
||||
!$response->json('challenge_ts') or
|
||||
Carbon::parse($response->json('challenge_ts'))->lessThan(\now('utc')->subMinutes(5)) or
|
||||
$response->json('hostname') !== \parse_url(config('app.url'), \PHP_URL_HOST) or
|
||||
!$response->json('score')
|
||||
) {
|
||||
throw new \ErrorException();
|
||||
}
|
||||
if (\floatval($response->json('score')) < 0.8) {
|
||||
$fail('Vous n\'avez pas obtenu un score assez élevé lors de la validation du recaptcha');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$fail('Erreur lors de la validation du formulaire veuillez réssayer');
|
||||
} //end try
|
||||
}
|
||||
]
|
||||
], [], ['g-recaptcha-response' => 'reCaptcha']);
|
||||
}
|
||||
|
||||
/**
|
||||
* * Register CSP report URI
|
||||
*
|
||||
|
|
@ -371,4 +228,72 @@ class ToolboxHelper
|
|||
->withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class)
|
||||
->name('cspReportUri');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unused pictures in ckeditor
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $attribute
|
||||
* @return void
|
||||
*/
|
||||
public static function removeUnusedImagesOnCkEditor(Model $model, string $attribute)
|
||||
{
|
||||
// * Remove unused inline uploaded images
|
||||
if ($model->{$attribute} != $model->getOriginal($attribute)) {
|
||||
$updatedPicturesPath = collect(
|
||||
ToolboxHelper::getCkEditorLocalUploadedFiles($model->{$attribute})
|
||||
);
|
||||
collect(ToolboxHelper::getCkEditorLocalUploadedFiles($model->getOriginal($attribute)))
|
||||
->map(function (string $originalPicturePath) use ($updatedPicturesPath) {
|
||||
// * If a picture is not used anymore
|
||||
if (!$updatedPicturesPath->contains($originalPicturePath)) {
|
||||
if (File::exists(\public_path($originalPicturePath))) {
|
||||
File::delete(\public_path($originalPicturePath));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get files path uploaded with ckeditor custom upload plugin (crud-policies)
|
||||
*
|
||||
* @param string $text
|
||||
* @param array $detectExtensions
|
||||
* @return array
|
||||
*/
|
||||
public static function getCkEditorLocalUploadedFiles(
|
||||
string $text,
|
||||
array $detectExtensions = ['png', 'svg', 'jpg', 'gif']
|
||||
): array {
|
||||
$out = [];
|
||||
\preg_match_all(
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
'/src="(?P<url>\/storage\/images\/crud-policies\/uploads\/\d{4}-\d{2}-\d{2}\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-[-a-zA-Z0-9@%._\+~#=]{2,256}\.(?:' . implode('|', $detectExtensions) . '))"/',
|
||||
$text,
|
||||
$out
|
||||
);
|
||||
return collect($out['url'] ?? [])->map(function (string $urlEncodedFilePath) {
|
||||
return \urldecode($urlEncodedFilePath);
|
||||
})->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove pictures in ckeditor
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $attribute
|
||||
* @return void
|
||||
*/
|
||||
public static function removeImagesOnCkEditor(Model $model, string $attribute)
|
||||
{
|
||||
// * Remove inline uploaded images
|
||||
collect(ToolboxHelper::getCkEditorLocalUploadedFiles($model->getOriginal($attribute)))
|
||||
->map(function (string $originalPicturePath) {
|
||||
// * If a picture is not used anymore
|
||||
if (File::exists(\public_path($originalPicturePath))) {
|
||||
File::delete(\public_path($originalPicturePath));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
11
app/Models/Breeder.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Breeder extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
}
|
||||
96
app/Models/Engagement.php
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Engagements\EngagementType;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Traits\Models\MiniCrudModel;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Engagement extends Model
|
||||
{
|
||||
use CastsEnums;
|
||||
use MiniCrudModel;
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'type';
|
||||
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'data' => 'array',
|
||||
'banner_dimensions' => 'array'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to Enums.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'type' => EngagementType::class
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'title_simple',
|
||||
'slug',
|
||||
'seo_title',
|
||||
'seo_description',
|
||||
'banner',
|
||||
'banner_title',
|
||||
'banner_alt',
|
||||
'banner_dimensions',
|
||||
'type',
|
||||
'data'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
$model->data = clean($model->data);
|
||||
$model->banner = FileStorageHelper::storeFile($model, $model->banner);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
if ($model->banner != $model->getOriginal('banner')) {
|
||||
$model->banner = FileStorageHelper::storeFile($model, $model->banner);
|
||||
}
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
ToolboxHelper::removeUnusedImagesOnCkEditor($model, 'title');
|
||||
if ($model->banner != $model->getOriginal('banner')) {
|
||||
$model->banner = FileStorageHelper::removeOldFile($model, 'banner');
|
||||
}
|
||||
});
|
||||
static::deleted(function (self $model) {
|
||||
FileStorageHelper::removeFile($model, 'banner');
|
||||
ToolboxHelper::removeImagesOnCkEditor($model, 'title');
|
||||
});
|
||||
}
|
||||
}
|
||||
72
app/Models/IncoNutritionalValue.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class IncoNutritionalValue extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'weight',
|
||||
'energy',
|
||||
'energy_kj',
|
||||
'protein',
|
||||
'carbohydrates',
|
||||
'sugar',
|
||||
'fat',
|
||||
'saturated_fatty_acids',
|
||||
'dietary_fiber',
|
||||
'salt',
|
||||
];
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Inherited from LG, will use for arcadia stream inbound
|
||||
*
|
||||
* @param array $xmlInco
|
||||
* @return array
|
||||
*/
|
||||
public static function convertTranslateXmlInco(array $xmlInco): array
|
||||
{
|
||||
$cvt = [
|
||||
'' => 'weight',
|
||||
'val_energ_kcal_r' => 'energy',
|
||||
'val_energ_kj_r' => 'energy_kj',
|
||||
'proteine_r' => 'protein',
|
||||
'sucre_r' => 'sugar',
|
||||
'mat_grass_r' => 'fat',
|
||||
'fib_alim_r' => 'dietary_fiber',
|
||||
'glucides_r' => 'carbohydrates',
|
||||
'ags_r' => 'saturated_fatty_acids',
|
||||
'sel_r' => 'salt'
|
||||
];
|
||||
$o = [];
|
||||
|
||||
foreach ($cvt as $k => $t) {
|
||||
$o[$t] = $xmlInco[$k] ?? null;
|
||||
}
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* BelongsTo product relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function product()
|
||||
{
|
||||
return $this->belongsTo(Product::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\NiceDate;
|
||||
use App\Enums\PublishedStatus;
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
|
|
@ -63,7 +62,7 @@ class News extends Model
|
|||
ToolboxHelper::assertNoInternalBoUrls('content', $model->content);
|
||||
$model->slug = Str::slug($model->title);
|
||||
$model->content = clean($model->content);
|
||||
$model->picture = ToolboxHelper::storeImage($model, $model->picture);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
if ($model->published->equals(PublishedStatus::published())) {
|
||||
$model->published_at = now();
|
||||
}
|
||||
|
|
@ -82,31 +81,18 @@ class News extends Model
|
|||
}
|
||||
// * Store new picture if changed
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = ToolboxHelper::storeImage($model, $model->picture);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
}
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
// * Remove unused inline uploaded images
|
||||
if ($model->content != $model->getOriginal('content')) {
|
||||
$updatedPicturesPath = collect(
|
||||
ToolboxHelper::getCkEditorLocalUploadedFiles($model->content)
|
||||
);
|
||||
collect(ToolboxHelper::getCkEditorLocalUploadedFiles($model->getOriginal('content')))
|
||||
->map(function (string $originalPicturePath) use ($updatedPicturesPath) {
|
||||
// * If a picture is not used anymore
|
||||
if (!$updatedPicturesPath->contains($originalPicturePath)) {
|
||||
if (File::exists(\public_path($originalPicturePath))) {
|
||||
File::delete(\public_path($originalPicturePath));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ToolboxHelper::removeUnusedImagesOnCkEditor($model, 'content');
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = ToolboxHelper::removeOldImage($model, 'picture');
|
||||
$model->picture = FileStorageHelper::removeOldFile($model, 'picture');
|
||||
}
|
||||
});
|
||||
static::deleting(function (self $model) {
|
||||
ToolboxHelper::removeImage($model, 'picture');
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
ToolboxHelper::removeImagesOnCkEditor($model, 'content');
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -148,14 +134,4 @@ class News extends Model
|
|||
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* updated_at_date getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedAtDateAttribute(): string
|
||||
{
|
||||
return (new NiceDate())->get($this, 'published_at', $this->published_at, []);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
220
app/Models/Product.php
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Products\ProductLabelEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Product extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'slug',
|
||||
'seo_title',
|
||||
'seo_description',
|
||||
'description',
|
||||
'picture',
|
||||
'picture_title',
|
||||
'picture_alt',
|
||||
'picture_thumb',
|
||||
'picture_thumb_title',
|
||||
'picture_thumb_alt',
|
||||
'labels',
|
||||
'ref_article',
|
||||
'ingredients',
|
||||
'preparation_advices',
|
||||
'sales_pitch',
|
||||
'allergen_list',
|
||||
'number_portion',
|
||||
'status',
|
||||
'publish_at',
|
||||
'unpublish_at',
|
||||
'range_id',
|
||||
'show_ingredients',
|
||||
'show_preparation_advices',
|
||||
'show_sales_pitch',
|
||||
'show_allergen_list',
|
||||
'show_nutrionnal_values',
|
||||
'show_portions',
|
||||
'link1',
|
||||
'link2'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'labels' => 'array',
|
||||
'status' => 'bool',
|
||||
'show_ingredients' => 'bool',
|
||||
'show_preparation_advices' => 'bool',
|
||||
'show_sales_pitch' => 'bool',
|
||||
'show_allergen_list' => 'bool',
|
||||
'show_nutrionnal_values' => 'bool',
|
||||
'publish_at' => 'datetime',
|
||||
'unpublish_at' => 'datetime'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
static::assertTitleIsUnique($model->title);
|
||||
self::validateLabels($model->labels);
|
||||
$model->slug = Str::slug($model->title);
|
||||
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
|
||||
ToolboxHelper::assertNoInternalBoUrls('ingredients', $model->ingredients);
|
||||
ToolboxHelper::assertNoInternalBoUrls('preparation_advices', $model->preparation_advices);
|
||||
ToolboxHelper::assertNoInternalBoUrls('sales_pitch', $model->sales_pitch);
|
||||
ToolboxHelper::assertNoInternalBoUrls('allergen_list', $model->allergen_list);
|
||||
$model->slug = Str::slug($model->title);
|
||||
$model->description = clean($model->description);
|
||||
$model->ingredients = clean($model->ingredients);
|
||||
$model->preparation_advices = clean($model->preparation_advices);
|
||||
$model->sales_pitch = clean($model->sales_pitch);
|
||||
$model->allergen_list = clean($model->allergen_list);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
$model->picture_thumb = FileStorageHelper::storeFile($model, $model->picture_thumb);
|
||||
if (!$model->status) {
|
||||
$model->publish_at = null;
|
||||
} else {
|
||||
$model->unpublish_at = null;
|
||||
}
|
||||
if ($model->link1) {
|
||||
self::validateLinkAttribute('link1', $model->link1);
|
||||
}
|
||||
if ($model->link2) {
|
||||
self::validateLinkAttribute('link2', $model->link2);
|
||||
}
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
static::assertTitleIsUnique($model->title, $model->id);
|
||||
self::validateLabels($model->labels);
|
||||
$model->slug = Str::slug($model->title);
|
||||
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
|
||||
ToolboxHelper::assertNoInternalBoUrls('ingredients', $model->ingredients);
|
||||
ToolboxHelper::assertNoInternalBoUrls('preparation_advices', $model->preparation_advices);
|
||||
ToolboxHelper::assertNoInternalBoUrls('sales_pitch', $model->sales_pitch);
|
||||
ToolboxHelper::assertNoInternalBoUrls('allergen_list', $model->allergen_list);
|
||||
$model->slug = Str::slug($model->title);
|
||||
$model->description = clean($model->description);
|
||||
$model->ingredients = clean($model->ingredients);
|
||||
$model->preparation_advices = clean($model->preparation_advices);
|
||||
$model->sales_pitch = clean($model->sales_pitch);
|
||||
$model->allergen_list = clean($model->allergen_list);
|
||||
// * Store new picture if changed
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
}
|
||||
// * Store new picture_thumb if changed
|
||||
if ($model->picture_thumb != $model->getOriginal('picture_thumb')) {
|
||||
$model->picture_thumb = FileStorageHelper::storeFile($model, $model->picture_thumb);
|
||||
}
|
||||
if ($model->link1) {
|
||||
self::validateLinkAttribute('link1', $model->link1);
|
||||
}
|
||||
if ($model->link2) {
|
||||
self::validateLinkAttribute('link2', $model->link2);
|
||||
}
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
ToolboxHelper::removeUnusedImagesOnCkEditor($model, 'description');
|
||||
ToolboxHelper::removeUnusedImagesOnCkEditor($model, 'ingredients');
|
||||
ToolboxHelper::removeUnusedImagesOnCkEditor($model, 'preparation_advices');
|
||||
ToolboxHelper::removeUnusedImagesOnCkEditor($model, 'sales_pitch');
|
||||
ToolboxHelper::removeUnusedImagesOnCkEditor($model, 'allergen_list');
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = FileStorageHelper::removeOldFile($model, 'picture');
|
||||
}
|
||||
});
|
||||
static::deleting(function (self $model) {
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
FileStorageHelper::removeFile($model, 'picture_thumb');
|
||||
ToolboxHelper::removeImagesOnCkEditor($model, 'description');
|
||||
ToolboxHelper::removeImagesOnCkEditor($model, 'ingredients');
|
||||
ToolboxHelper::removeImagesOnCkEditor($model, 'preparation_advices');
|
||||
ToolboxHelper::removeImagesOnCkEditor($model, 'sales_pitch');
|
||||
ToolboxHelper::removeImagesOnCkEditor($model, 'allergen_list');
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* HasOne incoNutritionalValue relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function incoNutritionalValues()
|
||||
{
|
||||
return $this->hasOne(IncoNutritionalValue::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* BelongsTo range relation
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function range()
|
||||
{
|
||||
return $this->belongsTo(Range::class);
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the title is unique
|
||||
*
|
||||
* @param mixed $title
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If an actu title already exists.
|
||||
*/
|
||||
private static function assertTitleIsUnique($title, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param array $labels
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If a label is invalid.
|
||||
*/
|
||||
private static function validateLabels(array $labels)
|
||||
{
|
||||
collect($labels)->map(function ($label) {
|
||||
if (!\is_object($label) or !($label instanceof ProductLabelEnum)) {
|
||||
if (
|
||||
!\is_array($label) or
|
||||
!\array_key_exists('value', $label) or
|
||||
!\in_array($label['value'], ProductLabelEnum::toValues())
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
'labels' => 'Invalid label given'
|
||||
]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
185
app/Models/Range.php
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\Ranges\RangeCategoryEnum;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Range extends Model
|
||||
{
|
||||
use CastsEnums;
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'slug',
|
||||
'seo_title',
|
||||
'seo_description',
|
||||
'description',
|
||||
'argumentation',
|
||||
'category',
|
||||
'picture',
|
||||
'picture_title',
|
||||
'picture_alt',
|
||||
'picture_thumb',
|
||||
'picture_thumb_title',
|
||||
'picture_thumb_alt',
|
||||
'status',
|
||||
'publish_at',
|
||||
'unpublish_at',
|
||||
'link1',
|
||||
'link2'
|
||||
];
|
||||
|
||||
/**
|
||||
* Map attribute names to enum classes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'category' => RangeCategoryEnum::class
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'status' => 'bool',
|
||||
'publish_at' => 'datetime',
|
||||
'unpublish_at' => 'datetime'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
// phpcs:enable
|
||||
static::creating(function (self $model) {
|
||||
static::assertTitleIsUnique($model->title);
|
||||
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
|
||||
ToolboxHelper::assertNoInternalBoUrls('argumentation', $model->argumentation);
|
||||
$model->slug = Str::slug($model->title);
|
||||
$model->description = clean($model->description);
|
||||
$model->argumentation = clean($model->argumentation);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
$model->picture_thumb = FileStorageHelper::storeFile($model, $model->picture_thumb);
|
||||
if (!$model->status) {
|
||||
$model->publish_at = null;
|
||||
} else {
|
||||
$model->unpublish_at = null;
|
||||
}
|
||||
if ($model->link1) {
|
||||
self::validateLinkAttribute('link1', $model->link1);
|
||||
}
|
||||
if ($model->link2) {
|
||||
self::validateLinkAttribute('link2', $model->link2);
|
||||
}
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
static::assertTitleIsUnique($model->title, $model->id);
|
||||
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
|
||||
ToolboxHelper::assertNoInternalBoUrls('argumentation', $model->argumentation);
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
}
|
||||
if ($model->picture_thumb != $model->getOriginal('picture_thumb')) {
|
||||
$model->picture_thumb = FileStorageHelper::storeFile($model, $model->picture_thumb);
|
||||
}
|
||||
// * If status changed
|
||||
if (\boolval($model->getOriginal('status')) !== $model->status) {
|
||||
if ($model->status) {
|
||||
$model->publish_at = null;
|
||||
Session::flash('info', trans('La gamme est à présent publié'));
|
||||
} else {
|
||||
$model->unpublish_at = null;
|
||||
Session::flash('warning', trans('La gamme est à présent dépublié'));
|
||||
}
|
||||
}
|
||||
if ($model->link1) {
|
||||
self::validateLinkAttribute('link1', $model->link1);
|
||||
}
|
||||
if ($model->link2) {
|
||||
self::validateLinkAttribute('link2', $model->link2);
|
||||
}
|
||||
});
|
||||
static::updated(function (self $model) {
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = FileStorageHelper::removeOldFile($model, 'picture');
|
||||
}
|
||||
if ($model->picture_v != $model->getOriginal('picture_v')) {
|
||||
$model->picture_v = FileStorageHelper::removeOldFile($model, 'picture_v');
|
||||
}
|
||||
});
|
||||
static::deleting(function (self $model) {
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
FileStorageHelper::removeFile($model, 'picture_v');
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Asserts using validation that the title is unique
|
||||
*
|
||||
* @param mixed $title
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If an actu title already exists.
|
||||
*/
|
||||
private static function assertTitleIsUnique($title, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an internal link
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $value
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If the $attribute link is invalid.
|
||||
*/
|
||||
public static function validateLinkAttribute(string $attribute, string $value): void
|
||||
{
|
||||
// Validate case internal url.
|
||||
$params = collect(\explode('#', \ltrim(\rtrim($value, '#'), '#')));
|
||||
if (
|
||||
!\in_array(
|
||||
$params->get(0),
|
||||
[Breeder::class, Recipe::class]
|
||||
)
|
||||
) {
|
||||
throw ValidationException::withMessages([
|
||||
$attribute => trans('Le lien contient un modèle invalide \':model\'', [
|
||||
'model' => $params->get(0)
|
||||
])
|
||||
]);
|
||||
}
|
||||
// Check if model id exists. Param 0 is class, 1 is id.
|
||||
if (!\forward_static_call([$params->get(0), 'find'], $params->get(1))) {
|
||||
throw ValidationException::withMessages([
|
||||
$attribute => trans('Le lien n\'est plus valide.')
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
95
app/Models/Recipe.php
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class Recipe extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'slug',
|
||||
'seo_title',
|
||||
'seo_description',
|
||||
'banner',
|
||||
'banner_alt',
|
||||
'banner_title',
|
||||
'category',
|
||||
'servings',
|
||||
'preparation',
|
||||
'cooking',
|
||||
'ingredients',
|
||||
'description',
|
||||
'video',
|
||||
'picture',
|
||||
'picture_alt',
|
||||
'picture_title',
|
||||
'status'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'ingredients' => 'array'
|
||||
];
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Undocumented function
|
||||
*
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
private static function manageFields(Model $model)
|
||||
{
|
||||
static::assertTitleIsUnique($model->title, $model->id);
|
||||
$model->banner = FileStorageHelper::storeFile($model, $model->banner);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
$model->slug = Str::slug($model->title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts using validation that the title is unique
|
||||
*
|
||||
* @param mixed $title
|
||||
* @param integer|null $id
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException If an actu title already exists.
|
||||
*/
|
||||
private static function assertTitleIsUnique($title, ?int $id = null)
|
||||
{
|
||||
$table = (new self())->getTable();
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
|
||||
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Casts\HtmlColor;
|
||||
use App\Casts\NiceDate;
|
||||
use App\Enums\Sliders\SliderPosition;
|
||||
use App\Casts\RgbaColorNullable;
|
||||
use App\Enums\Sliders\SliderElementPosition;
|
||||
use App\Enums\Sliders\SliderType;
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
|
@ -29,13 +29,15 @@ class Slider extends Model
|
|||
'subtitle',
|
||||
'subtitle_color',
|
||||
'subtitle_color_bg',
|
||||
'position',
|
||||
'type',
|
||||
'text_position_desktop',
|
||||
'picture_position_mobile',
|
||||
'picture',
|
||||
'link',
|
||||
'link_title',
|
||||
'link_color',
|
||||
'link_color_hover',
|
||||
'link_color_bg',
|
||||
'link_color_bg_hover',
|
||||
'link_new_tab',
|
||||
'status',
|
||||
'order',
|
||||
|
|
@ -49,7 +51,8 @@ class Slider extends Model
|
|||
* @var array
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'position' => SliderPosition::class,
|
||||
'text_position_desktop' => SliderElementPosition::class,
|
||||
'picture_position_mobile' => SliderElementPosition::class,
|
||||
'type' => SliderType::class
|
||||
];
|
||||
|
||||
|
|
@ -59,12 +62,14 @@ class Slider extends Model
|
|||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'title_color' => HtmlColor::class,
|
||||
'title_color_bg' => HtmlColor::class,
|
||||
'subtitle_color' => HtmlColor::class,
|
||||
'subtitle_color_bg' => HtmlColor::class,
|
||||
'link_color' => HtmlColor::class,
|
||||
'link_color_bg' => HtmlColor::class,
|
||||
'title_color' => RgbaColorNullable::class,
|
||||
'title_color_bg' => RgbaColorNullable::class,
|
||||
'subtitle_color' => RgbaColorNullable::class,
|
||||
'subtitle_color_bg' => RgbaColorNullable::class,
|
||||
'link_color' => RgbaColorNullable::class,
|
||||
'link_color_hover' => RgbaColorNullable::class,
|
||||
'link_color_bg' => RgbaColorNullable::class,
|
||||
'link_color_bg_hover' => RgbaColorNullable::class,
|
||||
'link_new_tab' => 'bool',
|
||||
'status' => 'bool',
|
||||
'publish_at' => 'datetime',
|
||||
|
|
@ -80,8 +85,8 @@ class Slider extends Model
|
|||
{
|
||||
static::creating(function (self $model) {
|
||||
ToolboxHelper::assertNoInternalBoUrls('link', $model->link);
|
||||
$model->picture = ToolboxHelper::storeImage($model, $model->picture);
|
||||
$model->order = self::whereType($model->type)->max('order') ?? 0;
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
$model->order = \intval(Slider::query()->max('order')) + 1;
|
||||
if (!$model->status) {
|
||||
$model->publish_at = null;
|
||||
} else {
|
||||
|
|
@ -91,7 +96,7 @@ class Slider extends Model
|
|||
static::updating(function (self $model) {
|
||||
ToolboxHelper::assertNoInternalBoUrls('link', $model->link);
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = ToolboxHelper::storeImage($model, $model->picture);
|
||||
$model->picture = FileStorageHelper::storeFile($model, $model->picture);
|
||||
}
|
||||
// * If status changed
|
||||
if (\boolval($model->getOriginal('status')) !== $model->status) {
|
||||
|
|
@ -106,23 +111,13 @@ class Slider extends Model
|
|||
});
|
||||
static::updated(function (self $model) {
|
||||
if ($model->picture != $model->getOriginal('picture')) {
|
||||
$model->picture = ToolboxHelper::removeOldImage($model, 'picture');
|
||||
$model->picture = FileStorageHelper::removeOldFile($model, 'picture');
|
||||
}
|
||||
});
|
||||
static::deleting(function (self $model) {
|
||||
ToolboxHelper::removeImage($model, 'picture');
|
||||
FileStorageHelper::removeFile($model, 'picture');
|
||||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* updated_at_date getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedAtDateAttribute(): string
|
||||
{
|
||||
return (new NiceDate())->get($this, 'updated_at', $this->updated_at, []);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Str;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Models\News;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use App\Models\Recipe;
|
||||
use App\Models\Slider;
|
||||
use Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
|
@ -43,12 +46,15 @@ class RouteServiceProvider extends ServiceProvider
|
|||
->group(base_path('routes/web-fo.php'));
|
||||
});
|
||||
|
||||
Route::model('product', Product::class);
|
||||
Route::model('range', Range::class);
|
||||
Route::model('slider', Slider::class);
|
||||
Route::model('news', News::class, function ($value) {
|
||||
return News::where(function ($q) use ($value) {
|
||||
$q->whereSlug($value)->orWhere('id', $value);
|
||||
})->firstOrFail();
|
||||
});
|
||||
Route::model('recipe', Recipe::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
25
app/Providers/ViewServiceProvider.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\Engagement;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\Facades\View;
|
||||
|
||||
class ViewServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
// Shares this data with all views.
|
||||
if (Schema::hasTable('engagements')) {
|
||||
$globalEngagements = Engagement::get();
|
||||
View::share('globalEngagements', $globalEngagements);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
namespace App\Traits\Controllers;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ trait UpdateModelStatus
|
|||
/**
|
||||
* Change model published
|
||||
*
|
||||
* @param Model $model
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function changePublished(Model $model)
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
trait HasPicture
|
||||
{
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pictureRule(): array
|
||||
{
|
||||
return [
|
||||
'picture' => [
|
||||
'required',
|
||||
function (string $attribute, $value, callable $fail) {
|
||||
return $this->validatePicture($attribute, $value, $fail);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pictureRules(): array
|
||||
{
|
||||
return [
|
||||
'pictures' => 'sometimes|array',
|
||||
'pictureValidate' => 'required_with:pictures|array',
|
||||
'pictureValidate.*' => [
|
||||
'required',
|
||||
function (string $attribute, $value, callable $fail) {
|
||||
return $this->validatePicture($attribute, $value, $fail);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate picture
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param callback $fail
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
*/
|
||||
private function validatePicture(string $attribute, $value, callable $fail)
|
||||
{
|
||||
if (!File::exists($value)) {
|
||||
$fail(trans(":attribute n'existe pas dans le système de fichier"));
|
||||
return;
|
||||
}
|
||||
if (!\in_array(File::mimeType($value), ['image/jpeg', 'image/jpg', 'image/png'])) {
|
||||
$fail(trans(":attribute doit être une image de type 'image/jpeg,image/jpg,image/png'"));
|
||||
}
|
||||
if (!($dimensions = \getimagesize($value)) or !isset($dimensions[0]) or !isset($dimensions[1])) {
|
||||
$fail(trans(":attribute impossible de valider les dimensions"));
|
||||
} else {
|
||||
$minWith = 400;
|
||||
$maxWith = 1800;
|
||||
$minHeight = 300;
|
||||
$maxHeight = 1800;
|
||||
$width = intval($dimensions[0]);
|
||||
$height = intval($dimensions[1]);
|
||||
if (($width < $minWith) or ($width > $maxWith)) {
|
||||
$fail(trans(":attribute la largeur doit être comprise entre $minWith et $maxWith"));
|
||||
}
|
||||
if (($height < $minHeight) or ($height > $maxHeight)) {
|
||||
$fail(trans(":attribute la hauteur doit être comprise entre $minHeight et $maxHeight"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges products pictures fields with request data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePicture(): void
|
||||
{
|
||||
$picturePath = $this->picture;
|
||||
if (is_string($picturePath)) {
|
||||
$this->merge(['pictureValidate' => []]);
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge(['pictures' => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
'pictureValidate' => array_merge(
|
||||
$this->pictureValidate,
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = $this->picture;
|
||||
$changedPicture = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
'picture' => $changedPicture
|
||||
]);
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges products pictures fields with request data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePictures(): void
|
||||
{
|
||||
$pictures = $this->pictures;
|
||||
if (is_array($pictures)) {
|
||||
$this->merge(['pictureValidate' => []]);
|
||||
foreach ($pictures as $key => $picturePath) {
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge(['pictures' => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
'pictureValidate' => array_merge(
|
||||
$this->pictureValidate,
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = $this->pictures;
|
||||
$changedPicture[$key] = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
'pictures' => $changedPicture
|
||||
]);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
}
|
||||
39
app/Traits/Models/ChangesModelOrder.php
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Models;
|
||||
|
||||
use App\Http\Requests\Bo\ChangeOrderRequest;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
trait ChangesModelOrder
|
||||
{
|
||||
/**
|
||||
* Change order
|
||||
*
|
||||
* @param \App\Traits\Models\ChangeOrderRequest $request
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @throws \RuntimeException Si le nom du parametre de la route ne correspond pas au model utilisé.
|
||||
*/
|
||||
public function changeOrder(ChangeOrderRequest $request, Model $model): RedirectResponse
|
||||
{
|
||||
return DB::transaction(function () use ($request, $model) {
|
||||
/** @var \Illuminate\Database\Eloquent\Model|null */
|
||||
$tmp = (\get_class($model))::where('order', $request->direction ? '<' : '>', $model->order)
|
||||
->orderBy('order', $request->direction ? 'DESC' : 'ASC')->first();
|
||||
if (!$tmp) {
|
||||
return back()->with('error', trans('L\'ordre n\'a pas pu être modifié'));
|
||||
}
|
||||
$newOrder = $tmp->order;
|
||||
$tmp->order = $model->order;
|
||||
$model->order = $newOrder;
|
||||
$tmp->saveOrFail();
|
||||
$model->saveOrFail();
|
||||
Session::flash('success', trans('L\'ordre à été modifié'));
|
||||
return back();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits;
|
||||
namespace App\Traits\Models;
|
||||
|
||||
use App\Enums\InputType;
|
||||
use App\Lib\ToolboxHelper;
|
||||
use App\Lib\Helpers\ToolboxHelper;
|
||||
use App\Lib\Helpers\FileStorageHelper;
|
||||
|
||||
trait MiniCrudModel
|
||||
{
|
||||
|
|
@ -42,29 +43,6 @@ trait MiniCrudModel
|
|||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data attribute
|
||||
* Cast json_decode to object.
|
||||
*
|
||||
* @return object|null
|
||||
*/
|
||||
protected function getDataAttribute(): ?object
|
||||
{
|
||||
return \json_decode($this->getAttributes()['data'] ?? '[]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data attribute
|
||||
* Cast json_decode to object.
|
||||
*
|
||||
* @param array $data
|
||||
* @return void
|
||||
*/
|
||||
protected function setDataAttribute(array $data): void
|
||||
{
|
||||
$this->setData($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize properties
|
||||
*
|
||||
|
|
@ -80,11 +58,12 @@ trait MiniCrudModel
|
|||
foreach ($data as $o => $input) {
|
||||
switch ($input['inputType']->value ?? $input['inputType']['value']) {
|
||||
case InputType::image()->value:
|
||||
$data[$o]['value'] = ToolboxHelper::storeImage($this->getTable(), $input['value']);
|
||||
$data[$o]['value'] = FileStorageHelper::storeFile($this, $input['value']);
|
||||
break;
|
||||
case InputType::boolean()->value:
|
||||
case InputType::text()->value:
|
||||
case InputType::number()->value:
|
||||
case InputType::email()->value:
|
||||
case InputType::string()->value:
|
||||
case InputType::videoVimeo()->value:
|
||||
break;
|
||||
|
|
@ -96,8 +75,8 @@ trait MiniCrudModel
|
|||
'Unknown input Type %s',
|
||||
$input['inputType']->label ?? $input['inputType']['label']
|
||||
));
|
||||
}
|
||||
}//end foreach
|
||||
} //end switch
|
||||
} //end foreach
|
||||
return json_encode($data);
|
||||
}
|
||||
}
|
||||
216
app/Traits/Requests/HasPicture.php
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
namespace App\Traits\Requests;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
trait HasPicture
|
||||
{
|
||||
/**
|
||||
* Minimum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minWith = 400;
|
||||
|
||||
/**
|
||||
* Maximum width
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxWith = 1800;
|
||||
|
||||
/**
|
||||
* Minimum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $minHeight = 300;
|
||||
|
||||
/**
|
||||
* Maximum height
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $maxHeight = 1800;
|
||||
|
||||
/**
|
||||
* Image rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param integer|null $minWith
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWith
|
||||
* @param integer|null $maxHeight
|
||||
* @return array
|
||||
*/
|
||||
public function pictureRules(
|
||||
string $name = 'picture',
|
||||
?int $minWith = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWith = null,
|
||||
?int $maxHeight = null
|
||||
): array {
|
||||
$sizes = (object)[
|
||||
'minWith' => $minWith ?? $this->minWith,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWith' => $maxWith ?? $this->maxWith,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight
|
||||
];
|
||||
return [
|
||||
$name => [
|
||||
'sometimes',
|
||||
function (string $attribute, $value, callable $fail) use ($sizes) {
|
||||
return $this->validatePicture($attribute, $value, $fail, $sizes);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Images rules
|
||||
*
|
||||
* @param string $name
|
||||
* @param integer|null $minWith
|
||||
* @param integer|null $minHeight
|
||||
* @param integer|null $maxWith
|
||||
* @param integer|null $maxHeight
|
||||
* @return array
|
||||
*/
|
||||
public function picturesRules(
|
||||
string $name = 'pictures',
|
||||
?int $minWith = null,
|
||||
?int $minHeight = null,
|
||||
?int $maxWith = null,
|
||||
?int $maxHeight = null
|
||||
): array {
|
||||
$sizes = (object)[
|
||||
'minWith' => $minWith ?? $this->minWith,
|
||||
'minHeight' => $minHeight ?? $this->minHeight,
|
||||
'maxWith' => $maxWith ?? $this->maxWith,
|
||||
'maxHeight' => $maxHeight ?? $this->maxHeight
|
||||
];
|
||||
return [
|
||||
$name => 'sometimes|array',
|
||||
"{$name}Validate" => "required_with:{$name}|array",
|
||||
"{$name}Validate.*" => [
|
||||
'required',
|
||||
function (string $attribute, $value, callable $fail) use ($sizes) {
|
||||
return $this->validatePicture($attribute, $value, $fail, $sizes);
|
||||
}
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate picture
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param callback $fail
|
||||
* @param object $sizes
|
||||
* @return void
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
|
||||
*/
|
||||
private function validatePicture(string $attribute, $value, callable $fail, object $sizes)
|
||||
{
|
||||
if (!File::exists($value)) {
|
||||
$fail(trans(":attribute n'existe pas dans le système de fichier"));
|
||||
return;
|
||||
}
|
||||
if (!\in_array(File::mimeType($value), ['image/jpeg', 'image/jpg', 'image/png', 'image/gif'])) {
|
||||
$fail(trans(":attribute doit être une image de type 'image/jpeg,image/jpg,image/png,image/gif'"));
|
||||
}
|
||||
if (!($dimensions = \getimagesize($value)) or !isset($dimensions[0]) or !isset($dimensions[1])) {
|
||||
$fail(trans(":attribute impossible de valider les dimensions"));
|
||||
} else {
|
||||
$width = intval($dimensions[0]);
|
||||
$height = intval($dimensions[1]);
|
||||
|
||||
$xMessage = trans(
|
||||
":attribute la largeur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
);
|
||||
if ($sizes->minWith === $sizes->maxWith) {
|
||||
$xMessage = trans(":attribute la largeur doit être de {$sizes->minWith}px");
|
||||
}
|
||||
$yMessage = trans(
|
||||
":attribute la hauteur doit être comprise entre {$sizes->minWith}px et {$sizes->maxWith}px"
|
||||
);
|
||||
if ($sizes->minHeight === $sizes->maxHeight) {
|
||||
$yMessage = trans(":attribute la hauteur doit être de {$sizes->minWith}px");
|
||||
}
|
||||
|
||||
if (($width < $sizes->minWith) or ($width > $sizes->maxWith)) {
|
||||
$fail($xMessage);
|
||||
}
|
||||
if (($height < $sizes->minHeight) or ($height > $sizes->maxHeight)) {
|
||||
$fail($yMessage);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges products pictures fields with request data
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePicture(string $name = 'picture'): void
|
||||
{
|
||||
$picturePath = $this->{$name};
|
||||
if (is_string($picturePath)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedPicture
|
||||
]);
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges products pictures fields with request data
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
protected function mergePictures(string $name = 'pictures'): void
|
||||
{
|
||||
$pictures = $this->{$name};
|
||||
if (is_array($pictures)) {
|
||||
$this->merge(["{$name}Validate" => []]);
|
||||
foreach ($pictures as $key => $picturePath) {
|
||||
// ! Empty picture.
|
||||
if ($picturePath === 'false') {
|
||||
// * Empty picture and exit
|
||||
$this->merge([$name => []]);
|
||||
return;
|
||||
}
|
||||
$decodedPath = \urldecode($picturePath);
|
||||
$this->merge([
|
||||
"{$name}Validate" => array_merge(
|
||||
$this->{"{$name}Validate"},
|
||||
[\public_path(ltrim($decodedPath, '/'))]
|
||||
)
|
||||
]);
|
||||
$changedPicture = $this->{$name};
|
||||
$changedPicture[$key] = ltrim($decodedPath, '/');
|
||||
$this->merge([
|
||||
$name => $changedPicture
|
||||
]);
|
||||
}
|
||||
} //end if
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@
|
|||
"name": "laravel/laravel",
|
||||
"type": "project",
|
||||
"description": "The Laravel Framework.",
|
||||
"keywords": ["framework", "laravel"],
|
||||
"keywords": [
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
|
|
@ -18,7 +21,6 @@
|
|||
"laravel/tinker": "^2.5",
|
||||
"laravel/ui": "^3.4",
|
||||
"mews/purifier": "^3.3",
|
||||
"slevomat/coding-standard": "^7.2",
|
||||
"spatie/laravel-mix-preload": "^1.3"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
@ -27,7 +29,8 @@
|
|||
"laravel/sail": "^1.0.1",
|
||||
"mockery/mockery": "^1.4.4",
|
||||
"nunomaduro/collision": "^5.10",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
"phpunit/phpunit": "^9.5.10",
|
||||
"slevomat/coding-standard": "^7.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
@ -47,7 +50,8 @@
|
|||
"@php artisan package:discover --ansi"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force",
|
||||
"local-php-security-checker"
|
||||
],
|
||||
"post-root-package-install": [
|
||||
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
|
||||
|
|
|
|||
933
composer.lock
generated
|
|
@ -176,7 +176,7 @@ return [
|
|||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
App\Providers\TranslationServiceProvider::class,
|
||||
|
||||
App\Providers\ViewServiceProvider::class
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
@ -191,7 +191,6 @@ return [
|
|||
*/
|
||||
|
||||
'aliases' => [
|
||||
|
||||
'App' => Illuminate\Support\Facades\App::class,
|
||||
'Arr' => Illuminate\Support\Arr::class,
|
||||
'Artisan' => Illuminate\Support\Facades\Artisan::class,
|
||||
|
|
@ -229,8 +228,6 @@ return [
|
|||
'Str' => Illuminate\Support\Str::class,
|
||||
'URL' => Illuminate\Support\Facades\URL::class,
|
||||
'Validator' => Illuminate\Support\Facades\Validator::class,
|
||||
'View' => Illuminate\Support\Facades\View::class,
|
||||
|
||||
],
|
||||
|
||||
'View' => Illuminate\Support\Facades\View::class
|
||||
]
|
||||
];
|
||||
|
|
|
|||
29
database/factories/BreederFactory.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\Breeder;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class BreederFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Breeder::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'firstname' => $this->faker->firstName(),
|
||||
'lastname' => $this->faker->lastName(),
|
||||
];
|
||||
}
|
||||
}
|
||||
50
database/factories/IncoNutritionalValueFactory.php
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\IncoNutritionalValue;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
/**
|
||||
* phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
*/
|
||||
class IncoNutritionalValueFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = IncoNutritionalValue::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
"weight" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(231 - 150, 231 + 150),
|
||||
"energy" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(231 - 150, 231 + 150),
|
||||
"energy_kj" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(964 - 150, 964 + 150),
|
||||
"protein" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(9.2 - 3, 9.2 + 15),
|
||||
"carbohydrates" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(20 - 10, 20 + 25),
|
||||
"sugar" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(1.5 - 1, 1.5 + 6),
|
||||
"fat" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(12 - 6, 12 + 6),
|
||||
"saturated_fatty_acids" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(3.4 - 2, 3.4 + 6),
|
||||
"dietary_fiber" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(2.9 - 2, 2.9 + 6),
|
||||
"salt" => $this->faker->boolean(10) ?
|
||||
null : $this->faker->numberBetween(1.1 - 1, 1.1 + 1.1)
|
||||
];
|
||||
}
|
||||
}
|
||||
54
database/factories/ProductFactory.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\Products\ProductLabelEnum;
|
||||
use App\Models\Product;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
||||
class ProductFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Product::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$title = $this->faker->unique()->words(\mt_rand(3, 4), true);
|
||||
return [
|
||||
'title' => $title,
|
||||
'slug' => \Illuminate\Support\Str::slug($title),
|
||||
'seo_title' => $this->faker->words(8, true),
|
||||
'seo_description' => $this->faker->words(20, true),
|
||||
'description' => $this->faker->words(25, true),
|
||||
'picture' => '/assets/images/products/product_fake_700_700.jpg',
|
||||
'picture_title' => $this->faker->words(8, true),
|
||||
'picture_alt' => $this->faker->words(8, true),
|
||||
'picture_thumb' => '/assets/images/products/product_fake_thumb_300_300.jpg',
|
||||
'picture_thumb_title' => $this->faker->words(8, true),
|
||||
'picture_thumb_alt' => $this->faker->words(8, true),
|
||||
'labels' => collect(ProductLabelEnum::toArray())->random(rand(0, 4))->all(),
|
||||
'ref_article' => $this->faker->numberBetween(0, 1000000000),
|
||||
'ingredients' => $this->faker->words(8, true),
|
||||
'preparation_advices' => $this->faker->text(\mt_rand(15, 150)),
|
||||
'sales_pitch' => $this->faker->text(\mt_rand(15, 150)),
|
||||
'allergen_list' => $this->faker->words(8, true),
|
||||
'number_portion' => $this->faker->numberBetween(0, 8),
|
||||
'status' => $this->faker->boolean(75),
|
||||
'show_ingredients' => $this->faker->boolean(75),
|
||||
'show_preparation_advices' => $this->faker->boolean(75),
|
||||
'show_sales_pitch' => $this->faker->boolean(75),
|
||||
'show_allergen_list' => $this->faker->boolean(75),
|
||||
'show_nutrionnal_values' => $this->faker->boolean(75),
|
||||
'show_portions' => $this->faker->boolean(75),
|
||||
];
|
||||
}
|
||||
}
|
||||
44
database/factories/RangeFactory.php
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\Ranges\RangeCategoryEnum;
|
||||
use App\Models\Range;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RangeFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Range::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$title = $this->faker->unique()->words(rand(6, 12), true);
|
||||
return [
|
||||
'title' => $title,
|
||||
'slug' => Str::slug($title),
|
||||
'seo_title' => $this->faker->words(8, true),
|
||||
'seo_description' => $this->faker->words(20, true),
|
||||
'description' => $this->faker->words(25, true),
|
||||
'argumentation' => $this->faker->words(25, true),
|
||||
'category' => collect(RangeCategoryEnum::toArray())->random(1)->get(0),
|
||||
'picture' => '/assets/images/ranges/range_fake_700_700.jpg',
|
||||
'picture_title' => $this->faker->words(8, true),
|
||||
'picture_alt' => $this->faker->words(8, true),
|
||||
'picture_thumb' => '/assets/images/ranges/range_fake_thumb_300_300.jpg',
|
||||
'picture_thumb_title' => $this->faker->words(8, true),
|
||||
'picture_thumb_alt' => $this->faker->words(8, true),
|
||||
'status' => $this->faker->boolean(75),
|
||||
];
|
||||
}
|
||||
}
|
||||
48
database/factories/RecipeFactory.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\Recipes\RecipeCategoryEnum;
|
||||
use App\Models\Recipe;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RecipeFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $model = Recipe::class;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function definition()
|
||||
{
|
||||
$title = $this->faker->unique()->words(rand(2, 4), true);
|
||||
return [
|
||||
'title' => $title,
|
||||
'slug' => Str::slug($title),
|
||||
'seo_title' => $this->faker->words(8, true),
|
||||
'seo_description' => $this->faker->words(20, true),
|
||||
'banner' => '/assets/images/products/product_fake_700_700.jpg',
|
||||
'banner_title' => $this->faker->words(5, true),
|
||||
'banner_alt' => $this->faker->words(8, true),
|
||||
'category' => collect(RecipeCategoryEnum::toArray())->random(1)->get(0),
|
||||
'servings' => $this->faker->numberBetween(1, 10),
|
||||
'preparation' => $this->faker->words(5, true),
|
||||
'cooking' => $this->faker->words(5, true),
|
||||
'ingredients' => [],
|
||||
'description' => $this->faker->words(25, true),
|
||||
'video' => 'https://www.youtube.com/',
|
||||
'picture' => '/assets/images/products/product_fake_700_700.jpg',
|
||||
'picture_title' => $this->faker->words(5, true),
|
||||
'picture_alt' => $this->faker->words(8, true),
|
||||
'status' => $this->faker->boolean(75)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Enums\Sliders\SliderPosition;
|
||||
use App\Enums\Sliders\SliderElementPosition;
|
||||
use App\Enums\Sliders\SliderType;
|
||||
use App\Models\Slider;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
|
|
@ -34,7 +34,8 @@ class SliderFactory extends Factory
|
|||
$subtitle = $this->faker->unique()->words(rand(8, 10), true);
|
||||
return [
|
||||
'type' => Collect(SliderType::toArray())->random(),
|
||||
'position' => Collect(SliderPosition::toArray())->random(),
|
||||
'text_position_desktop' => Collect(SliderElementPosition::toArray())->random(),
|
||||
'picture_position_mobile' => Collect(SliderElementPosition::toArray())->random(),
|
||||
'title' => $title,
|
||||
'title_color' => $this->faker->hexColor(),
|
||||
'title_color_bg' => $this->faker->hexColor(),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class CreateTagsTable extends Migration
|
|||
{
|
||||
Schema::create('tags', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('slug')->unique();
|
||||
$table->string('slug')->unique()->comment('Slug de du tag.');
|
||||
$table->string('name')->comment('Nom du tag qui sera unique.');
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ class CreateNewsTable extends Migration
|
|||
Schema::create('news', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('title')->comment('Titre actualité');
|
||||
$table->string('slug')->unique();
|
||||
$table->string('title')->comment('Titre actualité.');
|
||||
$table->string('slug')->unique()->comment('Slug de l\'actualité.');
|
||||
$table->string('seo_title')
|
||||
->comment('Utilisé pour la balise title de la page. De 53 à 60 chars recommandés.');
|
||||
$table->string('seo_description')
|
||||
|
|
@ -27,9 +27,9 @@ class CreateNewsTable extends Migration
|
|||
$table->string('picture_title')->nullable()->comment('Attribut title de l\'image.');
|
||||
$table->string('picture_alt')->nullable()->comment('Attribut alt de l\'image.');
|
||||
|
||||
$table->mediumText('content')->comment('Contenu du WYSIWYG');
|
||||
$table->mediumText('content')->comment('Contenu du WYSIWYG.');
|
||||
|
||||
$table->unsignedTinyInteger('published')->comment('PublishedStatus enum');
|
||||
$table->unsignedTinyInteger('published')->comment('PublishedStatus enum.');
|
||||
|
||||
$table->timestamp('published_at')->nullable()
|
||||
->comment('La date à laquelle l\'élément à été publié.');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Sliders\SliderPosition;
|
||||
use App\Enums\Sliders\SliderElementPosition;
|
||||
use App\Enums\Sliders\SliderType;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
|
|
@ -19,28 +19,28 @@ class CreateSlidersTable extends Migration
|
|||
$table->id();
|
||||
|
||||
$table->unsignedTinyInteger('type')->default(SliderType::home())
|
||||
->comment('Enums\\SliderType type de slider');
|
||||
$table->unsignedTinyInteger('position')->default(SliderPosition::center())
|
||||
->comment('Enums\\SliderPosition slider centrage du slider en responsive');
|
||||
->comment('Enums\\SliderType type de slider.');
|
||||
$table->unsignedTinyInteger('position')->default(SliderElementPosition::center())
|
||||
->comment('Enums\\SliderElementPosition slider centrage du slider en responsive.');
|
||||
|
||||
$table->string('title')->comment('Titre du slider');
|
||||
$table->unsignedInteger('title_color')->comment('Couleur du titre');
|
||||
$table->unsignedInteger('title_color_bg')->comment('Couleur de fond du titre');
|
||||
$table->string('title')->comment('Titre du slider.');
|
||||
$table->unsignedInteger('title_color')->comment('Couleur du titre.');
|
||||
$table->unsignedInteger('title_color_bg')->comment('Couleur de fond du titre.');
|
||||
|
||||
$table->string('subtitle')->comment('Soustitre du slider');
|
||||
$table->unsignedInteger('subtitle_color')->comment('Couleur du soustitre');
|
||||
$table->unsignedInteger('subtitle_color_bg')->comment('Couleur de fond du soustitre');
|
||||
$table->string('subtitle')->comment('Soustitre du slider.');
|
||||
$table->unsignedInteger('subtitle_color')->comment('Couleur du soustitre.');
|
||||
$table->unsignedInteger('subtitle_color_bg')->comment('Couleur de fond du soustitre.');
|
||||
|
||||
$table->string('picture')->comment('Image du slider 1400px par 630px');
|
||||
$table->string('picture')->comment('Image du slider 1400px par 630px.');
|
||||
|
||||
$table->string('link')->comment('Url du lien');
|
||||
$table->string('link_title')->comment('Titre du lien');
|
||||
$table->string('link_color')->comment('Couleur du lien');
|
||||
$table->string('link_color_bg')->comment('Couleur de fond du lien');
|
||||
$table->string('link')->comment('Url du lien.');
|
||||
$table->string('link_title')->comment('Titre du lien.');
|
||||
$table->string('link_color')->comment('Couleur du lien.');
|
||||
$table->string('link_color_bg')->comment('Couleur de fond du lien.');
|
||||
$table->string('link_new_tab')->comment('Ouvrir le lien dans un nouvel onglet ?');
|
||||
|
||||
$table->boolean('status')->comment('Le slider est-il visible ?');
|
||||
$table->unsignedSmallInteger('order')->comment('Ordre de visibilité du slider');
|
||||
$table->unsignedSmallInteger('order')->comment('Ordre de visibilité du slider.');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddHoverColorsOnSliders extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('sliders', function (Blueprint $table) {
|
||||
$table->unsignedInteger('link_color')->comment('Couleur du lien.')->change();
|
||||
$table->unsignedInteger('link_color_bg')->comment('Couleur de fond du lien.')->change();
|
||||
|
||||
// Add columns.
|
||||
$table->unsignedInteger('link_color_hover')->comment('Couleur du lien (au survol).')->after('link_color');
|
||||
$table->unsignedInteger('link_color_bg_hover')->comment('Couleur de fond du lien (au survol).')
|
||||
->after('link_color_bg');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('sliders', function (Blueprint $table) {
|
||||
$table->string('link_color')->change()->comment('Couleur du lien.');
|
||||
$table->string('link_color_bg')->change()->comment('Couleur de fond du lien.');
|
||||
|
||||
// Add columns.
|
||||
$table->removeColumn('link_color_hover');
|
||||
$table->removeColumn('link_color_bg_hover');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class SetColorsNullableToSliders extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('sliders', function (Blueprint $table) {
|
||||
$table->unsignedInteger('title_color')->nullable()->comment('Couleur du titre.')->change();
|
||||
$table->unsignedInteger('title_color_bg')->nullable()->comment('Couleur de fond du titre.')->change();
|
||||
$table->unsignedInteger('subtitle_color')->nullable()->comment('Couleur du soustitre.')->change();
|
||||
$table->unsignedInteger('subtitle_color_bg')->nullable()
|
||||
->comment('Couleur de fond du soustitre.')->change();
|
||||
$table->unsignedInteger('link_color')->nullable()->comment('Couleur du lien.')->change();
|
||||
$table->unsignedInteger('link_color_hover')->nullable()
|
||||
->comment('Couleur de fond du lien (au survol).')->change();
|
||||
$table->unsignedInteger('link_color_bg')->nullable()->comment('Couleur de fond du lien.')->change();
|
||||
$table->unsignedInteger('link_color_bg_hover')->nullable()
|
||||
->comment('Couleur de fond du lien (au survol).')->change();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('sliders', function (Blueprint $table) {
|
||||
$table->unsignedInteger('title_color')->comment('Couleur du titre.')->change();
|
||||
$table->unsignedInteger('title_color_bg')->comment('Couleur de fond du titre.')->change();
|
||||
$table->unsignedInteger('subtitle_color')->comment('Couleur du soustitre.')->change();
|
||||
$table->unsignedInteger('subtitle_color_bg')->comment('Couleur de fond du soustitre.')->change();
|
||||
$table->unsignedInteger('link_color')->comment('Couleur du lien.')->change();
|
||||
$table->unsignedInteger('link_color_bg')->comment('Couleur de fond du lien.')->change();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\Sliders\SliderElementPosition;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class TextPositionOnSliders extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('sliders', function (Blueprint $table) {
|
||||
$table->renameColumn('position', 'picture_position_mobile');
|
||||
$table->unsignedTinyInteger('text_position_desktop')
|
||||
->after('subtitle_color_bg')
|
||||
->default(SliderElementPosition::right())
|
||||
->comment('Enums\\SliderElementPosition postionnement du texte en desktop.');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('sliders', function (Blueprint $table) {
|
||||
$table->renameColumn('picture_position_mobile', 'position');
|
||||
$table->dropColumn('text_position_desktop');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateBreedersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('breeders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('firstname')->comment('The breeder firstname.');
|
||||
$table->string('lastname')->comment('The breeder lastname.');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('breeders');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateRecipesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('recipes', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('title')->comment('Nom de la recette.');
|
||||
$table->string('slug')->unique()->comment('Slug de la recette.');
|
||||
$table->string('seo_title')->comment('Titre SEO de la recette.');
|
||||
$table->string('seo_description')->comment('Description SEO de la recette.');
|
||||
$table->string('banner')->comment('Visuel d\'en tête 695px par 557px.');
|
||||
$table->string('banner_alt')->comment('Attribut alt de la bannière.');
|
||||
$table->string('banner_title')->comment('Attribut title de la bannière.');
|
||||
$table->string('category')->comment('Catégorie de la recette.');
|
||||
$table->integer('servings')->comment('Chiffre/nombre de portions de la recette.');
|
||||
$table->string('preparation')->comment('Temps de préparation de la recette.');
|
||||
$table->string('cooking')->comment('Temps de cuisson de la recette.');
|
||||
$table->json('ingredients')->comment('Ingrédients contenus dans la recette.');
|
||||
$table->string('description')->comment('Description des étapes de la recette.');
|
||||
$table->string('video')->nullable()->comment('Vidéo de présentation de la recette.');
|
||||
$table->string('picture')->nullable()->comment('Image de présentation 333px par 182px.');
|
||||
$table->string('picture_alt')->nullable()
|
||||
->comment('Attribut alt de l\'image de présentation de la recette.');
|
||||
$table->string('picture_title')->nullable()
|
||||
->comment('Attribut title de l\'image de présentation de la recette.');
|
||||
$table->boolean('status')->comment('État de publication de la recette.');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('recipes');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateRangesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('ranges', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('title')->comment('Nom de la gamme.');
|
||||
$table->string('slug')->unique()->comment('slug de la gamme.');
|
||||
|
||||
$table->string('seo_title')
|
||||
->comment('Utilisé pour la balise title de la gamme. De 53 à 60 chars recommandés.');
|
||||
$table->string('seo_description')
|
||||
->comment('Utilisé pour la balise méta description de la gamme. De 50 à 160 chars recommandés.');
|
||||
|
||||
$table->text('description')->comment('Description de la gamme.');
|
||||
$table->text('argumentation')->comment('Argumentation sur les labels.');
|
||||
|
||||
$table->unsignedTinyInteger('category')->comment('Enum Ranges\\RangeCategoryEnum.');
|
||||
|
||||
$table->string('picture')->nullable()->comment('L\'illustration de la gamme 700x700 px.');
|
||||
$table->string('picture_title')->nullable()->comment('Attribut title de l\'image.');
|
||||
$table->string('picture_alt')->nullable()->comment('Attribut alt de l\'image.');
|
||||
|
||||
$table->string('picture_thumb')->nullable()->comment('La vignette de la gamme 300x300 px.');
|
||||
$table->string('picture_thumb_title')->nullable()->comment('Attribut title de l\'image.');
|
||||
$table->string('picture_thumb_alt')->nullable()->comment('Attribut alt de l\'image.');
|
||||
|
||||
$table->boolean('status')->comment('La gamme est visible ou non.');
|
||||
|
||||
$table->timestamp('publish_at')->nullable()
|
||||
->comment('The date where the category status state should be set on.');
|
||||
$table->timestamp('unpublish_at')->nullable()
|
||||
->comment('The date where the category status state should be set off.');
|
||||
|
||||
$table->string('link1')->nullable()
|
||||
->comment('Category Link 1 #App\Models\Model#ID#, Sould be validated on store and update.');
|
||||
$table->string('link2')->nullable()
|
||||
->comment('Category Link 2 #App\Models\Model#ID#, Sould be validated on store and update.');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('ranges');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Range;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProductsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('products', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('title')->comment('Nom du produit.');
|
||||
$table->string('slug')->unique()->comment('slug du produit.');
|
||||
|
||||
$table->string('seo_title')
|
||||
->comment('Utilisé pour la balise title de la page. De 53 à 60 chars recommandés.');
|
||||
$table->string('seo_description')
|
||||
->comment('Utilisé pour la balise méta description de la page. De 50 à 160 chars recommandés.');
|
||||
|
||||
$table->text('description')->comment('Description du produit.');
|
||||
|
||||
$table->string('picture')->nullable()->comment('L\'illustration du produit 700x700 px.');
|
||||
$table->string('picture_title')->nullable()->comment('Attribut title de l\'image.');
|
||||
$table->string('picture_alt')->nullable()->comment('Attribut alt de l\'image.');
|
||||
|
||||
$table->string('picture_thumb')->nullable()->comment('La vignette du produit 300x300 px.');
|
||||
$table->string('picture_thumb_title')->nullable()->comment('Attribut title de l\'image.');
|
||||
$table->string('picture_thumb_alt')->nullable()->comment('Attribut alt de l\'image.');
|
||||
|
||||
$table->json('labels')->comment('List of Products\\ProductLabelEnum.');
|
||||
|
||||
$table->text('ref_article')->comment('Ref. Article Inco.');
|
||||
$table->text('ingredients')->comment('Ingredients.');
|
||||
$table->text('preparation_advices')->comment('Preparation advices.');
|
||||
$table->text('sales_pitch')->comment('Arguments de vente.');
|
||||
$table->text('allergen_list')->comment('Liste allergene issues de inco.');
|
||||
$table->unsignedTinyInteger('number_portion')->comment('Portion number.');
|
||||
|
||||
$table->boolean('show_ingredients')->comment('Afficher en front ou non.');
|
||||
$table->boolean('show_preparation_advices')->comment('Afficher en front ou non.');
|
||||
$table->boolean('show_sales_pitch')->comment('Afficher en front ou non.');
|
||||
$table->boolean('show_allergen_list')->comment('Afficher en front ou non.');
|
||||
$table->boolean('show_nutrionnal_values')->comment('Afficher en front ou non.');
|
||||
$table->boolean('show_portions')->comment('Afficher en front ou non.');
|
||||
|
||||
$table->boolean('status')->comment('Le produit est visible ou non.');
|
||||
|
||||
$table->timestamp('publish_at')->nullable()
|
||||
->comment('The date where the product status state should be set on.');
|
||||
$table->timestamp('unpublish_at')->nullable()
|
||||
->comment('The date where the product status state should be set off.');
|
||||
|
||||
$table->string('link1')->nullable()
|
||||
->comment('Category Link 1 #App\Models\Model#ID#, Sould be validated on store and update.');
|
||||
$table->string('link2')->nullable()
|
||||
->comment('Category Link 2 #App\Models\Model#ID#, Sould be validated on store and update.');
|
||||
|
||||
$table->foreignIdFor(Range::class)->constrained();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('products');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
class CreateIncoNutritionalValuesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('inco_nutritional_values', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->double('weight')->nullable();
|
||||
$table->double('energy')->nullable();
|
||||
$table->double('energy_kj')->nullable();
|
||||
$table->double('protein')->nullable();
|
||||
$table->double('carbohydrates')->nullable();
|
||||
$table->double('sugar')->nullable();
|
||||
$table->double('fat')->nullable();
|
||||
$table->double('saturated_fatty_acids')->nullable();
|
||||
$table->double('dietary_fiber')->nullable();
|
||||
$table->double('salt')->nullable();
|
||||
|
||||
$table->foreignId('product_id');
|
||||
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('inco_nutritional_values');
|
||||
}
|
||||
}
|
||||
1487
database/migrations/2022_09_05_131055_create_engagements_table.php
Normal file
21
database/seeders/BreederSeeder.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Breeder;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class BreederSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
DB::table('breeders')->truncate();
|
||||
Breeder::factory(150)->create();
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,11 @@ class DatabaseSeeder extends Seeder
|
|||
{
|
||||
$this->call([
|
||||
NewsSeeder::class,
|
||||
SliderSeeder::class
|
||||
SliderSeeder::class,
|
||||
BreederSeeder::class,
|
||||
RangeSeeder::class,
|
||||
RecipeSeeder::class,
|
||||
ProductSeeder::class
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
database/seeders/IncoNutritionalValueSeeder.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\IncoNutritionalValue;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class IncoNutritionalValueSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
DB::table('inco_nutritional_values')->truncate();
|
||||
IncoNutritionalValue::factory(150)->create();
|
||||
}
|
||||
}
|
||||
38
database/seeders/ProductSeeder.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\IncoNutritionalValue;
|
||||
use App\Models\Product;
|
||||
use App\Models\Range;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ProductSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
DB::table('inco_nutritional_values')->truncate();
|
||||
Product::all()->map(function (Product $product) {
|
||||
$product->deleteOrFail();
|
||||
});
|
||||
$ranges = Range::all();
|
||||
Product::factory(150)->make()->each(function (Product $product) use ($ranges) {
|
||||
\mt_srand();
|
||||
if (\mt_rand(0, 2)) {
|
||||
$product->range()->associate($ranges->random(1)->first());
|
||||
$product->saveOrFail();
|
||||
$product->fresh();
|
||||
/** @var \App\Models\IncoNutritionalValue $incValue */
|
||||
$incValue = IncoNutritionalValue::factory(1)->make()->get(0);
|
||||
$incValue->product()->associate($product);
|
||||
$incValue->saveOrFail();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
22
database/seeders/RangeSeeder.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Range;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class RangeSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Range::all()->map(function (Range $range) {
|
||||
$range->deleteOrFail();
|
||||
});
|
||||
Range::factory(8)->create();
|
||||
}
|
||||
}
|
||||
20
database/seeders/RecipeSeeder.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\Recipe;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class RecipeSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
Recipe::truncate();
|
||||
Recipe::factory(75)->create();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
{
|
||||
"include": [
|
||||
"./resources/js/**/*"
|
||||
]
|
||||
}
|
||||
{
|
||||
"include": ["./resources/js/**/*"]
|
||||
}
|
||||
|
|
|
|||
5456
package-lock.json
generated
34
package.json
|
|
@ -7,35 +7,57 @@
|
|||
"watch-poll": "mix watch -- --watch-options-poll=1000",
|
||||
"hot": "mix watch --hot",
|
||||
"prod": "npm run production",
|
||||
"production": "mix --production"
|
||||
"production": "mix --production",
|
||||
"eslint": "node ./node_modules/.bin/eslint --max-warnings 0 -c .eslintrc.json ./resources",
|
||||
"eslint-ci": "node ./node_modules/.bin/eslint --max-warnings 0 -c .eslintrc.json ./resources -f junit",
|
||||
"stylelint": "node ./node_modules/.bin/stylelint --max-warnings 0 '**/*.scss'",
|
||||
"stylelint-ci": "node ./node_modules/.bin/stylelint --max-warnings 0 '**/*.scss' -f json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.1.1",
|
||||
"@ckpack/vue-color": "^1.1.9",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.1.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.1.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.1.2",
|
||||
"@fortawesome/vue-fontawesome": "^3.0.1",
|
||||
"@popperjs/core": "^2.11.5",
|
||||
"@sipec/vue3-tags-input": "^3.0.4",
|
||||
"@types/bootstrap": "^5.1.12",
|
||||
"@types/cropperjs": "^1.3.0",
|
||||
"@types/toastify-js": "^1.11.1",
|
||||
"@types/vue-select": "^3.16.0",
|
||||
"@types/webpack-env": "^1.17.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.35.1",
|
||||
"@typescript-eslint/parser": "^5.35.1",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"axios": "^0.21",
|
||||
"bootstrap": "^5.1.3",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-vue": "^9.4.0",
|
||||
"eslint-webpack-plugin": "^3.2.0",
|
||||
"cropperjs": "^1.5.12",
|
||||
"laravel-mix": "^6.0.6",
|
||||
"laravel-mix-clean": "^0.1.0",
|
||||
"laravel-mix-eslint": "^0.2.0",
|
||||
"laravel-mix-polyfill": "^3.0.1",
|
||||
"laravel-mix-stylelint": "^1.0.0",
|
||||
"lodash": "^4.17.19",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"sass": "^1.52.1",
|
||||
"sass-loader": "^12.6.0",
|
||||
"slugify": "^1.6.5",
|
||||
"stylelint": "^14.11.0",
|
||||
"stylelint-config-idiomatic-order": "^8.1.0",
|
||||
"stylelint-config-sass-guidelines": "^9.0.1",
|
||||
"stylelint-config-standard-scss": "^6.1.0",
|
||||
"stylelint-webpack-plugin": "^3.3.0",
|
||||
"sweetalert2": "^11.4.17",
|
||||
"toastify-js": "^1.11.2",
|
||||
"ts-loader": "^9.3.0",
|
||||
"typescript": "^4.7.3",
|
||||
"vue": "^3.2.36",
|
||||
"vue-loader": "^16.8.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"autoprefixer": "10.4.5"
|
||||
"vue-loader": "^16.8.3",
|
||||
"vue-select": "^4.0.0-beta.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
BIN
resources/assets/images/engagements/1920x340.jpg
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
resources/assets/images/engagements/342x200.jpg
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
resources/assets/images/engagements/412x319.jpg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
resources/assets/images/engagements/486x278.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
resources/assets/images/engagements/500x637.jpg
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
resources/assets/images/engagements/525x451.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
resources/assets/images/engagements/680x350.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |