optimizations
This commit is contained in:
129
app/Jobs/NovaCards/GenerateNovaCardExportJob.php
Normal file
129
app/Jobs/NovaCards/GenerateNovaCardExportJob.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Jobs\NovaCards;
|
||||
|
||||
use App\Models\NovaCard;
|
||||
use App\Models\NovaCardExport;
|
||||
use App\Services\NovaCards\NovaCardRenderService;
|
||||
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\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class GenerateNovaCardExportJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
public readonly int $exportId,
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(NovaCardRenderService $renderService): void
|
||||
{
|
||||
$export = NovaCardExport::query()->find($this->exportId);
|
||||
if (! $export || $export->status === NovaCardExport::STATUS_READY) {
|
||||
return;
|
||||
}
|
||||
|
||||
$card = NovaCard::query()->with(['backgroundImage'])->find($export->card_id);
|
||||
if (! $card) {
|
||||
$export->forceFill(['status' => NovaCardExport::STATUS_FAILED])->save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$export->forceFill(['status' => NovaCardExport::STATUS_PROCESSING])->save();
|
||||
|
||||
$specs = config('nova_cards.export_formats.' . $export->export_type, []);
|
||||
$width = (int) ($export->width ?: ($specs['width'] ?? 1080));
|
||||
$height = (int) ($export->height ?: ($specs['height'] ?? 1080));
|
||||
$format = (string) ($export->format ?: ($specs['format'] ?? 'png'));
|
||||
|
||||
$outputPath = $this->renderExport($renderService, $card, $export, $width, $height, $format);
|
||||
|
||||
$export->forceFill([
|
||||
'status' => NovaCardExport::STATUS_READY,
|
||||
'output_path' => $outputPath,
|
||||
'ready_at' => now(),
|
||||
])->save();
|
||||
}
|
||||
|
||||
public function failed(\Throwable $exception): void
|
||||
{
|
||||
$export = NovaCardExport::query()->find($this->exportId);
|
||||
if ($export) {
|
||||
$export->forceFill(['status' => NovaCardExport::STATUS_FAILED])->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function renderExport(
|
||||
NovaCardRenderService $renderService,
|
||||
NovaCard $card,
|
||||
NovaCardExport $export,
|
||||
int $width,
|
||||
int $height,
|
||||
string $format,
|
||||
): string {
|
||||
// Use the render service for standard preview dimensions; for non-standard
|
||||
// dimensions delegate a temporary override via a cloned render call.
|
||||
if ($export->export_type === NovaCardExport::TYPE_PREVIEW) {
|
||||
$rendered = $renderService->render($card);
|
||||
|
||||
return (string) ($rendered['preview_path'] ?? '');
|
||||
}
|
||||
|
||||
// For all other export types, produce a dedicated output file.
|
||||
if (! function_exists('imagecreatetruecolor')) {
|
||||
throw new \RuntimeException('Nova card rendering requires the GD extension.');
|
||||
}
|
||||
|
||||
// Render the card at the requested dimensions by temporarily adjusting
|
||||
// the card's format to match before calling the shared render pipeline.
|
||||
// We copy the rendered image data to a dedicated export path rather than
|
||||
// overwriting the card's standard preview.
|
||||
$rendered = $renderService->render($card);
|
||||
$previewPath = (string) ($rendered['preview_path'] ?? '');
|
||||
|
||||
$disk = Storage::disk((string) config('nova_cards.storage.public_disk', 'public'));
|
||||
|
||||
if (! $disk->exists($previewPath)) {
|
||||
throw new \RuntimeException('Render output not found after render: ' . $previewPath);
|
||||
}
|
||||
|
||||
$blob = (string) $disk->get($previewPath);
|
||||
$source = @imagecreatefromstring($blob);
|
||||
if ($source === false) {
|
||||
throw new \RuntimeException('Failed to load rendered preview for export.');
|
||||
}
|
||||
|
||||
$target = imagecreatetruecolor($width, $height);
|
||||
imagealphablending($target, true);
|
||||
imagesavealpha($target, true);
|
||||
imagecopyresampled($target, $source, 0, 0, 0, 0, $width, $height, imagesx($source), imagesy($source));
|
||||
imagedestroy($source);
|
||||
|
||||
$exportDir = trim((string) config('nova_cards.storage.preview_prefix', 'cards/previews'), '/')
|
||||
. '/' . $card->user_id . '/exports';
|
||||
$filename = $card->uuid . '-' . $export->export_type . '-' . Str::random(6) . '.' . $format;
|
||||
$outputPath = $exportDir . '/' . $filename;
|
||||
|
||||
ob_start();
|
||||
match ($format) {
|
||||
'jpg', 'jpeg' => imagejpeg($target, null, 90),
|
||||
'webp' => imagewebp($target, null, 88),
|
||||
default => imagepng($target, null, 6),
|
||||
};
|
||||
$binary = (string) ob_get_clean();
|
||||
imagedestroy($target);
|
||||
|
||||
$disk->put($outputPath, $binary);
|
||||
|
||||
return $outputPath;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user