Files
My-KOPKB/be/Modules/User/Services/MemberDigitalCardService.php
ismailmasseran d06c4701a4
Build Docker Image / build-backend (push) Successful in 1m56s
Build Docker Image / build-frontend (push) Successful in 1m55s
Dev/v1.2 (#4)
Co-authored-by: ISMAIL MASSERAN <topaz@Mac.dlinkrouter.local>
Reviewed-on: #4
2026-07-06 12:50:55 +08:00

142 lines
4.2 KiB
PHP

<?php
namespace Modules\User\Services;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\View;
use Modules\Auth\Entities\User;
use Spatie\Browsershot\Browsershot;
class MemberDigitalCardService
{
public const CARD_WIDTH = 840;
public const CARD_HEIGHT = 540;
public function renderPng(User $user, string $side): string
{
$view = $side === 'belakang'
? 'user::digital-card.back'
: 'user::digital-card.front';
$html = View::make($view, $this->prepareViewData($user))->render();
return $this->makeBrowsershot($html)->screenshot();
}
/**
* @return array<string, mixed>
*/
protected function prepareViewData(User $user): array
{
$user = app(PublicMemberProfileService::class)->loadVerifiableRelations($user);
$user->ensurePublicProfileToken();
$currentEmployment = $user->employments->firstWhere('is_current', true)
?? $user->employments->first();
$logoPath = config('user.digital_card_logo_path');
$profileUrl = rtrim(config('user.frontend_url'), '/').'/v/'.$user->public_profile_token;
return [
'memberNumber' => $this->displayCardValue($user->member_number),
'memberName' => $user->name ?: '-',
'memberType' => $this->displayCardValue($user->member_type),
'companyName' => $this->displayCardValue($currentEmployment?->company_name),
'avatarDataUri' => $this->resolveStorageDataUri($user->image_url),
'avatarInitials' => $this->avatarInitials($user->name),
'logoDataUri' => $this->resolveFileDataUri($logoPath),
'profileUrl' => $profileUrl,
'qrDataUri' => $this->generateQrDataUri($profileUrl),
];
}
protected function makeBrowsershot(string $html): Browsershot
{
$browsershot = Browsershot::html($html)
->setNodeModulePath(config('browsershot.node_module_path'))
->timeout(config('browsershot.timeout'))
->windowSize(self::CARD_WIDTH, self::CARD_HEIGHT)
->deviceScaleFactor(2)
->showBackground();
if ($nodeBinary = config('browsershot.node_binary')) {
$browsershot->setNodeBinary($nodeBinary);
}
if ($npmBinary = config('browsershot.npm_binary')) {
$browsershot->setNpmBinary($npmBinary);
}
if ($chromePath = config('browsershot.chrome_path')) {
$browsershot->setChromePath($chromePath);
}
if (config('browsershot.no_sandbox')) {
$browsershot->noSandbox();
}
return $browsershot;
}
protected function generateQrDataUri(string $content): string
{
$renderer = new ImageRenderer(
new RendererStyle(220, 1),
new SvgImageBackEnd
);
$writer = new Writer($renderer);
$svg = $writer->writeString($content);
return 'data:image/svg+xml;base64,'.base64_encode($svg);
}
protected function resolveFileDataUri(?string $path): ?string
{
if (empty($path) || ! file_exists($path)) {
return null;
}
$mime = mime_content_type($path) ?: 'image/svg+xml';
return 'data:'.$mime.';base64,'.base64_encode(file_get_contents($path));
}
protected function resolveStorageDataUri(?string $path): ?string
{
if (blank($path)) {
return null;
}
$fullPath = Storage::disk('public')->path($path);
return $this->resolveFileDataUri($fullPath);
}
protected function avatarInitials(?string $name): string
{
$trimmed = trim((string) $name);
if ($trimmed === '') {
return '--';
}
return strtoupper(mb_substr($trimmed, 0, 2));
}
protected function displayCardValue(mixed $value): string
{
if ($value === null || $value === '') {
return '-';
}
$trimmed = trim((string) $value);
return $trimmed !== '' ? $trimmed : '-';
}
}