Prepare Beta Version
This commit is contained in:
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
|
||||
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
trait HandlesSeederLoggingAndReporting
|
||||
{
|
||||
protected array $reportData = [];
|
||||
protected float $startTime;
|
||||
|
||||
/**
|
||||
* Registra métricas clave por módulo
|
||||
*/
|
||||
protected function logModuleMetrics(
|
||||
string $module,
|
||||
string $status,
|
||||
?string $file = null,
|
||||
?int $records = null,
|
||||
?string $details = null,
|
||||
bool $truncated = false,
|
||||
bool $isFake = false,
|
||||
?int $fakeCount = null
|
||||
): void {
|
||||
$this->reportData['modules'][$module] = [
|
||||
'status' => $status,
|
||||
'file' => $file ? basename($file) : null,
|
||||
'path' => $file ? dirname($file) : null,
|
||||
'records' => $records,
|
||||
'time' => round(microtime(true) - $this->startTime, 2) . 's',
|
||||
'truncated' => $truncated,
|
||||
'is_fake' => $isFake,
|
||||
'fake_qty' => $fakeCount,
|
||||
'details' => $details,
|
||||
];
|
||||
|
||||
$this->consoleOutput($module, $status, $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Genera reportes en múltiples formatos
|
||||
*/
|
||||
protected function generateReports(): void
|
||||
{
|
||||
$this->reportData['meta'] = [
|
||||
'env' => config('seeder.env', 'local'),
|
||||
'date' => now()->toDateTimeString(),
|
||||
'duration' => round(microtime(true) - $this->startTime, 2) . 's',
|
||||
'has_errors'=> collect($this->reportData['modules'])->contains('status', 'failed')
|
||||
];
|
||||
|
||||
$this->generateMarkdownReport();
|
||||
$this->generateJsonReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reporte Markdown Mejorado (Tabla + Resumen)
|
||||
*/
|
||||
protected function generateMarkdownReport(): void
|
||||
{
|
||||
$env = $this->reportData['meta']['env'];
|
||||
$path = database_path("seeders/reports/{$env}/seed-report-{$env}-" . now()->format('Y-m-d') . ".md");
|
||||
|
||||
$content = [
|
||||
"# 📊 Koneko ERP - Seeders Report",
|
||||
"| Metadata | Value |",
|
||||
"|------------------|--------------------|",
|
||||
"| **Environment** | `{$env}` |",
|
||||
"| **Execution** | {$this->reportData['meta']['date']} |",
|
||||
"| **Duration** | {$this->reportData['meta']['duration']} |",
|
||||
"| **Status** | " . ($this->reportData['meta']['has_errors'] ? '❌ With errors' : '✅ Success') . " |",
|
||||
"",
|
||||
"## 📦 Modules Summary",
|
||||
"",
|
||||
"| Module | Status | Records | Time | Truncated | Faker | File | Details |",
|
||||
"|--------|--------|---------|------|-----------|-------|------|---------|",
|
||||
];
|
||||
|
||||
foreach ($this->reportData['modules'] as $module => $data) {
|
||||
$content[] = sprintf(
|
||||
"| %s | %s | %s | %s | %s | %s | %s | %s |",
|
||||
Str::headline($module),
|
||||
$this->formatStatus($data['status']),
|
||||
$data['records'] ?? ($data['is_fake'] ? "~{$data['fake_qty']}" : 'N/A'),
|
||||
$data['time'],
|
||||
$data['truncated'] ? '✓' : '✗',
|
||||
$data['is_fake'] ? '✓' : '✗',
|
||||
$data['file'] ? "`{$data['file']}`" : 'N/A',
|
||||
$data['details'] ?? ''
|
||||
);
|
||||
}
|
||||
|
||||
// Sección de archivos usados
|
||||
$content[] = "";
|
||||
$content[] = "## 📂 Files Location";
|
||||
foreach (array_unique(array_column($this->reportData['modules'], 'path')) as $filePath) {
|
||||
if ($filePath) $content[] = "- `{$filePath}/`";
|
||||
}
|
||||
|
||||
File::put($path, implode("\n", $content));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers para formato visual
|
||||
*/
|
||||
private function formatStatus(string $status): string
|
||||
{
|
||||
return match(strtolower($status)) {
|
||||
'completed' => '✅',
|
||||
'failed' => '❌',
|
||||
'skipped' => '⏭️',
|
||||
default => 'ℹ️'
|
||||
};
|
||||
}
|
||||
|
||||
private function consoleOutput(string $module, string $status, ?string $details): void
|
||||
{
|
||||
$emoji = $this->formatStatus($status);
|
||||
$this->command?->line(" {$emoji} <fg=white>{$module}</>" . ($details ? " → <fg=yellow>{$details}</>" : ""));
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
|
||||
|
||||
/**
|
||||
* ⚡ Trait para soporte de procesamiento por lotes (chunks) en seeders.
|
||||
*
|
||||
* Permite:
|
||||
* - Cargar archivos grandes sin agotar memoria
|
||||
* - Delegar la lógica al parser correspondiente (si la soporta)
|
||||
* - Integrarse con la barra de progreso
|
||||
*
|
||||
* Requiere que el parser implemente `parseInChunks()`
|
||||
*
|
||||
* @package Koneko\VuexyAdmin\Support\Traits\Seeders
|
||||
*/
|
||||
trait HasSeederChunkSupport
|
||||
{
|
||||
/**
|
||||
* Procesa un archivo usando lotes si el parser lo soporta.
|
||||
*
|
||||
* @param object $parser Parser con método `parseInChunks()`
|
||||
* @param string $path Ruta absoluta del archivo
|
||||
* @return void
|
||||
*/
|
||||
|
||||
protected function processInChunks(object $parser, string $path): void
|
||||
{
|
||||
$this->log("⚡ Procesando por lotes...");
|
||||
$this->startProgress();
|
||||
|
||||
foreach ($parser->parseInChunks($path) as $chunk) {
|
||||
foreach ($chunk as $row) {
|
||||
$this->processSingleRow($row);
|
||||
$this->advanceProgress();
|
||||
}
|
||||
}
|
||||
|
||||
$this->finishProgress();
|
||||
}
|
||||
|
||||
/**
|
||||
* Procesa una sola fila dentro de un chunk.
|
||||
*
|
||||
* @param array $row
|
||||
* @return void
|
||||
*/
|
||||
protected function processSingleRow(array $row): void
|
||||
{
|
||||
try {
|
||||
$sanitized = $this->sanitizeRow($row);
|
||||
$modelClass = $this->getModelClass();
|
||||
$uniqueBy = $this->getUniqueBy();
|
||||
|
||||
$modelClass::updateOrCreate(
|
||||
is_array($uniqueBy)
|
||||
? array_intersect_key($sanitized, array_flip($uniqueBy))
|
||||
: [$uniqueBy => $sanitized[$uniqueBy] ?? null],
|
||||
$sanitized
|
||||
);
|
||||
|
||||
$this->processedCount++;
|
||||
} catch (\Exception $e) {
|
||||
$this->log("⚠️ Error en fila: " . json_encode($row) . " - " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
|
||||
|
||||
/**
|
||||
* Trait para generación de registros faker vía Factory con fallback.
|
||||
*/
|
||||
trait HasSeederFactorySupport
|
||||
{
|
||||
public function runFake(int $total, array $config = []): void
|
||||
{
|
||||
// Este método se define completo en el seeder concreto.
|
||||
throw new \LogicException('El método runFake debe ser implementado por el Seeder que use este trait.');
|
||||
}
|
||||
}
|
25
src/Application/Seeding/Concerns/Main/HasSeederLogger.php
Normal file
25
src/Application/Seeding/Concerns/Main/HasSeederLogger.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
|
||||
|
||||
/**
|
||||
* Trait para emitir logs desde seeders compatibles con Artisan o CLI.
|
||||
*/
|
||||
trait HasSeederLogger
|
||||
{
|
||||
protected function log(string $message): void
|
||||
{
|
||||
if (property_exists($this, 'command') && $this->command instanceof \Illuminate\Console\Command) {
|
||||
try {
|
||||
$this->command->line($message);
|
||||
return;
|
||||
} catch (\Throwable) {
|
||||
// fallback
|
||||
}
|
||||
}
|
||||
|
||||
echo strip_tags($message) . PHP_EOL;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
|
||||
|
||||
/**
|
||||
* Trait para manejar barras de progreso CLI desde seeders.
|
||||
*/
|
||||
trait HasSeederProgressBar
|
||||
{
|
||||
protected function startProgress(int $total = 0): void
|
||||
{
|
||||
if ($this->canUseProgress() && $total > 0) {
|
||||
$this->command->getOutput()->progressStart($total);
|
||||
}
|
||||
}
|
||||
|
||||
protected function advanceProgress(): void
|
||||
{
|
||||
if ($this->canUseProgress()) {
|
||||
$this->command->getOutput()->progressAdvance();
|
||||
}
|
||||
}
|
||||
|
||||
protected function finishProgress(): void
|
||||
{
|
||||
if ($this->canUseProgress()) {
|
||||
$this->command->getOutput()->progressFinish();
|
||||
}
|
||||
}
|
||||
|
||||
protected function canUseProgress(): bool
|
||||
{
|
||||
return property_exists($this, 'command')
|
||||
&& $this->command instanceof \Illuminate\Console\Command
|
||||
&& method_exists($this->command, 'getOutput');
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
|
||||
|
||||
trait SanitizeRowWithFillableAndCasts
|
||||
{
|
||||
protected function sanitizeRowWithFillableAndCasts(array $row, string $modelClass): array
|
||||
{
|
||||
$model = new $modelClass;
|
||||
|
||||
$fillable = $model->getFillable();
|
||||
$casts = $model->getCasts();
|
||||
|
||||
$sanitized = [];
|
||||
|
||||
foreach ($row as $key => $value) {
|
||||
if (!in_array($key, $fillable)) {
|
||||
continue; // Solo campos fillable
|
||||
}
|
||||
|
||||
// Aplicar cast manualmente si es necesario
|
||||
if (isset($casts[$key])) {
|
||||
$castType = $casts[$key];
|
||||
|
||||
$value = match ($castType) {
|
||||
'boolean', 'bool' => (bool) $value,
|
||||
'int', 'integer' => (int) $value,
|
||||
'float', 'double' => (float) $value,
|
||||
'datetime', 'date' => is_a($value, \DateTimeInterface::class) ? $value->format('Y-m-d H:i:s') : $value,
|
||||
'array', 'json' => is_string($value) ? json_decode($value, true) : $value,
|
||||
default => $value,
|
||||
};
|
||||
}
|
||||
|
||||
$sanitized[$key] = $value;
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
}
|
@ -7,10 +7,9 @@ namespace Koneko\VuexyAdmin\Application\Seeding;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Koneko\VuexyAdmin\Application\Seeding\SeederReportBuilder;
|
||||
use Koneko\VuexyAdmin\Application\Traits\Seeders\Main\HasSeederLogger;
|
||||
use Koneko\VuexyAdmin\Application\UI\Avatar\AvatarImageService;
|
||||
use Koneko\VuexyAdmin\Application\UI\Avatar\AvatarInitialsService;
|
||||
use Koneko\VuexyAdmin\Support\Seeders\AbstractDataSeeder;
|
||||
use Koneko\VuexyAdmin\Application\Seeding\Concerns\Main\HasSeederLogger;
|
||||
use Koneko\VuexyAdmin\Application\UI\Avatar\{AvatarImageService, AvatarInitialsService};
|
||||
use Koneko\VuexyAdmin\Support\Seeders\Base\AbstractDataSeeder;
|
||||
|
||||
class SeederOrchestrator
|
||||
{
|
||||
|
Reference in New Issue
Block a user