From 8a3058e7bc3b0fad954ed86ec4c1911b16bd77c0 Mon Sep 17 00:00:00 2001 From: Noah Lancien Date: Tue, 5 Sep 2023 15:28:55 +0200 Subject: [PATCH 01/27] Fixed some requests from the provided excel document --- app/Http/Controllers/ProductController.php | 14 +- .../Requests/Products/StoreProductRequest.php | 5 +- app/Models/Product.php | 19 +-- ...hange_products_pvc_from_int_to_decimal.php | 32 +++++ resources/lang/fr/models.php | 1 + resources/sass/base/_common.scss | 4 +- .../ts/components/ButtonMultiPdfComponent.vue | 8 +- .../ts/components/ImageInputComponent.vue | 10 +- .../components/MultipleCommentsComponent.vue | 6 +- .../MultipleDocumentsInputComponent.vue | 28 +--- .../ts/components/SourceInputsComponent.vue | 31 +++-- .../ts/components/TastedProductComponent.vue | 11 +- .../components/VerificationCodeComponent.vue | 8 +- resources/ts/utilities/fontawesome.ts | 70 +++++----- .../views/documents/product-body.blade.php | 19 ++- resources/views/documents/product.blade.php | 9 +- resources/views/layouts/nav-menu.blade.php | 4 +- .../views/modules/table-col-sorter.blade.php | 2 - resources/views/pages/dashboard.blade.php | 6 +- .../views/pages/products/create.blade.php | 14 +- resources/views/pages/products/edit.blade.php | 16 +-- resources/views/pages/products/form.blade.php | 123 +++++++++++------- .../views/pages/products/index.blade.php | 8 +- .../views/pages/products/indexList.blade.php | 26 ++-- .../pages/products/product-filters.blade.php | 2 +- .../inputs/bigint-foreign-id.blade.php | 5 +- .../partials/inputs/int-number.blade.php | 8 +- .../partials/inputs/select-enum.blade.php | 4 +- 28 files changed, 283 insertions(+), 210 deletions(-) create mode 100644 database/migrations/2023_09_05_131123_change_products_pvc_from_int_to_decimal.php diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php index bfcba2f..dacae6b 100644 --- a/app/Http/Controllers/ProductController.php +++ b/app/Http/Controllers/ProductController.php @@ -312,20 +312,20 @@ class ProductController extends Controller public function checkGencode(Request $request) { if ( - isset($request->GENCODE) && - $request->GENCODE !== null && - ctype_digit($request->GENCODE) && - strlen($request->GENCODE) > 0 + isset($request->GENCOD) && + $request->GENCOD !== null && + ctype_digit($request->GENCOD) && + strlen($request->GENCOD) > 0 ) { if ( isset($request->ID) && $request->ID !== null && - intval($request->GENCODE) === Product::where('id', $request->ID)->firstOrFail()->gencod + intval($request->GENCOD) === Product::where('id', $request->ID)->firstOrFail()->gencod ) { $result = null; } else { - $result = in_array(intval($request->GENCODE), Product::pluck('gencod')->toArray()) ? - Product::orderBy('created_at', 'desc')->where('gencod', intval($request->GENCODE))->first() : null; + $result = in_array(intval($request->GENCOD), Product::pluck('gencod')->toArray()) ? + Product::orderBy('created_at', 'desc')->where('gencod', intval($request->GENCOD))->first() : null; } } else { $result = null; diff --git a/app/Http/Requests/Products/StoreProductRequest.php b/app/Http/Requests/Products/StoreProductRequest.php index 8887dec..aae7317 100644 --- a/app/Http/Requests/Products/StoreProductRequest.php +++ b/app/Http/Requests/Products/StoreProductRequest.php @@ -44,6 +44,9 @@ class StoreProductRequest extends FormRequest 'is_tasted' => $this->is_tasted ? true : false, 'origins' => $this->origins ? $this->origins : [], 'claims' => $this->claims ? $this->claims : [], + 'picture_alt' => $this->name, + 'picture_title' => $this->name, + 'certification' => $this->certification == 0 ? null : $this->certification, ]); $this->mergePicture('picture'); $this->mergeDocuments('docs'); @@ -88,7 +91,7 @@ class StoreProductRequest extends FormRequest 'product_range' => ['required', new Enum(RangeEnum::class)], 'geo_area' => ['required', new Enum(GeoAreaEnum::class)], 'weight' => 'nullable|integer', - 'pvc' => 'nullable|integer', + 'pvc' => 'nullable|decimal:0,2', 'comment' => 'nullable|string|min:15', 'is_tasted' => 'required|boolean', 'tasters_number' => 'nullable|integer', diff --git a/app/Models/Product.php b/app/Models/Product.php index d0cb5e7..b47c85b 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -170,6 +170,7 @@ class Product extends Model */ private static function updateSpecifiedClaim(Product $model) { + $check = false; foreach ($model->claims as $claim) { $check = (intval($claim['id']) === \App\Enums\Products\ClaimEnum::others->value) ? true : false; } @@ -212,14 +213,16 @@ class Product extends Model */ public static function deleteOldDocuments(self $model) { - foreach ($model->getOriginal('docs') as $document) { - $originalPath = \ltrim(\urldecode($document), '/'); - if (\strpos($originalPath, 'storage') !== 0) { - return; - } - $originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath); - if (Storage::disk('public')->exists($originalPath)) { - Storage::disk('public')->delete($originalPath); + if ($model->getOriginal('docs')) { + foreach ($model->getOriginal('docs') as $document) { + $originalPath = \ltrim(\urldecode($document), '/'); + if (\strpos($originalPath, 'storage') !== 0) { + return; + } + $originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath); + if (Storage::disk('public')->exists($originalPath)) { + Storage::disk('public')->delete($originalPath); + } } } } diff --git a/database/migrations/2023_09_05_131123_change_products_pvc_from_int_to_decimal.php b/database/migrations/2023_09_05_131123_change_products_pvc_from_int_to_decimal.php new file mode 100644 index 0000000..367fee5 --- /dev/null +++ b/database/migrations/2023_09_05_131123_change_products_pvc_from_int_to_decimal.php @@ -0,0 +1,32 @@ +decimal('pvc', 12, 2)->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::table('products', function (Blueprint $table) { + $table->integer('pvc')->change(); + }); + } +}; diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index 6f12cf6..42342b1 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -3,6 +3,7 @@ return [ 'classes' => [ 'products' => 'Produits', + 'products_monitoring' => 'Veille produit|Veilles produit', 'brands' => 'Marques', ] ]; diff --git a/resources/sass/base/_common.scss b/resources/sass/base/_common.scss index b666315..a52b4d5 100644 --- a/resources/sass/base/_common.scss +++ b/resources/sass/base/_common.scss @@ -3,7 +3,9 @@ } .bo-image-preview { - max-height: 60px; + // max-height: 60px; + width: 180px; + height: auto; } .text-ellipsis { diff --git a/resources/ts/components/ButtonMultiPdfComponent.vue b/resources/ts/components/ButtonMultiPdfComponent.vue index 1c04c62..7fe8fe8 100644 --- a/resources/ts/components/ButtonMultiPdfComponent.vue +++ b/resources/ts/components/ButtonMultiPdfComponent.vue @@ -13,17 +13,19 @@ diff --git a/resources/views/pages/products/index.blade.php b/resources/views/pages/products/index.blade.php index 698104f..3197b69 100644 --- a/resources/views/pages/products/index.blade.php +++ b/resources/views/pages/products/index.blade.php @@ -1,6 +1,6 @@ @extends('layout') -@section('title', sprintf('%s - %s', __('models.classes.products'), Str::ucfirst(__('liste')))) +@section('title', sprintf('%s - %s', trans_choice('models.classes.products_monitoring', 2), Str::ucfirst(__('liste')))) @section('description', __('Liste des produits publiés')) @section('breadcrumb', 'no') @@ -10,14 +10,14 @@

- {{ __('models.classes.products') }} {{ Str::ucfirst(__('liste')) }} + {{ trans_choice('models.classes.products_monitoring', 2) }} {{ Str::ucfirst(__('liste')) }}

diff --git a/resources/views/pages/products/indexList.blade.php b/resources/views/pages/products/indexList.blade.php index df579d2..f363e0e 100644 --- a/resources/views/pages/products/indexList.blade.php +++ b/resources/views/pages/products/indexList.blade.php @@ -6,8 +6,7 @@ 'name' => __('Nom'), 'brand_id' => __('Marque'), 'picture' => __('Image'), - 'updated_at' => __('Mis à jour le'), - 'multi_pdf' => __('Multi .pdf'), + 'multi_pdf' => __('Générer en pdf la sélection'), ]; $routeName = request()->route()->getName(); $rst = !is_null(request()->rst) || !Session::has("$routeName.sorted"); @@ -37,12 +36,7 @@

{{ __('Aucune image enregistée') }}

