XML PARSER FLUX V2
This commit is contained in:
parent
ef50ebcefb
commit
8219e60947
18 changed files with 642 additions and 406 deletions
17
app/Enums/LogType.php
Normal file
17
app/Enums/LogType.php
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self debug()
|
||||
* @method static self log()
|
||||
* @method static self warning()
|
||||
* @method static self info()
|
||||
* @method static self error()
|
||||
* @method static self critical()
|
||||
*/
|
||||
final class LogType extends BaseEnum
|
||||
{
|
||||
}
|
||||
9
app/Exceptions/AbortXmlImportException.php
Normal file
9
app/Exceptions/AbortXmlImportException.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class AbortXmlImportException extends Exception
|
||||
{
|
||||
}
|
||||
|
|
@ -3,9 +3,9 @@
|
|||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Breeders;
|
||||
use App\Models\BreedersDatas;
|
||||
use App\Models\XmlBreeder;
|
||||
use App\Models\Traceability;
|
||||
use App\Models\TraceabilityDatas;
|
||||
use App\Models\XmlTraceability;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TraceabilityController extends Controller
|
||||
|
|
@ -13,7 +13,7 @@ class TraceabilityController extends Controller
|
|||
public function search(Request $request)
|
||||
{
|
||||
$searchCode = filter_var($request->code, FILTER_SANITIZE_NUMBER_INT);
|
||||
$tracaDatas = TraceabilityDatas::where('no_lot', 'LIKE', $searchCode)->first();
|
||||
$tracaDatas = XmlTraceability::where('lot_code', 'LIKE', $searchCode)->first();
|
||||
$log = new Traceability();
|
||||
$log->code = $searchCode;
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ class TraceabilityController extends Controller
|
|||
$log->save();
|
||||
return json_encode(['status' => 'KO', 'code' => 100]);
|
||||
} else {
|
||||
if ($tracaDatas->code_traca_prod_elab === 1) {
|
||||
if ($tracaDatas->code_traca_prod_elab) {
|
||||
// Produit élaboré
|
||||
$log->response_type = 3;
|
||||
$log->save();
|
||||
|
|
@ -32,11 +32,13 @@ class TraceabilityController extends Controller
|
|||
|
||||
// Si éleveur trouvé, alors c'est un éleveur OCB publié
|
||||
$breeder = Breeders::where([
|
||||
['status', 1], ['breeder_code', 'LIKE', '%' . $tracaDatas->no_opa . $tracaDatas->no_elev]
|
||||
['status', 1], [
|
||||
'breeder_code', 'LIKE', '%' . $tracaDatas->opa_code . $tracaDatas->inuav_code
|
||||
]
|
||||
])->first();
|
||||
if (!$breeder) {
|
||||
// Sinon, c'est un éleveur non OCB
|
||||
$breeder = BreedersDatas::where('code_eleveur', $tracaDatas->no_elev)->first();
|
||||
$breeder = XmlBreeder::where('code_eleveur', $tracaDatas->inuav_code)->first();
|
||||
if (!$breeder) {
|
||||
// Eleveur non trouvé
|
||||
$log->response_type = 4;
|
||||
|
|
|
|||
|
|
@ -2,134 +2,37 @@
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\BreedersDatas;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use SimpleXMLElement;
|
||||
use App\Models\XmlBreeder;
|
||||
|
||||
class XmlEleveursReader implements ShouldQueue
|
||||
class XmlEleveursReader extends XmlJobs
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $console = null;
|
||||
protected $disk = 'eleveurs';
|
||||
|
||||
protected $takeColumns = [
|
||||
'codeopa.Nom' => 'opa_nom',
|
||||
'codeopa.code' => 'opa_code',
|
||||
'NomEleveur' => 'nom',
|
||||
'CodeEleveur' => 'inuav_code',
|
||||
'Prenom' => 'prenom',
|
||||
'code' => 'unique_key',
|
||||
'codePostal' => 'postal',
|
||||
'Ville' => 'city',
|
||||
'LongitudeGps' => 'longitude',
|
||||
'LatitudeGps' => 'latitude'
|
||||
];
|
||||
|
||||
protected $nullableFields = ['longitude', 'latitude'];
|
||||
|
||||
protected $uniqueKeyField = ['unique_key', 'inuav_code'];
|
||||
|
||||
/**
|
||||
* TODO: Job clean very old XML PARSED DATA
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->log(__CLASS__ . ' Démarrage du traitement');
|
||||
$files = Storage::disk('eleveur')->files();
|
||||
|
||||
// Suppression des fichiers dans archives qui ont plus de 2j
|
||||
$archives = Storage::disk('eleveur')->files('archives');
|
||||
foreach ($archives as $archive) {
|
||||
if ($archive == ".gitignore" || $archive == ".gitkeep") {
|
||||
continue;
|
||||
}
|
||||
if (Storage::disk('eleveur')->lastModified($archive) < strtotime(Carbon::now()->subDays(2))) {
|
||||
Storage::disk('eleveur')->delete($archive);
|
||||
}
|
||||
}
|
||||
|
||||
$progress = 0;
|
||||
$total = count($files) - 1;
|
||||
foreach ($files as $filename) {
|
||||
if ($filename == ".gitignore" || $filename == ".gitkeep") {
|
||||
continue;
|
||||
}
|
||||
// ---- Récupération du contenu
|
||||
$file = Storage::disk('eleveur')->get($filename);
|
||||
$fileContent = simplexml_load_string($file, null, LIBXML_NOCDATA);
|
||||
|
||||
// Transforme le fichier en tableau de données
|
||||
$eleveurs = $this->extract($fileContent);
|
||||
|
||||
// Mise en BDD des données du fichier xml
|
||||
$fields = [];
|
||||
foreach ($eleveurs['ELEVEURS'] as $eleveur) {
|
||||
$fields[] = $this->processFields($eleveur);
|
||||
}
|
||||
|
||||
// Mise en base des données
|
||||
foreach ($fields as $field) {
|
||||
$checkFields = ['code' => $field["code"], 'code_eleveur' => $field['code_eleveur']];
|
||||
BreedersDatas::updateOrCreate($checkFields, $field);
|
||||
}
|
||||
|
||||
Storage::disk('eleveur')->move($filename, 'archives/' . $filename);
|
||||
$this->log(__CLASS__ . sprintf(' traitement %d sur %s ', $progress++, $total));
|
||||
}
|
||||
$this->log(__CLASS__ . ' Fin du traitement');
|
||||
}
|
||||
|
||||
private function log(string $message)
|
||||
{
|
||||
if ($this->console) {
|
||||
$this->console->info($message);
|
||||
}
|
||||
Log::debug($message);
|
||||
}
|
||||
|
||||
private function extract($sxe = null)
|
||||
{
|
||||
if (!$sxe instanceof SimpleXMLElement) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$extract = array();
|
||||
|
||||
foreach ($sxe->children() as $key => $value) {
|
||||
if (array_key_exists($key, $extract)) {
|
||||
if (!isset($extract[$key][0])) {
|
||||
$tmp_extract = $extract[$key];
|
||||
$extract[$key] = array();
|
||||
$extract[$key][0] = $tmp_extract;
|
||||
} else {
|
||||
$extract[$key] = (array)$extract[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if ($value->count()) {
|
||||
if (isset($extract[$key]) && is_array($extract[$key])) {
|
||||
$extract[$key][] = $this->extract($value);
|
||||
} else {
|
||||
$extract[$key] = $this->extract($value);
|
||||
}
|
||||
} else {
|
||||
if (isset($extract[$key]) && is_array($extract[$key])) {
|
||||
$extract[$key][] = empty(strval($value)) ? null : strval($value);
|
||||
} else {
|
||||
$extract[$key] = empty(strval($value)) ? null : strval($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $extract;
|
||||
}
|
||||
|
||||
private function processFields($fields)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($fields as $key => $field) {
|
||||
// Gestion des balises CodeOpa et OPA
|
||||
if (is_array($field)) {
|
||||
if (isset($field['OPA'])) {
|
||||
$result['code_opa'] = $field['OPA']['code'];
|
||||
$result['nom'] = $field['OPA']['Nom'];
|
||||
}
|
||||
} else {
|
||||
// Le ctype_upper est la pour les balise entièrement en majuscule
|
||||
$result[Str::snake(ctype_upper($key) ? strtolower($key) : $key)] = $field;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
$this->parseData('ELEVEURS', XmlBreeder::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
430
app/Jobs/XmlJobs.php
Normal file
430
app/Jobs/XmlJobs.php
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Enums\LogType;
|
||||
use App\Exceptions\AbortXmlImportException;
|
||||
use ErrorException;
|
||||
use Exception;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Filesystem\Filesystem;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use RecursiveArrayIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use SimpleXMLElement;
|
||||
use TypeError;
|
||||
|
||||
abstract class XmlJobs implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* The Laravel disk name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $disk = null;
|
||||
|
||||
/**
|
||||
* The columns to take from the XML
|
||||
* subcolumns can be added with a dot
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $takeColumns = [];
|
||||
|
||||
/**
|
||||
* The columns allowed to be null
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $nullableFields = [];
|
||||
|
||||
/**
|
||||
* This is for Laravel Console usage
|
||||
*/
|
||||
public $console = null;
|
||||
|
||||
/**
|
||||
* The relative folder where to archive data
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $archiveFolderName = 'archives';
|
||||
|
||||
/**
|
||||
* The files extension to consider
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $wantedFileExt = 'xml';
|
||||
|
||||
/**
|
||||
* Need to encapsulate the freeking XML
|
||||
* Because provided one is dirty
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $encapsulate = false;
|
||||
|
||||
/**
|
||||
* The files to ignore
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ignoreFiles = [
|
||||
'.gitignore', '.gitkeep'
|
||||
];
|
||||
|
||||
/**
|
||||
* Parse Data from the XML and gives it to the callback
|
||||
*
|
||||
* @param string $entityName
|
||||
* @param string $modelClass
|
||||
* @return void
|
||||
*/
|
||||
protected function parseData(string $entityName, string $modelClass): void
|
||||
{
|
||||
/** @var Filesystem */
|
||||
$disk = $this->getDisk();
|
||||
$this->log('Démarrage du traitement');
|
||||
$files = $disk->files();
|
||||
|
||||
$progress = 1;
|
||||
$total = count(array_filter($files, function ($filename) {
|
||||
if ($this->fileHasToBeIgnored($filename)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
|
||||
foreach ($files as $filename) {
|
||||
if ($this->fileHasToBeIgnored($filename)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->log(sprintf(' traitement %d sur %s ', $progress++, $total));
|
||||
|
||||
/** @var string $data */
|
||||
$data = $disk->get($filename);
|
||||
|
||||
if ($this->encapsulate) {
|
||||
$data = sprintf('<Root>%s</Root>', $data);
|
||||
/** @var SimpleXMLElement|false */
|
||||
$xmlContent = simplexml_load_string($data);
|
||||
} else {
|
||||
/** @var SimpleXMLElement|false */
|
||||
$xmlContent = simplexml_load_string($data, null, LIBXML_NOCDATA);
|
||||
}
|
||||
|
||||
if ($xmlContent === false) {
|
||||
$this->log(
|
||||
sprintf('File %s could not be parsed (simplexml_load_string)', $filename),
|
||||
LogType::critical()
|
||||
);
|
||||
continue; // ignore the file
|
||||
}
|
||||
|
||||
$data = $this->extract($xmlContent);
|
||||
|
||||
try {
|
||||
$lines = [];
|
||||
|
||||
foreach ($data[$entityName] as $traca) {
|
||||
$lines[] = $this->processFields($traca);
|
||||
}
|
||||
|
||||
// Mise en base des données
|
||||
foreach ($lines as $line) {
|
||||
/** @var array Unique Key columns */
|
||||
$uniqueColumnsKey = [];
|
||||
foreach ($this->uniqueKeyField as $uField) {
|
||||
$uniqueColumnsKey[$uField] = $line[$uField];
|
||||
}
|
||||
forward_static_call_array(
|
||||
[$modelClass, 'updateOrCreate'],
|
||||
[$uniqueColumnsKey, $line]
|
||||
);
|
||||
}
|
||||
} catch (ErrorException | TypeError $e) {
|
||||
$this->log(
|
||||
sprintf(
|
||||
'File %s could not be parsed => %s on line %d',
|
||||
$filename,
|
||||
$e->getMessage(),
|
||||
$e->getLine()
|
||||
),
|
||||
LogType::critical()
|
||||
);
|
||||
continue; // ignore the file
|
||||
} catch (AbortXmlImportException $e) {
|
||||
$this->log(
|
||||
sprintf(
|
||||
'File %s could not be parsed => %s on line %d',
|
||||
$filename,
|
||||
$e->getMessage(),
|
||||
$e->getLine()
|
||||
),
|
||||
LogType::error()
|
||||
);
|
||||
continue; // ignore the file
|
||||
}
|
||||
|
||||
$this->moveToArchives($filename);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive extract to array
|
||||
*
|
||||
* @param \SimpleXMLElement|false $sxe
|
||||
* @return array
|
||||
*/
|
||||
protected function extract($sxe = null): array
|
||||
{
|
||||
if (!$sxe instanceof SimpleXMLElement) {
|
||||
return array();
|
||||
}
|
||||
$extract = array();
|
||||
foreach ($sxe->children() as $key => $value) {
|
||||
if ($value->count()) {
|
||||
$extract[$key][] = $this->extract($value);
|
||||
continue;
|
||||
}
|
||||
$value = strval($value);
|
||||
if (array_key_exists($key, $extract) and is_array($extract[$key])) {
|
||||
$extract[$key][] = strlen($value) ? $value : null;
|
||||
} else {
|
||||
$extract[$key] = strlen($value) ? $value : null;
|
||||
}
|
||||
}
|
||||
return $extract;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process fields to gather data
|
||||
*
|
||||
* @param array $fields
|
||||
* @return array
|
||||
* @throws AbortXmlImportException
|
||||
*/
|
||||
protected function processFields(array $fields): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($fields as $key => $field) {
|
||||
if (!($key = $this->shouldFieldBeConsidered($key))) {
|
||||
continue;
|
||||
}
|
||||
if (!$this->isFieldNullable($key) && is_null($field)) {
|
||||
throw new AbortXmlImportException(sprintf('The field %s is not allowed to be null', $key));
|
||||
}
|
||||
if (is_array($field)) {
|
||||
// Flatten data
|
||||
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($field));
|
||||
foreach ($it as $k => $f) {
|
||||
$ko = sprintf('%s.%s', $key, $k);
|
||||
if (!$this->isFieldNullable($ko) && is_null($f)) {
|
||||
throw new AbortXmlImportException(sprintf('%s : The field %s is not allowed to be null', get_class($this), $ko));
|
||||
}
|
||||
if (!($k = $this->shouldFieldBeConsidered($ko))) {
|
||||
continue;
|
||||
}
|
||||
$result[$k] = $f;
|
||||
}
|
||||
} else {
|
||||
$result[$key] = $field;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the filed nullable
|
||||
*
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
private function isFieldNullable(string $key): bool
|
||||
{
|
||||
if (in_array($key, $this->nullableFields)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* - replace key name with the corresponding alias
|
||||
* - strtolower the key
|
||||
* - snake_case the key
|
||||
*
|
||||
* @param string $key
|
||||
* @return string|false
|
||||
*/
|
||||
private function shouldFieldBeConsidered(string $key)
|
||||
{
|
||||
$key = strtolower($key);
|
||||
foreach ($this->takeColumns as $k => $alias) {
|
||||
if (strtolower($k) === $key) {
|
||||
return Str::snake(strtolower($alias));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the file to be ignored
|
||||
*
|
||||
* @param string $filename
|
||||
* @return boolean
|
||||
*/
|
||||
protected function fileHasToBeIgnored(string $filename): bool
|
||||
{
|
||||
return in_array($filename, $this->ignoreFiles) or !$this->isFileXml($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the file is an xml file
|
||||
*
|
||||
* @param string $filename
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isFileXml(string $filename): bool
|
||||
{
|
||||
$isXml = strtolower(pathinfo($filename, \PATHINFO_EXTENSION)) === 'xml';
|
||||
if (!$isXml) {
|
||||
Log::warning(sprintf('%s : unwanted file type in %s', get_class($this), $filename));
|
||||
}
|
||||
return $isXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the file to archives
|
||||
*
|
||||
* @param string $filename
|
||||
* @return void
|
||||
*/
|
||||
protected function moveToArchives(string $filename): void
|
||||
{
|
||||
if (!$this->getDisk()->move($filename, sprintf('%s/%s', $this->archiveFolderName, $filename))) {
|
||||
$this->log(
|
||||
sprintf(
|
||||
'File %s could not be moved to archives',
|
||||
$filename
|
||||
),
|
||||
LogType::warning()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Suppression des fichiers dans
|
||||
* archives qui ont plus de 2j
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function clearOldArchives(): void
|
||||
{
|
||||
$disk = $this->getDisk();
|
||||
$archives = $disk->files($this->archiveFolderName);
|
||||
|
||||
// /** @var File */
|
||||
foreach ($archives as $archive) {
|
||||
if (in_array($archive, $this->ignoreFiles)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($disk->lastModified($archive) < strtotime(Carbon::now()->subDays(2))) {
|
||||
if (!$disk->delete($archive)) {
|
||||
Log::critical(sprintf('%s : Could not delete file %s', get_class($this), $archive));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the concerned disk
|
||||
*
|
||||
* @return Filesystem
|
||||
*/
|
||||
protected function getDisk(): Filesystem
|
||||
{
|
||||
return Storage::disk($this->disk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs data
|
||||
*
|
||||
* @param string $message
|
||||
* @param LogType $logtype
|
||||
* @return void
|
||||
*/
|
||||
protected function log(string $message, LogType $logtype = null): void
|
||||
{
|
||||
if (!$logtype) {
|
||||
$logtype = LogType::debug();
|
||||
}
|
||||
$message = sprintf('%s : %s', get_class($this), $message);
|
||||
switch ($logtype->value) {
|
||||
case LogType::debug()->value:
|
||||
Log::debug($message);
|
||||
break;
|
||||
case LogType::info()->value:
|
||||
Log::info($message);
|
||||
break;
|
||||
case LogType::log()->value:
|
||||
Log::log($message);
|
||||
break;
|
||||
case LogType::warning()->value:
|
||||
Log::warning($message);
|
||||
break;
|
||||
case LogType::error()->value:
|
||||
Log::error($message);
|
||||
break;
|
||||
case LogType::critical()->value:
|
||||
Log::critical($message);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
|
||||
}
|
||||
$this->consolePrint($message, $logtype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints to the console
|
||||
*
|
||||
* @param string $message
|
||||
* @param LogType $logtype
|
||||
* @return void
|
||||
*/
|
||||
protected function consolePrint(string $message, LogType $logtype): void
|
||||
{
|
||||
if ($this->console) {
|
||||
switch ($logtype->value) {
|
||||
case LogType::debug()->value:
|
||||
case LogType::info()->value:
|
||||
case LogType::log()->value:
|
||||
$this->console->info($message);
|
||||
break;
|
||||
case LogType::warning()->value:
|
||||
$this->console->comment($message);
|
||||
break;
|
||||
case LogType::error()->value:
|
||||
case LogType::critical()->value:
|
||||
$this->console->error($message);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,135 +2,38 @@
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\TraceabilityDatas;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use SimpleXMLElement;
|
||||
use App\Models\XmlTraceability;
|
||||
|
||||
class XmlTracaReader implements ShouldQueue
|
||||
class XmlTracaReader extends XmlJobs
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $console = null;
|
||||
protected $disk = 'tracabilite';
|
||||
|
||||
protected $takeColumns = [
|
||||
'NO_LOT' => 'lot_code',
|
||||
'NO_OPA' => 'opa_code',
|
||||
'NO_ELEV' => 'inuav_code',
|
||||
'LOTABA' => 'abatage_code',
|
||||
'DATABA' => 'abatage_date',
|
||||
'CODE_TRACA_PROD_ELAB' => 'produit_elab'
|
||||
];
|
||||
|
||||
protected $encapsulate = true;
|
||||
|
||||
protected $nullableFields = [
|
||||
'opa_code', 'inuav_code',
|
||||
'abatage_code', 'abatage_date'
|
||||
];
|
||||
|
||||
protected $uniqueKeyField = ['lot_code'];
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
* TODO: Job clean very old XML PARSED DATA
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->log(__CLASS__ . ' Démarrage du traitement');
|
||||
$files = Storage::disk('tracabilite')->files();
|
||||
|
||||
// Suppression des fichiers dans archives qui ont plus de 2j
|
||||
$archives = Storage::disk('tracabilite')->files('archives');
|
||||
foreach ($archives as $archive) {
|
||||
if ($archive == ".gitignore" || $archive == ".gitkeep") {
|
||||
continue;
|
||||
}
|
||||
if (Storage::disk('tracabilite')->lastModified($archive) < strtotime(Carbon::now()->subDays(2))) {
|
||||
Storage::disk('tracabilite')->delete($archive);
|
||||
}
|
||||
}
|
||||
|
||||
$progress = 0;
|
||||
$total = count($files) - 1;
|
||||
foreach ($files as $filename) {
|
||||
if ($filename == ".gitignore" || $filename == ".gitkeep") {
|
||||
continue;
|
||||
}
|
||||
// ---- Récupération du contenu
|
||||
$file = Storage::disk('tracabilite')->get($filename);
|
||||
|
||||
$fileContent = simplexml_load_string('<Root>' . $file . '</Root>');
|
||||
|
||||
// Transforme le fichier en tableau de données
|
||||
$tracas = $this->extract($fileContent);
|
||||
|
||||
// Mise en BDD des données du fichier xml
|
||||
$fields = [];
|
||||
foreach ($tracas['ENRLOT'] as $traca) {
|
||||
$fields[] = $this->processFields($traca);
|
||||
}
|
||||
|
||||
// Mise en base des données
|
||||
foreach ($fields as $field) {
|
||||
$checkFields = [
|
||||
'no_lot' => $field["no_lot"],
|
||||
'no_opa' => $field['no_opa'],
|
||||
'no_elev' => $field['no_elev']
|
||||
];
|
||||
TraceabilityDatas::updateOrCreate($checkFields, $field);
|
||||
}
|
||||
|
||||
Storage::disk('tracabilite')->move($filename, 'archives/' . $filename);
|
||||
$this->log(__CLASS__ . sprintf(' traitement %d sur %s ', $progress++, $total));
|
||||
}
|
||||
$this->log(__CLASS__ . ' Fin du traitement');
|
||||
}
|
||||
|
||||
private function log(string $message)
|
||||
{
|
||||
if ($this->console) {
|
||||
$this->console->info($message);
|
||||
}
|
||||
Log::debug($message);
|
||||
}
|
||||
|
||||
private function extract($sxe = null)
|
||||
{
|
||||
if (!$sxe instanceof SimpleXMLElement) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$extract = array();
|
||||
|
||||
foreach ($sxe->children() as $key => $value) {
|
||||
if (array_key_exists($key, $extract)) {
|
||||
if (!isset($extract[$key][0])) {
|
||||
$tmp_extract = $extract[$key];
|
||||
$extract[$key] = array();
|
||||
$extract[$key][0] = $tmp_extract;
|
||||
} else {
|
||||
$extract[$key] = (array)$extract[$key];
|
||||
}
|
||||
}
|
||||
|
||||
if ($value->count()) {
|
||||
if (isset($extract[$key]) && is_array($extract[$key])) {
|
||||
$extract[$key][] = $this->extract($value);
|
||||
} else {
|
||||
$extract[$key] = $this->extract($value);
|
||||
}
|
||||
} else {
|
||||
if (isset($extract[$key]) && is_array($extract[$key])) {
|
||||
$extract[$key][] = empty(strval($value)) && strval($value) !== '0' ? null : strval($value);
|
||||
} else {
|
||||
$extract[$key] = empty(strval($value)) && strval($value) !== '0' ? null : strval($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $extract;
|
||||
}
|
||||
|
||||
private function processFields($fields)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($fields as $key => $field) {
|
||||
// Le ctype_upper est la pour les balise entièrement en majuscule
|
||||
$result[strtolower($key)] = $field;
|
||||
}
|
||||
return $result;
|
||||
$this->parseData('ENRLOT', XmlTraceability::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class BreedersDatas extends Model
|
||||
{
|
||||
|
||||
protected $fillable = [
|
||||
"typemouvementinformatique",
|
||||
"code",
|
||||
"nom_eleveur",
|
||||
"code_opa",
|
||||
"nom",
|
||||
"telephone",
|
||||
"code_eleveur",
|
||||
"code_activite",
|
||||
"bague",
|
||||
"gsm",
|
||||
"fax",
|
||||
"prenom",
|
||||
"adresse",
|
||||
"adresse_compl",
|
||||
"etat_code",
|
||||
"code_postal",
|
||||
"ville",
|
||||
"email",
|
||||
"libelle",
|
||||
"longitude_gps",
|
||||
"latitude_gps",
|
||||
];
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TraceabilityDatas extends Model
|
||||
{
|
||||
|
||||
protected $fillable = [
|
||||
'no_lot',
|
||||
'no_opa',
|
||||
'no_elev',
|
||||
'lotaba',
|
||||
'databa',
|
||||
'num_sb',
|
||||
'site_abat',
|
||||
'code_traca_prod_elab',
|
||||
];
|
||||
}
|
||||
20
app/Models/XmlBreeder.php
Normal file
20
app/Models/XmlBreeder.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class XmlBreeder extends Model
|
||||
{
|
||||
|
||||
protected $fillable = [
|
||||
"unique_key",
|
||||
"inuav_code",
|
||||
"nom",
|
||||
"prenom",
|
||||
"postal",
|
||||
"city",
|
||||
"longitude",
|
||||
"latitude"
|
||||
];
|
||||
}
|
||||
20
app/Models/XmlTraceability.php
Normal file
20
app/Models/XmlTraceability.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class XmlTraceability extends Model
|
||||
{
|
||||
|
||||
protected $table = 'xml_tracability';
|
||||
|
||||
protected $fillable = [
|
||||
'lot_code',
|
||||
'opa_code',
|
||||
'inuav_code',
|
||||
'abatage_code',
|
||||
'abatage_date',
|
||||
'produit_elab'
|
||||
];
|
||||
}
|
||||
|
|
@ -70,9 +70,9 @@ return [
|
|||
'root' => storage_path('inco'),
|
||||
],
|
||||
|
||||
'eleveur' => [
|
||||
'eleveurs' => [
|
||||
'driver' => 'local',
|
||||
'root' => storage_path('eleveur'),
|
||||
'root' => storage_path('eleveurs'),
|
||||
],
|
||||
|
||||
'tracabilite' => [
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
class CreateBreedersDatasTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('breeders_datas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('typemouvementinformatique')->nullable();
|
||||
$table->string('code')->nullable();
|
||||
$table->string('nom_eleveur')->nullable();
|
||||
$table->string('code_opa')->nullable();
|
||||
$table->string('nom')->nullable();
|
||||
$table->string('telephone')->nullable();
|
||||
$table->string('code_eleveur')->nullable();
|
||||
$table->string('code_activite')->nullable();
|
||||
$table->string('bague')->nullable();
|
||||
$table->string('gsm')->nullable();
|
||||
$table->string('fax')->nullable();
|
||||
$table->string('prenom')->nullable();
|
||||
$table->string('adresse')->nullable();
|
||||
$table->string('adresse_compl')->nullable();
|
||||
$table->string('etat_code')->nullable();
|
||||
$table->string('code_postal')->nullable();
|
||||
$table->string('ville')->nullable();
|
||||
$table->string('email')->nullable();
|
||||
$table->string('libelle')->nullable();
|
||||
$table->string('longitude_gps')->nullable();
|
||||
$table->string('latitude_gps')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->index('code_eleveur');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('breeders_datas');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
class CreateXmlBreedersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('xml_breeders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('unique_key');
|
||||
$table->string('inuav_code');
|
||||
$table->string('nom');
|
||||
$table->string('prenom');
|
||||
$table->string('postal');
|
||||
$table->string('city');
|
||||
$table->string('longitude')->nullable();
|
||||
$table->string('latitude')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['unique_key', 'inuav_code']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('breeders_datas');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
class CreateTracabilityDataTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('tracability_data', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('no_lot')->nullable();
|
||||
$table->string('no_elev')->nullable();
|
||||
$table->string('databa')->nullable();
|
||||
|
||||
$table->boolean('code_traca_prod_elab')->default(false);
|
||||
|
||||
$table->integer('no_opa')->unsigned()->default(0);
|
||||
$table->integer('site_abat')->unsigned()->default(0);
|
||||
|
||||
$table->bigInteger('lotaba')->nullable()->default(0);
|
||||
$table->bigInteger('num_sb')->nullable()->default(0);
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('tracability_data');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
// @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
|
||||
class CreateXmlTracabilityTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('xml_tracability', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->string('lot_code')->unique();
|
||||
$table->string('opa_code')->nullable();
|
||||
$table->string('inuav_code')->nullable();
|
||||
$table->boolean('produit_elab');
|
||||
$table->bigInteger('abatage_code')->nullable();
|
||||
$table->date('abatage_date')->nullable();
|
||||
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('tracability_data');
|
||||
}
|
||||
}
|
||||
1
public/storage
Symbolic link
1
public/storage
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
/mnt/c/www/legaulois2020/storage/app/public
|
||||
2
storage/.gitignore
vendored
Normal file
2
storage/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
eleveurs/*
|
||||
tracabilite/*
|
||||
Loading…
Add table
Reference in a new issue