203 lines
8 KiB
PHP
203 lines
8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Dto\ArcadiaIncoDataDto;
|
|
use App\Dto\ArcadiaIncoPreviewDto;
|
|
use Domain\Enums\Nutriscore;
|
|
use Domain\Models\ValueObjects\NutritionalValues;
|
|
use Psr\Log\LoggerInterface;
|
|
use Uwil\ArcadiaApiPhpClient\Error\ApiError;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco as LibInco;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo as LibIncoCompo;
|
|
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId;
|
|
use Uwil\ArcadiaApiPhpClient\Service\IncoService as LibIncoService;
|
|
|
|
class ArcadiaIncoService
|
|
{
|
|
public function __construct(
|
|
private readonly LibIncoService $incoService,
|
|
private readonly LoggerInterface $logger,
|
|
) {
|
|
}
|
|
|
|
public function fetchInco(int $noArt): ?ArcadiaIncoDataDto
|
|
{
|
|
$action = "INCO sheet #{$noArt}";
|
|
$this->logger->debug("Fetching {$action} from Arcadia API");
|
|
|
|
try {
|
|
$incoId = new IncoId((string) $noArt);
|
|
} catch (\DomainException $e) {
|
|
$this->logger->warning("Invalid IncoId format for #{$noArt}: {$e->getMessage()}");
|
|
|
|
return null;
|
|
}
|
|
|
|
$inco = $this->incoService->findBy($incoId);
|
|
|
|
if ($inco instanceof ApiError) {
|
|
$this->logger->error("Failed to fetch {$action}: {$inco->message()}");
|
|
|
|
throw new \RuntimeException($inco->message());
|
|
}
|
|
|
|
if (! ($inco instanceof LibInco)) {
|
|
$this->logger->info("{$action} not found on Arcadia API (404)");
|
|
|
|
return null;
|
|
}
|
|
|
|
$compo = $this->incoService->findCompo($incoId);
|
|
if ($compo instanceof ApiError) {
|
|
$this->logger->warning("Failed to fetch composition for {$action}: {$compo->message()}");
|
|
$compo = null;
|
|
}
|
|
|
|
return $this->mapToDataDto($inco, $compo instanceof LibIncoCompo ? $compo : null);
|
|
}
|
|
|
|
public function getPreview(int $noArt): ?ArcadiaIncoPreviewDto
|
|
{
|
|
$data = $this->fetchInco($noArt);
|
|
|
|
if ($data === null) {
|
|
return null;
|
|
}
|
|
|
|
return new ArcadiaIncoPreviewDto(
|
|
noArt: $data->noArt,
|
|
name: $data->name,
|
|
libelle: $data->libelle,
|
|
brand: $data->brand,
|
|
nutriscore: $data->nutriscore?->value,
|
|
servings: $data->servings,
|
|
allergens: $data->allergens,
|
|
ingredients: $data->ingredients,
|
|
preparation: $data->preparation,
|
|
energyKcal: $data->nutrition->kcal,
|
|
energyKj: $data->nutrition->kj,
|
|
fat: $data->nutrition->fatG,
|
|
satFat: $data->nutrition->satFatG,
|
|
carbs: $data->nutrition->carbG,
|
|
sugar: $data->nutrition->sugarG,
|
|
fiber: $data->nutrition->fiberG,
|
|
protein: $data->nutrition->proteinG,
|
|
salt: $data->nutrition->saltG,
|
|
);
|
|
}
|
|
|
|
private function mapToDataDto(LibInco $inco, ?LibIncoCompo $compo): ArcadiaIncoDataDto
|
|
{
|
|
$ingredientsHtml = $this->buildIngredientsHtml($inco);
|
|
|
|
$preparationHtml = ! empty($inco->preparationAdvices)
|
|
? '<p>' . nl2br(e(trim($inco->preparationAdvices))) . '</p>'
|
|
: null;
|
|
|
|
$sellingArgumentHtml = ! empty($inco->sellArguments)
|
|
? '<p>' . nl2br(e(trim($inco->sellArguments))) . '</p>'
|
|
: null;
|
|
|
|
$servings = $inco->portions !== null ? (string) $inco->portions : null;
|
|
if ($servings !== null && mb_strlen($servings) > 20) {
|
|
$servings = mb_substr($servings, 0, 20);
|
|
}
|
|
|
|
$allergens = ! empty($inco->allergens)
|
|
? implode(', ', array_map(fn ($a) => $a->label, $inco->allergens))
|
|
: null;
|
|
|
|
$nutriscore = $this->resolveNutriscore($inco, $compo);
|
|
$nutrition = $compo !== null ? $this->mapNutrition($compo) : new NutritionalValues();
|
|
|
|
return new ArcadiaIncoDataDto(
|
|
noArt: (int) $inco->id->value,
|
|
name: $inco->name,
|
|
libelle: $inco->libelle,
|
|
brand: $inco->brand->label !== '' ? $inco->brand->label : $inco->brand->code->value,
|
|
ingredients: $ingredientsHtml,
|
|
preparation: $preparationHtml,
|
|
servings: $servings,
|
|
allergens: $allergens,
|
|
sellingArgument: $sellingArgumentHtml,
|
|
nutriscore: $nutriscore,
|
|
nutriscoreArgs: null,
|
|
nutrition: $nutrition,
|
|
);
|
|
}
|
|
|
|
private function buildIngredientsHtml(LibInco $inco): ?string
|
|
{
|
|
if ($inco->ingredients->isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
$paragraphs = [];
|
|
foreach ($inco->ingredients->groups as $title => $lines) {
|
|
$text = implode(', ', $lines);
|
|
if (is_string($title) && ! is_numeric($title) && $title !== '') {
|
|
$paragraphs[] = '<p><strong>' . e($title) . ' :</strong> ' . e($text) . '</p>';
|
|
} else {
|
|
$paragraphs[] = '<p>' . e($text) . '</p>';
|
|
}
|
|
}
|
|
|
|
return implode('', $paragraphs);
|
|
}
|
|
|
|
private function resolveNutriscore(LibInco $inco, ?LibIncoCompo $compo): ?Nutriscore
|
|
{
|
|
$nutriscoreStr = $inco->nutriscore?->grade;
|
|
if (empty($nutriscoreStr) && $compo !== null && ! empty($compo->nutriScore->classement)) {
|
|
$nutriscoreStr = $compo->nutriScore->classement;
|
|
}
|
|
|
|
return ! empty($nutriscoreStr) ? Nutriscore::tryFrom(mb_strtoupper(trim($nutriscoreStr))) : null;
|
|
}
|
|
|
|
private function mapNutrition(LibIncoCompo $compo): NutritionalValues
|
|
{
|
|
$parseFloat = static function (?string $val): ?float {
|
|
if ($val === null || trim($val) === '' || ! is_numeric(trim($val))) {
|
|
return null;
|
|
}
|
|
$floatVal = (float) trim($val);
|
|
|
|
return $floatVal >= 0 ? $floatVal : null;
|
|
};
|
|
|
|
return new NutritionalValues(
|
|
refWeightG: $parseFloat($compo->energie->tailleReference),
|
|
kj: $parseFloat($compo->energie->valEnergKjReference),
|
|
kcal: $parseFloat($compo->energie->valEnergKcalReference),
|
|
fatG: $parseFloat($compo->macronutrimentsRef->matGrassReference),
|
|
satFatG: $parseFloat($compo->macronutrimentsRef->acideGrasSatureReference),
|
|
carbG: $parseFloat($compo->macronutrimentsRef->glucidesReference),
|
|
sugarG: $parseFloat($compo->macronutrimentsRef->sucreReference),
|
|
fiberG: $parseFloat($compo->macronutrimentsRef->fibAlimReference),
|
|
proteinG: $parseFloat($compo->macronutrimentsRef->proteineReference),
|
|
saltG: $parseFloat($compo->macronutrimentsRef->selReference),
|
|
servingSizeG: $parseFloat($compo->energie->taillePortion),
|
|
servingKj: $parseFloat($compo->energie->valEnergKjPortion),
|
|
servingKcal: $parseFloat($compo->energie->valEnergKcalPortion),
|
|
servingFatG: $parseFloat($compo->macronutrimentsPortion->matGrassPortion),
|
|
servingSatFatG: $parseFloat($compo->macronutrimentsPortion->acideGrasSaturePortion),
|
|
servingCarbG: $parseFloat($compo->macronutrimentsPortion->glucidesPortion),
|
|
servingSugarG: $parseFloat($compo->macronutrimentsPortion->sucrePortion),
|
|
servingFiberG: $parseFloat($compo->macronutrimentsPortion->fibAlimPortion),
|
|
servingProteinG: $parseFloat($compo->macronutrimentsPortion->proteinePortion),
|
|
servingSaltG: $parseFloat($compo->macronutrimentsPortion->selPortion),
|
|
dailyEnergy: $parseFloat($compo->apportsReference->pourcentApportRefKjoP),
|
|
dailyFat: $parseFloat($compo->apportsReference->pourcentApportRefFatP),
|
|
dailySatFat: $parseFloat($compo->apportsReference->pourcentApportRefFatsatP),
|
|
dailyCarb: $parseFloat($compo->apportsReference->pourcentApportRefChovalP),
|
|
dailySugar: $parseFloat($compo->apportsReference->pourcentApportRefSugarP),
|
|
dailyFiber: null,
|
|
dailyProtein: $parseFloat($compo->apportsReference->pourcentApportRefProP),
|
|
dailySalt: $parseFloat($compo->apportsReference->pourcentApportRefSalteqP),
|
|
);
|
|
}
|
|
}
|