@endif - - - {{ $productModel->updated_at->formatLocalized('%d/%m/%Y à %I:%M') }} - - - @if(!$noOrder or $rst) + {{-- @if(!$noOrder or $rst) --}} @php $data = [ @@ -53,7 +47,7 @@ @endphp
- @endif + {{-- @endif --}}
@method('DELETE') @@ -62,38 +56,38 @@ @@ -106,7 +100,7 @@ {{ __('Aucun résultat pour les :models', ['models' => __('models.classes.products')]) }} + class="h3">{{ __('Aucun résultat pour les :models', ['models' => trans_choice('models.classes.products_monitoring', 2)]) }} diff --git a/resources/views/pages/products/product-filters.blade.php b/resources/views/pages/products/product-filters.blade.php index 7214175..430f80d 100644 --- a/resources/views/pages/products/product-filters.blade.php +++ b/resources/views/pages/products/product-filters.blade.php @@ -33,7 +33,7 @@
- +
diff --git a/resources/views/partials/inputs/bigint-foreign-id.blade.php b/resources/views/partials/inputs/bigint-foreign-id.blade.php index 1a665d6..0cab24a 100644 --- a/resources/views/partials/inputs/bigint-foreign-id.blade.php +++ b/resources/views/partials/inputs/bigint-foreign-id.blade.php @@ -1,9 +1,12 @@ @if(!empty($label)) @endif +{{-- @dd($model->{$name}) --}} + @foreach ($associatedModels as $associatedModel) - @endforeach From 52dda4e346453d5a584a4f23556a81de3319c00f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Mon, 14 Aug 2023 13:36:18 +0200 Subject: [PATCH 02/27] Migration automatique de table START_C --- config/database.php | 12 + .../2023_08_14_124752_migrate_oci_tables.php | 217 ++++++++++++++++++ 2 files changed, 229 insertions(+) create mode 100644 database/migrations/2023_08_14_124752_migrate_oci_tables.php diff --git a/config/database.php b/config/database.php index ebf8175..e384506 100644 --- a/config/database.php +++ b/config/database.php @@ -63,6 +63,18 @@ return [ ]) : [], ], + 'oracle' => [ + 'driver' => 'oracle', + 'host' => env('DB_OCI_HOST', ''), + 'port' => env('DB_OCI_PORT', '1521'), + 'database' => env('DB_OCI_DATABASE', ''), + 'username' => env('DB_OCI_USERNAME', ''), + 'password' => env('DB_OCI_PASSWORD', ''), + 'charset' => env('DB_OCI_CHARSET', 'AL32UTF8'), + 'prefix' => env('DB_OCI_PREFIX', ''), + 'prefix_schema' => env('DB_OCI_SCHEMA_PREFIX', ''), + ], + 'pgsql' => [ 'driver' => 'pgsql', 'url' => env('DATABASE_URL'), diff --git a/database/migrations/2023_08_14_124752_migrate_oci_tables.php b/database/migrations/2023_08_14_124752_migrate_oci_tables.php new file mode 100644 index 0000000..b910c85 --- /dev/null +++ b/database/migrations/2023_08_14_124752_migrate_oci_tables.php @@ -0,0 +1,217 @@ +tableList as $tableName) { + $header = $this->fetOciTableHeader($tableName); + $this->createTable( + "{$this->tablePrefix}_{$tableName}", + $header + ); + $content = $this->fetchOciTableContent($tableName, $header); + foreach ($content as $row) { + DB::table("{$this->tablePrefix}_{$tableName}")->insert((array)$row); + } + } + } + + /** + * Reverse the migrations. + * + * @return void + * @throws \Error Wont rollback. + */ + public function down(): void + { + throw new \Error('Wont rollback'); + } + + /** + * Create table in SQL + * + * @param string $tableName + * @param array $tableColumns With name, type, size. + * @return void + */ + private function createTable(string $tableName, array $tableColumns) + { + Schema::dropIfExists($tableName); + Schema::create($tableName, function (Blueprint $table) use ($tableColumns) { + foreach ($tableColumns as $tableColumn) { + $tableComment = "{$tableColumn->name} {$tableColumn->type} {$tableColumn->size}"; + switch ($tableColumn->type) { + case 'NUMBER': + $table->bigInteger($tableColumn->name) + ->nullable()->comment($tableComment); + break; + case 'VARCHAR2': + case 'CHAR': + $table->string($tableColumn->name, $tableColumn->size) + ->nullable()->comment($tableComment); + break; + case 'DATE': + $table->dateTime($tableColumn->name) + ->nullable()->comment($tableComment); + break; + default: + throw new Error("Unhandled type {$tableColumn->type}"); + } + } + }); + } + + /** + * Oci table header + * + * @param string $tableName + * @return array with name, type, size + * @throws \Error If connection or query fails. + */ + private function fetOciTableHeader(string $tableName): array + { + $tableName = "{$this->tablePrefix}_{$tableName}"; + $conn = $this->getOciConnection(); + + // Prepare the statement. + $stid = oci_parse($conn, "SELECT * FROM {$tableName}"); + + if (!$stid) { + $e = oci_error($conn); + throw new \Error(htmlentities($e['message'], ENT_QUOTES)); + } + + // Use OCI_DESCRIBE_ONLY if not fetching rows. + oci_execute($stid, OCI_DESCRIBE_ONLY); + + if (!$stid) { + $e = oci_error($conn); + throw new \Error(htmlentities($e['message'], ENT_QUOTES)); + } + + $ncols = oci_num_fields($stid); + + $out = []; + + for ($i = 1; $i <= $ncols; $i++) { + $column_name = oci_field_name($stid, $i); + $column_type = oci_field_type($stid, $i); + $column_size = oci_field_size($stid, $i); + + $out[] = (object)[ + 'name' => $column_name, + 'type' => $column_type, + 'size' => $column_size + ]; + } + + oci_free_statement($stid); + oci_close($conn); + return $out; + } + + /** + * Oci table content + * + * @param string $tableName Table name. + * @param array $header With name, type,size. + * @return array With DB content. + * @throws \Error If connection or query fails. + */ + private function fetchOciTableContent(string $tableName, array $header): array + { + $out = []; + $tableName = "{$this->tablePrefix}_{$tableName}"; + $conn = $this->getOciConnection(); + + // Prepare the statement. + $stid = oci_parse($conn, "SELECT * FROM {$tableName}"); + + if (!$stid) { + $e = oci_error($conn); + throw new \Error(htmlentities($e['message'], ENT_QUOTES)); + } + + $r = oci_execute($stid); + if (!$stid) { + $e = oci_error($conn); + throw new \Error(htmlentities($e['message'], ENT_QUOTES)); + } + + while ($row = oci_fetch_array($stid, OCI_ASSOC + OCI_RETURN_NULLS)) { + $outRow = []; + foreach ($row as $k => $item) { + /** @var string */ + $type = collect($header)->where('name', $k)->firstOrFail()->type; + if ($type === 'DATE' and $item !== null) { + $item = $this->tryToParseCarbon($item); + } + $outRow[$k] = $item; + } + $out[] = (object)$outRow; + } + + oci_free_statement($stid); + oci_close($conn); + return $out; + } + + /** + * get Oci connection + * + * @return resource oci8 connection resource + * @throws \Error If connection fails. + */ + private function getOciConnection() + { + $conn = oci_connect('system', 'secret', 'localhost/XE'); + if (!$conn) { + $e = oci_error(); + throw new \Error(htmlentities($e['message'], ENT_QUOTES)); + } + return $conn; + } + + /** + * Try to parse freaking Start coq dates + * + * @param string $date + * @return \Carbon\Carbon + * @throws \Carbon\Exceptions\InvalidFormatException In case of failure. + */ + private function tryToParseCarbon(string $date): Carbon + { + return Carbon::parse($date); + } +}; From f539cdd99a5618183105977b980cae1f9d8ce167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Mon, 14 Aug 2023 14:15:23 +0200 Subject: [PATCH 03/27] add drop temp table system --- .../2023_08_14_124752_migrate_oci_tables.php | 69 +++++++++++++++---- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/database/migrations/2023_08_14_124752_migrate_oci_tables.php b/database/migrations/2023_08_14_124752_migrate_oci_tables.php index b910c85..adb4317 100644 --- a/database/migrations/2023_08_14_124752_migrate_oci_tables.php +++ b/database/migrations/2023_08_14_124752_migrate_oci_tables.php @@ -34,17 +34,9 @@ return new class extends Migration */ public function up(): void { - foreach ($this->tableList as $tableName) { - $header = $this->fetOciTableHeader($tableName); - $this->createTable( - "{$this->tablePrefix}_{$tableName}", - $header - ); - $content = $this->fetchOciTableContent($tableName, $header); - foreach ($content as $row) { - DB::table("{$this->tablePrefix}_{$tableName}")->insert((array)$row); - } - } + $this->createAndFillTempTables(); + + $this->dropTempTables(); } /** @@ -58,6 +50,21 @@ return new class extends Migration throw new \Error('Wont rollback'); } + /** + * Get a table if it exists + * + * @param string $tableName + * @return string + * @throws \Error If table is unkown. + */ + private function getTableName(string $tableName): string + { + if (!in_array($tableName, $this->tableList)) { + throw new Error("Unknown table {$tableName}"); + } + return "{$this->tablePrefix}_{$tableName}"; + } + /** * Create table in SQL * @@ -92,6 +99,40 @@ return new class extends Migration }); } + /** + * Create temp tables and fill them + * + * @return void + * @throws \Error In case of failure. + */ + private function createAndFillTempTables(): void + { + foreach ($this->tableList as $tableName) { + $header = $this->fetchOciTableHeader($tableName); + $this->createTable( + $this->getTableName($tableName), + $header + ); + $content = $this->fetchOciTableContent($tableName, $header); + foreach ($content as $row) { + DB::table($this->getTableName($tableName))->insert((array)$row); + } + } + } + + /** + * Create temp tables and fill them + * + * @return void + * @throws \Error In case of failure. + */ + private function dropTempTables(): void + { + foreach ($this->tableList as $tableName) { + Schema::drop($this->getTableName($tableName)); + } + } + /** * Oci table header * @@ -99,9 +140,9 @@ return new class extends Migration * @return array with name, type, size * @throws \Error If connection or query fails. */ - private function fetOciTableHeader(string $tableName): array + private function fetchOciTableHeader(string $tableName): array { - $tableName = "{$this->tablePrefix}_{$tableName}"; + $tableName = $this->getTableName($tableName); $conn = $this->getOciConnection(); // Prepare the statement. @@ -152,7 +193,7 @@ return new class extends Migration private function fetchOciTableContent(string $tableName, array $header): array { $out = []; - $tableName = "{$this->tablePrefix}_{$tableName}"; + $tableName = $this->getTableName($tableName); $conn = $this->getOciConnection(); // Prepare the statement. From 634dbe832e278705ee8a9f98e30e8648069c2bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 16 Aug 2023 12:23:07 +0200 Subject: [PATCH 04/27] Products and Brands Migration OK except for products assets Waiting for https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9782 --- app/Enums/Products/RayEnum.php | 8 +- app/Http/Resources/ProductResource.php | 4 +- app/Models/Keyword.php | 8 +- app/Models/Product.php | 7 +- database/factories/ProductFactory.php | 2 +- ...023_02_23_120913_create_products_table.php | 2 +- .../2023_08_14_124752_migrate_oci_tables.php | 466 +++++++++++++++++- database/migrations/other_allegations.txt | 185 +++++++ database/seeders/KeywordInitSeeder.php | 197 ++++++++ database/seeders/KeywordSeeder.php | 183 +------ .../views/documents/product-body.blade.php | 6 +- 11 files changed, 863 insertions(+), 205 deletions(-) create mode 100644 database/migrations/other_allegations.txt create mode 100644 database/seeders/KeywordInitSeeder.php diff --git a/app/Enums/Products/RayEnum.php b/app/Enums/Products/RayEnum.php index cf746bd..ddd32c0 100644 --- a/app/Enums/Products/RayEnum.php +++ b/app/Enums/Products/RayEnum.php @@ -13,10 +13,10 @@ enum RayEnum : int case freshlyPackaged = 3; case roastery = 4; case caterer = 5; - case creamery = 6; - case groceryStore = 7; - case frozen = 8; - case others = 9; + case creamery = 7; + case groceryStore = 8; + case frozen = 9; + case others = 10; /** * Custom added functions definition diff --git a/app/Http/Resources/ProductResource.php b/app/Http/Resources/ProductResource.php index 9ae2f2c..4c144b9 100644 --- a/app/Http/Resources/ProductResource.php +++ b/app/Http/Resources/ProductResource.php @@ -22,7 +22,7 @@ class ProductResource extends JsonResource 'user_id' => null, 'brand_id' => null, 'name' => null, - 'saw_at' => null, + 'seen_at' => null, 'source' => null, 'fair' => null, 'shop' => null, @@ -112,7 +112,7 @@ class ProductResource extends JsonResource 'user_id' => User::where('id', $this->product->user_id)->firstOrFail(), 'brand_id' => Brand::where('id', $this->product->brand_id)->firstOrFail(), 'name' => $this->product->name, - 'saw_at' => Carbon::createFromFormat('Y-m-d H:i:s', $this->product->saw_at)->format('d/m/Y'), + 'seen_at' => Carbon::createFromFormat('Y-m-d H:i:s', $this->product->seen_at)->format('d/m/Y'), 'source' => $this->product->source->label(), 'fair' => $this->product->fair, 'shop' => $this->product->shop, diff --git a/app/Models/Keyword.php b/app/Models/Keyword.php index 346f729..2607945 100644 --- a/app/Models/Keyword.php +++ b/app/Models/Keyword.php @@ -8,9 +8,9 @@ use Illuminate\Database\Eloquent\Model; /** * @property integer $id * @property string $label - * @property string $keyword_cat - * @property string $created_at - * @property string $updated_at + * @property \App\Enums\KeywordCategoryEnum $keyword_cat + * @property \Carbon\Carbon $created_at + * @property \Carbon\Carbon $updated_at * @property Keywordable[] $keywordables */ class Keyword extends Model @@ -39,7 +39,7 @@ class Keyword extends Model */ public function keywordables() { - return $this->hasMany('App\Models\Keywordable'); + return $this->hasMany(Keywordable::class); } /** diff --git a/app/Models/Product.php b/app/Models/Product.php index b47c85b..74e0ca3 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -23,7 +23,7 @@ use Symfony\Component\HttpFoundation\StreamedResponse; * @property integer $brand_id * @property string $name * @property string $brand - * @property string $saw_at + * @property string $seen_at * @property enum $source * @property string $fair * @property string $shop @@ -62,8 +62,9 @@ class Product extends Model 'user_id', 'brand_id', 'name', + // ? FIXME: ?why is there brand here, column is not in DB. 'brand', - 'saw_at', + 'seen_at', 'source', 'fair', 'shop', @@ -108,7 +109,7 @@ class Product extends Model 'product_range' => RangeEnum::class, 'geo_area' => GeoAreaEnum::class, 'is_tasted' => 'boolean', - 'saw_at' => 'datetime:Y-m-d', + 'seen_at' => 'datetime:Y-m-d', 'created_at' => 'datetime:Y-m-d', 'updated_at' => 'datetime:Y-m-d', 'deleted_at' => 'datetime:Y-m-d', diff --git a/database/factories/ProductFactory.php b/database/factories/ProductFactory.php index 9e4c9ef..a524965 100644 --- a/database/factories/ProductFactory.php +++ b/database/factories/ProductFactory.php @@ -50,7 +50,7 @@ final class ProductFactory extends Factory 'user_id' => fake()->randomElement(User::pluck('id')), 'brand_id' => fake()->randomElement(Brand::pluck('id')), 'name' => fake()->unique()->word . " " . fake()->unique()->word, - 'saw_at' => fake()->dateTime(), + 'seen_at' => fake()->dateTime(), 'source' => $source, 'fair' => $fair, 'shop' => $shop, diff --git a/database/migrations/2023_02_23_120913_create_products_table.php b/database/migrations/2023_02_23_120913_create_products_table.php index dc63b5d..37da7c9 100644 --- a/database/migrations/2023_02_23_120913_create_products_table.php +++ b/database/migrations/2023_02_23_120913_create_products_table.php @@ -19,7 +19,7 @@ return new class extends Migration $table->bigInteger('user_id')->index(); $table->bigInteger('brand_id')->index()->comment('Marque - Required - Texte libre avec auto-suggestion sur la table elle même'); $table->string('name')->comment('Nom du produit - Required'); - $table->timestamp('saw_at')->useCurrentOnUpdate()->useCurrent()->comment('Produit vue le (champs date)'); + $table->timestamp('seen_at')->useCurrentOnUpdate()->useCurrent()->comment('Produit vue le (champs date)'); $table->unsignedTinyInteger('source')->comment('Source - Required - (valeur de l\'enum SourceEnum) -> champ SELECT'); $table->string('fair')->nullable()->comment('Salon - Affiché en front si SourceEnum = Salon Professionnel ou Salon Clients ou Salon Grand Public - Texte libre avec auto-suggestion sur la table elle même'); $table->string('shop')->nullable()->comment('Magasin - Affiché en front si SourceEnum = Magasin - Texte libre avec auto-suggestion sur la table elle même'); diff --git a/database/migrations/2023_08_14_124752_migrate_oci_tables.php b/database/migrations/2023_08_14_124752_migrate_oci_tables.php index adb4317..b65e0e9 100644 --- a/database/migrations/2023_08_14_124752_migrate_oci_tables.php +++ b/database/migrations/2023_08_14_124752_migrate_oci_tables.php @@ -1,10 +1,30 @@ truncate(); + DB::table('keywordables')->truncate(); + DB::table('keywords')->truncate(); + DB::table('products')->truncate(); + Schema::enableForeignKeyConstraints(); + if (Artisan::call('db:seed', ['--class' => 'KeywordInitSeeder']) !== 0) { + throw new \RuntimeException('Failed to seed keywords'); + } + + // * Seed a dummy user. + $this->dummyUser = User::factory()->createOne([ + 'firstname' => 'Dummy', + 'lastname' => 'User' + ]); + + // * Apply DB corrections + $this->setUniqueOnBrandName(); + $this->setJsonColumnsAsJsonOnProducts(); + $this->fixForeignKeyActionOnKeywordables(); + $this->fixForeignKeyActionOnProcesses(); + + // ! DEV: Comment this after running once, to develop a migration without remigrating every temp table. $this->createAndFillTempTables(); + // * Migrate PRODUCTS, comment all except the one you want to develop here. + $this->migrateBrands(); + $this->migrateProducts(); + + + // ! DEV: Add a die here to develop migration and prevent droping temp tables + dd('toto'); + $this->dropTempTables(); } @@ -50,6 +106,376 @@ return new class extends Migration throw new \Error('Wont rollback'); } + // - MIGRATION METHODS - + + /** + * Migrate products + * + * + * @return void + * @throws \Error In case of failure. + * @phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded + */ + private function migrateProducts(): void + { + // phpcs:enable + $otherAllegations = Str::of(File::get(base_path('database/migrations/other_allegations.txt'))) + ->explode(PHP_EOL)->map(fn (string $other_allegation) => Str::of($other_allegation)->squish()) + ->unique(); + + $MOTKS = Keyword::all(); + /** @var \Illuminate\Support\Collection */ + $START_SOURCES = $this->getQuery('SOURCE')->get(); + /** @var \Illuminate\Support\Collection */ + $START_CERTIFICATIONS = $this->getQuery('CERTIFICATION')->get(); + /** @var \Illuminate\Support\Collection */ + $START_FAMILLES = $this->getQuery('FAMILLE')->get(); + /** @var \Illuminate\Support\Collection */ + $START_SECTEURS = $this->getQuery('SECTEUR')->get(); + /** @var \Illuminate\Support\Collection */ + $START_RAYONS = $this->getQuery('RAYON')->get(); + + // No need to paginate here, column name is unique. + $brands = Brand::all(); + $sources = collect(SourceEnum::cases()); + $certifications = collect(CertificationEnum::cases()); + $claims = collect(ClaimEnum::cases()); + $ranges = collect(RangeEnum::cases()); + $geo_areas = collect(GeoAreaEnum::cases()); + $origins = collect(OriginEnum::cases()); + + $index = "NUM_VEILLE_PROD"; + $query = $this->getQuery("VEILLE_PROD")->orderBy($index); + $nbRowsToMigrate = (clone $query)->count(); + $totalRowsToMigrate = $nbRowsToMigrate; + + $console = new ConsoleOutput(); + $console->writeln(''); + $progress = new ProgressBar($console, $nbRowsToMigrate); + $progress->setMessage(str_pad('Migrate Products', 24, ' ')); + $progress->setFormat(' - %message% [%bar%] %percent:3s%% %current%/%max% %elapsed:6s%/%estimated:-6s%'); + + while ($nbRowsToMigrate > 0) { + $progress->display(); + $progress->advance(1); + $row = (clone $query)->skip($totalRowsToMigrate - $nbRowsToMigrate--)->limit(1)->first(); + + $resolveAllegation = function (string $alleg) use ($claims, $otherAllegations) { + // phpcs:ignore Generic.Files.LineLength.TooLong + if ($alleg === 'Sans additif, sans colorant, sans conservateur,100% simple, sans polyphosphate,sans sel nutrite') { + return ClaimEnum::withoutAdditives; + } + if (in_array(Str::of($alleg)->squish(), $otherAllegations->all())) { + return ClaimEnum::others; + } + try { + return $claims->firstOrFail( + fn (ClaimEnum $claim) => Str::of($claim->label())->ascii()->replace(' ', '') + == Str::of($alleg)->trim()->ascii()->replace(' ', '') + ); + } catch (\Exception) { + dump( + $alleg, + $claims->map(fn (ClaimEnum $claim) => $claim->label()) + ); + throw new \Error("allegation `{$alleg}` no found"); + } + }; + $resolveOrigin = function (string $ori) use ($origins) { + if ( + in_array(Str::of($ori)->squish(), [ + 'N??,??lev?? et pr??par?? en France', + 'N???,???lev??? et pr???par??? en France' + ]) + ) { + return ClaimEnum::others; + } + try { + return $origins->firstOrFail( + fn (OriginEnum $origin) => Str::of($origin->label())->ascii()->replace(' ', '') + == Str::of($ori)->trim()->ascii()->replace(' ', '') + ); + } catch (\Exception) { + dump( + $ori, + $origins->map(fn (OriginEnum $origin) => $origin->label()) + ); + throw new \Error("origin `{$ori}` no found"); + } + }; + $resolveMotk = function (string $motk) use ($MOTKS) { + // * Uu OMG !!! + if ($motk === 'Gain de temps Nomadisme') { + return $MOTKS->where('label', 'Gain de temps') + ->where('keyword_cat', KeywordCategoryEnum::productPracticality)->firstOrFail(); + } + if ($motk === 'Vari??t?? des sens') { + return $MOTKS->where('label', 'Variété des sens') + ->where('keyword_cat', KeywordCategoryEnum::productPleasure)->firstOrFail(); + } + if ($motk === 'V??g??tal') { + return $MOTKS->where('label', 'Végétal') + ->where('keyword_cat', KeywordCategoryEnum::productHealth)->firstOrFail(); + } + try { + return $MOTKS->firstOrFail( + fn (Keyword $keyword) => Str::of($keyword->label)->ascii()->replace(' ', '') + == Str::of($motk)->trim()->ascii()->replace(' ', '') + ); + } catch (\Exception) { + dump( + $motk, + $MOTKS->pluck('label') + ); + throw new \Error("keyword `{$motk}` no found"); + } + }; + + // FIXME: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9740 . + if (empty($row->MARQUE)) { + continue; + } + // FIXME: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9750 . + if (empty($row->NUM_RESEAU)) { + continue; + } + // FIXME: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9758 . + if (empty($row->NUM_SECTEUR)) { + continue; + } + // FIXME: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9760 . + if (empty($row->NUM_RAYON)) { + continue; + } + // FIXME: thread https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9767 . + if (empty($row->NUM_FAMILLE)) { + continue; + } + + $brandName = Str::of($row->MARQUE)->slug()->studly(); + /** name is unique on brand */ + $brand = null; + try { + $brand = $brands->where('name', $brandName)->firstOrFail(); + } catch (\Exception) { + throw new \Error("brand` {$brandName}` not found"); + } + $url = $row->URL ? Str::of(url($row->URL))->lower() : null; + $date_saisie = $row->DATE_SAISIE ? $this->tryToParseCarbon($row->DATE_SAISIE, true) : null; + $is_tasted = Str::of($row->DEGUSTER_O_N)->lower() === 'o' ? true : false; + $claimList = collect([ + $row->ALLEG0, $row->ALLEG1, $row->ALLEG2 + ])->filter()->map(fn (string $alleg) => $resolveAllegation($alleg)); + $originList = collect([ + $row->ORI0, $row->ORI1, $row->ORI2 + ])->filter()->map(fn (string $origin) => $resolveOrigin($origin)); + $keywordsList = collect([ + $row->MOTK0, $row->MOTK1, $row->MOTK2, $row->MOTK3, $row->MOTK4, $row->MOTK5 + ])->filter()->map(fn (string $motk) => $resolveMotk($motk)); + + $sourceEnum = null; + if ($row->NUM_SOURCE) { + $sourceEnum = $sources->firstOrFail(fn (SourceEnum $source) => $source->value() == $row->NUM_SOURCE); + } + + $certificationEnum = null; + if ($row->CERTIFICATION) { + $certificationEnum = $certifications->firstOrFail( + fn (CertificationEnum $certification) => $certification->value() == $row->CERTIFICATION + ); + } + + $distributionNetwork = $row->NUM_RESEAU ? DistributionNetworkEnum::from($row->NUM_RESEAU) : null; + $ray = $row->NUM_RAYON ? RayEnum::from($row->NUM_RAYON) : null; + $product_range = null; + if ($row->NUM_FAMILLE) { + $product_range = $ranges->firstOrFail( + fn (RangeEnum $range) => $range->value() == $row->NUM_FAMILLE + ); + } + $geo_area = null; + if ($row->NUM_SECTEUR) { + $geo_area = $geo_areas->firstOrFail( + fn (GeoAreaEnum $geoArea) => $geoArea->value() == $row->NUM_SECTEUR + ); + } + + $claim = null; + if ($row->COMP_ALLEG) { + try { + $claim = $resolveAllegation($row->COMP_ALLEG); + } catch (\Exception) { + throw new \Error("`{$row->COMP_ALLEG}` not found"); + } + } + + /** @var \App\Models\Product $product */ + $product = Product::query()->where('id', $row->{$index})->firstOrNew(); + $product->id = $row->NUM_VEILLE_PROD; + $fill = [ + 'brand_id' => $brand->id, + 'catalogue' => $row->CATALOGUE, + 'certification' => $certificationEnum, + 'comment' => $row->COMM_LIBRE, + 'distribution_network' => $distributionNetwork, + 'fair' => $row->SALON, + 'geo_area' => $geo_area, + 'name' => $row->NOM, + 'product_range' => $product_range, + 'ray' => $ray, + 'shop' => $row->MAGASIN, + 'source' => $sourceEnum, + 'pvc' => $row->PVC, + 'weight' => $row->POIDS, + 'is_tasted' => $is_tasted, + 'gencod' => $row->GENCOD, + 'url' => $url, + 'claims' => $claimList, + 'specified_claim' => $claim, + 'tasters_number' => $row->NB_DEGUST, + 'origins' => $originList, + 'docs' => 'tttt', + 'picture_alt' => 'tttt', + 'picture_title' => 'tttt', + 'picture' => 'tttt', + 'user_id' => $this->dummyUser->id + ]; + if ($date_saisie) { + // ? FIXME: IS this pertinent ? + $fill['seen_at'] = $date_saisie; + $fill['created_at'] = $date_saisie; + $fill['updated_at'] = $date_saisie; + } + if (Str::of($row->SUPPRIMER_O_N)->lower() === 'o') { + $fill['deleted_at'] = $date_saisie; + } + $product->fill($fill); + if (!DB::transaction(fn () => $product->saveQuietly())) { + throw new \Error("Failed to save product {$row->NUM_VEILLE_PROD}"); + } + + $product->productKeywords()->sync($keywordsList->pluck('id')); + } //end while + $progress->finish(); + } + + /** + * Create unique brands using veille products + * + * @return void + */ + private function migrateBrands(): void + { + $query = $this->getQuery("VEILLE_PROD"); + /** @var \Illuminate\Support\Collection */ + $brands = (clone $query)->pluck('MARQUE')->filter() + ->map(fn (string $brandName) => Str::of($brandName)->slug()->studly())->unique(); + $console = new ConsoleOutput(); + $console->writeln(''); + $progress = new ProgressBar($console, $brands->count()); + $progress->setMessage(str_pad('Migrate Brands', 24, ' ')); + $progress->setFormat(' - %message% [%bar%] %percent:3s%% %current%/%max% %elapsed:6s%/%estimated:-6s%'); + // FIXME: Insert brand->filter and find from where it should be fetched. + $brands->each(function ($brandName) use ($progress) { + Brand::query()->where('name', $brandName)->firstOrNew()->fill([ + 'name' => $brandName + ])->saveQuietly(); + $progress->advance(); + }); + $progress->finish(); + } + + // - END MIGRATION METHODS - + // -- DB CORRECTIONS -- + + /** + * Set unique on brands.name + * + * @return void + */ + private function setUniqueOnBrandName(): void + { + try { + Schema::table('brands', function (Blueprint $table) { + $table->dropUnique(['name']); + }); + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch + } catch (QueryException) { + // * Do nothing + } finally { + Schema::table('brands', function (Blueprint $table) { + $table->unique(['name']); + }); + } + } + + /** + * Correct some columns to effectively json + * + * @return void + */ + private function setJsonColumnsAsJsonOnProducts(): void + { + Schema::table('products', function (Blueprint $table) { + $table->json('origins')->nullable()->change() + ->comment('Origine - Tableau d\'Enum (entier) JSON (valeur de l\'enum OriginEnum)'); + $table->json('claims')->nullable()->change() + ->comment('Autres allégations - Tableau d\'Enum (entier) JSON (valeur de l\'enum ClaimEnum)'); + $table->json('docs')->nullable()->change() + ->comment('Docs complémentaires - Tableau de path - 0 a 5 docs PDF'); + }); + } + + /** + * Restrict foreign keys on keywordables + * + * @return void + */ + private function fixForeignKeyActionOnKeywordables(): void + { + Schema::table('keywordables', function (Blueprint $table) { + $table->dropForeign(['keyword_id']); + }); + Schema::table('keywordables', function (Blueprint $table) { + $table->foreign(['keyword_id'])->references(['id'])->on('keywords') + ->restrictOnUpdate()->cascadeOnDelete(); + }); + } + + /** + * Restrict foreign keys on processes + * + * @return void + */ + private function fixForeignKeyActionOnProcesses(): void + { + Schema::table('processes', function (Blueprint $table) { + $table->dropForeign(['user_id']); + }); + Schema::table('processes', function (Blueprint $table) { + $table->foreign(['user_id'])->references(['id'])->on('users') + ->restrictOnUpdate()->restrictOnDelete(); + }); + } + + // -- END DB CORRECTION -- + // --- LIB METHODS --- + + /** + * Get Temp DB query builder + * + * @param string $tableName + * @return \Illuminate\Contracts\Database\Query\Builder + * @throws \Error If $tableName does not exists. + */ + private function getQuery(string $tableName): Builder + { + if (!DB::table($this->getTableName($tableName))->exists()) { + throw new \Error("table {$tableName} does not exists"); + } + return DB::table($this->getTableName($tableName)); + } + /** * Get a table if it exists * @@ -107,17 +533,27 @@ return new class extends Migration */ private function createAndFillTempTables(): void { + $console = new ConsoleOutput(); foreach ($this->tableList as $tableName) { - $header = $this->fetchOciTableHeader($tableName); + $section = $console->section(); + $header = $this->fetchOciTableHeader($tableName); $this->createTable( $this->getTableName($tableName), $header ); + $message = str_pad("{$this->tablePrefix} {$tableName}", 24, ' '); + $section->write(" - {$message} -> Fetching OCI DATA.."); $content = $this->fetchOciTableContent($tableName, $header); + $section->clear(); + $progress = new ProgressBar($section, count($content)); + $progress->setMessage($message); + $progress->setFormat(' - %message% [%bar%] %percent:3s%% %current%/%max% %elapsed:6s%/%estimated:-6s%'); foreach ($content as $row) { + $progress->advance(); DB::table($this->getTableName($tableName))->insert((array)$row); } - } + $progress->finish(); + }//end foreach } /** @@ -247,12 +683,30 @@ return new class extends Migration /** * Try to parse freaking Start coq dates * - * @param string $date + * @param string $date The string date to parse. + * @param boolean $asTimestamp To force the return of a valid TIMESTAMP (in epoch). * @return \Carbon\Carbon - * @throws \Carbon\Exceptions\InvalidFormatException In case of failure. + * @throws \Error If the date cannot fit in a timestamp. */ - private function tryToParseCarbon(string $date): Carbon + private function tryToParseCarbon(string $date, bool $asTimestamp = false): Carbon { - return Carbon::parse($date); + $utcTimeZone = new DateTimeZone('UTC'); + $cDate = Carbon::parse($date, $utcTimeZone); + if ( + $asTimestamp and + $cDate->lt(Carbon::createFromTimestamp(0, $utcTimeZone)) or + $cDate->gt(Carbon::createFromTimestamp( + (new DateTime("99999/12/31 00:00:00"))->getTimestamp(), + $utcTimeZone + )) + ) { + throw new \Error("The date {$date} is cannot fit in a Timestamp"); + } + + if ($cDate->timestamp === 0) { + // * Prevent Invalid datetime format: 1292 Incorrect datetime value: '1970-01-01 00:00:00' + $cDate->addSecond(); + } + return $cDate; } }; diff --git a/database/migrations/other_allegations.txt b/database/migrations/other_allegations.txt new file mode 100644 index 0000000..8821579 --- /dev/null +++ b/database/migrations/other_allegations.txt @@ -0,0 +1,185 @@ +# EXPORT THIS FROM DB USING +# `COMP_ALLEG` NOT IN( +# 'Sans additif, sans colorant, sans conservateur,100% simple, sans polyphosphate,sans sel nutrite', +# 'Sans additif, sans colorant, sans conservateur, 100% simple, sans polyphosphate, sans sel nitrité', +# 'Produit de l\'année', +# 'Bleu Blanc Coeur, source d\'Omega 3', +# 'Sans OGM', +# 'Sans gluten', +# 'Sans Antibiotique', +# 'Vegan', +# 'Allégé', +# 'Halal, Casher', +# 'Riche en protéines, fibres, calcium…', +# 'Autres' +# ) +Agri ethique france commerce equitable +Commerce equitable +eleveurs engages, alimentation 100% vegetaux, mineraux vitamines, cereales d'origines locales, collecte de proximite, respect du bien-etre animal +Emballage produit en Vendee +Faible en MG / pauvre en sel +producteurs partenaires +Recette Vegetarienne +Sans colorant +Sans pesticides +Saumon garanti jamais congele +saumon garanti jamais congele +Creme fraiche origine france et chablis AOC +Elabore a la main, petits pois origine france +sans colorant ni conservateur , sans huile de palme +SANS COLORANT SANS HUILE DE PALME SANS EXHAUSTEUR DE GOUT +sans colorant, conservateur et huile de palme +sans colorant, conservatuer et huile de palme +VEGETAL +vegetarien +Antioxydant/ Vitamine C +equitable +Filiere cacao equitable +Ferme engagee +Filiere courte des eleveurs aux consommateurs ; +meilleur remuneration des producteurs +-25% sel +-60% de plastique +100% filets +Barquette en bois issue de cultures durables +bien eleve et nourri +COLLECTION FESTIVE +filet de poulet +fume au bois de hetre +Origine France +Sans additif, Sans conservateur +sans conservateur +sans conservateur - vegetarien +sans conservateur, colorant, arome ajoute, +sans conservateur, sans colorant, sans huile de palme, sans exhausteur de gout, sans huile hydrogenee +sans huile de palem , sans exhausteur de gout +Sans huile de palme, Sans exhausteur de gout +Sans sel nitrite ajoute +source de proteines vegetales et sources de fibres +viande 100% Filet +Eleve en plein air +certifie +Cooperative engagee +Faible en matiere grasse +Le porc Francais +Mache cultivee en Pays de la Loire +Melusines Bieres Artisanales +Pauvre en sel +Porc francais +Porc Francais, produit de Bretagne +Poulet fermier +Poulet Francais, sans conservateur ni colorant +SANS ADDITIF +Sans colorant, avec du poulet eleve au sol et bien nourri +Sans huile de palme / sans exhausteur de gout +Sans sel nitrite +Source de proteines ++ de 40 eleveurs de votre region +100 % local +100% Alimentation vegetale, minerale er vitamines +100% local, +100% local, issu de votre region +100% viande de volaille +5 min a la poele +AB +Agri cnfiance +Alimentation 100% vegetale +Alimentation 100% vegetale CLASSE A +alimentation 100% vegetale minerale et vitaminee +alimentation 100% vegetale minerale et vitamines +alimentation 100% vegetale, 50% mais +Alimentation 100% vegetale, minerale et vitamines +Alimentation 100% vegetale, Minerale et vitamines +Alimentation cereales 100% francaises +Alimentation cereales entieres 100% francaises +alimente avec 100% vegetaux mineraux et vitamines +Avec de la dinde bien nourrie +avec du poulet bien nourri +avec du poulet bien nourri, sans colorant +barquette avec 60% de plastique recycle +Barquette avec 60% recycle +Barquette cuisson +Barquettes recyclables a 60 % en carton +Bien etre animal +Bien-etre animal +Bq 100% recyclable issu de 85% de plastique recycle +Cereales entieres 100% francaises +Cereales produites localement; preparation et abattage local; j'achete local +Chair plus gouteuse +Clean label +Clean Label +Confit de France +Contribue a l'economie locale, revenu des eleveurs protege, + de 5 900 emplois soutenus en regions +cuit a l'etouffee +Eleve en France +Eleve en plein air et Emballage eco-concu -45% de plastique +Eleve en plein air, Emballage eco-concu +Elevee en plein air, Emballage eco-concu +Emballage eco-concu -45% de plastique +Engagee pour la vie de nos eleveurs, Barquette avec 60% de plastique recycle +Ethik food +Faconne traditionnellement a la main +Fait main +Faite main +Ficele a la main +Fromage AOP +fume au bois de hetre et de chene +Garantie volaille bien elevee et bien nourrie +Garantie Volaille bien elevee et bien nourrie et bien +Issu de souche rustique +J'aime manger local +Juste remuneration des eleveurs +La moins calorique +La viande du quotidien la moins calorique +moins de 2% de mG +montees a la main +Montees a la main +montees main +naturellement tendres +ne et eleve en France abattue en Allemagne +NIVEAU BIEN ETRE ANIMAL +nourri aux cereales locales +Nourri avec 100% de vegetaux, mineraux et vitamines +Nourri avec 100% vegetaux, mineraux et vitamines +Nourri avec des cereales 100% francaises +Nourrie sans OGM (<0,9%) cereales francaises, elevage respectueux, barquette 100% recyclable +Origine certifie sud ouest +Planete positive +PRET A CONSOMMER +sans antibio +sans huile de palme +Sans Huile de palme +sans huile de palme sans colorant +Sans lactose +Sans lactose, Sources de proteines +sans traitement antibiotique , aux aromes naturels, sans conservateur +Sortant a l'exterieur +Sources de proteines +Viande francaise +Volaille bien nourie +volaille francaise +volaille nourrie 100% vegetaux mineraux et vitamines +Barquette Cuisson +Cuisson facile +Elabores par nos bouchers +Eleve en large enlos +En direct de nos ateliers +extra tendre +Faite mains +Filiere responsable engagee pour vous +gigot d'agneau +les eleveurs cooperateurs; sans colorant ni conservateur +Montees main, pic inox, qualite superieur +Omega 3 +Porc Francais, fabrication savoie +qualite superieur +recette traditionnelle, sans colorant ni conservateur +Sans additif +sans os +Savoir faire boucher francais +Selectionne par nos Bouchers +Terres d'Eure et Loir +Tous engages : nourris aux cereales francaise sans OGM +Viande de cheval fraiche +100% pur beure +100% vegetal diff --git a/database/seeders/KeywordInitSeeder.php b/database/seeders/KeywordInitSeeder.php new file mode 100644 index 0000000..646a25d --- /dev/null +++ b/database/seeders/KeywordInitSeeder.php @@ -0,0 +1,197 @@ +keyword_cat = \collect(KeywordCategoryEnum::cases())[0]; + $keyword->label = "Sophistication"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0]; + $keyword->label = "Exotisme"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0]; + $keyword->label = "Fun"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1]; + $keyword->label = "Clean label/naturalité"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1]; + $keyword->label = "Végétarien/vegan"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1]; + $keyword->label = "Bien être"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2]; + $keyword->label = "Solidarité"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2]; + $keyword->label = "Écologie"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3]; + $keyword->label = "Emballage individuel"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3]; + $keyword->label = "Facilité de mise en oeuvre"; + $keyword->saveOrFail(); + + /** + * * PRODUCTS + */ + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; + $keyword->label = "Sophistication"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; + $keyword->label = "Exotisme"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; + $keyword->label = "Variété des sens"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; + $keyword->label = "Fun"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5]; + $keyword->label = "Minceur"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5]; + $keyword->label = "Énergie/Bien-être"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5]; + $keyword->label = "Cosmétique"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6]; + $keyword->label = "Manipulation produit"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6]; + $keyword->label = "Gain de temps"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6]; + $keyword->label = "Nomadisme"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7]; + $keyword->label = "Naturalité"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7]; + $keyword->label = "Végétal"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7]; + $keyword->label = "Médical"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8]; + $keyword->label = "Solidarité"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8]; + $keyword->label = "Écologie"; + $keyword->saveOrFail(); + + /** + * * PACKAGING + */ + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9]; + $keyword->label = "Sophistication"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9]; + $keyword->label = "Exotisme"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9]; + $keyword->label = "Fun"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10]; + $keyword->label = "Manipulation produit"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10]; + $keyword->label = "Gain de temps"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10]; + $keyword->label = "Nomadisme"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11]; + $keyword->label = "Décor/forme"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11]; + $keyword->label = "Matériaux"; + $keyword->saveOrFail(); + + $keyword = new Keyword(); + $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[12]; + $keyword->label = "Écologie"; + $keyword->saveOrFail(); + } +} diff --git a/database/seeders/KeywordSeeder.php b/database/seeders/KeywordSeeder.php index 5aabe81..e3c0dd2 100644 --- a/database/seeders/KeywordSeeder.php +++ b/database/seeders/KeywordSeeder.php @@ -16,186 +16,7 @@ class KeywordSeeder extends Seeder */ public function run() { - $id = 0; - /** - * * INGREDIENTS - */ - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0]; - $keyword->label = "Sophistication"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0]; - $keyword->label = "Exotisme"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[0]; - $keyword->label = "Fun"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1]; - $keyword->label = "Clean label/naturalité"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1]; - $keyword->label = "Végétarien/vegan"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[1]; - $keyword->label = "Bien être"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2]; - $keyword->label = "Solidarité"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[2]; - $keyword->label = "Écologie"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3]; - $keyword->label = "Emballage individuel"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[3]; - $keyword->label = "Facilité de mise en oeuvre"; - $keyword->save(); - - /** - * * PRODUCTS - */ - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; - $keyword->label = "Sophistication"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; - $keyword->label = "Exotisme"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; - $keyword->label = "Variété des sens"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[4]; - $keyword->label = "Fun"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5]; - $keyword->label = "Minceur"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5]; - $keyword->label = "Énergie/Bien-être"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[5]; - $keyword->label = "Cosmétique"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6]; - $keyword->label = "Manipulation produit"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6]; - $keyword->label = "Gain de temps"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[6]; - $keyword->label = "Nomadisme"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7]; - $keyword->label = "Naturalité"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7]; - $keyword->label = "Végétal"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[7]; - $keyword->label = "Médical"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8]; - $keyword->label = "Solidarité"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[8]; - $keyword->label = "Écologie"; - $keyword->save(); - - /** - * * PACKAGING - */ - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9]; - $keyword->label = "Sophistication"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9]; - $keyword->label = "Exotisme"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[9]; - $keyword->label = "Fun"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10]; - $keyword->label = "Manipulation produit"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10]; - $keyword->label = "Gain de temps"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[10]; - $keyword->label = "Nomadisme"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11]; - $keyword->label = "Décor/forme"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[11]; - $keyword->label = "Matériaux"; - $keyword->save(); - - $keyword = new Keyword(); - $keyword->keyword_cat = \collect(KeywordCategoryEnum::cases())[12]; - $keyword->label = "Écologie"; - $keyword->save(); - + $this->call(KeywordInitSeeder::class); // Seeder for keywordables table. Keyword::query()->get()->each(function (Keyword $keyword) { Keywordable::factory(mt_rand(1, 3))->make()->each(function (Keywordable $keywordable) use ($keyword) { @@ -226,7 +47,7 @@ class KeywordSeeder extends Seeder $keywordable->keyword()->associate($keyword); $keywordable->keywordable_type = 'App\Models\Packaging'; $keywordable->saveOrFail(); - } + }//end if }); }); } diff --git a/resources/views/documents/product-body.blade.php b/resources/views/documents/product-body.blade.php index b1d5a6f..0a2c620 100644 --- a/resources/views/documents/product-body.blade.php +++ b/resources/views/documents/product-body.blade.php @@ -101,15 +101,15 @@ @endif - @if (isset($product->saw_at)) + @if (isset($product->seen_at)) @endif @if (isset($product->source)) - From d9c132ad25a6ec3c0eb99bb747339d0f1a5cdac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 16 Aug 2023 12:30:43 +0200 Subject: [PATCH 05/27] Fix comp alleg --- database/migrations/2023_08_14_124752_migrate_oci_tables.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/database/migrations/2023_08_14_124752_migrate_oci_tables.php b/database/migrations/2023_08_14_124752_migrate_oci_tables.php index b65e0e9..45d5779 100644 --- a/database/migrations/2023_08_14_124752_migrate_oci_tables.php +++ b/database/migrations/2023_08_14_124752_migrate_oci_tables.php @@ -303,7 +303,10 @@ return new class extends Migration $claim = null; if ($row->COMP_ALLEG) { try { - $claim = $resolveAllegation($row->COMP_ALLEG); + if (!$resolveAllegation($row->COMP_ALLEG)->equals(ClaimEnum::others)) { + throw new \Error("`{$row->COMP_ALLEG}` is not resolved as `other`"); + } + $claim = $row->COMP_ALLEG; } catch (\Exception) { throw new \Error("`{$row->COMP_ALLEG}` not found"); } From 22b598a11a9d5c0df2b590871d5f6288333e3209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 16 Aug 2023 12:37:33 +0200 Subject: [PATCH 06/27] console output fix --- .../migrations/2023_08_14_124752_migrate_oci_tables.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/database/migrations/2023_08_14_124752_migrate_oci_tables.php b/database/migrations/2023_08_14_124752_migrate_oci_tables.php index 45d5779..9be10fc 100644 --- a/database/migrations/2023_08_14_124752_migrate_oci_tables.php +++ b/database/migrations/2023_08_14_124752_migrate_oci_tables.php @@ -58,6 +58,9 @@ return new class extends Migration */ public function up(): void { + $console = new ConsoleOutput(); + $section = $console->section(); + // * SEED KEYWORDS As they are needed on runtime. Schema::disableForeignKeyConstraints(); DB::table('brands')->truncate(); @@ -90,9 +93,10 @@ return new class extends Migration // ! DEV: Add a die here to develop migration and prevent droping temp tables - dd('toto'); + // dd('toto'); $this->dropTempTables(); + $section->clear(); } /** @@ -537,6 +541,7 @@ return new class extends Migration private function createAndFillTempTables(): void { $console = new ConsoleOutput(); + $console->writeln(''); foreach ($this->tableList as $tableName) { $section = $console->section(); $header = $this->fetchOciTableHeader($tableName); From ecaec2823fe240a497d30c4f49704e4584af255d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 6 Sep 2023 09:23:44 +0200 Subject: [PATCH 07/27] color setup --- .vscode/settings.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..49d5339 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,22 @@ +{ + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#90431a", + "activityBar.background": "#90431a", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#1da24b", + "activityBarBadge.foreground": "#e7e7e7", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#90431a", + "statusBar.background": "#652f12", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.hoverBackground": "#90431a", + "statusBarItem.remoteBackground": "#652f12", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#652f12", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#652f1299", + "titleBar.inactiveForeground": "#e7e7e799" + }, + "peacock.remoteColor": "#652f12" +} From f44d9d7849a39dda7f629a5caddcfbfe1c6552c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 6 Sep 2023 09:34:11 +0200 Subject: [PATCH 08/27] upgrade vite config --- vite.config.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/vite.config.ts b/vite.config.ts index b3ac2ab..363c51e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,11 +1,11 @@ +import legacy from "@vitejs/plugin-legacy"; +import vue from "@vitejs/plugin-vue"; +import laravel from "laravel-vite-plugin"; +import path from "path"; import { defineConfig } from "vite"; import checker from "vite-plugin-checker"; import eslint from "vite-plugin-eslint"; -import laravel from "laravel-vite-plugin"; -import legacy from "@vitejs/plugin-legacy"; -import path from "path"; import stylelint from "vite-plugin-stylelint"; -import vue from "@vitejs/plugin-vue"; export default defineConfig({ build: { @@ -41,6 +41,7 @@ export default defineConfig({ }, plugins: [ checker({ + enableBuild: true, vueTsc: { root: ".", tsconfigPath: "tsconfig.json", @@ -74,7 +75,7 @@ export default defineConfig({ "**/vendor/**/*.scss", "**/vendor/**/*.css", ], - lintOnStart: true, + lintOnStart: false, emitError: true, emitWarning: true, emitErrorAsWarning: false, @@ -84,7 +85,7 @@ export default defineConfig({ cache: false, extensions: ["js", "ts"], include: "**/*.ts", - lintOnStart: true, + lintOnStart: false, emitWarning: true, emitError: true, failOnWarning: false, From 61e900ee32cb8bedf766341b6e09a59d42b9314d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 6 Sep 2023 09:40:48 +0200 Subject: [PATCH 09/27] Fixes #27 ProductController fix filter code --- app/Http/Controllers/ProductController.php | 97 +++++++++++----------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php index dacae6b..1e03b0c 100644 --- a/app/Http/Controllers/ProductController.php +++ b/app/Http/Controllers/ProductController.php @@ -12,6 +12,7 @@ use App\Models\Product; use App\Traits\Controllers\HasDocuments; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; @@ -28,7 +29,7 @@ class ProductController extends Controller public function index(Request $request) { /** @var \Illuminate\Database\Eloquent\Builder $brands */ - $brands = Brand::get(); + $brands = Brand::query(); /** @var \Illuminate\Database\Eloquent\Builder $products */ $products = Product::query(); @@ -42,11 +43,9 @@ class ProductController extends Controller /** @var array $printMultiProduct Array of id */ $printMultiProduct = session('printMultiProduct') ?? []; - /** @var \Illuminate\Database\Eloquent\Builder $brands */ - $brands = Brand::get(); + $brands = $brands->get(); - /** @var \Illuminate\Database\Eloquent\Builder $keyword */ - $keywords = Keyword::where('keyword_cat', 'LIKE', 'product_%')->get(); + $keywords = Keyword::query()->where('keyword_cat', 'LIKE', 'product_%')->get(); return view('pages.products.index', [ 'products' => $products, @@ -56,49 +55,6 @@ class ProductController extends Controller 'queryParameters' => $request->all(), ]); } - /** - * Show the form for creating a new resource. - * - * @param \Illuminate\Database\Eloquent\Builder $products - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Contracts\View\View - * @phpcs:disable Generic.Metrics.NestingLevel.TooHigh - */ - private function requestFilters(Builder $products, Request $request): \Illuminate\Database\Eloquent\Builder - { - //phpcs:enable - foreach ($request->all() as $key => $queryParameter) { - if (request()->sort_col === null && request()->sort_way === null && request()->rst === null) { - $products->when( - isset($queryParameter) && $queryParameter != 0, - function ($q) use ($queryParameter, $key) { - if ($key === "name") { - $q->where($key, 'LIKE', '%' . $queryParameter . '%'); - } elseif ($key === "claims" || $key === "origins") { - foreach ($queryParameter as $item) { - $q->where($key, 'LIKE', '%{"id":"' . $item['value'] . '"}%'); - } - } elseif ($key === "keywords") { - foreach ($queryParameter as $keyword) { - $q->whereHas('productKeywords', function ($q) use ($keyword, $key) { - $q->where($key . '.id', intval($keyword['value'])); - }); - } - } elseif ($key === "date_before" || $key === "date_after") { - if ($key === "date_before") { - $q->whereDate('created_at', '>=', $queryParameter); - } else { - $q->whereDate('created_at', '<=', $queryParameter); - } - } else { - $q->where($key, $queryParameter); - } //end if - } - ); - } //end if - } //end foreach - return $products; - } /** * Show the form for creating a new resource. @@ -427,4 +383,49 @@ class ProductController extends Controller { \dispatch(new ExportProduct($request->id))->onConnection('database'); } + + /** + * Show the form for creating a new resource. + * + * @param \Illuminate\Database\Eloquent\Builder $products + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Contracts\View\View + * @phpcs:disable Generic.Metrics.NestingLevel.TooHigh + */ + private function requestFilters(Builder $products, Request $request): \Illuminate\Database\Eloquent\Builder + { + //phpcs:enable + $queryFetch = ['name', 'claims', 'origins', 'keywords', 'date_before', 'date_after']; + foreach (Arr::only($request->all(), $queryFetch) as $key => $queryParameter) { + if ($request->sort_col === null && $request->sort_way === null && $request->rst === null) { + $products->when( + isset($queryParameter) && $queryParameter != 0, + function ($q) use ($queryParameter, $key) { + if ($key === "name") { + $q->where($key, 'LIKE', '%' . $queryParameter . '%'); + } elseif ($key === "claims" || $key === "origins") { + foreach ($queryParameter as $item) { + $q->where($key, 'LIKE', '%{"id":"' . $item['value'] . '"}%'); + } + } elseif ($key === "keywords") { + foreach ($queryParameter as $keyword) { + $q->whereHas('productKeywords', function ($q) use ($keyword, $key) { + $q->where($key . '.id', intval($keyword['value'])); + }); + } + } elseif ($key === "date_before" || $key === "date_after") { + if ($key === "date_before") { + $q->whereDate('created_at', '>=', $queryParameter); + } else { + $q->whereDate('created_at', '<=', $queryParameter); + } + } else { + $q->where($key, $queryParameter); + } //end if + } + ); + } //end if + } //end foreach + return $products; + } } From c0bce88e75f64b2aa23eee817d44bea5432ac51c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 6 Sep 2023 09:53:52 +0200 Subject: [PATCH 10/27] Overhaul pagination --- .../Pagination/ItemsPerPaginationEnum.php | 16 ++ app/Http/Controllers/Controller.php | 27 ++++ app/Http/Controllers/ProductController.php | 2 +- app/Lib/Helpers/ToolboxHelper.php | 20 +++ resources/lang/fr/crud.php | 83 +++++++++++ resources/sass/base/_common.scss | 5 + resources/ts/utilities/fontawesome.ts | 12 ++ resources/views/modules/pagination.blade.php | 140 +++++++++++------- 8 files changed, 253 insertions(+), 52 deletions(-) create mode 100644 app/Enums/Pagination/ItemsPerPaginationEnum.php create mode 100644 resources/lang/fr/crud.php diff --git a/app/Enums/Pagination/ItemsPerPaginationEnum.php b/app/Enums/Pagination/ItemsPerPaginationEnum.php new file mode 100644 index 0000000..b1682f8 --- /dev/null +++ b/app/Enums/Pagination/ItemsPerPaginationEnum.php @@ -0,0 +1,16 @@ +route()->getName()); + $cacheKey = "pagination.{$currentRoutePath}"; + $pagination = $pagination ?? ItemsPerPaginationEnum::twelve->value; + $pagination = ToolboxHelper::getValidatedPaginate( + Cache::get($cacheKey, $pagination) + ); + if (Cache::get($cacheKey) !== $pagination) { + Cache::put($cacheKey, $pagination); + } + $query = $query->paginate($pagination); + return $query; + } } diff --git a/app/Http/Controllers/ProductController.php b/app/Http/Controllers/ProductController.php index 1e03b0c..9c731ad 100644 --- a/app/Http/Controllers/ProductController.php +++ b/app/Http/Controllers/ProductController.php @@ -38,7 +38,7 @@ class ProductController extends Controller $this->sortQuery($products); - $products = $products->paginate(10); + $products = $this->paginate($products); /** @var array $printMultiProduct Array of id */ $printMultiProduct = session('printMultiProduct') ?? []; diff --git a/app/Lib/Helpers/ToolboxHelper.php b/app/Lib/Helpers/ToolboxHelper.php index e82533e..e280f90 100644 --- a/app/Lib/Helpers/ToolboxHelper.php +++ b/app/Lib/Helpers/ToolboxHelper.php @@ -2,6 +2,7 @@ namespace App\Lib\Helpers; +use App\Enums\Pagination\ItemsPerPaginationEnum; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; @@ -9,6 +10,7 @@ use Illuminate\Support\Facades\Session; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; use Illuminate\Validation\Rule; +use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\ValidationException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -288,4 +290,22 @@ class ToolboxHelper { return (array_diff($array1, $array2) == [] && array_diff($array2, $array1) == []); } + + /** + * Get validated pagination from request + * + * @param integer $default + * @return integer + */ + public static function getValidatedPaginate(int $default): int + { + try { + return \intval(Validator::make( + ['pagination' => \request()->get('pagination')], + ['pagination' => new Enum(ItemsPerPaginationEnum::class)] + )->validated()['pagination']); + } catch (ValidationException $e) { + return $default; + } + } } diff --git a/resources/lang/fr/crud.php b/resources/lang/fr/crud.php new file mode 100644 index 0000000..3465c18 --- /dev/null +++ b/resources/lang/fr/crud.php @@ -0,0 +1,83 @@ + [ + 'list' => 'Liste', + 'view' => 'Voir', + 'create' => 'Créer', + 'duplicate' => 'Dupliquer', + 'update' => 'Éditer', + 'delete' => 'Supprimer', + 'save' => 'Sauvegarder', + 'edit' => 'Mettre à jour', + ], + + 'helpers' => [ + 'list' => 'Liste', + 'list_all' => 'Lister tous les :model', + 'create_model' => 'Créer :model', + 'view_model' => 'Voir :model', + 'update_model' => 'Éditer :model', + 'duplicate_model' => 'Dupliquer :model', + 'delete_model' => 'Supprimer :model', + 'save_model' => 'Sauvegarder :model', + 'edit_model' => 'Mettre à jour :model', + 'no_results' => 'Aucun résultat pour :model', + 'publish' => 'Publié', + 'unpublish' => 'Dépublié', + ], + + 'libs' => [ + 'toolbox' => [ + 'assertFieldIsUnique' => 'La :valeur du champ :attribut est déjà utilisée', + 'assertNoInternalBoUrls' => ':attribute : Il est interdit de coller l\'uri de back-office dans ce champ', + 'assertNoInternalUrls' => ':attribute : Il est interdit de coller une url de :appname dans ce champ.', + ] + ], + + 'messages' => [ + 'has_been_created' => ':model a été créé', + 'cannot_be_created' => ':model ne peut être créé', + 'has_been_updated' => ':model a été mis à jour', + 'cannot_be_updated' => ':model ne peut être mis à jour', + 'has_been_deleted' => ':model a été supprimé', + 'cannot_be_deleted' => ':model ne peut être supprimé', + 'order_changed' => 'L\'ordre a été changé !', + 'order_not_changed' => 'L\'ordre n\'a pas été changé !', + 'publish_status_saved' => 'Changement de l\'état de publication réussit !', + 'right' => 'Vous n\'avez pas les droits pour accéder à cette partie !' + ], + + 'minicrud' => [ + 'visibility_label' => 'Visibilité', + 'visibility_helper' => 'Vérifier qu\'il est visible.', + 'enum_select' => 'Sélectionnez :enum dans la liste.', + 'associated_enum' => 'Associé :enum', + 'dimensions_must_be' => 'Doit être :widthpx (largeur) par :heightpx (hauteur).', + 'picture_title_helper' => 'Utile pour les utilisateurs, s\'affiche en passant la souris sur l\'image', + 'picture_title_label' => 'Attribut du titre de l\'image', + 'picture_alt_helper' => 'Doit décrire votre image pour les moteurs de recherche et les aveugles (5 à 6 mots)', + 'picture_alt_label' => 'Attribut Alt de l\'image', + 'text_warning' => 'Relisez votre contenu avant de le publier', + ], + + 'pagination' => [ + "previous" => "Page précédente", + "next" => "Page suivante", + "specific_page" => "Accéder à la page n°:id", + 'paginate_list' => 'Nombre d\'éléments par page', + ], + + 'other' => [ + 'up' => 'Modifier l\'ordre d\'apparition vers le haut', + 'down' => 'Modifier l\'ordre d\'apparition vers le bas', + 'no_model_found' => 'Aucun :model trouvés', + 'publish' => 'Publié', + 'unpublish' => 'Dépublié', + 'sort_ascending' => 'Trier :name dans l\'ordre croissant', + 'sort_descending' => 'Trier :name dans l\'ordre décroissant', + 'sort_delete' => 'Supprimer le tri', + 'sort_arrow' => 'Ici, les flèches sont utilisées pour trier et non pour filtrer', + 'right' => 'Vous n\'avez pas les droits', + ] +]; diff --git a/resources/sass/base/_common.scss b/resources/sass/base/_common.scss index a52b4d5..479a389 100644 --- a/resources/sass/base/_common.scss +++ b/resources/sass/base/_common.scss @@ -15,6 +15,11 @@ white-space: nowrap; } +.btn-md { + min-width: 38px; + height: 38px; +} + // Print fiche commerciale @media print { #print-id-product { diff --git a/resources/ts/utilities/fontawesome.ts b/resources/ts/utilities/fontawesome.ts index fbcd640..e148221 100644 --- a/resources/ts/utilities/fontawesome.ts +++ b/resources/ts/utilities/fontawesome.ts @@ -1,5 +1,6 @@ import { config, dom, library } from "~/@fortawesome/fontawesome-svg-core"; import { + faAnglesRight, faArrowAltCircleDown, faArrowAltCircleUp, faArrowDown, @@ -12,6 +13,9 @@ import { faArrowsAltV, faBackspace, faCheck, + faChevronLeft, + faChevronRight, + faCircleNotch, faCopy, faCrop, faDownload, @@ -20,8 +24,10 @@ import { faFile, faFilePdf, faFolderOpen, + faHeading, faImage, faLink, + faListUl, faMinus, faPencil, faPlus, @@ -79,5 +85,11 @@ library.add(faPrint); library.add(faImage); library.add(faDownload); library.add(faFilePdf); +library.add(faListUl); +library.add(faHeading); +library.add(faAnglesRight); +library.add(faChevronLeft); +library.add(faChevronRight); +library.add(faCircleNotch); dom.watch(); diff --git a/resources/views/modules/pagination.blade.php b/resources/views/modules/pagination.blade.php index eca3d1a..c86ebd1 100644 --- a/resources/views/modules/pagination.blade.php +++ b/resources/views/modules/pagination.blade.php @@ -1,60 +1,98 @@ -@if ($paginator->hasPages()) - From fe3eb7a955c8ba5f533044d35337477d5fc848be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 6 Sep 2023 09:55:21 +0200 Subject: [PATCH 11/27] laravel lang install --- composer.json | 1 + composer.lock | 869 +++++++++++++++++++++++++++- resources/lang/fr.json | 39 ++ resources/lang/fr/auth.php | 9 + resources/lang/fr/http-statuses.php | 84 +++ resources/lang/fr/pagination.php | 8 + resources/lang/fr/passwords.php | 11 + resources/lang/fr/validation.php | 124 +++- 8 files changed, 1130 insertions(+), 15 deletions(-) create mode 100644 resources/lang/fr.json create mode 100644 resources/lang/fr/auth.php create mode 100644 resources/lang/fr/http-statuses.php create mode 100644 resources/lang/fr/pagination.php create mode 100644 resources/lang/fr/passwords.php diff --git a/composer.json b/composer.json index 8c2853e..4137d78 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "fakerphp/faker": "^1.9.1", "kitloong/laravel-migrations-generator": "^6.8", "krlove/eloquent-model-generator": "dev-l10-compatibility", + "laravel-lang/common": "^4.0", "laravel/pint": "^1.0", "laravel/sail": "^1.18", "mockery/mockery": "^1.4.4", diff --git a/composer.lock b/composer.lock index 839df8e..ce07a81 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ccb7cd412f6693c7769bc05503e32610", + "content-hash": "72bd46bbe723badbb255fbe8130d9305", "packages": [ { "name": "brick/math", @@ -5876,6 +5876,133 @@ } ], "packages-dev": [ + { + "name": "archtechx/enums", + "version": "v0.3.2", + "source": { + "type": "git", + "url": "https://github.com/archtechx/enums.git", + "reference": "475f45e682b0771253707f9403b704759a08da5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/archtechx/enums/zipball/475f45e682b0771253707f9403b704759a08da5f", + "reference": "475f45e682b0771253707f9403b704759a08da5f", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "nunomaduro/larastan": "^1.0|^2.4", + "orchestra/testbench": "^6.9|^7.0|^8.0", + "pestphp/pest": "^1.2|^2.0", + "pestphp/pest-plugin-laravel": "^1.0|^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ArchTech\\Enums\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Samuel Štancl", + "email": "samuel@archte.ch" + } + ], + "description": "Helpers for making PHP enums more lovable.", + "support": { + "issues": "https://github.com/archtechx/enums/issues", + "source": "https://github.com/archtechx/enums/tree/v0.3.2" + }, + "time": "2023-02-15T13:05:41+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-31T09:50:34+00:00" + }, { "name": "dealerdirect/phpcodesniffer-composer-installer", "version": "v1.0.0", @@ -6293,6 +6420,262 @@ ], "time": "2022-10-12T20:59:15+00:00" }, + { + "name": "dragon-code/contracts", + "version": "v2.20.0", + "source": { + "type": "git", + "url": "https://github.com/TheDragonCode/contracts.git", + "reference": "410d35bec404f465a126bb1e672f3abe1150f5d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TheDragonCode/contracts/zipball/410d35bec404f465a126bb1e672f3abe1150f5d7", + "reference": "410d35bec404f465a126bb1e672f3abe1150f5d7", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-message": "^1.0.1 || ^2.0", + "symfony/http-kernel": "^4.0 || ^5.0 || ^6.0", + "symfony/polyfill-php80": "^1.23" + }, + "conflict": { + "andrey-helldar/contracts": "*" + }, + "require-dev": { + "illuminate/database": "^10.0", + "phpdocumentor/reflection-docblock": "^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "DragonCode\\Contracts\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://github.com/andrey-helldar" + } + ], + "description": "A set of contracts for any project", + "keywords": [ + "contracts", + "interfaces" + ], + "support": { + "source": "https://github.com/TheDragonCode/contracts" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://github.com/sponsors/TheDragonCode", + "type": "github" + }, + { + "url": "https://opencollective.com/dragon-code", + "type": "open_collective" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2023-06-02T11:06:31+00:00" + }, + { + "name": "dragon-code/pretty-array", + "version": "v4.1.0", + "source": { + "type": "git", + "url": "https://github.com/TheDragonCode/pretty-array.git", + "reference": "6c84e2454491b414efbd37985c322712cdf9012f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TheDragonCode/pretty-array/zipball/6c84e2454491b414efbd37985c322712cdf9012f", + "reference": "6c84e2454491b414efbd37985c322712cdf9012f", + "shasum": "" + }, + "require": { + "dragon-code/contracts": "^2.20", + "dragon-code/support": "^6.11.2", + "ext-dom": "*", + "ext-mbstring": "*", + "php": "^8.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^10.2" + }, + "suggest": { + "symfony/thanks": "Give thanks (in the form of a GitHub) to your fellow PHP package maintainers" + }, + "type": "library", + "autoload": { + "psr-4": { + "DragonCode\\PrettyArray\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://github.com/andrey-helldar" + } + ], + "description": "Simple conversion of an array to a pretty view", + "keywords": [ + "andrey helldar", + "array", + "dragon", + "dragon code", + "pretty", + "pretty array" + ], + "support": { + "issues": "https://github.com/TheDragonCode/pretty-array/issues", + "source": "https://github.com/TheDragonCode/pretty-array" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://github.com/sponsors/TheDragonCode", + "type": "github" + }, + { + "url": "https://opencollective.com/dragon-code", + "type": "open_collective" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2023-06-02T11:37:44+00:00" + }, + { + "name": "dragon-code/support", + "version": "v6.11.3", + "source": { + "type": "git", + "url": "https://github.com/TheDragonCode/support.git", + "reference": "3dbf1715e83e216ae1fea31eecc022829dc864df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TheDragonCode/support/zipball/3dbf1715e83e216ae1fea31eecc022829dc864df", + "reference": "3dbf1715e83e216ae1fea31eecc022829dc864df", + "shasum": "" + }, + "require": { + "dragon-code/contracts": "^2.19.1", + "ext-bcmath": "*", + "ext-ctype": "*", + "ext-dom": "*", + "ext-json": "*", + "ext-mbstring": "*", + "php": "^8.0", + "psr/http-message": "^1.0.1 || ^2.0", + "symfony/polyfill-php81": "^1.25", + "voku/portable-ascii": "^1.4.8 || ^2.0.1" + }, + "conflict": { + "andrey-helldar/support": "*" + }, + "require-dev": { + "illuminate/contracts": "^9.0 || ^10.0", + "phpunit/phpunit": "^9.6", + "symfony/var-dumper": "^6.0" + }, + "suggest": { + "dragon-code/laravel-support": "Various helper files for the Laravel and Lumen frameworks", + "symfony/thanks": "Give thanks (in the form of a GitHub) to your fellow PHP package maintainers" + }, + "type": "library", + "extra": { + "dragon-code": { + "docs-generator": { + "preview": { + "brand": "php", + "vendor": "The Dragon Code" + } + } + } + }, + "autoload": { + "psr-4": { + "DragonCode\\Support\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://github.com/andrey-helldar" + } + ], + "description": "Support package is a collection of helpers and tools for any project.", + "keywords": [ + "dragon", + "dragon-code", + "framework", + "helper", + "helpers", + "laravel", + "php", + "support", + "symfony", + "yii", + "yii2" + ], + "support": { + "issues": "https://github.com/TheDragonCode/support/issues", + "source": "https://github.com/TheDragonCode/support" + }, + "funding": [ + { + "url": "https://boosty.to/dragon-code", + "type": "boosty" + }, + { + "url": "https://www.donationalerts.com/r/dragon_code", + "type": "donationalerts" + }, + { + "url": "https://github.com/sponsors/TheDragonCode", + "type": "github" + }, + { + "url": "https://opencollective.com/dragon-code", + "type": "open_collective" + }, + { + "url": "https://yoomoney.ru/to/410012608840929", + "type": "yoomoney" + } + ], + "time": "2023-08-03T09:43:52+00:00" + }, { "name": "fakerphp/faker", "version": "v1.21.0", @@ -6642,6 +7025,411 @@ }, "time": "2023-01-30T20:15:06+00:00" }, + { + "name": "laravel-lang/attributes", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/attributes.git", + "reference": "d8526492361502e88b7f6cd1e7f3952b6becc388" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/attributes/zipball/d8526492361502e88b7f6cd1e7f3952b6becc388", + "reference": "d8526492361502e88b7f6cd1e7f3952b6becc388", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^14.0", + "php": "^8.1" + }, + "require-dev": { + "laravel-lang/status-generator": "^1.19", + "phpunit/phpunit": "^10.0", + "symfony/var-dumper": "^6.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Attributes\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Attributes\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "List of 78 languages for form field names", + "keywords": [ + "attributes", + "fields", + "form", + "lang", + "languages", + "laravel", + "messages", + "translations", + "validation" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/attributes/issues", + "source": "https://github.com/Laravel-Lang/attributes/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://opencollective.com/laravel-lang", + "type": "open_collective" + } + ], + "time": "2023-08-09T09:01:13+00:00" + }, + { + "name": "laravel-lang/common", + "version": "v4.0.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/common.git", + "reference": "31744c7f64610990a77c7705e590e04c1fca372d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/common/zipball/31744c7f64610990a77c7705e590e04c1fca372d", + "reference": "31744c7f64610990a77c7705e590e04c1fca372d", + "shasum": "" + }, + "require": { + "laravel-lang/attributes": "^2.3.3", + "laravel-lang/http-statuses": "^3.3.1", + "laravel-lang/lang": "^13.0", + "laravel-lang/publisher": "^14.6.4", + "php": "^8.1" + }, + "require-dev": { + "orchestra/testbench": "^8.5 || ^9.0", + "phpunit/phpunit": "^10.0", + "symfony/var-dumper": "^6.3" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + }, + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro", + "homepage": "https://github.com/andrey-helldar" + } + ], + "description": "Easily connect the necessary language packs to the application", + "keywords": [ + "Laravel-lang", + "attribute", + "attributes", + "breeze", + "cashier", + "fortify", + "framework", + "http", + "http-status", + "http-status-code", + "i18n", + "jetstream", + "lang", + "language", + "languages", + "laravel", + "locale", + "locales", + "nova", + "publisher", + "spark", + "translation", + "translations", + "ui" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/common/issues", + "source": "https://github.com/Laravel-Lang/common" + }, + "funding": [ + { + "url": "https://opencollective.com/laravel-lang", + "type": "open_collective" + } + ], + "time": "2023-08-09T09:24:50+00:00" + }, + { + "name": "laravel-lang/http-statuses", + "version": "v3.4.4", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/http-statuses.git", + "reference": "a040c261c10f68980e865c9900b76c32fd6f0275" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/http-statuses/zipball/a040c261c10f68980e865c9900b76c32fd6f0275", + "reference": "a040c261c10f68980e865c9900b76c32fd6f0275", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^14.1", + "php": "^8.1" + }, + "conflict": { + "laravel-lang/publisher": "<14.0" + }, + "require-dev": { + "laravel-lang/status-generator": "^1.19", + "phpunit/phpunit": "^10.0", + "symfony/var-dumper": "^6.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\HttpStatuses\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\HttpStatuses\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "List of 78 languages for HTTP statuses", + "keywords": [ + "http", + "lang", + "languages", + "laravel", + "messages", + "status", + "translations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/http-statuses/issues", + "source": "https://github.com/Laravel-Lang/http-statuses/tree/v3.4.4" + }, + "funding": [ + { + "url": "https://opencollective.com/laravel-lang", + "type": "open_collective" + } + ], + "time": "2023-08-24T01:17:58+00:00" + }, + { + "name": "laravel-lang/lang", + "version": "13.2.2", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/lang.git", + "reference": "bbef7ad051664626373ab68628676f140b2ec733" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/lang/zipball/bbef7ad051664626373ab68628676f140b2ec733", + "reference": "bbef7ad051664626373ab68628676f140b2ec733", + "shasum": "" + }, + "require": { + "ext-json": "*", + "laravel-lang/publisher": "^14.0", + "php": "^8.1" + }, + "require-dev": { + "laravel-lang/status-generator": "^1.19", + "phpunit/phpunit": "^10.0", + "symfony/var-dumper": "^6.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Lang\\ServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "LaravelLang\\Lang\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "List of 78 languages for Laravel Framework, Laravel Jetstream, Laravel Fortify, Laravel Breeze, Laravel Cashier, Laravel Nova, Laravel Spark and Laravel UI", + "keywords": [ + "lang", + "languages", + "laravel", + "lpm" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/lang/issues", + "source": "https://github.com/Laravel-Lang/lang" + }, + "funding": [ + { + "url": "https://opencollective.com/laravel-lang", + "type": "open_collective" + } + ], + "time": "2023-08-26T02:07:55+00:00" + }, + { + "name": "laravel-lang/publisher", + "version": "v14.7.0", + "source": { + "type": "git", + "url": "https://github.com/Laravel-Lang/publisher.git", + "reference": "4e6fa2d8c446282b832ffb16b9b3c33aa4296a5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Laravel-Lang/publisher/zipball/4e6fa2d8c446282b832ffb16b9b3c33aa4296a5b", + "reference": "4e6fa2d8c446282b832ffb16b9b3c33aa4296a5b", + "shasum": "" + }, + "require": { + "archtechx/enums": "^0.3", + "composer/semver": "^3.3", + "dragon-code/pretty-array": "^4.0", + "dragon-code/support": "^6.3", + "ext-json": "*", + "illuminate/console": "^8.79 || ^9.18 || ^10.0 || ^11.0", + "illuminate/support": "^8.79 || ^9.18 || ^10.0 || ^11.0", + "league/commonmark": "^2.3", + "league/config": "^1.2", + "php": "^8.1" + }, + "conflict": { + "laravel-lang/attributes": "<2.0", + "laravel-lang/http-statuses": "<3.0", + "laravel-lang/lang": "<11.0" + }, + "require-dev": { + "laravel-lang/json-fallback-hotfix": "^1.0", + "orchestra/testbench": "^6.25 || ^7.22 || ^8.0 || ^9.0", + "phpunit/phpunit": "^9.6 || ^10.0", + "symfony/var-dumper": "^5.0 || ^6.0" + }, + "suggest": { + "laravel-lang/attributes": "List of 78 languages for form field names", + "laravel-lang/http-statuses": "List of 78 languages for HTTP statuses", + "laravel-lang/lang": "List of 78 languages for Laravel Framework, Jetstream, Fortify, Breeze, Cashier, Nova, Spark and UI." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "LaravelLang\\Publisher\\ServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "helper.php" + ], + "psr-4": { + "LaravelLang\\Publisher\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrey Helldar", + "email": "helldar@dragon-code.pro" + }, + { + "name": "Laravel-Lang Team", + "homepage": "https://github.com/Laravel-Lang" + } + ], + "description": "Publisher lang files for the Laravel and Lumen Frameworks, Jetstream, Fortify, Cashier, Spark and Nova from Laravel-Lang/lang", + "keywords": [ + "breeze", + "cashier", + "fortify", + "framework", + "i18n", + "jetstream", + "lang", + "languages", + "laravel", + "locale", + "locales", + "localization", + "lpm", + "lumen", + "nova", + "publisher", + "spark", + "trans", + "translations", + "validations" + ], + "support": { + "issues": "https://github.com/Laravel-Lang/publisher/issues", + "source": "https://github.com/Laravel-Lang/publisher" + }, + "funding": [ + { + "url": "https://opencollective.com/laravel-lang", + "type": "open_collective" + } + ], + "time": "2023-08-09T09:09:57+00:00" + }, { "name": "laravel/pint", "version": "v1.10.0", @@ -8968,6 +9756,85 @@ }, "time": "2023-02-22T23:07:41+00:00" }, + { + "name": "symfony/polyfill-php81", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, { "name": "symfony/yaml", "version": "v6.2.10", diff --git a/resources/lang/fr.json b/resources/lang/fr.json new file mode 100644 index 0000000..db9f718 --- /dev/null +++ b/resources/lang/fr.json @@ -0,0 +1,39 @@ +{ + "(and :count more error)": "(et :count erreur en plus)", + "(and :count more errors)": "(et :count erreurs en plus)", + "All rights reserved.": "Tous droits réservés.", + "Forbidden": "Interdit", + "Go to page :page": "Aller à la page :page", + "Hello!": "Bonjour !", + "If you did not create an account, no further action is required.": "Si vous n'avez pas créé de compte, vous pouvez ignorer ce message.", + "If you did not request a password reset, no further action is required.": "Si vous n'avez pas demandé de réinitialisation de mot de passe, vous pouvez ignorer ce message.", + "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "Si vous avez des difficultés à cliquer sur le bouton \":actionText\", copiez et collez l'URL ci-dessous\ndans votre navigateur Web :", + "Invalid JSON was returned from the route.": "Un JSON non valide a été renvoyé par la route.", + "Login": "Connexion", + "Logout": "Déconnexion", + "Not Found": "Non trouvé", + "of": "de", + "Page Expired": "Page expirée", + "Pagination Navigation": "Pagination", + "Payment Required": "Paiement requis", + "Please click the button below to verify your email address.": "Veuillez cliquer sur le bouton ci-dessous pour vérifier votre adresse e-mail :", + "Regards": "Cordialement", + "Register": "Inscription", + "Reset Password": "Réinitialisation du mot de passe", + "Reset Password Notification": "Notification de réinitialisation du mot de passe", + "results": "résultats", + "Server Error": "Erreur serveur", + "Service Unavailable": "Service indisponible", + "Showing": "Montrant", + "The given data was invalid.": "La donnée renseignée est incorrecte.", + "The response is not a streamed response.": "La réponse n'est pas une réponse diffusée.", + "The response is not a view.": "La réponse n'est pas une vue.", + "This password reset link will expire in :count minutes.": "Ce lien de réinitialisation du mot de passe expirera dans :count minutes.", + "to": "à", + "Toggle navigation": "Afficher / masquer le menu de navigation", + "Too Many Requests": "Trop de requêtes", + "Unauthorized": "Non autorisé", + "Verify Email Address": "Vérifier l'adresse e-mail", + "Whoops!": "Oups !", + "You are receiving this email because we received a password reset request for your account.": "Vous recevez cet e-mail car nous avons reçu une demande de réinitialisation de mot de passe pour votre compte." +} \ No newline at end of file diff --git a/resources/lang/fr/auth.php b/resources/lang/fr/auth.php new file mode 100644 index 0000000..a22cd3f --- /dev/null +++ b/resources/lang/fr/auth.php @@ -0,0 +1,9 @@ + 'Ces identifiants ne correspondent pas à nos enregistrements.', + 'password' => 'Le mot de passe est incorrect', + 'throttle' => 'Tentatives de connexion trop nombreuses. Veuillez essayer de nouveau dans :seconds secondes.', +]; diff --git a/resources/lang/fr/http-statuses.php b/resources/lang/fr/http-statuses.php new file mode 100644 index 0000000..af44920 --- /dev/null +++ b/resources/lang/fr/http-statuses.php @@ -0,0 +1,84 @@ + 'Erreur inconnue', + '100' => 'Continuer', + '101' => 'Protocoles de commutation', + '102' => 'En traitement', + '200' => 'D\'accord', + '201' => 'Créé', + '202' => 'Accepté', + '203' => 'Informations non autorisées', + '204' => 'Pas content', + '205' => 'Réinitialiser le contenu', + '206' => 'Contenu partiel', + '207' => 'Multi-statut', + '208' => 'Déjà rapporté', + '226' => 'J\'ai l\'habitude', + '300' => 'Choix multiples', + '301' => 'Déplacé de façon permanente', + '302' => 'A trouvé', + '303' => 'Voir autre', + '304' => 'Non modifié', + '305' => 'Utiliser un proxy', + '307' => 'Redirection temporaire', + '308' => 'Redirection permanente', + '400' => 'Mauvaise Demande', + '401' => 'Non autorisé', + '402' => 'Paiement Requis', + '403' => 'Interdit', + '404' => 'Page non trouvée', + '405' => 'Méthode Non Autorisée', + '406' => 'Pas acceptable', + '407' => 'Authentification proxy requise', + '408' => 'Délai d\'expiration de la demande', + '409' => 'Conflit', + '410' => 'Disparu', + '411' => 'Longueur requise', + '412' => 'La précondition a échoué', + '413' => 'Charge utile trop grande', + '414' => 'URI trop long', + '415' => 'Type de support non supporté', + '416' => 'Plage non satisfaisante', + '417' => 'Échec de l\'attente', + '418' => 'Je suis une théière', + '419' => 'La session a expiré', + '421' => 'Demande mal dirigée', + '422' => 'Entité non traitable', + '423' => 'Fermé à clef', + '424' => 'Dépendance échouée', + '425' => 'Trop tôt', + '426' => 'Mise à niveau requise', + '428' => 'Condition préalable requise', + '429' => 'Trop de demandes', + '431' => 'Demander des champs d\'en-tête trop grands', + '444' => 'Connexion fermée sans réponse', + '449' => 'Réessayer avec', + '451' => 'Indisponible pour des raisons légales', + '499' => 'Demande fermée du client', + '500' => 'Erreur Interne du Serveur', + '501' => 'Pas mis en œuvre', + '502' => 'Mauvaise passerelle', + '503' => 'Mode de Maintenance', + '504' => 'Délai d\'attente de la passerelle', + '505' => 'Version HTTP non prise en charge', + '506' => 'La variante négocie également', + '507' => 'Espace insuffisant', + '508' => 'Boucle détectée', + '509' => 'Limite de bande passante dépassée', + '510' => 'Non prolongé', + '511' => 'Authentification réseau requise', + '520' => 'Erreur inconnue', + '521' => 'Le serveur Web est en panne', + '522' => 'La connexion a expiré', + '523' => 'L\'origine est inaccessible', + '524' => 'Un dépassement de délai s\'est produit', + '525' => 'Échec de la prise de contact SSL', + '526' => 'Certificat SSL invalide', + '527' => 'Erreur de canon ferroviaire', + '598' => 'Erreur de délai de lecture réseau', + '599' => 'Erreur de délai de connexion réseau', + 'unknownError' => 'Erreur inconnue', +]; diff --git a/resources/lang/fr/pagination.php b/resources/lang/fr/pagination.php new file mode 100644 index 0000000..225391e --- /dev/null +++ b/resources/lang/fr/pagination.php @@ -0,0 +1,8 @@ + 'Suivant »', + 'previous' => '« Précédent', +]; diff --git a/resources/lang/fr/passwords.php b/resources/lang/fr/passwords.php new file mode 100644 index 0000000..75ae148 --- /dev/null +++ b/resources/lang/fr/passwords.php @@ -0,0 +1,11 @@ + 'Votre mot de passe a été réinitialisé !', + 'sent' => 'Nous vous avons envoyé par email le lien de réinitialisation du mot de passe !', + 'throttled' => 'Veuillez patienter avant de réessayer.', + 'token' => 'Ce jeton de réinitialisation du mot de passe n\'est pas valide.', + 'user' => 'Aucun utilisateur n\'a été trouvé avec cette adresse email.', +]; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index f8cbe75..4fbae2a 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -1,5 +1,7 @@ 'Le champ :attribute doit être accepté.', 'accepted_if' => 'Le champ :attribute doit être accepté quand :other a la valeur :value.', @@ -10,6 +12,7 @@ return [ 'alpha_dash' => 'Le champ :attribute doit contenir uniquement des lettres, des chiffres et des tirets.', 'alpha_num' => 'Le champ :attribute doit contenir uniquement des chiffres et des lettres.', 'array' => 'Le champ :attribute doit être un tableau.', + 'ascii' => 'Le champ :attribute ne doit contenir que des caractères alphanumériques et des symboles codés sur un octet.', 'before' => 'Le champ :attribute doit être une date antérieure au :date.', 'before_or_equal' => 'Le champ :attribute doit être une date antérieure ou égale au :date.', 'between' => [ @@ -19,11 +22,18 @@ return [ 'string' => 'Le texte :attribute doit contenir entre :min et :max caractères.', ], 'boolean' => 'Le champ :attribute doit être vrai ou faux.', + 'can' => 'Le champ :attribute contient une valeur non autorisée.', + 'color' => ':attribute doit être un code couleur valide (hexadecimal, RGB, RGBA ou un nom de couleur CSS)', + 'color_hex' => ':attribute doit être un code couleur hexadecimal valide', + 'color_name' => ':attribute doit être un nom de couleur CSS valide', + 'color_rgb' => ':attribute doit être un code couleur RGB valide', + 'color_rgba' => ':attribute doit être un code couleur RGBA valide', 'confirmed' => 'Le champ de confirmation :attribute ne correspond pas.', 'current_password' => 'Le mot de passe est incorrect.', 'date' => 'Le champ :attribute n\'est pas une date valide.', 'date_equals' => 'Le champ :attribute doit être une date égale à :date.', 'date_format' => 'Le champ :attribute ne correspond pas au format :format.', + 'decimal' => 'Le champ :attribute doit comporter :decimal décimales.', 'declined' => 'Le champ :attribute doit être décliné.', 'declined_if' => 'Le champ :attribute doit être décliné quand :other a la valeur :value.', 'different' => 'Les champs :attribute et :other doivent être différents.', @@ -31,6 +41,8 @@ return [ 'digits_between' => 'Le champ :attribute doit contenir entre :min et :max chiffres.', 'dimensions' => 'La taille de l\'image :attribute n\'est pas conforme.', 'distinct' => 'Le champ :attribute a une valeur en double.', + 'doesnt_end_with' => 'Le champ :attribute ne doit pas finir avec une des valeurs suivantes : :values.', + 'doesnt_start_with' => 'Le champ :attribute ne doit pas commencer avec une des valeurs suivantes : :values.', 'email' => 'Le champ :attribute doit être une adresse e-mail valide.', 'ends_with' => 'Le champ :attribute doit se terminer par une des valeurs suivantes : :values', 'enum' => 'Le champ :attribute sélectionné est invalide.', @@ -57,6 +69,7 @@ return [ 'ipv4' => 'Le champ :attribute doit être une adresse IPv4 valide.', 'ipv6' => 'Le champ :attribute doit être une adresse IPv6 valide.', 'json' => 'Le champ :attribute doit être un document JSON valide.', + 'lowercase' => 'Le champ :attribute doit être en minuscules.', 'lt' => [ 'array' => 'Le tableau :attribute doit contenir moins de :value éléments.', 'file' => 'La taille du fichier de :attribute doit être inférieure à :value kilo-octets.', @@ -71,24 +84,39 @@ return [ ], 'mac_address' => 'Le champ :attribute doit être une adresse MAC valide.', 'max' => [ - 'array' => 'Le tableau :attribute ne peut contenir plus de :max éléments.', + 'array' => 'Le tableau :attribute ne peut pas contenir plus que :max éléments.', 'file' => 'La taille du fichier de :attribute ne peut pas dépasser :max kilo-octets.', - 'numeric' => 'La valeur de :attribute ne peut être supérieure à :max.', - 'string' => 'Le texte de :attribute ne peut contenir plus de :max caractères.', + 'numeric' => 'La valeur de :attribute ne peut pas être supérieure à :max.', + 'string' => 'Le texte de :attribute ne peut pas contenir plus de :max caractères.', ], + 'max_digits' => 'Le champ :attribute ne doit pas avoir plus de :max chiffres.', 'mimes' => 'Le champ :attribute doit être un fichier de type : :values.', 'mimetypes' => 'Le champ :attribute doit être un fichier de type : :values.', 'min' => [ 'array' => 'Le tableau :attribute doit contenir au moins :min éléments.', - 'file' => 'La taille du fichier de :attribute doit être supérieure à :min kilo-octets.', + 'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :min kilo-octets.', 'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :min.', - 'string' => 'Le texte :attribute doit contenir au moins :min caractères.', + 'string' => 'Le texte de :attribute doit contenir au moins :min caractères.', ], + 'min_digits' => 'Le champ :attribute doit avoir au moins :min chiffres.', + 'missing' => 'Le champ :attribute doit être manquant.', + 'missing_if' => 'Le champ :attribute doit être manquant quand :other a la valeur :value.', + 'missing_unless' => 'Le champ :attribute doit être manquant sauf si :other a la valeur :value.', + 'missing_with' => 'Le champ :attribute doit être manquant quand :values est présent.', + 'missing_with_all' => 'Le champ :attribute doit être manquant quand :values sont présents.', 'multiple_of' => 'La valeur de :attribute doit être un multiple de :value', 'not_in' => 'Le champ :attribute sélectionné n\'est pas valide.', 'not_regex' => 'Le format du champ :attribute n\'est pas valide.', 'numeric' => 'Le champ :attribute doit contenir un nombre.', - 'password' => 'Le mot de passe est incorrect', + 'password' => [ + 'letters' => 'Le champ :attribute doit contenir au moins une lettre.', + 'mixed' => 'Le champ :attribute doit contenir au moins une majuscule et une minuscule.', + 'numbers' => 'Le champ :attribute doit contenir au moins un chiffre.', + 'symbols' => 'Le champ :attribute doit contenir au moins un symbole.', + 'uncompromised' => 'La valeur du champ :attribute est apparue dans une fuite de données. Veuillez choisir une valeur différente.', + ], + 'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide.', + 'postal_code' => 'Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.', 'present' => 'Le champ :attribute doit être présent.', 'prohibited' => 'Le champ :attribute est interdit.', 'prohibited_if' => 'Le champ :attribute est interdit quand :other a la valeur :value.', @@ -98,6 +126,7 @@ return [ 'required' => 'Le champ :attribute est obligatoire.', 'required_array_keys' => 'Le champ :attribute doit contenir des entrées pour : :values.', 'required_if' => 'Le champ :attribute est obligatoire quand la valeur de :other est :value.', + 'required_if_accepted' => 'Le champ :attribute est obligatoire quand le champ :other a été accepté.', 'required_unless' => 'Le champ :attribute est obligatoire sauf si :other est :values.', 'required_with' => 'Le champ :attribute est obligatoire quand :values est présent.', 'required_with_all' => 'Le champ :attribute est obligatoire quand :values sont présents.', @@ -113,16 +142,83 @@ return [ 'starts_with' => 'Le champ :attribute doit commencer avec une des valeurs suivantes : :values', 'string' => 'Le champ :attribute doit être une chaîne de caractères.', 'timezone' => 'Le champ :attribute doit être un fuseau horaire valide.', + 'ulid' => 'Le champ :attribute doit être un ULID valide.', 'unique' => 'La valeur du champ :attribute est déjà utilisée.', 'uploaded' => 'Le fichier du champ :attribute n\'a pu être téléversé.', + 'uppercase' => 'Le champ :attribute doit être en majuscules.', 'url' => 'Le format de l\'URL de :attribute n\'est pas valide.', 'uuid' => 'Le champ :attribute doit être un UUID valide', - 'custom' => [], - 'phone' => 'Le champ :attribute n\'est pas un numéro de téléphone valide.', - 'color' => ':attribute doit être un code couleur valide (hexadecimal, RGB, RGBA ou un nom de couleur CSS)', - 'color_hex' => ':attribute doit être un code couleur hexadecimal valide', - 'color_rgb' => ':attribute doit être un code couleur RGB valide', - 'color_rgba' => ':attribute doit être un code couleur RGBA valide', - 'color_name' => ':attribute doit être un nom de couleur CSS valide', - 'postal_code' => 'Le champ :attribute accepte les codes postaux des pays suivants :countries comme par exemple :examples.' + 'attributes' => [ + 'address' => 'adresse', + 'age' => 'âge', + 'amount' => 'montant', + 'area' => 'zone', + 'available' => 'disponible', + 'birthday' => 'anniversaire', + 'body' => 'corps', + 'city' => 'ville', + 'content' => 'contenu', + 'country' => 'pays', + 'created_at' => 'créé à', + 'creator' => 'créateur', + 'current_password' => 'mot de passe actuel', + 'date' => 'Date', + 'date_of_birth' => 'date de naissance', + 'day' => 'jour', + 'deleted_at' => 'supprimé à', + 'description' => 'la description', + 'district' => 'quartier', + 'duration' => 'durée', + 'email' => 'adresse e-mail', + 'excerpt' => 'extrait', + 'filter' => 'filtre', + 'first_name' => 'prénom', + 'gender' => 'genre', + 'group' => 'groupe', + 'hour' => 'heure', + 'image' => 'image', + 'last_name' => 'nom', + 'lesson' => 'leçon', + 'line_address_1' => 'ligne d\'adresse 1', + 'line_address_2' => 'ligne d\'adresse 2', + 'message' => 'message', + 'middle_name' => 'deuxième prénom', + 'minute' => 'minute', + 'mobile' => 'portable', + 'month' => 'mois', + 'name' => 'nom', + 'national_code' => 'code national', + 'number' => 'numéro', + 'password' => 'mot de passe', + 'password_confirmation' => 'confirmation du mot de passe', + 'phone' => 'téléphone', + 'photo' => 'photo', + 'postal_code' => 'code postal', + 'price' => 'prix', + 'province' => 'région', + 'recaptcha_response_field' => 'champ de réponse recaptcha', + 'remember' => 'se souvenir', + 'restored_at' => 'restauré à', + 'result_text_under_image' => 'texte de résultat sous l\'image', + 'role' => 'rôle', + 'second' => 'seconde', + 'sex' => 'sexe', + 'short_text' => 'texte court', + 'size' => 'taille', + 'state' => 'état', + 'street' => 'rue', + 'student' => 'étudiant', + 'subject' => 'sujet', + 'teacher' => 'professeur', + 'terms' => 'conditions', + 'test_description' => 'description test', + 'test_locale' => 'localisation test', + 'test_name' => 'nom test', + 'text' => 'texte', + 'time' => 'heure', + 'title' => 'titre', + 'updated_at' => 'mis à jour à', + 'username' => 'nom d\'utilisateur', + 'year' => 'année', + ], ]; From 6de828bc313425ec49a9991ff9289a529a7ff6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Wed, 6 Sep 2023 12:50:04 +0200 Subject: [PATCH 12/27] Fix product old file delete --- app/Models/Product.php | 32 +++++++++++-------- app/Traits/Controllers/HasDocuments.php | 12 +++---- app/Traits/Requests/HasDocument.php | 3 +- .../MultipleDocumentsInputComponent.vue | 30 ++++++++++++++--- 4 files changed, 51 insertions(+), 26 deletions(-) diff --git a/app/Models/Product.php b/app/Models/Product.php index 74e0ca3..b493b7b 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -132,7 +132,7 @@ class Product extends Model $model->user_id = 1; }); static::updating(function (self $model) { - self::deleteOldDocuments($model); + self::deleteOldDocuments($model, true); $model->picture = FileStorageHelper::storeFile($model, $model->picture, true); self::updateSource($model); self::updateSpecifiedClaim($model); @@ -209,21 +209,27 @@ class Product extends Model /** * Delete old documents. * - * @param self $model + * @param self $model + * @param boolean $onlyIfUpdated * @return void */ - public static function deleteOldDocuments(self $model) + public static function deleteOldDocuments(self $model, bool $onlyIfUpdated = false) { - if ($model->getOriginal('docs')) { - foreach ($model->getOriginal('docs') as $document) { - $originalPath = \ltrim(\urldecode($document), '/'); - if (\strpos($originalPath, 'storage') !== 0) { - return; - } - $originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath); - if (Storage::disk('public')->exists($originalPath)) { - Storage::disk('public')->delete($originalPath); - } + $original = \collect($model->getOriginal('docs'))->map(fn ($document) => strval($document)); + $new = \collect($model->docs)->map(fn ($document) => strval($document))->flip(); + foreach ($original as $document) { + // * Ignore if document is to keep. + if ($onlyIfUpdated and $new->has($document)) { + continue; + } + + $originalPath = \ltrim(\urldecode($document), '/'); + if (\strpos($originalPath, 'storage') !== 0) { + continue; + } + $originalPath = ToolboxHelper::mbReplace('storage/', '', $originalPath); + if (Storage::disk('public')->exists($originalPath)) { + Storage::disk('public')->delete($originalPath); } } } diff --git a/app/Traits/Controllers/HasDocuments.php b/app/Traits/Controllers/HasDocuments.php index d187067..bf21a2b 100644 --- a/app/Traits/Controllers/HasDocuments.php +++ b/app/Traits/Controllers/HasDocuments.php @@ -17,12 +17,10 @@ trait HasDocuments */ private function storeDocuments(Request $request, Model $model): void { - if (is_array($request->docs)) { - $model->docs = []; - $model->docs = collect($request->docs) - ->map(function ($document) use ($model) { - return FileStorageHelper::storeFile($model, $document); - })->all(); - } + $model->docs = []; + $model->docs = collect($request->docs) + ->map(function ($document) use ($model) { + return FileStorageHelper::storeFile($model, $document); + })->all(); } } diff --git a/app/Traits/Requests/HasDocument.php b/app/Traits/Requests/HasDocument.php index 75beba8..f760f48 100644 --- a/app/Traits/Requests/HasDocument.php +++ b/app/Traits/Requests/HasDocument.php @@ -62,8 +62,9 @@ trait HasDocument protected function mergeDocuments(string $name = 'documents'): void { $documents = $this->{$name}; + $this->merge(['doc' => $this->docs ?? []]); + $this->merge(["{$name}Validate" => []]); if (is_array($documents)) { - $this->merge(["{$name}Validate" => []]); foreach ($documents as $key => $documentPath) { // ! Empty document. if ($documentPath === 'false') { diff --git a/resources/ts/components/MultipleDocumentsInputComponent.vue b/resources/ts/components/MultipleDocumentsInputComponent.vue index 73a4163..f2e0425 100644 --- a/resources/ts/components/MultipleDocumentsInputComponent.vue +++ b/resources/ts/components/MultipleDocumentsInputComponent.vue @@ -114,12 +114,22 @@ +
{{ __('Téléchargez les documents :') }} {{ __('ICI') }} -

{{ __('Vu le : ') }}{{ $product->saw_at }}

+
+

{{ __('Vu le : ') }}{{ $product->seen_at }}

{{ __('Lieu : ') }}{{ $product->source }}, {{ $product->fair }}{{ $product->shop }}{{ $product->catalogue }}

{{ __('Par : ') }}{{ $product->user_id->firstname }} {{ $product->user_id->lastname }}