netgescon-day0/app/Services/Catalog/ProductAssetIngestionService.php

151 lines
5.5 KiB
PHP

<?php
namespace App\Services\Catalog;
use App\Models\Fornitore;
use App\Models\Product;
use App\Models\ProductMedia;
use App\Support\ArchivioPaths;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class ProductAssetIngestionService
{
/**
* @return array<string,int>
*/
public function ingestForFornitore(Fornitore $fornitore, int $limit = 50, bool $dryRun = false): array
{
$stats = [
'products' => 0,
'images' => 0,
'documents' => 0,
'skipped_existing' => 0,
'skipped_unsupported' => 0,
'failed' => 0,
];
$products = Product::query()
->where('default_fornitore_id', (int) $fornitore->id)
->orderBy('id')
->limit(max(1, $limit))
->get(['id', 'internal_code', 'meta']);
foreach ($products as $product) {
$stats['products']++;
$catalog = is_array($product->meta['catalog'] ?? null) ? $product->meta['catalog'] : [];
$links = is_array($catalog['private_source_links'] ?? null) ? $catalog['private_source_links'] : [];
$imageResult = $this->ingestSingleAsset($product, $fornitore, (string) ($links['image_link'] ?? ''), 'image', $dryRun);
$stats[$imageResult['bucket']]++;
$documentResult = $this->ingestSingleAsset($product, $fornitore, (string) ($links['product_page'] ?? ''), 'document', $dryRun);
$stats[$documentResult['bucket']]++;
}
return $stats;
}
/**
* @return array{bucket:string}
*/
private function ingestSingleAsset(Product $product, Fornitore $fornitore, string $url, string $mediaType, bool $dryRun): array
{
$url = trim($url);
if ($url === '' || ! str_starts_with($url, 'http')) {
return ['bucket' => 'skipped_unsupported'];
}
$existing = ProductMedia::query()
->where('product_id', (int) $product->id)
->where('media_type', $mediaType)
->whereIn('disk', ['local', 'public'])
->exists();
if ($existing) {
return ['bucket' => 'skipped_existing'];
}
if ($dryRun) {
return ['bucket' => $mediaType === 'image' ? 'images' : 'documents'];
}
try {
$response = Http::timeout(20)
->withHeaders(['User-Agent' => 'NetgesconCatalogBot/1.0'])
->get($url);
} catch (\Throwable) {
return ['bucket' => 'failed'];
}
if (! $response->successful()) {
return ['bucket' => 'failed'];
}
$mime = strtolower(trim((string) $response->header('Content-Type')));
if ($mime !== '' && str_contains($mime, ';')) {
$mime = trim((string) Str::before($mime, ';'));
}
if ($mediaType === 'image' && ! str_starts_with($mime, 'image/')) {
return ['bucket' => 'skipped_unsupported'];
}
if ($mediaType === 'document' && ! str_contains($mime, 'pdf') && ! str_ends_with(Str::lower(parse_url($url, PHP_URL_PATH) ?: ''), '.pdf')) {
return ['bucket' => 'skipped_unsupported'];
}
$extension = $this->guessExtension($url, $mime, $mediaType);
$folder = $this->buildMediaFolder($fornitore, $product, $mediaType);
$filename = Str::slug((string) ($product->internal_code ?? ('product-' . $product->id))) . '-' . substr(sha1($url), 0, 12) . '.' . $extension;
$path = $folder . '/' . $filename;
Storage::disk('public')->put($path, $response->body());
ProductMedia::query()->create([
'product_id' => (int) $product->id,
'media_type' => $mediaType,
'disk' => 'public',
'path' => $path,
'title' => $mediaType === 'image' ? 'Immagine catalogo interna' : 'Scheda prodotto interna',
'mime_type' => $mime !== '' ? $mime : null,
'size_bytes' => strlen($response->body()),
'sort_order' => 0,
'meta' => [
'source_url' => $url,
'source_system' => 'private_catalog_link',
'internalized' => true,
],
]);
return ['bucket' => $mediaType === 'image' ? 'images' : 'documents'];
}
private function buildMediaFolder(Fornitore $fornitore, Product $product, string $mediaType): string
{
$base = ArchivioPaths::fornitoreBase($fornitore);
if (! is_string($base) || $base === '') {
$base = 'catalogo/fornitori/' . ($fornitore->codice_univoco ?: $fornitore->id);
}
return trim('catalogo-interno/' . trim($base, '/') . '/prodotti/' . ($product->internal_code ?: $product->id) . '/' . $mediaType, '/');
}
private function guessExtension(string $url, string $mime, string $mediaType): string
{
$path = Str::lower((string) (parse_url($url, PHP_URL_PATH) ?: ''));
$ext = pathinfo($path, PATHINFO_EXTENSION);
if (is_string($ext) && $ext !== '') {
return $ext;
}
return match (true) {
str_contains($mime, 'png') => 'png',
str_contains($mime, 'webp') => 'webp',
str_contains($mime, 'jpeg'), str_contains($mime, 'jpg') => 'jpg',
$mediaType === 'document' => 'pdf',
default => 'bin',
};
}
}