Compare commits

...
Sign in to create a new pull request.

5 commits

Author SHA1 Message Date
Jérémy Munsch
69f3849350
console output fix 2023-08-16 12:37:33 +02:00
Jérémy Munsch
351840db4c
Fix comp alleg 2023-08-16 12:30:43 +02:00
Jérémy Munsch
7579c7eb5b
Products and Brands Migration OK except for products assets
Waiting for
https://lab.s-web.fr/ldc-web/projets/startcoq/-/issues/28#note_9782
2023-08-16 12:23:07 +02:00
Jérémy Munsch
bcb6fd1709
add drop temp table system 2023-08-14 14:15:23 +02:00
Jérémy Munsch
a6de982ef1
Migration automatique de table START_C 2023-08-14 13:36:18 +02:00
12 changed files with 1134 additions and 198 deletions

View file

@ -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

View file

@ -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,

View file

@ -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);
}
/**

View file

@ -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',

View file

@ -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'),

View file

@ -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,

View file

@ -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');

View file

@ -0,0 +1,720 @@
<?php
use App\Enums\KeywordCategoryEnum;
use App\Enums\Products\CertificationEnum;
use App\Enums\Products\ClaimEnum;
use App\Enums\Products\DistributionNetworkEnum;
use App\Enums\Products\GeoAreaEnum;
use App\Enums\Products\OriginEnum;
use App\Enums\Products\RangeEnum;
use App\Enums\Products\RayEnum;
use App\Enums\Products\SourceEnum;
use App\Models\Brand;
use App\Models\Keyword;
use App\Models\Product;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Contracts\Database\Query\Builder;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\QueryException;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Output\ConsoleOutput;
return new class extends Migration
{
/**
* StartC table prefix
*
* @var string
*/
private $tablePrefix = 'STARTC';
/**
* StartC table to migrate
*
* @var string[]
*/
private $tableList = [
'FAMILLE', 'CERTIFICATION',
'RAYON', 'RESEAU',
'SECTEUR', 'SOURCE',
'VEILLE_PROD', 'VEILLE_PROD_COMM',
];
/** @var \App\Model\User */
private $dummyUser;
/**
* Run the migrations.
*
* @return void
* @throws \RuntimeException In case of failure.
*/
public function up(): void
{
$console = new ConsoleOutput();
$section = $console->section();
// * SEED KEYWORDS As they are needed on runtime.
Schema::disableForeignKeyConstraints();
DB::table('brands')->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();
$section->clear();
}
/**
* Reverse the migrations.
*
* @return void
* @throws \Error Wont rollback.
*/
public function down(): void
{
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<string,string> */
$START_SOURCES = $this->getQuery('SOURCE')->get();
/** @var \Illuminate\Support\Collection<string,string> */
$START_CERTIFICATIONS = $this->getQuery('CERTIFICATION')->get();
/** @var \Illuminate\Support\Collection<string,string> */
$START_FAMILLES = $this->getQuery('FAMILLE')->get();
/** @var \Illuminate\Support\Collection<string,string> */
$START_SECTEURS = $this->getQuery('SECTEUR')->get();
/** @var \Illuminate\Support\Collection<string,string> */
$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 {
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");
}
}
/** @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<string,string> */
$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
*
* @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
*
* @param string $tableName
* @param array<object> $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}");
}
}
});
}
/**
* Create temp tables and fill them
*
* @return void
* @throws \Error In case of failure.
*/
private function createAndFillTempTables(): void
{
$console = new ConsoleOutput();
$console->writeln('');
foreach ($this->tableList as $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
}
/**
* 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
*
* @param string $tableName
* @return array<object> with name, type, size
* @throws \Error If connection or query fails.
*/
private function fetchOciTableHeader(string $tableName): array
{
$tableName = $this->getTableName($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<object> $header With name, type,size.
* @return array<object> With DB content.
* @throws \Error If connection or query fails.
*/
private function fetchOciTableContent(string $tableName, array $header): array
{
$out = [];
$tableName = $this->getTableName($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 The string date to parse.
* @param boolean $asTimestamp To force the return of a valid TIMESTAMP (in epoch).
* @return \Carbon\Carbon
* @throws \Error If the date cannot fit in a timestamp.
*/
private function tryToParseCarbon(string $date, bool $asTimestamp = false): Carbon
{
$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;
}
};

View file

@ -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

View file

@ -0,0 +1,197 @@
<?php
namespace Database\Seeders;
use App\Enums\KeywordCategoryEnum;
use App\Models\Keyword;
use Illuminate\Database\Seeder;
class KeywordInitSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
/**
* * INGREDIENTS
*/
$keyword = new Keyword();
$keyword->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();
}
}

View file

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

View file

@ -96,7 +96,7 @@
@endif
<table width="100%" class="product-saw">
<tr>
@if (isset($product->saw_at))
@if (isset($product->seen_at))
<td class="table-cell-text-center" width="50%">
<strong>{{ __('Téléchargez les documents :') }}</strong>
<a href="{{ route('products.download', $product->id) }}">{{ __('ICI') }}</a>
@ -104,7 +104,7 @@
@endif
@if (isset($product->source))
<td class="table-cell-text-center" width="50%">
<p><strong>{{ __('Vu le : ') }}</strong>{{ $product->saw_at }}</p>
<p><strong>{{ __('Vu le : ') }}</strong>{{ $product->seen_at }}</p>
<p><strong>{{ __('Lieu : ') }}</strong>{{ $product->source }},&nbsp;{{ $product->fair }}{{ $product->shop }}{{ $product->catalogue }}</p>
<p><strong>{{ __('Par : ') }}</strong>{{ $product->user_id->firstname }}&nbsp;{{ $product->user_id->lastname }}</p>
</td>