Compare commits
No commits in common. "main" and "init" have entirely different histories.
180 changed files with 1741 additions and 16770 deletions
|
|
@ -1,21 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.php]
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.scss]
|
||||
quote_type = single
|
||||
|
||||
[*.css]
|
||||
quote_type = single
|
||||
6
.gitattributes
vendored
6
.gitattributes
vendored
|
|
@ -1,6 +0,0 @@
|
|||
/tests export-ignore
|
||||
/.gitlab-ci.yml export-ignore
|
||||
/packages export-ignore
|
||||
/phpstan.neon.dist export-ignore
|
||||
.gitignore export-ignore
|
||||
.gitattributes export-ignore
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -1,10 +1 @@
|
|||
/packages/
|
||||
/vendor/
|
||||
.php-cs-fixer.cache
|
||||
junit.xml
|
||||
.phpunit.cache
|
||||
coverage
|
||||
coverage.xml
|
||||
test.php
|
||||
.scannerwork/
|
||||
/composer.lock
|
||||
|
|
|
|||
110
.gitlab-ci.yml
110
.gitlab-ci.yml
|
|
@ -1,110 +0,0 @@
|
|||
include:
|
||||
- project: "ldc-web/shared-ci"
|
||||
ref: "main"
|
||||
file: "lint/phpcs.yml"
|
||||
- project: "ldc-web/shared-ci"
|
||||
ref: "main"
|
||||
file: "lint/phpstan.yml"
|
||||
- project: "ldc-web/shared-ci"
|
||||
ref: "main"
|
||||
file: "security/composer.yml"
|
||||
- project: "ldc-web/shared-ci"
|
||||
ref: "main"
|
||||
file: "test/test.yml"
|
||||
|
||||
stages:
|
||||
- lint
|
||||
- security
|
||||
- test
|
||||
- publish
|
||||
|
||||
phplint:
|
||||
stage: lint
|
||||
image: $CI_REGISTRY/ldc-web/projets/ldc-web-images-docker/uwil-ci-8.5:latest
|
||||
variables:
|
||||
TEXT_REPORT: php-l-report.txt
|
||||
before_script:
|
||||
- composer config gitlab-token.lab.s-web.fr $CI_DEPLOY_USER $CI_DEPLOY_PASSWORD
|
||||
- composer install --no-scripts --no-interaction --no-ansi --no-progress --prefer-dist
|
||||
script:
|
||||
- composer run-script lint:php > "$TEXT_REPORT" 2>&1; status=$?
|
||||
- cat "$TEXT_REPORT"
|
||||
- if [ $status -ne 0 ] || grep -qE "Parse error|Errors parsing" "$TEXT_REPORT"; then echo "PHP lint failed"; exit 1; fi
|
||||
artifacts:
|
||||
when: always
|
||||
expose_as: "php-l report"
|
||||
paths:
|
||||
- php-l-report.txt
|
||||
allow_failure: false
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
|
||||
pint:
|
||||
stage: lint
|
||||
image: $CI_REGISTRY/ldc-web/projets/ldc-web-images-docker/uwil-ci-8.5:latest
|
||||
variables:
|
||||
PINT_REPORT: pint-report.txt
|
||||
before_script:
|
||||
- composer config gitlab-token.lab.s-web.fr $CI_DEPLOY_USER $CI_DEPLOY_PASSWORD
|
||||
- composer install --no-scripts --no-interaction --no-ansi --no-progress --prefer-dist
|
||||
script:
|
||||
- vendor/bin/pint --test > "$PINT_REPORT" 2>&1; status=$?
|
||||
- cat "$PINT_REPORT"
|
||||
- if [ $status -ne 0 ] || grep -qE "ERROR|ERRORS|ERROR:" "$PINT_REPORT"; then echo "Pint reported errors"; exit 1; fi
|
||||
artifacts:
|
||||
when: always
|
||||
expose_as: "pint report"
|
||||
paths:
|
||||
- pint-report.txt
|
||||
allow_failure: false
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
|
||||
pest:
|
||||
stage: test
|
||||
image: $CI_REGISTRY/ldc-web/projets/ldc-web-images-docker/uwil-ci-8.5:latest
|
||||
variables:
|
||||
JUNIT_REPORT: junit.xml
|
||||
TEXT_REPORT: test-output.txt
|
||||
COVERAGE_REPORT: coverage.xml
|
||||
before_script:
|
||||
- composer config gitlab-token.lab.s-web.fr $CI_DEPLOY_USER $CI_DEPLOY_PASSWORD
|
||||
- composer install --no-scripts --no-interaction --no-ansi --no-progress --prefer-dist
|
||||
script:
|
||||
- rm -f "$JUNIT_REPORT" "$COVERAGE_REPORT"
|
||||
- composer run-script test:coverage -- --min=85 > "$TEXT_REPORT" 2>&1; status=$?
|
||||
- cat "$TEXT_REPORT"
|
||||
- if [ -f "$COVERAGE_REPORT" ]; then echo "Coverage report generated $COVERAGE_REPORT"; else echo "No coverage report found"; fi
|
||||
- exit $status
|
||||
artifacts:
|
||||
when: always
|
||||
expose_as: "pest report"
|
||||
paths:
|
||||
- junit.xml
|
||||
- test-output.txt
|
||||
- coverage.xml
|
||||
reports:
|
||||
junit: junit.xml
|
||||
allow_failure: false
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
||||
|
||||
publish:
|
||||
stage: publish
|
||||
image: curlimages/curl:latest
|
||||
variables:
|
||||
URL: "$CI_SERVER_PROTOCOL://$CI_SERVER_HOST:$CI_SERVER_PORT/api/v4/projects/$CI_PROJECT_ID/packages/composer?job_token=$CI_JOB_TOKEN"
|
||||
script:
|
||||
- version=$(echo "tag=$CI_COMMIT_TAG")
|
||||
- insecure=$([ "$CI_SERVER_PROTOCOL" = "http" ] && echo "--insecure" || echo "")
|
||||
- response=$(curl -s -w "\n%{http_code}" $insecure --data $version $URL)
|
||||
- code=$(echo "$response" | tail -n 1)
|
||||
- body=$(echo "$response" | head -n 1)
|
||||
- if [ "$code" -eq 201 ]; then
|
||||
echo "Package created - Code $code - $body";
|
||||
else
|
||||
echo "Could not create package - Code $code - $body";
|
||||
exit 1;
|
||||
fi
|
||||
rules:
|
||||
- if: '$CI_COMMIT_TAG =~ /^\d+.\d+.\d+/'
|
||||
File diff suppressed because it is too large
Load diff
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"bmewburn.vscode-intelephense-client",
|
||||
"editorconfig.editorconfig",
|
||||
"valeryanm.vscode-phpsab",
|
||||
"open-southeners.laravel-pint"
|
||||
]
|
||||
}
|
||||
20
.vscode/launch.json
vendored
20
.vscode/launch.json
vendored
|
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9003
|
||||
},
|
||||
{
|
||||
"name": "Listen for Xdebug (Docker)",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9004,
|
||||
"pathMappings": {
|
||||
"/app": "${workspaceFolder}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
111
.vscode/settings.json
vendored
111
.vscode/settings.json
vendored
|
|
@ -1,111 +0,0 @@
|
|||
{
|
||||
"sonarlint.analysisExcludes": "vendor/**,packages/**,junit.xml,coverage/**",
|
||||
"php.version": "8.5.0",
|
||||
"intelephense.environment.phpVersion": "8.5.0",
|
||||
"intelephense.environment.includePaths": [
|
||||
"./vendor/pestphp",
|
||||
"./vendor/phpunit/phpunit",
|
||||
"./vendor/laravel"
|
||||
],
|
||||
"phpSniffer.executablePath": "${workspaceFolder}/vendor/bin/phpcs",
|
||||
"phpcs.executablePath": "${workspaceFolder}/vendor/bin/phpcs",
|
||||
"phpsab.executablePathCS": "vendor/bin/phpcs",
|
||||
"phpsab.executablePathCBF": "vendor/bin/phpcbf",
|
||||
"phpsab.standard": "phpcs.xml",
|
||||
"phpsab.autoRulesetSearch": false,
|
||||
"phpstan.binCommand": [
|
||||
"${workspaceFolder}/vendor/bin/phpstan"
|
||||
],
|
||||
"phpstan.configFile": "phpstan.neon.dist",
|
||||
"files.associations": { "*.module": "php" },
|
||||
"php.suggest.basic": false,
|
||||
"laravel-pint.enable": true,
|
||||
"[php]": {
|
||||
"editor.formatOnSave": false
|
||||
},
|
||||
"editor.formatOnSave": true,
|
||||
"eslint.validate": ["javascript", "typescript", "html", "vue"],
|
||||
"[html]": {
|
||||
"html.format.enable": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "always"
|
||||
}
|
||||
},
|
||||
"[blade]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": false
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.autoIndent": "full",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "always"
|
||||
},
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"editor.detectIndentation": false,
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "stylelint.vscode-stylelint",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.stylelint": "always"
|
||||
}
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.autoIndent": "full",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "always"
|
||||
},
|
||||
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
|
||||
"editor.detectIndentation": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "file"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "always"
|
||||
}
|
||||
},
|
||||
"peacock.color": "#022922",
|
||||
"emeraldwalk.runonsave": {
|
||||
"autoClearConsole": false,
|
||||
"commands": [
|
||||
{
|
||||
"match": "\\.php$",
|
||||
"isAsync": true,
|
||||
"cmd": "${workspaceFolder}/vendor/bin/pint ${file}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#045a4a",
|
||||
"activityBar.background": "#045a4a",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#d029f5",
|
||||
"activityBarBadge.foreground": "#e7e7e7",
|
||||
"activityBarTop.activeBackground": "#045a4a",
|
||||
"activityBarTop.background": "#045a4a",
|
||||
"activityBarTop.foreground": "#e7e7e7",
|
||||
"activityBarTop.inactiveForeground": "#e7e7e799",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"commandCenter.foreground": "#e7e7e7",
|
||||
"sash.hoverBorder": "#045a4a",
|
||||
"statusBar.background": "#022922",
|
||||
"statusBar.debuggingBackground": "#022922",
|
||||
"statusBar.debuggingForeground": "#e7e7e7",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#045a4a",
|
||||
"statusBarItem.remoteBackground": "#9709b6",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#022922",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#02292299",
|
||||
"titleBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBar.activeBorder": "#e7e7e7",
|
||||
"activityBarTop.activeBorder": "#e7e7e7"
|
||||
}
|
||||
}
|
||||
19
.vscode/tasks.json
vendored
19
.vscode/tasks.json
vendored
|
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Pint Formatter",
|
||||
"type": "shell",
|
||||
"command": "./vendor/bin/pint",
|
||||
"problemMatcher": [],
|
||||
"presentation": {
|
||||
"reveal": "silent",
|
||||
"close": true
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
21
LICENSE
21
LICENSE
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 LDC Digital
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
343
README.md
343
README.md
|
|
@ -1,325 +1,32 @@
|
|||
# Arcadia API PHP Client
|
||||
# Arcadia Genesis PHP Client
|
||||
|
||||
[](https://www.php.net/)
|
||||
[](LICENSE)
|
||||
[](https://pestphp.com/)
|
||||
[](https://phpstan.org/)
|
||||
|
||||
Client PHP haut niveau, fortement typé et orienté domaine pour l'API Arcadia.
|
||||
Il s'appuie sur le client OpenAPI généré par Kiota (`uwil/arcadia-kiota-open-api-client`) et fournit :
|
||||
- Une couche de services métier (`EmployeeService`, `IncoService`, `BrandService`, `ApiService`).
|
||||
- Des Value Objects stricts (`ArcadiaEmployeeId`, `IncoId`, `IncoCompoId`, `BrandCode`, `SiteId`, etc.).
|
||||
- Des DTOs riches (`Employee`, `Inco`, `IncoCompo`, `IncoShort`, `Brand`) avec typage complet et conformité `JsonSerializable`.
|
||||
- Une gestion unifiée de la pagination (`PageOf`, `Pagination`) et du parcours continu par flux (`StreamOf`).
|
||||
- L'authentification automatique avec signature HMAC (`HMAC-SHA256`) et gestion des nonces.
|
||||
- Le support de la journalisation (PSR-3 `LoggerInterface`) et de l'observabilité (OpenTelemetry).
|
||||
- Une gestion explicite des erreurs via la hiérarchie `ApiError`.
|
||||
|
||||
---
|
||||
|
||||
## Sommaire
|
||||
|
||||
- [Prérequis](#prérequis)
|
||||
- [Installation](#installation)
|
||||
- [Initialisation](#initialisation)
|
||||
- [Services et Utilisation](#services-et-utilisation)
|
||||
- [Vérification de l'API (`apiService`)](#vérification-de-lapi-apiservice)
|
||||
- [Gestion des Collaborateurs (`employees`)](#gestion-des-collaborateurs-employees)
|
||||
- [Fiches INCO & Compositions (`incoSheets`)](#fiches-inco--compositions-incosheets)
|
||||
- [Gestion des Marques (`brands`)](#gestion-des-marques-brands)
|
||||
- [Pagination et Streaming](#pagination-et-streaming)
|
||||
- [Gestion des Erreurs](#gestion-des-erreurs)
|
||||
- [Tests et Qualité](#tests-et-qualité)
|
||||
- [Licence](#licence)
|
||||
|
||||
---
|
||||
|
||||
## Prérequis
|
||||
|
||||
- **PHP 8.5+**
|
||||
- Extensions PHP : `ext-curl`, `ext-mbstring`
|
||||
- Accès au registre GitLab Composer de LDC Digital
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Comme cette bibliothèque et sa sous-dépendance Kiota sont hébergées sur le registre privé GitLab de LDC, configurez le dépôt Composer dans le `composer.json` de votre projet :
|
||||
|
||||
```json
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Puis installez le client via Composer :
|
||||
|
||||
```bash
|
||||
composer require uwil/arcadia-api-php-client
|
||||
```
|
||||
|
||||
Assurez-vous d'avoir configuré vos identifiants d'accès GitLab pour Composer (via `auth.json` ou variable d'environnement CI) :
|
||||
|
||||
```bash
|
||||
composer config gitlab-token.lab.s-web.fr <deploy-token-user> <deploy-token-password>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Initialisation
|
||||
|
||||
L'entrée principale de la bibliothèque est [`ArcadiaApiClient`](src/ArcadiaApiClient.php), configurée avec [`ApiConfig`](src/Lib/ApiConfig.php) :
|
||||
## Models
|
||||
|
||||
### Employee
|
||||
#### Props
|
||||
```php
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
use Uwil\ArcadiaApiPhpClient\ArcadiaApiClient;
|
||||
use Uwil\ArcadiaApiPhpClient\Auth\ClientIdentifier;
|
||||
use Uwil\ArcadiaApiPhpClient\Auth\ClientSecret;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiConfig;
|
||||
|
||||
$config = new ApiConfig(
|
||||
endpoint: Utils::uriFor('https://arcadia-api.domain.tld/api'),
|
||||
apiKey: new ClientIdentifier('VOTRE_API_KEY'),
|
||||
secret: new ClientSecret('VOTRE_SECRET_KEY'),
|
||||
curlTimeOut: 20.0, // secondes (optionnel, défaut: 20.0)
|
||||
maxRetries: 3, // tentatives en cas d'erreur réseau (optionnel, défaut: 0)
|
||||
retryDelaySeconds: 1, // délai entre les retries (optionnel, défaut: 1)
|
||||
ignoreSelfSignedCert: false, // pour environnements locaux de test (défaut: false)
|
||||
logger: null, // ?Psr\Log\LoggerInterface
|
||||
tracer: null, // ?OpenTelemetry\API\Trace\TracerInterface
|
||||
);
|
||||
|
||||
$client = new ArcadiaApiClient($config);
|
||||
public string $matricule;
|
||||
public string $lastname;
|
||||
public string $firstname;
|
||||
public string $start_date;
|
||||
public string|null $end_date;
|
||||
public string|null $planned_end_date;
|
||||
public string $establishment;
|
||||
public string $site;
|
||||
public string $service;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Services et Utilisation
|
||||
|
||||
### Vérification de l'API (`apiService`)
|
||||
|
||||
Permet de vérifier la disponibilité et la santé de l'API Arcadia :
|
||||
|
||||
#### Usage
|
||||
```php
|
||||
$isAlive = $client->apiService()->isAlive();
|
||||
<?php
|
||||
|
||||
if ($isAlive) {
|
||||
echo "L'API Arcadia est joignable et opérationnelle.\n";
|
||||
}
|
||||
use Uwil\ArcadiaGenesisPhpClient\Datas\EmployeesDatas;
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
$employee = EmployeesDatas::getById("0115590");
|
||||
|
||||
var_dump($employee);
|
||||
?>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Gestion des Collaborateurs (`employees`)
|
||||
|
||||
Le service [`EmployeeService`](src/Service/EmployeeService.php) permet de consulter et rechercher les salariés.
|
||||
|
||||
#### Récupérer par identifiant
|
||||
```php
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\ArcadiaEmployeeId;
|
||||
use Uwil\ArcadiaApiPhpClient\Error\ApiError;
|
||||
|
||||
$employee = $client->employees()->findBy(new ArcadiaEmployeeId('0115590'));
|
||||
|
||||
if ($employee instanceof ApiError) {
|
||||
echo "Erreur API: " . $employee->getMessage();
|
||||
} elseif ($employee === null) {
|
||||
echo "Collaborateur non trouvé (404).\n";
|
||||
} else {
|
||||
echo "Salarié: {$employee->firstname} {$employee->lastname} (Site: {$employee->site})\n";
|
||||
}
|
||||
```
|
||||
|
||||
#### Récupérer par site(s)
|
||||
```php
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\SiteId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\EmployeeSorter;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiSorter\EmployeeSortColumn;
|
||||
|
||||
// Pour un site unique avec tri par matricule
|
||||
$page = $client->employees()->getAllOfSites(
|
||||
new SiteId('01'),
|
||||
new EmployeeSorter(EmployeeSortColumn::MATRICULE, 'asc')
|
||||
);
|
||||
|
||||
// Pour plusieurs sites modifiés depuis une date
|
||||
$page = $client->employees()->getAllOfSitesSince(
|
||||
[new SiteId('01'), new SiteId('02')],
|
||||
new DateTimeImmutable('2026-01-01')
|
||||
);
|
||||
```
|
||||
|
||||
#### Streaming de tous les collaborateurs
|
||||
Le streaming parcourt automatiquement toutes les pages de façon transparente grâce à un générateur PHP :
|
||||
|
||||
```php
|
||||
foreach ($client->employees()->streamAll() as $employee) {
|
||||
echo "{$employee->matricule} - {$employee->lastname}\n";
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Fiches INCO & Compositions (`incoSheets`)
|
||||
|
||||
Le service [`IncoService`](src/Service/IncoService.php) gère les fiches techniques produit (INCO) et leurs compositions nutritionnelles détaillées.
|
||||
|
||||
#### Consulter une fiche INCO
|
||||
```php
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId;
|
||||
|
||||
$inco = $client->incoSheets()->findBy(new IncoId('12345'));
|
||||
|
||||
if ($inco !== null && !($inco instanceof ApiError)) {
|
||||
echo "Fiche: {$inco->commercialLabel}\n";
|
||||
echo "Nutri-Score: {$inco->nutriscore?->grade}\n";
|
||||
}
|
||||
```
|
||||
|
||||
#### Consulter la composition nutritionnelle
|
||||
Vous pouvez interroger la composition par son `IncoCompoId` ou directement par l'`IncoId` :
|
||||
|
||||
```php
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoCompoId;
|
||||
|
||||
// Directement via IncoCompoId
|
||||
$compo = $client->incoSheets()->findCompo(new IncoCompoId('98765'));
|
||||
|
||||
// Ou via IncoId
|
||||
$compo = $client->incoSheets()->findCompo(new IncoId('12345'));
|
||||
|
||||
if ($compo !== null && !($compo instanceof ApiError)) {
|
||||
echo "Énergie (kJ): {$compo->energy?->kJ?->value}\n";
|
||||
echo "Acides gras saturés: {$compo->fattyAcids?->saturatedFattyAcids?->value}\n";
|
||||
echo "Points Nutri-Score: {$compo->nutriScorePoints?->totalPoints}\n";
|
||||
}
|
||||
```
|
||||
|
||||
#### Recherche et listage par marque
|
||||
```php
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode;
|
||||
|
||||
// Liste courte (IncoShort) pour optimiser les performances d'affichage
|
||||
$shortsPage = $client->incoSheets()->getAllShortOfBrands(new BrandCode('GAULOIS'));
|
||||
|
||||
// Recherche par motif d'identifiant
|
||||
$results = $client->incoSheets()->findByIncoIdLike('POULET%', new BrandCode('GAULOIS'));
|
||||
|
||||
// Streaming de toutes les fiches d'une marque
|
||||
foreach ($client->incoSheets()->streamAllOfBrands(new BrandCode('LEGAULOIS')) as $sheet) {
|
||||
echo "Fiche #{$sheet->id->value}: {$sheet->commercialLabel}\n";
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Gestion des Marques (`brands`)
|
||||
|
||||
Le service [`BrandService`](src/Service/BrandService.php) donne accès aux marques référencées :
|
||||
|
||||
```php
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode;
|
||||
|
||||
// Récupérer une marque
|
||||
$brand = $client->brands()->findBy(new BrandCode('GAULOIS'));
|
||||
|
||||
// Parcourir toutes les marques
|
||||
foreach ($client->brands()->streamAll() as $brand) {
|
||||
echo "Marque [{$brand->code->value}] : {$brand->label}\n";
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pagination et Streaming
|
||||
|
||||
### Objet `PageOf`
|
||||
|
||||
Toutes les méthodes de liste paginées retournent un objet [`PageOf<T>`](src/Lib/ApiDto/Dto/PageOf.php) contenant les éléments et les métadonnées de pagination :
|
||||
|
||||
```php
|
||||
$page = $client->employees()->getAll();
|
||||
|
||||
// Éléments de la page courante
|
||||
$employees = $page->items;
|
||||
|
||||
// Métadonnées
|
||||
echo "Page actuelle : " . $page->pagination->pageNumber->value . "\n";
|
||||
echo "Taille de page : " . $page->pagination->pageSize->value . "\n";
|
||||
echo "Total éléments : " . $page->pagination->totalItems . "\n";
|
||||
echo "Total pages : " . $page->pagination->totalPages . "\n";
|
||||
```
|
||||
|
||||
### Objet `StreamOf`
|
||||
|
||||
Toutes les méthodes `stream*()` retournent un [`StreamOf<T>`](src/Lib/Pagination/StreamOf.php) qui implémente `\IteratorAggregate`. Il effectue les requêtes de page suivante automatiquement à la demande :
|
||||
|
||||
```php
|
||||
$stream = $client->employees()->streamAll();
|
||||
|
||||
foreach ($stream as $employee) {
|
||||
// Les pages suivantes sont chargées en arrière-plan au fur et à mesure de l'itération
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gestion des Erreurs
|
||||
|
||||
Les méthodes du client ne lancent pas d'exceptions inattendues pour les erreurs HTTP ou de communication ; elles renvoient une instance de [`ApiError`](src/Error/ApiError.php) (ou ses sous-classes spécialisées) :
|
||||
|
||||
```php
|
||||
use Uwil\ArcadiaApiPhpClient\Error\AuthenticationError;
|
||||
use Uwil\ArcadiaApiPhpClient\Error\ValidationError;
|
||||
use Uwil\ArcadiaApiPhpClient\Error\NetworkError;
|
||||
use Uwil\ArcadiaApiPhpClient\Error\RateLimitError;
|
||||
use Uwil\ArcadiaApiPhpClient\Error\ServerError;
|
||||
use Uwil\ArcadiaApiPhpClient\Error\ApiError;
|
||||
|
||||
$result = $client->employees()->findBy(new ArcadiaEmployeeId('0115590'));
|
||||
|
||||
match (true) {
|
||||
$result instanceof AuthenticationError => logAuthFailure($result),
|
||||
$result instanceof ValidationError => logValidationDetails($result->getViolations()),
|
||||
$result instanceof NetworkError => retryLater($result),
|
||||
$result instanceof RateLimitError => handleRateLimit($result),
|
||||
$result instanceof ServerError => notifyAdmin($result),
|
||||
$result instanceof ApiError => logGenericError($result),
|
||||
$result === null => handleNotFound(),
|
||||
default => handleEmployee($result),
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tests et Qualité
|
||||
|
||||
Le projet est rigoureusement testé avec une suite complète de tests unitaires et d'architecture.
|
||||
|
||||
```bash
|
||||
# Lancer les tests unitaires
|
||||
composer test
|
||||
|
||||
# Vérifier la couverture de code (seuil minimal 85% configuré)
|
||||
composer test:coverage
|
||||
|
||||
# Analyse statique PHPStan (niveau 10)
|
||||
composer lint:phpstan
|
||||
|
||||
# Vérifier le style du code (Laravel Pint)
|
||||
composer lint:pint
|
||||
|
||||
# Formater le code automatiquement
|
||||
composer format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
Ce projet est sous licence MIT. Consultez le fichier [LICENSE](LICENSE) pour plus d'informations.
|
||||
### INCO
|
||||
TODO
|
||||
|
|
@ -1,63 +1,22 @@
|
|||
{
|
||||
"name": "uwil/arcadia-api-php-client",
|
||||
"description": "PHP Client implementation for Arcadia API",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Uwil\\ArcadiaApiPhpClient\\": "src/"
|
||||
"name": "uwil/arcadia-genesis-php-client",
|
||||
"description": "PHP Client implementation for Arcadia Genesis API",
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Uwil\\ArcadiaGenesisPhpClient\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "LDC Digital",
|
||||
"email": "digital@ldc.fr"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"ext-mbstring": "*",
|
||||
"guzzlehttp/guzzle": "^7.10",
|
||||
"nesbot/carbon": "^3.11"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "LDC Digital",
|
||||
"email": "digital@ldc.fr"
|
||||
}
|
||||
],
|
||||
"scripts": {
|
||||
"format": "vendor/bin/pint",
|
||||
"lint:php": "find . -path './vendor' -prune -o -type f -name '*.php' -print0 | xargs -0 -n1 -P4 -I{} php -l \"{}\"",
|
||||
"lint:phpcs": "vendor/bin/phpcs",
|
||||
"lint:phpstan": "vendor/bin/phpstan analyse",
|
||||
"lint:pint": "vendor/bin/pint --no-interaction --quiet",
|
||||
"test": "vendor/bin/pest --log-junit=junit.xml",
|
||||
"test:coverage": "vendor/bin/pest --log-junit=junit.xml --coverage --coverage-html=coverage --coverage-clover=coverage.xml"
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=8.3",
|
||||
"guzzlehttp/guzzle": "^7.10",
|
||||
"uwil/arcadia-kiota-open-api-client": "^0.1.0",
|
||||
"symfony/polyfill-php86": "^1.41"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"slevomat/coding-standard": "^8.28",
|
||||
"friendsofphp/php-cs-fixer": "^3.94",
|
||||
"laravel/pint": "^1.29",
|
||||
"pestphp/pest": "^4.4",
|
||||
"ace-of-aces/intellipest": "^0.3.0",
|
||||
"mockery/mockery": "^1.6",
|
||||
"dg/bypass-finals": "^1.9"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"pestphp/pest-plugin": true,
|
||||
"php-http/discovery": true,
|
||||
"tbachert/spi": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1265
composer.lock
generated
Normal file
1265
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,279 +0,0 @@
|
|||
# Classification Inco demandée a TOTO GPT
|
||||
|
||||
Voici une classification des champs en catégories sémantiques cohérentes pour des données nutritionnelles INCO :
|
||||
|
||||
---
|
||||
|
||||
## 🏷️ Classification des champs nutritionnels
|
||||
|
||||
### 1. **Identification / Métadonnées**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `nom` | `NOM` | Nom du produit/fiche |
|
||||
| `numero` | `NUMERO` | Numéro de la fiche |
|
||||
| `source` | `SOURCE` | Source des données |
|
||||
|
||||
---
|
||||
|
||||
### 2. **Valeurs énergétiques — par 100g/100ml (référence `_R`)**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `tailleReference` | `TAILLE_REF` | Taille de la portion de référence |
|
||||
| `valEnergKjReference` | `VAL_ENERG_KJ_R` | Énergie en kJ / 100g |
|
||||
| `valEnergKcalReference` | `VAL_ENERG_KCAL_R` | Énergie en kcal / 100g |
|
||||
|
||||
---
|
||||
|
||||
### 3. **Macronutriments — par 100g/100ml (`_R`) + précisions (`_PREC`)**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `matGrassReference` / `matGrassReferencePrec` | `MAT_GRASS_R` | Matières grasses / 100g |
|
||||
| `acideGrasSatureReference` / `acideGrasSatureReferencePrec` | `AGS_R` | Acides gras saturés / 100g |
|
||||
| `glucidesReference` / `glucidesReferencePrec` | `GLUCIDES_R` | Glucides / 100g |
|
||||
| `sucreReference` / `sucreReferencePrec` | `SUCRE_R` | Sucres / 100g |
|
||||
| `fibAlimReference` / `fibAlimReferencePrec` | `FIB_ALIM_R` | Fibres alimentaires / 100g |
|
||||
| `proteineReference` / `proteineReferencePrec` | `PROTEINE_R` | Protéines / 100g |
|
||||
| `selReference` / `selReferencePrec` | `SEL_R` | Sel / 100g |
|
||||
| `sodiumReference` | `SODIUM_R` | Sodium / 100g |
|
||||
|
||||
---
|
||||
|
||||
### 4. **Fruits, légumes, noix — par 100g/100ml (`_R`)**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `fruitsReference` | `FRUITS_R` | Teneur en fruits / 100g |
|
||||
| `fruitsCoqueSechReference` | `FRUITS_COQUE_SECH_R` | Fruits à coque et séchés / 100g |
|
||||
| `pourcentFruits` | `POURCENT_FRUITS` | % fruits (global) |
|
||||
|
||||
---
|
||||
|
||||
### 5. **Valeurs nutritionnelles — par portion (`_P`) + précisions (`_PREC`)**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `taillePortion` | `TAILLE_PORT` | Taille de la portion |
|
||||
| `valEnergKjPortion` | `VAL_ENERG_KJ_P` | Énergie kJ / portion |
|
||||
| `valEnergKcalPortion` | `VAL_ENERG_KCAL_P` | Énergie kcal / portion |
|
||||
| `matGrassPortion` / `matGrassPortionPrec` | `MAT_GRASS_P` | Matières grasses / portion |
|
||||
| `acideGrasSaturePortion` / `acideGrasSaturePortionPrec` | `AGS_P` | Acides gras saturés / portion |
|
||||
| `glucidesPortion` / `glucidesPortionPrec` | `GLUCIDES_P` | Glucides / portion |
|
||||
| `sucrePortion` / `sucrePortionPrec` | `SUCRE_P` | Sucres / portion |
|
||||
| `fibAlimPortion` / `fibAlimPortionPrec` | `FIB_ALIM_P` | Fibres / portion |
|
||||
| `proteinePortion` / `proteinePortionPrec` | `PROTEINE_P` | Protéines / portion |
|
||||
| `sodiumPortion` | `SODIUM_P` | Sodium / portion |
|
||||
| `selPortion` / `selPortionPrec` | `SEL_P` | Sel / portion |
|
||||
| `fruitsPortion` | `FRUITS_P` | Fruits / portion |
|
||||
| `fruitsCoqueSechPortion` | `FRUITS_COQUE_SECH_P` | Fruits à coque et séchés / portion |
|
||||
|
||||
---
|
||||
|
||||
### 6. **Pourcentages des apports de référence (`POURCENT_APPORT_REF_*`)**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `pourcentApportRefE14P` | `POURCENT_APPORT_REF_E14_P` | % AR énergie E14 / portion |
|
||||
| `pourcentApportRefKjoP` | `POURCENT_APPORT_REF_KJO_P` | % AR énergie kJ / portion |
|
||||
| `pourcentApportRefFatP` | `POURCENT_APPORT_REF_FAT_P` | % AR matières grasses / portion |
|
||||
| `pourcentApportRefFatsatP` | `POURCENT_APPORT_REF_FATSAT_P` | % AR graisses saturées / portion |
|
||||
| `pourcentApportRefChovalP` | `POURCENT_APPORT_REF_CHOVAL_P` | % AR glucides / portion |
|
||||
| `pourcentApportRefSugarP` | `POURCENT_APPORT_REF_SUGAR_P` | % AR sucres / portion |
|
||||
| `pourcentApportRefProP` | `POURCENT_APPORT_REF_PRO_P` | % AR protéines / portion |
|
||||
| `pourcentApportRefSalteqP` | `POURCENT_APPORT_REF_SALTEQ_P` | % AR sel / portion |
|
||||
|
||||
---
|
||||
|
||||
### 7. **Nutri-Score**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `composanteN` | `COMPOSANTE_N` | Composante négative (énergie, graisses, sucres, sel) |
|
||||
| `composanteN2022` | `COMPOSANTE_N_2022` | Composante négative — algorithme 2022 |
|
||||
| `composanteP` | `COMPOSANTE_P` | Composante positive (fibres, protéines, fruits) |
|
||||
| `composanteP2022` | `COMPOSANTE_P_2022` | Composante positive — algorithme 2022 |
|
||||
| `scoreNutri` | `SCORE_NUTRI` | Score Nutri-Score brut |
|
||||
| `scoreNutri2022` | `SCORE_NUTRI_2022` | Score Nutri-Score 2022 |
|
||||
| `classement` | `CLASSEMENT` | Lettre Nutri-Score (A–E) |
|
||||
| `nutriScore2022` | `NUTRI_SCORE_2022` | Nutri-Score lettre — algorithme 2022 |
|
||||
| `nutriAArt` | `NUTRI_A_ART` | Flag article Nutri-Score A |
|
||||
|
||||
---
|
||||
|
||||
### 8. **Points de calcul du Nutri-Score**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `pointsEnergie` | `POINTS_ENERGIE` | Points énergie |
|
||||
| `pointsAcideGrasSature` | `POINTS_AGS` | Points acides gras saturés |
|
||||
| `pointsSucres` | `POINTS_SUCRES` | Points sucres |
|
||||
| `pointsSel` | `POINTS_SEL` | Points sel |
|
||||
| `pointsFibres` | `POINTS_FIBRES` | Points fibres |
|
||||
| `pointsProteines` | `POINTS_PROTEINES` | Points protéines |
|
||||
| `pointsFruitsLegumesNoixFrais` | `POINTS_FNL_FRAIS` | Points fruits/noix/légumes frais |
|
||||
|
||||
---
|
||||
|
||||
### 9. **FNL (Fruits, Noix, Légumes)**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `fruitLegumeNoixFrais` | `FNL_FRAIS` | Teneur FNL frais / 100g |
|
||||
| `fruitLegumeNoixFraisPortion` | `FNL_FRAIS_PORTION` | Teneur FNL frais / portion |
|
||||
| `pourcentFruitsLegumesNoixFrais` | `POURCENT_FNL_FRAIS` | % FNL frais |
|
||||
|
||||
---
|
||||
|
||||
### 10. **Ratio / Indicateurs complémentaires**
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `pSurL` | `P_SUR_L` | Ratio P/L (protéines sur lipides) |
|
||||
| `viandeRouge` | `VIANDE_ROUGE` | Flag viande rouge (impact Nutri-Score 2022) |
|
||||
|
||||
---
|
||||
|
||||
### 11. **Micronutriments détaillés (`NUT_*`)**
|
||||
|
||||
Cette famille regroupe les vitamines, minéraux, acides aminés et acides gras détaillés — typiquement issus de bases comme **Ciqual** ou **FoodData Central**.
|
||||
|
||||
#### 11a. Minéraux & Oligo-éléments
|
||||
| Champ PHP | Constante |
|
||||
|---|---|
|
||||
| `nutCa` | `NUT_CA` — Calcium |
|
||||
| `nutMg` | `NUT_MG` — Magnésium |
|
||||
| `nutK` | `NUT_K` — Potassium |
|
||||
| `nutP` | `NUT_P` — Phosphore |
|
||||
| `nutCld` | `NUT_CLD` — Chlorure |
|
||||
| `nutFe` | `NUT_FE` — Fer |
|
||||
| `nutZn` | `NUT_ZN` — Zinc |
|
||||
| `nutCu` | `NUT_CU` — Cuivre |
|
||||
| `nutMn` | `NUT_MN` — Manganèse |
|
||||
| `nutSe` | `NUT_SE` — Sélénium |
|
||||
| `nutId` | `NUT_ID` — Iode |
|
||||
| `nutCr` | `NUT_CR` — Chrome |
|
||||
| `nutMo` | `NUT_MO` — Molybdène |
|
||||
| `nutS6` | `NUT_S6` — Soufre |
|
||||
| `nutNitra` | `NUT_NITRA` — Nitrates |
|
||||
|
||||
#### 11b. Vitamines
|
||||
| Champ PHP | Constante |
|
||||
|---|---|
|
||||
| `nutVita` | `NUT_VITA` — Vitamine A |
|
||||
| `nutVitb6` | `NUT_VITB6` — Vitamine B6 |
|
||||
| `nutVitb12` | `NUT_VITB12` — Vitamine B12 |
|
||||
| `nutVitc` | `NUT_VITC` — Vitamine C |
|
||||
| `nutVitd` | `NUT_VITD` — Vitamine D |
|
||||
| `nutVite` | `NUT_VITE` — Vitamine E |
|
||||
| `nutVitk` | `NUT_VITK` — Vitamine K |
|
||||
| `nutChocal` | `NUT_CHOCAL` — Cholécalciférol (vit. D3) |
|
||||
| `nutNia` | `NUT_NIA` — Niacine (B3) |
|
||||
| `nutRibf` | `NUT_RIBF` — Riboflavine (B2) |
|
||||
| `nutThia` | `NUT_THIA` — Thiamine (B1) |
|
||||
| `nutPantac` | `NUT_PANTAC` — Acide pantothénique (B5) |
|
||||
| `nutBiot` | `NUT_BIOT` — Biotine (B8) |
|
||||
| `nutFoldfe` | `NUT_FOLDFE` — Folates équivalents (B9) |
|
||||
|
||||
#### 11c. Glucides détaillés
|
||||
| Champ PHP | Constante |
|
||||
|---|---|
|
||||
| `nutStarch` | `NUT_STARCH` — Amidon |
|
||||
| `nutMaltdex` | `NUT_MALTDEX` — Maltodextrines |
|
||||
| `nutSucs` | `NUT_SUCS` — Saccharose |
|
||||
| `nutGlus` | `NUT_GLUS` — Glucose |
|
||||
| `nutLacs` | `NUT_LACS` — Lactose |
|
||||
| `nutMals` | `NUT_MALS` — Maltose |
|
||||
| `nutGalfb` | `NUT_GALFB` — Galactose |
|
||||
| `nutGHc` | `NUT_G_HC` — Glucides hydrates de carbone |
|
||||
| `nutFrufb` | `NUT_FRUFB` — Fructose lié (fibre) |
|
||||
| `nutFrus` | `NUT_FRUS` — Fructose libre |
|
||||
| `nutOlsac` | `NUT_OLSAC` — Oligosaccharides |
|
||||
| `nutPolyl` | `NUT_POLYL` — Polyols |
|
||||
| `nutFibsol` | `NUT_FIBSOL` — Fibres solubles |
|
||||
| `nutInotl` | `NUT_INOTL` — Inositol |
|
||||
|
||||
#### 11d. Lipides / Acides gras détaillés
|
||||
> Nomenclature lipidique standard (IUPAC / Ciqual) — `FXXdY` = chaîne carbonée XX, Y insaturations
|
||||
|
||||
| Champ PHP | Constante | Nom usuel |
|
||||
|---|---|---|
|
||||
| `nutAgt` | `NUT_AGT` | Acides gras trans |
|
||||
| `nutChol` | `NUT_CHOL` | Cholestérol |
|
||||
| `nutFd` | `NUT_FD` | Lipides totaux |
|
||||
| `nutFpalm` | `NUT_FPALM` | Palmitoleïque (C16:1) |
|
||||
| `nutF16d0` | `NUT_F16D0` | Acide palmitique (C16:0) |
|
||||
| `nutFsteariq` | `NUT_FSTEARIQ` | Acide stéarique (C18:0) |
|
||||
| `nutFoleiq` | `NUT_FOLEIQ` | Acide oléique (C18:1, ω-9) |
|
||||
| `nutF18d` | `NUT_F18D` | C18 insaturé (générique) |
|
||||
| `nutF18d2cn6` | `NUT_F18D2CN6` | Acide linoléique (C18:2, ω-6) |
|
||||
| `nutF18d3n3` | `NUT_F18D3N3` | Acide α-linolénique (C18:3, ω-3) |
|
||||
| `nutF20d4` | `NUT_F20D4` | Acide arachidonique (C20:4) |
|
||||
| `nutF20d5n3` | `NUT_F20D5N3` | EPA (C20:5, ω-3) |
|
||||
| `nutF22d6n3` | `NUT_F22D6N3` | DHA (C22:6, ω-3) |
|
||||
| `nutF11eico` | `NUT_F11EICO` | Acide gondoïque (C20:1) |
|
||||
| `nutFarachidiq` | `NUT_FARACHIDIQ` | Acide arachidique (C20:0) |
|
||||
| `nutFeruciq` | `NUT_FERUCIQ` | Acide érucique (C22:1) |
|
||||
| `nutFlauriq` | `NUT_FLAURIQ` | Acide laurique (C12:0) |
|
||||
| `nutFmyristiq` | `NUT_FMYRISTIQ` | Acide myristique (C14:0) |
|
||||
| `nutFbutyriq` | `NUT_FBUTYRIQ` | Acide butyrique (C4:0) |
|
||||
| `nutFcaproiq` | `NUT_FCAPROIQ` | Acide caproïque (C6:0) |
|
||||
| `nutFcapryliq` | `NUT_FCAPRYLIQ` | Acide caprylique (C8:0) |
|
||||
| `nutFcapriq` | `NUT_FCAPRIQ` | Acide caprique (C10:0) |
|
||||
| `nutFapun3` | `NUT_FAPUN3` | AG polyinsaturés ω-3 |
|
||||
| `nutFapun6` | `NUT_FAPUN6` | AG polyinsaturés ω-6 |
|
||||
| `nutFapun9` | `NUT_FAPUN9` | AG polyinsaturés ω-9 |
|
||||
| `nutFapucis` | `NUT_FAPUCIS` | AG polyinsaturés cis |
|
||||
| `nutFapulc` | `NUT_FAPULC` | AG polyinsaturés longue chaîne |
|
||||
| `nutFamscis` | `NUT_FAMSCIS` | AG monoinsaturés cis |
|
||||
| `nutFacetiq` | `NUT_FACETIQ` | Acide acétique (C2:0) |
|
||||
| `nutFpropioniq` | `NUT_FPROPIONIQ` | Acide propionique (C3:0) |
|
||||
| `nutFvaleriq` | `NUT_FVALERIQ` | Acide valérique (C5:0) |
|
||||
| `nutFcarboceriq` | `NUT_FCARBOCERIQ` | Acide carbocérique (C26:0) |
|
||||
| `nutFbeheniq` | `NUT_FBEHENIQ` | Acide béhénique (C22:0) |
|
||||
| `nutFlignoceriq` | `NUT_FLIGNOCERIQ` | Acide lignocérique (C24:0) |
|
||||
| `nutFnervoniq` | `NUT_FNERVONIQ` | Acide nervonique (C24:1) |
|
||||
| `nutFlaceroiq` | `NUT_FLACEROIQ` | Acide lacéroïque (C32:0) |
|
||||
| `nutFmelissiq` | `NUT_FMELISSIQ` | Acide mélissique (C30:0) |
|
||||
| `nutFmontaniq` | `NUT_FMONTANIQ` | Acide montanique (C28:0) |
|
||||
| `nutFcerotiq` | `NUT_FCEROTIQ` | Acide cérotique (C26:0 alt.) |
|
||||
| `nutFpsylliq` | `NUT_FPSYLLIQ` | Acide psyllique |
|
||||
| `nutFgeddiq` | `NUT_FGEDDIQ` | Acide geddique (C20:1) |
|
||||
| `nutFmargariq` | `NUT_FMARGARIQ` | Acide margarique (C17:0) |
|
||||
| `nutFenatiq` | `NUT_FENATIQ` | Acide énanthique (C7:0) |
|
||||
| `nutFformiq` | `NUT_FFORMIQ` | Acide formique (C1:0) |
|
||||
| `nutFhyeniq` | `NUT_FHYENIQ` | Acide hyénique |
|
||||
| `nutFrufb` | `NUT_FRUFB` | *(déjà classé glucides)* |
|
||||
| `nutFrus` | `NUT_FRUS` | *(déjà classé glucides)* |
|
||||
| `nutFpelargoniq` | `NUT_FPELARGONIQ` | Acide pélargonique (C9:0) |
|
||||
| `nutFundecyliq` | `NUT_FUNDECYLIQ` | Acide undécylique (C11:0) |
|
||||
| `nutFpentadecyliq` | `NUT_FPENTADECYLIQ` | Acide pentadécylique (C15:0) |
|
||||
| `nutFtridecyliq` | `NUT_FTRIDECYLIQ` | Acide tridécylique (C13:0) |
|
||||
| `nutFtricosiliq` | `NUT_FTRICOSILIQ` | Acide tétracosanoïque (C23:0) |
|
||||
| `nutFnonacosyliq` | `NUT_FNONACOSYLIQ` | Acide nonacosylique (C29:0) |
|
||||
| `nutFnonadylique` | `NUT_FNONADYLIQUE` | Acide nonadylique |
|
||||
| `nutFheneicosyliq` | `NUT_FHENEICOSYLIQ` | Acide héneicosylique (C21:0) |
|
||||
| `nutFhentriacontyliq` | `NUT_FHENTRIACONTYLIQ` | C31:0 |
|
||||
| `nutFheptatriacontyliq` | `NUT_FHEPTATRIACONTYLIQ` | C37:0 |
|
||||
| `nutFhextriacontyliq` | `NUT_FHEXTRIACONTYLIQ` | C36:0 |
|
||||
| `nutFoctatriacontyliq` | `NUT_FOCTATRIACONTYLIQ` | C38:0 |
|
||||
| `nutF19d` | `NUT_F19D` | C19 insaturé |
|
||||
| `nutFoldfe` | `NUT_FOLDFE` | *(déjà classé vitamines — folates)* |
|
||||
|
||||
#### 11e. Acides aminés & protéines spécifiques
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `nutAae` | `NUT_AAE` | Acides aminés essentiels (total) |
|
||||
| `nutCasn` | `NUT_CASN` | Caséines |
|
||||
| `nutWhey` | `NUT_WHEY` | Protéines de lactosérum |
|
||||
|
||||
#### 11f. Autres composés bioactifs / spéciaux
|
||||
| Champ PHP | Constante | Description |
|
||||
|---|---|---|
|
||||
| `nutCholn` | `NUT_CHOLN` | Choline |
|
||||
| `nutCarnitine` | `NUT_CARNITINE` | Carnitine |
|
||||
| `nutTau` | `NUT_TAU` | Taurine |
|
||||
| `nutNucleotide` | `NUT_NUCLEOTIDE` | Nucléotides |
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Points d'attention
|
||||
|
||||
1. **Doublon détecté** : `'acideGrasSaturePortion' => 'AGS_P'` est déclaré **deux fois** — l'une des deux entrées sera silencieusement ignorée par PHP.
|
||||
|
||||
2. **Champs `_PREC`** : Les suffixes `_PREC` semblent indiquer la **précision de mesure** (ex. `<`, `~`, tracé) — à documenter explicitement.
|
||||
|
||||
3. **Champs MISSING** : Nombreux champs présents dans le référentiel INCO mais absents du mapping — notamment toute la série `POURCENT_APPORT_REF_*_R` (apports de référence pour 100g) et certains AG comme `FATSAT_R`, `CHOVAL_R`, `SUGAR_R`.
|
||||
|
||||
4. **`classement` / `nutriscore`** : Vous notez vous-même que `CLASSEMENT` ne devrait pas être dans `INCO_COMPO` — à externaliser dans un objet Nutri-Score dédié.
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
{
|
||||
"VAL_ENERG_KCAL_R": "300",
|
||||
"VAL_ENERG_KJ_P": "",
|
||||
"VAL_ENERG_KCAL_P": "",
|
||||
"VAL_ENERG_KJ_R": "1251",
|
||||
"NUT_VITB12": "",
|
||||
"POINTS_PROTEINES": "",
|
||||
"POURCENT_APPORT_REF_FATSAT_P": "",
|
||||
"NUT_FVALERIQ": "",
|
||||
"NUT_WHEY": "",
|
||||
"NUT_FHENTRIACONTYLIQ": "",
|
||||
"NUT_FAPULC": "",
|
||||
"POINTS_AGS": "",
|
||||
"NUT_GLUS": "",
|
||||
"NUT_F20D4": "",
|
||||
"NUT_F19D": "",
|
||||
"NUT_F20D5N3": "",
|
||||
"ID": "44897",
|
||||
"NUT_FLAURIQ": "",
|
||||
"MAT_GRASS_R": "17",
|
||||
"NUT_FFORMIQ": "",
|
||||
"MAT_GRASS_P": "",
|
||||
"NUT_INOTL": "",
|
||||
"NUT_MALTDEX": "",
|
||||
"AGS_R_PREC": "0",
|
||||
"NUT_SUCS": "",
|
||||
"NUT_FAPUN6": "",
|
||||
"POURCENT_FNL_FRAIS": "",
|
||||
"NUT_FHEXTRIACONTYLIQ": "",
|
||||
"NUT_FERUCIQ": "",
|
||||
"NUT_FAPUN9": "",
|
||||
"NUT_NITRA": "",
|
||||
"CLASSEMENT": "D",
|
||||
"POINTS_SUCRES": "",
|
||||
"NUT_CHOL": "",
|
||||
"NUT_CHOCAL": "",
|
||||
"NUT_CHOLN": "",
|
||||
"NUT_FAPUN3": "",
|
||||
"NUT_FCAPRYLIQ": "",
|
||||
"NUT_VITE": "",
|
||||
"NUT_VITD": "",
|
||||
"NUT_VITC": "",
|
||||
"POURCENT_APPORT_REF_KJO_P": "",
|
||||
"NUT_F18D3N3": "",
|
||||
"AGS_R": "7",
|
||||
"NUT_VITA": "",
|
||||
"NUT_F18D": "",
|
||||
"SOURCE": "1",
|
||||
"SEL_P": "",
|
||||
"SEL_P_PREC": "",
|
||||
"NUT_FPELARGONIQ": "",
|
||||
"NUT_VITK": "",
|
||||
"NUT_FRUS": "",
|
||||
"NUT_FBUTYRIQ": "",
|
||||
"NUT_FE": "",
|
||||
"SEL_R": "1.4",
|
||||
"NUT_NUCLEOTIDE": "",
|
||||
"NUT_FD": "",
|
||||
"POURCENT_APPORT_REF_FAT_P": "",
|
||||
"NUT_BIOT": "",
|
||||
"NUT_FIBSOL": "",
|
||||
"POURCENT_APPORT_REF_PRO_P": "",
|
||||
"NUT_FCEROTIQ": "",
|
||||
"MAT_GRASS_R_PREC": "0",
|
||||
"NUT_PANTAC": "",
|
||||
"NUT_MG": "",
|
||||
"NUT_G_HC": "",
|
||||
"NUT_TAU": "",
|
||||
"SEL_R_PREC": "0",
|
||||
"GLUCIDES_R_PREC": "0",
|
||||
"TAILLE_REF": "100",
|
||||
"POURCENT_APPORT_REF_E14_P": "",
|
||||
"NUT_FCARBOCERIQ": "",
|
||||
"NUT_FLACEROIQ": "",
|
||||
"FIB_ALIM_R": "1.3",
|
||||
"FIB_ALIM_P": "",
|
||||
"NUT_FPENTADECYLIQ": "",
|
||||
"NUT_FBEHENIQ": "",
|
||||
"NUT_FNONACOSYLIQ": "",
|
||||
"NUT_FPSYLLIQ": "",
|
||||
"NUT_F18D2CN6": "",
|
||||
"NUT_FUNDECYLIQ": "",
|
||||
"NUT_FAPUCIS": "",
|
||||
"NUT_FMARGARIQ": "",
|
||||
"PROTEINE_R": "11",
|
||||
"NUT_MO": "",
|
||||
"POINTS_SEL": "",
|
||||
"NUT_MN": "",
|
||||
"PROTEINE_P": "",
|
||||
"NUT_F11EICO": "",
|
||||
"SUCRE_R": "1.5",
|
||||
"SUCRE_P": "",
|
||||
"SUCRE_R_PREC": "0",
|
||||
"NUT_FPALM": "",
|
||||
"NUT_FGEDDIQ": "",
|
||||
"NUT_FMYRISTIQ": "",
|
||||
"APPORT_PORT": "Apport de r\u00e9f\u00e9rence pour un adulte type (8400Kj-2000Kcal)",
|
||||
"NUT_CU": "",
|
||||
"SCORE_NUTRI": "14",
|
||||
"NUT_CR": "",
|
||||
"NUT_MALS": "",
|
||||
"NUT_F16D0": "",
|
||||
"NUT_FOCTATRIACONTYLIQ": "",
|
||||
"NUT_FRUFB": "",
|
||||
"NUT_CASN": "",
|
||||
"NUT_FMONTANIQ": "",
|
||||
"POINTS_ENERGIE": "",
|
||||
"FRUITS_R": "0",
|
||||
"COMPOSANTE_N_2022": "",
|
||||
"VIANDE_ROUGE": "",
|
||||
"NUT_POLYL": "",
|
||||
"NUT_VITB6": "",
|
||||
"NUTRI_A_ART": "",
|
||||
"PROTEINE_P_PREC": "",
|
||||
"NUT_STARCH": "",
|
||||
"P_SUR_L": ",65",
|
||||
"NUT_RIBF": "",
|
||||
"SODIUM_P": "",
|
||||
"NUT_FENATIQ": "",
|
||||
"NUT_K": "",
|
||||
"NUT_FTRIDECYLIQ": "",
|
||||
"SUCRE_P_PREC": "",
|
||||
"NUT_SE": "",
|
||||
"NUT_FTRICOSILIQ": "",
|
||||
"NUT_P": "",
|
||||
"SODIUM_R": "560",
|
||||
"NUT_FARACHIDIQ": "",
|
||||
"NUT_FPROPIONIQ": "",
|
||||
"NUT_FNERVONIQ": "",
|
||||
"SCORE_NUTRI_2022": "",
|
||||
"NUT_FNONADYLIQUE": "",
|
||||
"FIB_ALIM_P_PREC": "",
|
||||
"NUT_CA": "",
|
||||
"NUT_FMELISSIQ": "",
|
||||
"GLUCIDES_P_PREC": "",
|
||||
"POURCENT_FRUITS": "0",
|
||||
"NUT_OLSAC": "",
|
||||
"COMPOSANTE_P": "6",
|
||||
"GLUCIDES_P": "",
|
||||
"NUT_ZN": "",
|
||||
"COMPOSANTE_N": "15",
|
||||
"GLUCIDES_R": "25",
|
||||
"NUT_FCEROPLASTIQ": "",
|
||||
"FNL_FRAIS": "",
|
||||
"NOM": "-",
|
||||
"NUT_AAE": "",
|
||||
"NUT_FSTEARIQ": "",
|
||||
"TAILLE_PORT": "",
|
||||
"POURCENT_APPORT_REF_CHOVAL_P": "",
|
||||
"AGS_P_PREC": "",
|
||||
"PROTEINE_R_PREC": "0",
|
||||
"NUT_FCAPROIQ": "",
|
||||
"NUT_S6": "",
|
||||
"MAT_GRASS_P_PREC": "",
|
||||
"NUT_FHEPTATRIACONTYLIQ": "",
|
||||
"NUT_FAMSCIS": "",
|
||||
"NUT_ID": "",
|
||||
"NUT_CARNITINE": "",
|
||||
"POINTS_FIBRES": "",
|
||||
"AGS_P": "",
|
||||
"NUT_FOLDFE": "",
|
||||
"COMPOSANTE_P_2022": "",
|
||||
"NUT_GALFB": "",
|
||||
"NUT_CLD": "",
|
||||
"NUT_THIA": "",
|
||||
"POURCENT_APPORT_REF_SALTEQ_P": "",
|
||||
"NUT_FHENEICOSYLIQ": "",
|
||||
"NUT_FOLEIQ": "",
|
||||
"NUT_AGT": "",
|
||||
"POINTS_FNL_FRAIS": "",
|
||||
"NUT_FACETIQ": "",
|
||||
"NUT_NIA": "",
|
||||
"NUT_FCAPRIQ": "",
|
||||
"NUT_F22D6N3": "",
|
||||
"FIB_ALIM_R_PREC": "0",
|
||||
"NUTRI_SCORE_2022": "",
|
||||
"NUT_LACS": "",
|
||||
"POURCENT_APPORT_REF_SUGAR_P": "",
|
||||
"NUMERO": "-",
|
||||
"ID_INCO": {
|
||||
"code": "77310",
|
||||
"libelle": "SC 240GFX PALET CHEVR SOIGN IQF"
|
||||
},
|
||||
"FRUITS_COQUE_SECH_R": "0",
|
||||
"NUT_FHYENIQ": "",
|
||||
"NUT_FLIGNOCERIQ": ""
|
||||
}
|
||||
|
|
@ -1,335 +0,0 @@
|
|||
{
|
||||
"VILLE_GRPMT_QUAL": "",
|
||||
"DATEVERSION": "2022-03-07",
|
||||
"TYP_PORTION": "2",
|
||||
"NB_PORTION_QUAL": "",
|
||||
"CAP4_AVANTAGE": "",
|
||||
"EX_GOUT": "",
|
||||
"ALIMENTATION": "",
|
||||
"COMM_MKT": "",
|
||||
"GRAM_ENT_CARCERAL": "",
|
||||
"ANTIBIO": "",
|
||||
"NOM_GRPMT_QUAL": "",
|
||||
"DENOM_COMM": "Farce \u00e0 base de viande de dinde et de gras de dinde ayant fait l\u2019objet d\u2019un broyage, additionn\u00e9 \u00e0 une pr\u00e9paration aromatisante.",
|
||||
"GRPMT_QUAL": "",
|
||||
"NUTRISCORE_ON_PACK": "D",
|
||||
"CALIBRE_OEUFS": "",
|
||||
"AFF_DVT_FT": "",
|
||||
"AROME_ARTIF": "",
|
||||
"POIDS_MOYEN_UNI": "",
|
||||
"REGLEMENTATION": "",
|
||||
"PMC_EURO_UVC": "0",
|
||||
"DATE_STATUT_QUAL": "2022-10-26",
|
||||
"AROME_NATUREL": "",
|
||||
"BRIQUE_GPC": {
|
||||
"code": "10005784",
|
||||
"libelle": "Dinde - Pr\u00e9par\u00e9e\/Transform\u00e9e"
|
||||
},
|
||||
"CRIT_PHYSICO": "",
|
||||
"DAT_CRE": "2017-06-29T16:12:00Z",
|
||||
"AW": "",
|
||||
"LIB_CONSO": "",
|
||||
"AGEMINI": "",
|
||||
"NB_PORTION": "\/",
|
||||
"CONDI_SS_AP": "0",
|
||||
"NB_ADDITIF_BLACKLIST": "0",
|
||||
"UTIL_QUAL": {
|
||||
"code": "SC4911560",
|
||||
"libelle": "CHAUVEAU Severine"
|
||||
},
|
||||
"GRAM_ENT_ELEM": "",
|
||||
"PMC_MINI": "",
|
||||
"MENTION_COMPL": "",
|
||||
"CAP4_PREUVE": "",
|
||||
"COLOR_ARTIF": "",
|
||||
"UNITE_CDE": "0",
|
||||
"GRAM_ENT_MATERN": "",
|
||||
"NB_UVC": "",
|
||||
"CONDIT_EMB": {
|
||||
"code": "2",
|
||||
"libelle": "SOUS VIDE"
|
||||
},
|
||||
"CONDIT_CONSERV_1": "A conserver entre 0\u00b0C et +4\u00b0C",
|
||||
"LIB_SERV_CONSO": "LE GAULOIS PROFESSIONNEL",
|
||||
"DLC_RM": "",
|
||||
"DLC_COMM_GRP": "10",
|
||||
"POURCENT_CEREAL_MINI": "",
|
||||
"ID_SERV_CONSO": {
|
||||
"code": "2161",
|
||||
"libelle": "LE GAULOIS PROFESSIONNEL"
|
||||
},
|
||||
"COMM_COMPO": "",
|
||||
"GRAM_ENT_ADO": "",
|
||||
"CP_EXPLOIT": "61140",
|
||||
"VILLE_EXPLOIT": "Rives d andaine",
|
||||
"GRAM_ENT_AGEES": "",
|
||||
"PAYS_ORIGINE_PROV_ING1": "",
|
||||
"COD_FAMRGPT": {
|
||||
"code": "4",
|
||||
"libelle": "PRODUITS ELAB CRUS"
|
||||
},
|
||||
"IS_HALAL": "0",
|
||||
"UTIL_MKT": "",
|
||||
"ADR_2_SERV_CONSO": "",
|
||||
"FICHE_MERE": {
|
||||
"code": "44863",
|
||||
"libelle": ""
|
||||
},
|
||||
"CONSEIL_PREPA": "A consommer cuit \u00e0 c\u0153ur\nUne farce de volaille \u00e0 utiliser au gr\u00e9 de vos envies pour farcir des l\u00e9gumes, des volailles, des r\u00f4tis\u2026",
|
||||
"INNOVATION": "0",
|
||||
"TRACES_SIT_PROD": "",
|
||||
"POIDS_CST": "0",
|
||||
"TYPE_STOCK": "",
|
||||
"COMM_REF_COMPO": "",
|
||||
"ALLEGAT_AUTRE": "",
|
||||
"GENCOD_SYMBOL": "2667277",
|
||||
"LONGUEUR": "400",
|
||||
"IS_PAM": "0",
|
||||
"FICH_REF_COMPO": "0",
|
||||
"ALLEGATIONS": "",
|
||||
"NO_HOMOLOGATION": "",
|
||||
"ALLEGATION_MKT": "",
|
||||
"IONISATION": "Aucun ingr\u00e9dients ionis\u00e9 dans cette recette.",
|
||||
"STATUT_QUAL": "1",
|
||||
"CAP2_AVANTAGE": "",
|
||||
"DAT_MAJ": "2025-06-26T14:16:00Z",
|
||||
"GRAM_PLAT_ELEM": "",
|
||||
"ARG_VENTE": "Une farce de volaille moelleuse \u00e0 utiliser au gr\u00e9 de vos envies pour farcir des l\u00e9gumes, des volailles, des r\u00f4tis\u2026",
|
||||
"COLORANT_NATUREL": "",
|
||||
"CAP2_PREUVE": "",
|
||||
"SS_FAMILLE_VTE": {
|
||||
"code": "77",
|
||||
"libelle": "FARCES"
|
||||
},
|
||||
"POIDS_MAXI": "2700",
|
||||
"SOC_REF_INCO": {
|
||||
"code": "13",
|
||||
"libelle": "SNV"
|
||||
},
|
||||
"TRACES_RCT": [
|
||||
{
|
||||
"code": "21",
|
||||
"libelle": "Aucun allerg\u00e8ne"
|
||||
}
|
||||
],
|
||||
"CP_SERV_CONSO": "49640",
|
||||
"CAP4_CARAC": "",
|
||||
"CIBLE": "famille",
|
||||
"CALIBRE": "",
|
||||
"PDS_NET_UVC": "2500",
|
||||
"PDS_BRUT_UVC": "2518",
|
||||
"ADRESSE_OC": "",
|
||||
"SIGLE_ALLERG": "",
|
||||
"CONSEIL_PREPA_1": "A consommer cuit \u00e0 c\u0153ur",
|
||||
"CONSEIL_PREPA_2": "Une farce de volaille \u00e0 utiliser au gr\u00e9 de vos envies pour farcir des l\u00e9gumes, des volailles, des r\u00f4tis\u2026",
|
||||
"PROD_BIO": "0",
|
||||
"REF_COMPO": {
|
||||
"code": "44863",
|
||||
"libelle": ""
|
||||
},
|
||||
"NB_MUT_SHORT_LIST": "1",
|
||||
"CP_GRPMT_QUAL": "",
|
||||
"SAUCE": "",
|
||||
"GRAM_PLAT_CARCERAL": "",
|
||||
"LST_MUT": [
|
||||
{
|
||||
"code": "7",
|
||||
"libelle": "dextrose"
|
||||
},
|
||||
{
|
||||
"code": "11",
|
||||
"libelle": "ar\u00f4me"
|
||||
}
|
||||
],
|
||||
"DESCRIPTIF": "Farce de volaille",
|
||||
"TYPE_PRODUCTION": "",
|
||||
"ALLEGAT_NUTRIT": "",
|
||||
"PESTICIDE": "",
|
||||
"DATE_JOUR_PONTE": "",
|
||||
"LIBELLE_ARTICLE_INCO": "KG - LE GAULOIS PRO - 2.5KG FARCE POUR LEGUMES PURE DINDE S\/V",
|
||||
"UPDATE_VERSION": "1",
|
||||
"LST_ING": "Viande de dinde (origine France): 66%, gras de dinde, \u00e9chalote, conservateur: lactate de potassium, sel, persil, dextrose, betterave en poudre, oignon, ar\u00f4mes naturels, \u00e9pices, antioxydants: acide ascorbique - ascorbate de sodium, sucre.",
|
||||
"LOGO_EXPLOIT": "<a href=\"javascript:telechargerPJ('controller\/VisuFichier?sessInt=-1&TABLE=REF_FICHIERS&CLE=COD_FICHIER=270321&COLONNE=FICHIER&NOM_FICH=SNV.png&ACTION=90')\" title=\"T\u00e9l\u00e9charger le fichier SNV.png\" class=\"pseudo-link\">SNV.png<\/a>",
|
||||
"VISUEL_SUGG_PRESENTATION": "",
|
||||
"ADRESSE_GRPMT_QUAL": "",
|
||||
"L_ALLEG_NUTRITION": "0",
|
||||
"COD_GENCOD_POIDS": "2767277",
|
||||
"COD_EMBAL": {
|
||||
"code": "44",
|
||||
"libelle": "SOUS VIDE POCHE"
|
||||
},
|
||||
"COMM_VOL_FERM": "",
|
||||
"DENOM_LEG": "FARCE DE VOLAILLE POUR LEGUME PURE DINDE",
|
||||
"NUM_AGR": "",
|
||||
"COD_NDP": {
|
||||
"code": "02072610",
|
||||
"libelle": "DDE MRX DESOS FRAIS"
|
||||
},
|
||||
"ART_COMM_ACTIF": "1",
|
||||
"PROD_SAISON": "0",
|
||||
"COMM_TARE_SUPPL": "",
|
||||
"COD_COMPOSTAGE": "1",
|
||||
"LARGEUR": "300",
|
||||
"DATE_STATUT_CMCL": "2019-03-13",
|
||||
"DEMARCHE_QUAL": "",
|
||||
"FREQ_UTIL": "",
|
||||
"NB_ADDITIF_TROUVE": "3",
|
||||
"NITRITE": "",
|
||||
"CLASSEMENT": "D",
|
||||
"CRIT_ORGANO": "",
|
||||
"CAP3_AVANTAGE": "",
|
||||
"TYPE_CONSERV": {
|
||||
"code": "1",
|
||||
"libelle": "FRAIS : [0oC,4oC]"
|
||||
},
|
||||
"GENCOD": "3571320448638",
|
||||
"LST_ALLERGENE": [
|
||||
{
|
||||
"code": "21",
|
||||
"libelle": "Aucun allerg\u00e8ne"
|
||||
}
|
||||
],
|
||||
"CP_OC": "",
|
||||
"ALLEGAT_SANTE": "",
|
||||
"UTIL_CMCL": {
|
||||
"code": "JJ2400325",
|
||||
"libelle": "Jouin Joel"
|
||||
},
|
||||
"RESIDU_PESTICIDE": "",
|
||||
"PDS_MOY_THEO": "2500",
|
||||
"NB_MUT_TROUVE": "2",
|
||||
"TARE_BARQ": "18",
|
||||
"GARNITURE": "",
|
||||
"VILLE_OC": "",
|
||||
"CAP2_CARAC": "",
|
||||
"LST_ADDITIF_BLACKLISTES": "",
|
||||
"COMMENTAIRE_PROCESS": "",
|
||||
"GRAM_PLAT_MATERN": "",
|
||||
"NO_ART": {
|
||||
"code": "44863",
|
||||
"libelle": "FARCE PR LEG PUR DDE 2.5KG SV LGP"
|
||||
},
|
||||
"SPCB": "",
|
||||
"EXTRA_FRAICHEUR": "",
|
||||
"ISGENETICALMODIFIED": "",
|
||||
"GRAM_PLAT_ADO": "",
|
||||
"ALIM_ELEVAGE": "",
|
||||
"PMC_EURO_KG": "1",
|
||||
"ISNOTPLEINAIR": "",
|
||||
"CAP3_CARAC": "",
|
||||
"PMC_MAXI": "",
|
||||
"DATE_STATUT_MKT": "2019-03-13",
|
||||
"SITE_INT_SERV_CONSO": "www.legauloisprofessionnel.fr",
|
||||
"OGM": "Conforme aux r\u00e8glements CE n\u00b0 1829\/2003 et n\u00b0 1830\/2003 : Produit Conventionnel.",
|
||||
"CAP1_PREUVE": "",
|
||||
"TARE_SUPPL": "",
|
||||
"VERSION": "1",
|
||||
"EXPLOITANT": {
|
||||
"code": "13",
|
||||
"libelle": "SNV"
|
||||
},
|
||||
"NOM_FONCT_INCO_ART": "Volailles & gibiers",
|
||||
"NUTRI_A_ART": "1",
|
||||
"RESIDU_CONTAMIN": "Conforme au r\u00e8glement CE 2023\/915 et ses modifications",
|
||||
"SANS_OGM": "",
|
||||
"ADRESSE_EXPLOIT": "447 route de la chevroliere",
|
||||
"SEL_AJOUTE": "",
|
||||
"COD_RAYON": {
|
||||
"code": "6",
|
||||
"libelle": "BOUCHERIE TRAD"
|
||||
},
|
||||
"LIST_NUM_AGREMENT": "FR 41.238.001 CE",
|
||||
"LIST_COD_EMBAL": "EMB41238",
|
||||
"COMM_NUTRI": "1",
|
||||
"DUREE_VIE": "17",
|
||||
"CAP1_AVANTAGE": "",
|
||||
"TYPE_PAL": {
|
||||
"code": "1",
|
||||
"libelle": "Europe"
|
||||
},
|
||||
"LIST_CERTIF": "IFS FOOD",
|
||||
"CRIT_MICROBIO_J0": "",
|
||||
"CAP1_CARAC": "",
|
||||
"CARAC_ELEVAGE": "1",
|
||||
"FAMILLE_VTE": {
|
||||
"code": "313",
|
||||
"libelle": "FARCE"
|
||||
},
|
||||
"ETIQ_DONNEES_INCO": "2",
|
||||
"MAIL_SERV_CONSO": "rhd@ldc.fr",
|
||||
"NOM_SERV_CONSO": "GUILLET",
|
||||
"INTERPROFESSION": [
|
||||
{
|
||||
"code": "13",
|
||||
"libelle": "VOLAILLE FRANCAISE (logo homologue APVF)"
|
||||
}
|
||||
],
|
||||
"NUTRISCORE_ON_PACK_QUALITE": "D",
|
||||
"VILLE_SERV_CONSO": {
|
||||
"code": "49119",
|
||||
"libelle": "DAUMERAY"
|
||||
},
|
||||
"STATUT_CMCL": "1",
|
||||
"NOM_EXPLOITANT": "SNV",
|
||||
"MARQUE": {
|
||||
"code": "504",
|
||||
"libelle": "LE GAULOIS PROFESSIONNEL"
|
||||
},
|
||||
"UNITE_FACT": "2",
|
||||
"VIAND_POISS": "",
|
||||
"ASTUCES": "",
|
||||
"DTS": "7",
|
||||
"TEL_SERV_CONSO": "0241325665",
|
||||
"ACTIVE": "1",
|
||||
"SITE_REF_INCO": {
|
||||
"code": "34",
|
||||
"libelle": "SNV - VOLABRAYE"
|
||||
},
|
||||
"CAP3_PREUVE": "",
|
||||
"HAUTEUR": "80",
|
||||
"CRIT_MICROBIO": "Selon r\u00e8glement europ\u00e9en CE n\u00b0 2073\/2005 du 15\/11\/2005 compl\u00e9t\u00e9 avec des normes internes.",
|
||||
"SUIVI_MODIF": "",
|
||||
"LIBELLE_FACTURE_ARTICLE": "2.5 KG FARCE POUR LEGUMES PURE DINDE SOUS VIDE LE GAULOIS PROFESSIONNEL",
|
||||
"NOM_OC": "",
|
||||
"FAX_SERV_CONSO": "",
|
||||
"CONDIT_CONSERV": "A conserver entre 0\u00b0C et +4\u00b0C\n",
|
||||
"ADR_1_SERV_CONSO": "ZA Le Grand Clos",
|
||||
"CRIT_MICROBIO_DLC": "",
|
||||
"ORG_CERTIF": "",
|
||||
"GRAM_PLAT_AGEES": "",
|
||||
"LST_MUT_SHORTLIST": [
|
||||
{
|
||||
"code": "7",
|
||||
"libelle": "dextrose"
|
||||
}
|
||||
],
|
||||
"LIB_TYP_EMB": "SOUS VIDE POCHE",
|
||||
"PMC": "",
|
||||
"IS_CALCUL_GEMRCN": "",
|
||||
"AGE_ABATT_MOY": "",
|
||||
"POIDS_MINI": "2300",
|
||||
"LST_ADDITIF": [
|
||||
{
|
||||
"code": "E326",
|
||||
"libelle": "lactate de potassium"
|
||||
},
|
||||
{
|
||||
"code": "E301",
|
||||
"libelle": "Ascorbate de sodium"
|
||||
},
|
||||
{
|
||||
"code": "E300",
|
||||
"libelle": "Acide ascorbique"
|
||||
}
|
||||
],
|
||||
"NUTRI_SCORE_2022": "D",
|
||||
"REF_RCT": "",
|
||||
"ELEVAGE": "",
|
||||
"PH": "",
|
||||
"STATUT_MKT": "1",
|
||||
"UTILISABLE_GROUPE": {
|
||||
"code": "1",
|
||||
"libelle": "Oui"
|
||||
},
|
||||
"VIP": ""
|
||||
}
|
||||
20
index.php
Normal file
20
index.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
use Uwil\ArcadiaGenesisPhpClient\Datas\EmployeesDatas;
|
||||
|
||||
use Uwil\ArcadiaGenesisPhpClient\Datas\IncoDatas;
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
|
||||
$employee = EmployeesDatas::getById("0115590");
|
||||
var_dump($employee);
|
||||
|
||||
// $employees = EmployeesDatas::getListModifiedSince("01/01/2026 00:00", ['LDCSER', 'SLELA', 'DDE']);
|
||||
// var_dump($employees);
|
||||
|
||||
// $inco = IncoDatas::getById("2842");
|
||||
// var_dump($inco);
|
||||
54
phpcs.xml
54
phpcs.xml
|
|
@ -1,54 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ruleset name="Php Coding Standard"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/squizlabs/php_codesniffer/phpcs.xsd">
|
||||
|
||||
<description>Standard optimisé : Analyse statique complémentaire à Pint.</description>
|
||||
|
||||
<arg name="colors" />
|
||||
<arg name="extensions" value="php" />
|
||||
<arg name="parallel" value="75" />
|
||||
<file>src/</file>
|
||||
|
||||
<exclude-pattern>*/vendor/*</exclude-pattern>
|
||||
<exclude-pattern>*/tests/*</exclude-pattern>
|
||||
|
||||
<rule ref="PSR12"/>
|
||||
|
||||
<rule ref="Generic.CodeAnalysis.EmptyStatement"/>
|
||||
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/>
|
||||
<rule ref="Generic.Metrics.CyclomaticComplexity">
|
||||
<properties>
|
||||
<property name="complexity" value="10"/>
|
||||
<property name="absoluteComplexity" value="20"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Generic.Metrics.NestingLevel"/>
|
||||
|
||||
<rule ref="Generic.PHP.DeprecatedFunctions"/>
|
||||
<rule ref="Generic.PHP.ForbiddenFunctions"/>
|
||||
<rule ref="Generic.PHP.NoSilencedErrors"/>
|
||||
<rule ref="Squiz.PHP.Eval"/>
|
||||
<rule ref="Squiz.PHP.GlobalKeyword"/>
|
||||
<rule ref="Squiz.PHP.NonExecutableCode"/>
|
||||
<rule ref="Squiz.Scope.StaticThisUsage"/>
|
||||
|
||||
<rule ref="Generic.Files.LineLength">
|
||||
<properties>
|
||||
<property name="lineLimit" value="150"/>
|
||||
<property name="absoluteLineLimit" value="250"/>
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses" />
|
||||
|
||||
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
|
||||
<properties>
|
||||
<property name="spacesCountAroundEqualsSign" value="0" />
|
||||
<property name="declareOnFirstLine" value="false" />
|
||||
<property name="linesCountBeforeDeclare" value="1" />
|
||||
<property name="linesCountAfterDeclare" value="1" />
|
||||
</properties>
|
||||
</rule>
|
||||
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace"/>
|
||||
</ruleset>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
parameters:
|
||||
paths:
|
||||
- src
|
||||
|
||||
level: 10
|
||||
|
||||
|
||||
# Enforce strict types analysis
|
||||
treatPhpDocTypesAsCertain: false
|
||||
25
phpunit.xml
25
phpunit.xml
|
|
@ -1,25 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" bootstrap="vendor/autoload.php" colors="true" cacheDirectory=".phpunit.cache">
|
||||
<testsuites>
|
||||
<testsuite name="Architecture">
|
||||
<directory>tests/Architecture</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Integration">
|
||||
<directory>tests/Integration</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Feature">
|
||||
<directory>tests/Feature</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>src</directory>
|
||||
</include>
|
||||
</source>
|
||||
|
||||
<php>
|
||||
</php>
|
||||
</phpunit>
|
||||
77
pint.json
77
pint.json
|
|
@ -1,77 +0,0 @@
|
|||
{
|
||||
"preset": "laravel",
|
||||
"rules": {
|
||||
"indentation_type": true,
|
||||
"single_line_empty_body": false,
|
||||
"types_spaces": {
|
||||
"space": "single",
|
||||
"space_multiple_catch": "single"
|
||||
},
|
||||
"control_structure_braces": true,
|
||||
"new_with_parentheses": {
|
||||
"anonymous_class": true,
|
||||
"named_class": true
|
||||
},
|
||||
"class_definition": {
|
||||
"single_item_single_line": false,
|
||||
"single_line": false,
|
||||
"space_before_parenthesis": true
|
||||
},
|
||||
"curly_braces_position": {
|
||||
"allow_single_line_empty_anonymous_classes": false,
|
||||
"classes_opening_brace": "next_line_unless_newline_at_signature_end",
|
||||
"anonymous_classes_opening_brace": "next_line_unless_newline_at_signature_end",
|
||||
"control_structures_opening_brace": "same_line",
|
||||
"functions_opening_brace": "next_line_unless_newline_at_signature_end",
|
||||
"anonymous_functions_opening_brace": "same_line"
|
||||
},
|
||||
"binary_operator_spaces": {
|
||||
"default": "align_single_space_minimal",
|
||||
"operators": {
|
||||
"=": "align_single_space",
|
||||
"|": "single_space",
|
||||
"=>": "align_single_space",
|
||||
".=": "align_single_space"
|
||||
}
|
||||
},
|
||||
"concat_space": {
|
||||
"spacing": "one"
|
||||
},
|
||||
"align_multiline_comment": {
|
||||
"comment_type": "all_multiline"
|
||||
},
|
||||
"array_indentation": true,
|
||||
"method_chaining_indentation": true,
|
||||
"statement_indentation": {
|
||||
"stick_comment_to_next_continuous_control_statement": true
|
||||
},
|
||||
"no_unused_imports": true,
|
||||
"fully_qualified_strict_types": {
|
||||
"import_symbols": true,
|
||||
"leading_backslash_in_global_namespace": true,
|
||||
"phpdoc_tags": []
|
||||
},
|
||||
"visibility_required": {
|
||||
"elements": [
|
||||
"const",
|
||||
"method",
|
||||
"property"
|
||||
]
|
||||
},
|
||||
"no_useless_else": true,
|
||||
"function_declaration": {
|
||||
"closure_function_spacing": "one",
|
||||
"closure_fn_spacing": "one",
|
||||
"trailing_comma_single_line": false
|
||||
},
|
||||
"single_line_after_imports": true,
|
||||
"combine_consecutive_issets": true,
|
||||
"array_syntax": {
|
||||
"syntax": "short"
|
||||
},
|
||||
"single_quote": true,
|
||||
"blank_line_after_namespace": true,
|
||||
"blank_line_after_opening_tag": true,
|
||||
"declare_strict_types": true
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
sonar.projectKey=arcadia-genesis-php-client
|
||||
sonar.projectName=Arcadia Genesis PHP Client
|
||||
sonar.sources=src
|
||||
sonar.tests=tests
|
||||
sonar.sourceEncoding=UTF-8
|
||||
sonar.exclusions=**/vendor/**,**/packages/**,**/coverage/**,junit.xml
|
||||
sonar.php.exclusions=**/vendor/**,**/packages/**
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Factory\ApiClientFactory;
|
||||
use Uwil\ArcadiaApiPhpClient\Factory\ServiceFactory;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiConfig;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\ApiService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\BrandService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\EmployeeService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\IncoService;
|
||||
|
||||
class ArcadiaApiClient
|
||||
{
|
||||
private readonly ServiceFactory $serviceFactory;
|
||||
|
||||
public function __construct(
|
||||
private readonly ApiConfig $apiConfig
|
||||
) {
|
||||
$apiClientFactory = new ApiClientFactory($this->apiConfig);
|
||||
$apiClient = $apiClientFactory->createClient();
|
||||
|
||||
$this->serviceFactory = new ServiceFactory(
|
||||
$apiClient,
|
||||
$this->apiConfig
|
||||
);
|
||||
}
|
||||
|
||||
public function apiService(): ApiService
|
||||
{
|
||||
return $this->serviceFactory->createApiService();
|
||||
}
|
||||
|
||||
public function employees(): EmployeeService
|
||||
{
|
||||
return $this->serviceFactory->createEmployeeService();
|
||||
}
|
||||
|
||||
public function incoSheets(): IncoService
|
||||
{
|
||||
return $this->serviceFactory->createIncoService();
|
||||
}
|
||||
|
||||
public function brands(): BrandService
|
||||
{
|
||||
return $this->serviceFactory->createBrandService();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Auth;
|
||||
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Uwil\ArcadiaApiPhpClient\Enum\HttpMethod;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\LibMiddleware;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ApiMandatoryHeadersMiddleware implements LibMiddleware
|
||||
{
|
||||
public const string HANDLER_NAME = 'Arcadia API request Signature';
|
||||
|
||||
public const string HEADER_CLIENT_IDENTIFIER = 'X-Client-Identifier';
|
||||
|
||||
public const string HEADER_BODY_HASH = 'X-Body-Hash';
|
||||
|
||||
public const string HEADER_TIMESTAMP = 'X-Timestamp';
|
||||
|
||||
public const string HEADER_SIGNATURE = 'X-Signature';
|
||||
|
||||
public const string HEADER_NONCE = 'X-Nonce';
|
||||
|
||||
public static function signRequest(
|
||||
ClientIdentifier $apiKey,
|
||||
ClientSecret $withSecret,
|
||||
RequestSignatureCalculator $signatureCalculator,
|
||||
): callable {
|
||||
return static function (callable $handler) use ($apiKey, $withSecret, $signatureCalculator): self {
|
||||
return new self(
|
||||
$handler,
|
||||
$apiKey,
|
||||
$withSecret,
|
||||
$signatureCalculator
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(RequestInterface, array<string, mixed>): PromiseInterface $nextHandler
|
||||
*/
|
||||
public function __construct(
|
||||
private $nextHandler,
|
||||
private readonly ClientIdentifier $apiKey,
|
||||
private readonly ClientSecret $withSecret,
|
||||
private readonly RequestSignatureCalculator $signatureCalculator,
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(RequestInterface $request, array $options): PromiseInterface
|
||||
{
|
||||
$fn = $this->nextHandler;
|
||||
|
||||
return $fn($this->sign($request), $options);
|
||||
}
|
||||
|
||||
private function sign(RequestInterface $request): RequestInterface
|
||||
{
|
||||
$body = $request->getBody();
|
||||
$body->rewind();
|
||||
$bodyContents = $body->getContents();
|
||||
|
||||
$timestamp = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
|
||||
$nonce = new Nonce(value: Uuid::uuid7());
|
||||
|
||||
$signatureResult = $this->signatureCalculator->sign(
|
||||
new RequestSignaturePayload(
|
||||
method: HttpMethod::from($request->getMethod()),
|
||||
uri: $request->getUri(),
|
||||
timestamp: $timestamp,
|
||||
nonce: $nonce,
|
||||
body: $bodyContents,
|
||||
),
|
||||
$this->withSecret,
|
||||
);
|
||||
|
||||
$headers = [
|
||||
self::HEADER_CLIENT_IDENTIFIER => $this->apiKey->value,
|
||||
self::HEADER_TIMESTAMP => $timestamp->format(\DateTimeInterface::RFC3339),
|
||||
self::HEADER_NONCE => $nonce->value->toString(),
|
||||
self::HEADER_BODY_HASH => $signatureResult->bodyHash,
|
||||
self::HEADER_SIGNATURE => $signatureResult->value,
|
||||
];
|
||||
|
||||
return Utils::modifyRequest($request, [
|
||||
'set_headers' => array_filter(
|
||||
$headers,
|
||||
static fn (string $name): bool => ! $request->hasHeader($name),
|
||||
ARRAY_FILTER_USE_KEY
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Auth;
|
||||
|
||||
final readonly class ClientIdentifier
|
||||
{
|
||||
public function __construct(
|
||||
#[\SensitiveParameter]
|
||||
public string $value,
|
||||
) {
|
||||
if (empty(mb_trim($value))) {
|
||||
throw new \InvalidArgumentException('ClientIdentifier cannot be empty.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Auth;
|
||||
|
||||
final readonly class ClientSecret
|
||||
{
|
||||
public function __construct(
|
||||
#[\SensitiveParameter]
|
||||
public string $value,
|
||||
) {
|
||||
if (empty(mb_trim($value))) {
|
||||
throw new \InvalidArgumentException('ClientSecret cannot be empty.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Auth;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
|
||||
final readonly class Nonce
|
||||
{
|
||||
public function __construct(
|
||||
public readonly UuidInterface $value,
|
||||
) {
|
||||
if (! Uuid::isValid($value->toString())) {
|
||||
throw new \InvalidArgumentException('Nonce value is not a valid UUID');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Auth;
|
||||
|
||||
final readonly class RequestSignature
|
||||
{
|
||||
public function __construct(
|
||||
public string $value,
|
||||
public string $bodyHash,
|
||||
) {
|
||||
if (empty(mb_trim($value))) {
|
||||
throw new \InvalidArgumentException('Signature value cannot be empty.');
|
||||
}
|
||||
if (empty(mb_trim($bodyHash))) {
|
||||
throw new \InvalidArgumentException('Body hash cannot be empty.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Auth;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class RequestSignatureCalculator
|
||||
{
|
||||
private const DATE_FORMAT = \DateTimeInterface::RFC3339;
|
||||
|
||||
private const HASH_ALGORITHM = 'sha256';
|
||||
|
||||
public function sign(RequestSignaturePayload $payload, ClientSecret $withSecret): RequestSignature
|
||||
{
|
||||
$bodyHash = hash(self::HASH_ALGORITHM, $payload->body);
|
||||
|
||||
$payloadToSign = [
|
||||
$payload->method->value,
|
||||
$this->normalizePathAndQuery($payload->uri),
|
||||
$payload->timestamp->format(self::DATE_FORMAT),
|
||||
$payload->nonce->value->toString(),
|
||||
$bodyHash,
|
||||
];
|
||||
|
||||
$signature = hash_hmac(
|
||||
self::HASH_ALGORITHM,
|
||||
implode('|', $payloadToSign),
|
||||
$withSecret->value
|
||||
);
|
||||
|
||||
return new RequestSignature(
|
||||
value: $signature,
|
||||
bodyHash: $bodyHash,
|
||||
);
|
||||
}
|
||||
|
||||
private function normalizePathAndQuery(UriInterface $uri): string
|
||||
{
|
||||
$path = $uri->getPath();
|
||||
$query = $uri->getQuery();
|
||||
|
||||
if ($query === '') {
|
||||
return $path;
|
||||
}
|
||||
|
||||
parse_str($query, $queryParams);
|
||||
ksort($queryParams);
|
||||
$sortedQuery = http_build_query($queryParams);
|
||||
|
||||
return "{$path}?{$sortedQuery}";
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Auth;
|
||||
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Uwil\ArcadiaApiPhpClient\Enum\HttpMethod;
|
||||
|
||||
final readonly class RequestSignaturePayload
|
||||
{
|
||||
public function __construct(
|
||||
public HttpMethod $method,
|
||||
public UriInterface $uri,
|
||||
public \DateTimeInterface $timestamp,
|
||||
public Nonce $nonce,
|
||||
public string $body,
|
||||
) {
|
||||
}
|
||||
}
|
||||
75
src/Datas/EmployeesDatas.php
Normal file
75
src/Datas/EmployeesDatas.php
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Uwil\ArcadiaGenesisPhpClient\Datas;
|
||||
|
||||
use Uwil\ArcadiaGenesisPhpClient\Models\Employee;
|
||||
use Uwil\ArcadiaGenesisPhpClient\Traits\ArcadiaTrait;
|
||||
use Uwil\ArcadiaGenesisPhpClient\Transport;
|
||||
|
||||
class EmployeesDatas
|
||||
{
|
||||
use ArcadiaTrait;
|
||||
|
||||
private static string $table = "RU_PERSONNE_SIRH";
|
||||
|
||||
private static array $definition = [
|
||||
'matricule' => 'MATRICULE_FIC',
|
||||
'lastname' => 'NOM_FIC',
|
||||
'firstname' => 'PRENOM_FIC',
|
||||
'start_date' => 'DATE_DEB_FIC',
|
||||
'end_date' => 'DATE_FIN_FIC',
|
||||
'planned_end_date' => 'DATE_EFFET_FIC',
|
||||
'establishment' => 'ETAB_FIC',
|
||||
'site' => 'SITE_FIC',
|
||||
'service' => 'SERVICE_FIC',
|
||||
'created_at' => 'DAT_CRE',
|
||||
'updated_at' => 'DAT_MAJ',
|
||||
];
|
||||
|
||||
public static function getById(string $id): Employee|null
|
||||
{
|
||||
$params = [
|
||||
'TABLE' => self::$table,
|
||||
'S_COLS' => self::getArcadiaDefinition(),
|
||||
'C_COLS' => self::getArcadiaDefinition(['matricule']),
|
||||
'C_OPES' => ['='],
|
||||
'C_VALS' => [$id],
|
||||
];
|
||||
|
||||
$response = Transport::call($params);
|
||||
|
||||
if (empty($response[0]->values)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$model = new Employee();
|
||||
$model->fill(self::mapObject($response[0]->values));
|
||||
return $model;
|
||||
}
|
||||
|
||||
public static function getListModifiedSince(string $date, array $sites = [])
|
||||
{
|
||||
$params = [
|
||||
'TABLE' => self::$table,
|
||||
'S_COLS' => self::getArcadiaDefinition(),
|
||||
'C_COLS' => self::getArcadiaDefinition(
|
||||
!empty($sites) ? ['site', 'updated_at'] : ['updated_at']
|
||||
),
|
||||
'C_OPES' => !empty($sites) ? ['IN', '>='] : ['>='],
|
||||
'C_VALS' => !empty($sites) ? [implode(',', $sites), $date] : [$date],
|
||||
];
|
||||
|
||||
$response = Transport::call($params);
|
||||
|
||||
var_dump($response);
|
||||
exit;
|
||||
|
||||
if (empty($response[0]->values)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$model = new Employee();
|
||||
$model->fill(self::mapObject($response[0]->values));
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
48
src/Datas/IncoDatas.php
Normal file
48
src/Datas/IncoDatas.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Uwil\ArcadiaGenesisPhpClient\Datas;
|
||||
|
||||
use Uwil\ArcadiaGenesisPhpClient\Models\Inco;
|
||||
use Uwil\ArcadiaGenesisPhpClient\Traits\ArcadiaTrait;
|
||||
use Uwil\ArcadiaGenesisPhpClient\Transport;
|
||||
|
||||
class IncoDatas
|
||||
{
|
||||
use ArcadiaTrait;
|
||||
|
||||
private static string $table = "INCO";
|
||||
|
||||
private static array $definition = [
|
||||
'id' => 'NO_ART',
|
||||
'description' => 'DESCRIPTIF',
|
||||
'tips' => 'ASTUCES',
|
||||
'ingredients' => 'LST_ING',
|
||||
'portions' => 'NB_PORTION',
|
||||
'portion_type' => 'TYP_PORTION', // FIXME: Lié à une autre table ?
|
||||
'allergens' => 'LST_ALLERGENE', // FIXME: Lié à une autre table ?
|
||||
'brand' => 'MARQUE', // FIXME: Lié à une autre table ?
|
||||
'created_at' => 'DAT_CRE',
|
||||
'updated_at' => 'DAT_MAJ',
|
||||
];
|
||||
|
||||
public static function getById(string $id)
|
||||
{
|
||||
$params = [
|
||||
'TABLE' => self::$table,
|
||||
'S_COLS' => self::getArcadiaDefinition(),
|
||||
'C_COLS' => self::getArcadiaDefinition(['id']),
|
||||
'C_OPES' => ['='],
|
||||
'C_VALS' => [$id],
|
||||
];
|
||||
|
||||
$response = Transport::call($params);
|
||||
|
||||
if (empty($response[0]->values)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$model = new Inco();
|
||||
$model->fill(self::mapObject($response[0]->values));
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Enum;
|
||||
|
||||
enum HttpMethod: string
|
||||
{
|
||||
case GET = 'GET';
|
||||
case POST = 'POST';
|
||||
case PUT = 'PUT';
|
||||
case PATCH = 'PATCH';
|
||||
case DELETE = 'DELETE';
|
||||
case HEAD = 'HEAD';
|
||||
case OPTIONS = 'OPTIONS';
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
abstract readonly class AbstractApiError implements ApiError
|
||||
{
|
||||
public function __construct(
|
||||
public string $message,
|
||||
public int $statusCode = 0,
|
||||
public ?\Throwable $previous = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function message(): string
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
public function statusCode(): int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function previous(): ?\Throwable
|
||||
{
|
||||
return $this->previous;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
interface ApiError
|
||||
{
|
||||
public function message(): string;
|
||||
|
||||
public function statusCode(): int;
|
||||
|
||||
public function previous(): ?\Throwable;
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
final readonly class AuthenticationError extends AbstractApiError
|
||||
{
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
final readonly class NetworkError extends AbstractApiError
|
||||
{
|
||||
public function __construct(
|
||||
string $message,
|
||||
?\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct($message, 0, $previous);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
final readonly class RateLimitError extends AbstractApiError
|
||||
{
|
||||
public function __construct(
|
||||
string $message,
|
||||
public ?int $retryAfter = null,
|
||||
?\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct($message, 429, $previous);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
final readonly class ServerError extends AbstractApiError
|
||||
{
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
final readonly class UnexpectedApiError extends AbstractApiError
|
||||
{
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Error;
|
||||
|
||||
final readonly class ValidationError extends AbstractApiError
|
||||
{
|
||||
/**
|
||||
* @param array<string, string> $violations Map of invalid parameter => reason
|
||||
*/
|
||||
public function __construct(
|
||||
string $message,
|
||||
public array $violations = [],
|
||||
?\Throwable $previous = null,
|
||||
) {
|
||||
parent::__construct($message, 422, $previous);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Factory;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Microsoft\Kiota\Bundle\DefaultRequestAdapter;
|
||||
use Microsoft\Kiota\Http\KiotaClientFactory;
|
||||
use Microsoft\Kiota\Http\Middleware\KiotaMiddleware;
|
||||
use Microsoft\Kiota\Http\Middleware\Options\RetryOption;
|
||||
use Microsoft\Kiota\Http\Middleware\Options\UserAgentHandlerOption;
|
||||
use Microsoft\Kiota\Http\Middleware\RetryHandler;
|
||||
use Microsoft\Kiota\Http\Middleware\UserAgentHandler;
|
||||
use Uwil\ArcadiaApiPhpClient\Auth\ApiMandatoryHeadersMiddleware;
|
||||
use Uwil\ArcadiaApiPhpClient\Auth\RequestSignatureCalculator;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiConfig;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\LibProperties;
|
||||
use Uwil\ArcadiaApiPhpClient\Logger\ApiLoggerMiddleware;
|
||||
use Uwil\ArcadiaApiPhpClient\Observability\ObservabilityOption;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\ApiClient;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ApiClientFactory
|
||||
{
|
||||
public function __construct(
|
||||
private ApiConfig $apiConfig
|
||||
) {
|
||||
}
|
||||
|
||||
public function createClient(): ApiClient
|
||||
{
|
||||
$signatureCalculator = new RequestSignatureCalculator();
|
||||
$libProperties = new LibProperties();
|
||||
$uaOptions = new UserAgentHandlerOption();
|
||||
$uaOptions->setProductName($libProperties->productName);
|
||||
$uaOptions->setProductVersion($libProperties->productVersion);
|
||||
|
||||
$handlerStack = KiotaClientFactory::getDefaultHandlerStack();
|
||||
|
||||
if ($this->apiConfig->logger !== null) {
|
||||
$handlerStack->push(
|
||||
ApiLoggerMiddleware::log($this->apiConfig->logger),
|
||||
ApiLoggerMiddleware::HANDLER_NAME
|
||||
);
|
||||
}
|
||||
|
||||
$handlerStack->push(
|
||||
ApiMandatoryHeadersMiddleware::signRequest(
|
||||
apiKey: $this->apiConfig->apiKey,
|
||||
withSecret: $this->apiConfig->secret,
|
||||
signatureCalculator: $signatureCalculator
|
||||
),
|
||||
ApiMandatoryHeadersMiddleware::HANDLER_NAME
|
||||
);
|
||||
|
||||
$handlerStack->push(
|
||||
KiotaMiddleware::userAgent($uaOptions),
|
||||
UserAgentHandler::HANDLER_NAME
|
||||
);
|
||||
|
||||
$handlerStack->push(
|
||||
KiotaMiddleware::retry(
|
||||
new RetryOption(
|
||||
maxRetries: $this->apiConfig->maxRetries,
|
||||
delaySeconds: $this->apiConfig->retryDelaySeconds
|
||||
)
|
||||
),
|
||||
RetryHandler::HANDLER_NAME
|
||||
);
|
||||
|
||||
$guzzleClient = new Client([
|
||||
'base_uri' => $this->apiConfig->endpoint,
|
||||
'timeout' => $this->apiConfig->curlTimeOut,
|
||||
'connect_timeout' => min(5.0, $this->apiConfig->curlTimeOut),
|
||||
'handler' => $handlerStack,
|
||||
'verify' => ! $this->apiConfig->ignoreSelfSignedCert,
|
||||
]);
|
||||
|
||||
$observabilityOption = new ObservabilityOption(
|
||||
$libProperties,
|
||||
$this->apiConfig->tracer
|
||||
);
|
||||
|
||||
$adapter = DefaultRequestAdapter::builder()
|
||||
->withGuzzleClient($guzzleClient)
|
||||
->withObservabilityOption($observabilityOption)
|
||||
->build();
|
||||
|
||||
$adapter->setBaseUrl($this->apiConfig->endpoint->__toString());
|
||||
|
||||
return new ApiClient($adapter);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Factory;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiConfig;
|
||||
use Uwil\ArcadiaApiPhpClient\Mapper\BrandMapper;
|
||||
use Uwil\ArcadiaApiPhpClient\Mapper\EmployeeMapper;
|
||||
use Uwil\ArcadiaApiPhpClient\Mapper\IncoCompoMapper;
|
||||
use Uwil\ArcadiaApiPhpClient\Mapper\IncoMapper;
|
||||
use Uwil\ArcadiaApiPhpClient\Mapper\IncoShortMapper;
|
||||
use Uwil\ArcadiaApiPhpClient\Repository\BrandApiRepository;
|
||||
use Uwil\ArcadiaApiPhpClient\Repository\EmployeeApiRepository;
|
||||
use Uwil\ArcadiaApiPhpClient\Repository\IncoApiRepository;
|
||||
use Uwil\ArcadiaApiPhpClient\Repository\IncoCompoApiRepository;
|
||||
use Uwil\ArcadiaApiPhpClient\Repository\IncoShortApiRepository;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\ApiService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\ArcadiaApiService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\ArcadiaBrandService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\ArcadiaEmployeeService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\ArcadiaIncoService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\BrandService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\EmployeeService;
|
||||
use Uwil\ArcadiaApiPhpClient\Service\IncoService;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\ApiClient;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ServiceFactory
|
||||
{
|
||||
private ?ApiService $apiService = null;
|
||||
|
||||
private ?EmployeeService $employeeService = null;
|
||||
|
||||
private ?IncoService $incoService = null;
|
||||
|
||||
private ?BrandService $brandService = null;
|
||||
|
||||
public function __construct(
|
||||
private readonly ApiClient $apiClient,
|
||||
private readonly ApiConfig $apiConfig
|
||||
) {
|
||||
}
|
||||
|
||||
public function createApiService(): ApiService
|
||||
{
|
||||
return $this->apiService ??= new ArcadiaApiService(
|
||||
$this->apiConfig
|
||||
);
|
||||
}
|
||||
|
||||
public function createEmployeeService(): EmployeeService
|
||||
{
|
||||
return $this->employeeService ??= new ArcadiaEmployeeService(
|
||||
new EmployeeApiRepository(
|
||||
$this->apiClient,
|
||||
new EmployeeMapper()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function createIncoService(): IncoService
|
||||
{
|
||||
return $this->incoService ??= new ArcadiaIncoService(
|
||||
new IncoApiRepository(
|
||||
$this->apiClient,
|
||||
new IncoMapper()
|
||||
),
|
||||
new IncoShortApiRepository(
|
||||
$this->apiClient,
|
||||
new IncoShortMapper()
|
||||
),
|
||||
new IncoCompoApiRepository(
|
||||
$this->apiClient,
|
||||
new IncoCompoMapper()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function createBrandService(): BrandService
|
||||
{
|
||||
return $this->brandService ??= new ArcadiaBrandService(
|
||||
new BrandApiRepository(
|
||||
$this->apiClient,
|
||||
new BrandMapper()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function employeeService(): EmployeeService
|
||||
{
|
||||
return $this->createEmployeeService();
|
||||
}
|
||||
|
||||
public function incoService(): IncoService
|
||||
{
|
||||
return $this->createIncoService();
|
||||
}
|
||||
|
||||
public function brandService(): BrandService
|
||||
{
|
||||
return $this->createBrandService();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib;
|
||||
|
||||
use OpenTelemetry\API\Trace\TracerInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Uwil\ArcadiaApiPhpClient\Auth\ClientIdentifier;
|
||||
use Uwil\ArcadiaApiPhpClient\Auth\ClientSecret;
|
||||
|
||||
final readonly class ApiConfig
|
||||
{
|
||||
public function __construct(
|
||||
#[\SensitiveParameter]
|
||||
public UriInterface $endpoint,
|
||||
#[\SensitiveParameter]
|
||||
public ClientIdentifier $apiKey,
|
||||
#[\SensitiveParameter]
|
||||
public ClientSecret $secret,
|
||||
public float $curlTimeOut = 20.0,
|
||||
public int $maxRetries = 0,
|
||||
public int $retryDelaySeconds = 1,
|
||||
public bool $ignoreSelfSignedCert = false,
|
||||
public ?LoggerInterface $logger = null,
|
||||
public ?TracerInterface $tracer = null,
|
||||
) {
|
||||
if (! in_array(strtolower($endpoint->getScheme()), ['http', 'https'])) {
|
||||
throw new \InvalidArgumentException('endpoint must have http or https scheme');
|
||||
}
|
||||
if ($curlTimeOut <= 0) {
|
||||
throw new \InvalidArgumentException('curlTimeOut must be positive');
|
||||
}
|
||||
if ($maxRetries < 0) {
|
||||
throw new \InvalidArgumentException('maxRetries must be non-negative');
|
||||
}
|
||||
if ($retryDelaySeconds < 0) {
|
||||
throw new \InvalidArgumentException('retryDelaySeconds must be non-negative');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto;
|
||||
|
||||
interface Dto extends \JsonSerializable
|
||||
{
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode;
|
||||
|
||||
final readonly class Brand implements Dto
|
||||
{
|
||||
public function __construct(
|
||||
public BrandCode $code,
|
||||
public string $label,
|
||||
public ?string $arcadiaId = null,
|
||||
public ?\DateTimeInterface $createdAt = null,
|
||||
) {
|
||||
if (empty(mb_trim($label))) {
|
||||
throw new \DomainException(
|
||||
"Invalid label '{$label}' should not be empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'code' => $this->code->value,
|
||||
'label' => $this->label,
|
||||
'arcadiaId' => $this->arcadiaId,
|
||||
'createdAt' => $this->createdAt?->format(\DateTimeInterface::ATOM),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
use DateTimeInterface;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\ArcadiaEmployeeId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\EstablishmentCode;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\ServiceId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\SiteId;
|
||||
|
||||
final readonly class Employee implements Dto
|
||||
{
|
||||
public function __construct(
|
||||
public ArcadiaEmployeeId $matriculePublique,
|
||||
public string $lastname,
|
||||
public string $firstname,
|
||||
public DateTimeInterface $start_date,
|
||||
public ?DateTimeInterface $end_date,
|
||||
public ?DateTimeInterface $planned_end_date,
|
||||
public EstablishmentCode $establishment,
|
||||
public SiteId $site,
|
||||
public ServiceId $service,
|
||||
public DateTimeInterface $created_at,
|
||||
public ?DateTimeInterface $updated_at,
|
||||
) {
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'matriculePublique' => $this->matriculePublique->value,
|
||||
'lastname' => $this->lastname,
|
||||
'firstname' => $this->firstname,
|
||||
'start_date' => $this->start_date->format(DATE_RFC3339),
|
||||
'end_date' => $this->end_date?->format(DATE_RFC3339),
|
||||
'planned_end_date' => $this->planned_end_date?->format(DATE_RFC3339),
|
||||
'establishment' => $this->establishment->value,
|
||||
'site' => $this->site->value,
|
||||
'service' => $this->service->value,
|
||||
'created_at' => $this->created_at->format(DATE_RFC3339),
|
||||
'updated_at' => $this->updated_at?->format(DATE_RFC3339),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Additive;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Allegations;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Allergen;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\IncoWeight;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Ingredients;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\Nutriscore;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\PortionAmount;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco\StorageCondition;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoCompoId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId;
|
||||
|
||||
/**
|
||||
* Represents an Inco sheet (ingredients / recipe).
|
||||
*/
|
||||
final readonly class Inco implements Dto
|
||||
{
|
||||
/**
|
||||
* @param Allergen[] $allergens
|
||||
* @param Additive[] $additives
|
||||
*/
|
||||
public function __construct(
|
||||
public IncoId $id,
|
||||
public string $name,
|
||||
public string $libelle,
|
||||
public string $description,
|
||||
public Allegations $allegations,
|
||||
public string $sellArguments,
|
||||
public string $preparationAdvices,
|
||||
public ?string $tips,
|
||||
public ?string $sellFamilly,
|
||||
public Ingredients $ingredients,
|
||||
public ?PortionAmount $portions,
|
||||
public array $allergens,
|
||||
public Brand $brand,
|
||||
public bool $isSeasonal,
|
||||
public ?IncoWeight $weight,
|
||||
public ?Nutriscore $nutriscore,
|
||||
public ?StorageCondition $storageCondition,
|
||||
public array $additives,
|
||||
public ?IncoCompoId $compo,
|
||||
public ?\DateTimeInterface $createdAt,
|
||||
public ?\DateTimeInterface $updatedAt,
|
||||
) {
|
||||
foreach ($this->allergens as $allergen) {
|
||||
if (! is_object($allergen) || ! ($allergen instanceof Allergen)) {
|
||||
throw new \InvalidArgumentException('Allergens shall be instance of ' . Allergen::class);
|
||||
}
|
||||
}
|
||||
foreach ($this->additives as $additive) {
|
||||
if (! is_object($additive) || ! ($additive instanceof Additive)) {
|
||||
throw new \InvalidArgumentException('Additives shall be instance of ' . Additive::class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'id' => $this->id->value,
|
||||
'name' => $this->name,
|
||||
'libelle' => $this->libelle,
|
||||
'description' => $this->description,
|
||||
'allegations' => $this->allegations,
|
||||
'sellArguments' => $this->sellArguments,
|
||||
'preparationAdvices' => $this->preparationAdvices,
|
||||
'tips' => $this->tips,
|
||||
'sellFamilly' => $this->sellFamilly,
|
||||
'ingredients' => $this->ingredients,
|
||||
'portions' => $this->portions,
|
||||
'allergens' => $this->allergens,
|
||||
'brand' => [
|
||||
'code' => $this->brand->code,
|
||||
'label' => $this->brand->label,
|
||||
],
|
||||
'isSeasonal' => $this->isSeasonal,
|
||||
'weight' => $this->weight,
|
||||
'nutriscore' => $this->nutriscore,
|
||||
'storageCondition' => $this->storageCondition,
|
||||
'additives' => $this->additives,
|
||||
'compo' => $this->compo?->value,
|
||||
'created_at' => $this->createdAt,
|
||||
'updated_at' => $this->updatedAt,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
/**
|
||||
* Represents a food additive identified by its E-number code.
|
||||
*
|
||||
* Maps to a single entry of LST_ADDITIF.
|
||||
*
|
||||
* Valid formats: E300, E471a, E160ai, E160a(i), E160a(ii), E1404, E1442
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/E_number
|
||||
*/
|
||||
final readonly class Additive implements Dto
|
||||
{
|
||||
public readonly string $code;
|
||||
|
||||
public function __construct(
|
||||
string $code,
|
||||
public readonly string $label,
|
||||
) {
|
||||
$this->code = $code;
|
||||
$trimmedLabel = mb_trim($label);
|
||||
|
||||
if (! preg_match('/^E\d+(?:[a-z]+(?:\([ivx]+\))?)?$/', $this->code)) {
|
||||
throw new \DomainException(
|
||||
"Invalid additive code '{$code}': must match E-number format (e.g. E300, E471a, E160ai, E160a(ii), E1404)"
|
||||
);
|
||||
}
|
||||
|
||||
if ($trimmedLabel === '') {
|
||||
throw new \DomainException(
|
||||
"Invalid label for additive '{$code}': must not be empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'code' => $this->code,
|
||||
'label' => $this->label,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
final readonly class Allegations implements Dto
|
||||
{
|
||||
/** @var string[] */
|
||||
private array $allegations;
|
||||
|
||||
/** @var string[] */
|
||||
private array $health;
|
||||
|
||||
/** @var string[] */
|
||||
private array $nutrition;
|
||||
|
||||
/** @var string[] */
|
||||
private array $other;
|
||||
|
||||
/**
|
||||
* ! All array are filtered keeping only strings.
|
||||
*
|
||||
* @param string[] $allegations Allegations.
|
||||
* @param string[] $health Health allegations.
|
||||
* @param string[] $nutrition Nutrition allegations.
|
||||
* @param string[] $other Other allegations.
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
array $allegations = [],
|
||||
array $health = [],
|
||||
array $nutrition = [],
|
||||
array $other = []
|
||||
) {
|
||||
$this->allegations = \array_filter($allegations, fn (string $el) => ! empty($el));
|
||||
$this->health = \array_filter($health, fn (string $el) => ! empty($el));
|
||||
$this->nutrition = \array_filter($nutrition, fn (string $el) => ! empty($el));
|
||||
$this->other = \array_filter($other, fn (string $el) => ! empty($el));
|
||||
}
|
||||
|
||||
// * Methods
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function allegations(): array
|
||||
{
|
||||
return [...$this->allegations];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function health(): array
|
||||
{
|
||||
return [...$this->health];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function nutrition(): array
|
||||
{
|
||||
return [...$this->nutrition];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function other(): array
|
||||
{
|
||||
return [...$this->other];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all
|
||||
*
|
||||
* @return array<string,string[]>
|
||||
*/
|
||||
public function all(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
// * Implements Interfaces
|
||||
|
||||
/**
|
||||
* Get the instance as an array.
|
||||
*
|
||||
* @return array{'allegations':string[],'health':string[],'nutrition':string[],'other':string[]}
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return [
|
||||
'allegations' => [...$this->allegations],
|
||||
'health' => [...$this->health],
|
||||
'nutrition' => [...$this->nutrition],
|
||||
'other' => [...$this->other],
|
||||
];
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
final readonly class Allergen implements Dto
|
||||
{
|
||||
public function __construct(
|
||||
public readonly int $code,
|
||||
public readonly string $label
|
||||
) {
|
||||
if ($code <= 0) {
|
||||
throw new \DomainException(
|
||||
"Invalid code '{$code}' shall be greater than 0"
|
||||
);
|
||||
}
|
||||
|
||||
if (empty(mb_trim($label))) {
|
||||
throw new \DomainException(
|
||||
"Invalid label '{$label}' should not be empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'code' => $this->code,
|
||||
'label' => $this->label,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
final readonly class IncoWeight implements Dto
|
||||
{
|
||||
/**
|
||||
* @param int $netUvc In grams.
|
||||
* @param int $brutUvc In grams.
|
||||
* @param int $moyTheo In grams.
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly int $netUvc,
|
||||
public readonly int $brutUvc,
|
||||
public readonly int $moyTheo,
|
||||
) {
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'netUvc' => $this->netUvc,
|
||||
'brutUvc' => $this->brutUvc,
|
||||
'moyTheo' => $this->moyTheo,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
/**
|
||||
* Represents a structured and parsed ingredient list.
|
||||
*
|
||||
* Each entry is either:
|
||||
* - an indexed group (int key) -> list of ingredient strings without a section label
|
||||
* - a labelled group (string key) -> list of ingredient strings under a section label
|
||||
* (e.g. "Ingrédients", "Traces possibles")
|
||||
*
|
||||
* @phpstan-type IngredientGroup array<int|string,string[]>
|
||||
*/
|
||||
final readonly class Ingredients implements Dto
|
||||
{
|
||||
/**
|
||||
* @param array<int|string,string[]> $groups
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly array $groups
|
||||
) {
|
||||
foreach ($groups as $key => $lines) {
|
||||
if (! is_array($lines)) {
|
||||
throw new \DomainException(
|
||||
"Invalid ingredient group at key '{$key}': value must be an array of strings"
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($lines as $i => $line) {
|
||||
if (! is_string($line)) {
|
||||
throw new \DomainException(
|
||||
"Invalid ingredient at group '{$key}', index '{$i}': value must be a string"
|
||||
);
|
||||
}
|
||||
|
||||
if (empty(mb_trim($line))) {
|
||||
throw new \DomainException(
|
||||
"Invalid ingredient at group '{$key}', index '{$i}': string should not be empty"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all ingredient strings flattened into a single list, regardless of group.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function flatten(): array
|
||||
{
|
||||
return array_merge(...array_values($this->groups));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the ingredient list is empty (no groups or all groups are empty).
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->flatten());
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->groups;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
/**
|
||||
* Represents the Nutri-Score grade of a product as displayed on pack.
|
||||
*
|
||||
* Valid grades are A, B, C, D and E.
|
||||
*/
|
||||
final readonly class Nutriscore implements Dto
|
||||
{
|
||||
public const array VALID_GRADES = ['A', 'B', 'C', 'D', 'E'];
|
||||
|
||||
public function __construct(
|
||||
public readonly string $grade,
|
||||
) {
|
||||
if (! in_array($this->grade, self::VALID_GRADES, true)) {
|
||||
throw new \DomainException(
|
||||
sprintf(
|
||||
"Invalid Nutri-Score grade '%s': must be one of %s",
|
||||
$grade,
|
||||
implode(', ', self::VALID_GRADES)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->grade;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
/**
|
||||
* Si le type de portion est exact, seul la partie basse est utilisée,
|
||||
* Si le type de portion est environ, la partie basse seule ou les deux peuvent
|
||||
* etre utilisés.
|
||||
*/
|
||||
final readonly class PortionAmount implements \Stringable, Dto
|
||||
{
|
||||
/**
|
||||
* Valeur minimale, ou exacte
|
||||
*/
|
||||
private readonly int $lowPart;
|
||||
|
||||
/**
|
||||
* Valeur maximale
|
||||
*/
|
||||
private readonly int $highPart;
|
||||
|
||||
/**
|
||||
* The portion is approximative
|
||||
*/
|
||||
private readonly bool $approximative;
|
||||
|
||||
/**
|
||||
* The number of decimals allowed, this should not change
|
||||
*/
|
||||
private const ALLOWED_DECIMAL_SIZE = 2;
|
||||
|
||||
/**
|
||||
* NbPortion
|
||||
*
|
||||
* @param float $lowPart Min value is 0, max depends on lowPart mask.
|
||||
* @param float $highPart Min value is 0, max depends on lowPart mask.
|
||||
* @return void
|
||||
*
|
||||
* @throws \InvalidArgumentException If lowPart part does not fit in.
|
||||
* @throws \InvalidArgumentException If highPart part does not fit in.
|
||||
*/
|
||||
public function __construct(float $lowPart, float $highPart = 0, bool $approximative = false)
|
||||
{
|
||||
$maxMask = self::getMasks()['lowPart'];
|
||||
$scale = (int) pow(10, self::ALLOWED_DECIMAL_SIZE);
|
||||
$maxValue = (float) bcdiv((string) $maxMask, (string) $scale, self::ALLOWED_DECIMAL_SIZE);
|
||||
|
||||
if ($lowPart < 0.0 || ($lowPart > $maxValue)) {
|
||||
throw new \InvalidArgumentException("lowPart must be between 0 and '{$maxValue}', given '{$lowPart}'");
|
||||
}
|
||||
if ($highPart < 0.0 || ($highPart > $maxValue)) {
|
||||
throw new \InvalidArgumentException("highPart must be between 0 and '{$maxValue}', given '{$highPart}'");
|
||||
}
|
||||
|
||||
$this->lowPart = (int) bcmul((string) $lowPart, (string) $scale, 0);
|
||||
$this->highPart = (int) bcmul((string) $highPart, (string) $scale, 0);
|
||||
$this->approximative = $approximative;
|
||||
}
|
||||
|
||||
// * Casts
|
||||
|
||||
public function toIncoValue(): int
|
||||
{
|
||||
return $this->lowPart |
|
||||
$this->highPart << 16 |
|
||||
($this->approximative ? self::getMasks()['approximative'] : 0);
|
||||
}
|
||||
|
||||
public static function fromIncoValue(int $databaseValue): self
|
||||
{
|
||||
$scale = (int) pow(10, self::ALLOWED_DECIMAL_SIZE);
|
||||
|
||||
$lowInt = $databaseValue & self::getMasks()['lowPpart'];
|
||||
$highInt = ($databaseValue & self::getMasks()['highPart']) >> 16;
|
||||
$approx = (bool) ($databaseValue & self::getMasks()['approximative']);
|
||||
|
||||
$low = (float) bcdiv((string) $lowInt, (string) $scale, self::ALLOWED_DECIMAL_SIZE);
|
||||
$high = (float) bcdiv((string) $highInt, (string) $scale, self::ALLOWED_DECIMAL_SIZE);
|
||||
|
||||
return new self($low, $high, $approx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,int|bool> $jsonArray
|
||||
*
|
||||
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
*/
|
||||
public static function fromJson(array $jsonArray): self
|
||||
{
|
||||
// phpcs:enable
|
||||
$scale = (int) pow(10, self::ALLOWED_DECIMAL_SIZE);
|
||||
|
||||
$lowRaw = $jsonArray['lowPart'] ?? $jsonArray['lowPart'] ?? 0;
|
||||
$highRaw = $jsonArray['highPart'] ?? $jsonArray['highPart'] ?? 0;
|
||||
$approx = (bool) ($jsonArray['approximative'] ?? $jsonArray['approximative'] ?? false);
|
||||
|
||||
$lowVal = is_numeric($lowRaw) ? (float) $lowRaw : 0.0;
|
||||
$highVal = is_numeric($highRaw) ? (float) $highRaw : 0.0;
|
||||
|
||||
if (is_int($lowRaw) && $lowRaw > $scale) {
|
||||
$lowVal = (float) bcdiv((string) $lowRaw, (string) $scale, self::ALLOWED_DECIMAL_SIZE);
|
||||
}
|
||||
if (is_int($highRaw) && $highRaw > $scale) {
|
||||
$highVal = (float) bcdiv((string) $highRaw, (string) $scale, self::ALLOWED_DECIMAL_SIZE);
|
||||
}
|
||||
|
||||
return new self($lowVal, $highVal, $approx);
|
||||
}
|
||||
|
||||
// * STATIC Methods
|
||||
|
||||
/**
|
||||
* Binary masks to store and retrieve values
|
||||
*
|
||||
* @return array<string,int>
|
||||
*/
|
||||
public static function getMasks(): array
|
||||
{
|
||||
return [
|
||||
'lowPart' => ((1 << 15) - 1),
|
||||
'highPart' => (1 << 31) - (1 << 16),
|
||||
'approximative' => 1 << 31,
|
||||
];
|
||||
}
|
||||
|
||||
// * Methods
|
||||
|
||||
public function getLowValue(): float
|
||||
{
|
||||
$scale = (int) pow(10, self::ALLOWED_DECIMAL_SIZE);
|
||||
|
||||
return (float) bcdiv((string) $this->lowPart, (string) $scale, self::ALLOWED_DECIMAL_SIZE);
|
||||
}
|
||||
|
||||
public function getHighValue(): float
|
||||
{
|
||||
$scale = (int) pow(10, self::ALLOWED_DECIMAL_SIZE);
|
||||
|
||||
return (float) bcdiv((string) $this->highPart, (string) $scale, self::ALLOWED_DECIMAL_SIZE);
|
||||
}
|
||||
|
||||
public function getType(): PortionType
|
||||
{
|
||||
return match ($this->approximative) {
|
||||
true => PortionType::approximative,
|
||||
false => PortionType::exact
|
||||
};
|
||||
}
|
||||
|
||||
public function toJson(int $options = 0): string
|
||||
{
|
||||
return (string) \json_encode($this->jsonSerialize(), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{lowPart: int, highPart: int, approximative: bool}
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return $this->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{lowPart: int, highPart: int, approximative: bool}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'lowPart' => $this->lowPart,
|
||||
'highPart' => $this->highPart,
|
||||
'approximative' => $this->approximative,
|
||||
];
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$low = $this->formatNumberForDisplay($this->getLowValue());
|
||||
$high = $this->formatNumberForDisplay($this->getHighValue());
|
||||
|
||||
if ($this->highPart) {
|
||||
return sprintf('entre %s et %s', $low, $high);
|
||||
}
|
||||
|
||||
if ($this->approximative) {
|
||||
return sprintf('environ %s', $low);
|
||||
}
|
||||
|
||||
return $low;
|
||||
}
|
||||
|
||||
protected function formatNumberForDisplay(float $value): string
|
||||
{
|
||||
$decimals = self::ALLOWED_DECIMAL_SIZE;
|
||||
$format = number_format($value, $decimals, '.', '');
|
||||
|
||||
return mb_rtrim(mb_rtrim($format, '0'), '.');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
enum PortionType: int implements Dto
|
||||
{
|
||||
case unknown = -1;
|
||||
case approximative = 0;
|
||||
case exact = 1;
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return match ($this->value) {
|
||||
self::approximative->name => 'approximatif',
|
||||
self::exact->name => 'exact',
|
||||
self::unknown->name => 'inconnue',
|
||||
default => 'inconnue',
|
||||
};
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'value' => $this->value,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Inco;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
/**
|
||||
* Represents the storage/conservation conditions of a product.
|
||||
*
|
||||
* Maps to CONDIT_CONSERV.
|
||||
*/
|
||||
final readonly class StorageCondition implements Dto
|
||||
{
|
||||
public function __construct(
|
||||
public readonly string $instructions,
|
||||
) {
|
||||
if (empty(mb_trim($this->instructions))) {
|
||||
throw new \DomainException(
|
||||
'Invalid instructions: must not be empty'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->instructions;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\AcideGras;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\AcidesAmines;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\ApportsReference;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\ComposesSpeciaux;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Energie;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\FruitLegumeNoix;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\GlucidesDetail;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Identification;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\MacronutrimentsPortion;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\MacronutrimentsRef;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Mineraux;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\NutriScore;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\PointsNutriScore;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo\Vitamines;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoCompoId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId;
|
||||
|
||||
/**
|
||||
* Agrégat racine regroupant toutes les sous-sections nutritionnelles d'une fiche INCO.
|
||||
*/
|
||||
final readonly class IncoCompo implements Dto
|
||||
{
|
||||
public function __construct(
|
||||
public IncoCompoId $id,
|
||||
public IncoId $idInco,
|
||||
public Identification $identification,
|
||||
public Energie $energie,
|
||||
public MacronutrimentsRef $macronutrimentsRef,
|
||||
public MacronutrimentsPortion $macronutrimentsPortion,
|
||||
public FruitLegumeNoix $fnl,
|
||||
public ApportsReference $apportsReference,
|
||||
public NutriScore $nutriScore,
|
||||
public PointsNutriScore $pointsNutriScore,
|
||||
public Mineraux $mineraux,
|
||||
public Vitamines $vitamines,
|
||||
public GlucidesDetail $glucidesDetail,
|
||||
public AcideGras $acideGras,
|
||||
public AcidesAmines $acidesAmines,
|
||||
public ComposesSpeciaux $composesSpeciaux,
|
||||
) {
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'id' => $this->id->value,
|
||||
'idInco' => $this->idInco->value,
|
||||
'identification' => $this->identification,
|
||||
'energie' => $this->energie,
|
||||
'macronutrimentsRef' => $this->macronutrimentsRef,
|
||||
'macronutrimentsPortion' => $this->macronutrimentsPortion,
|
||||
'fnl' => $this->fnl,
|
||||
'apportsReference' => $this->apportsReference,
|
||||
'nutriScore' => $this->nutriScore,
|
||||
'pointsNutriScore' => $this->pointsNutriScore,
|
||||
'mineraux' => $this->mineraux,
|
||||
'vitamines' => $this->vitamines,
|
||||
'glucidesDetail' => $this->glucidesDetail,
|
||||
'acideGras' => $this->acideGras,
|
||||
'acidesAmines' => $this->acidesAmines,
|
||||
'composesSpeciaux' => $this->composesSpeciaux,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
/**
|
||||
* Base implementation for NutriDto.
|
||||
*
|
||||
* Concrete classes must implement:
|
||||
* - fieldLabels(): array<string, string> - [ propertyName => 'Human label' ]
|
||||
* - fieldUnits(): array<string, string|null> - [ propertyName => 'unit'|null ]
|
||||
*
|
||||
* toArray() is built automatically via reflection on public properties.
|
||||
*/
|
||||
abstract readonly class AbstractIncoCompoPartDto implements IncoCompoPartDto
|
||||
{
|
||||
/**
|
||||
* Map of [ propertyName => 'Human readable label' ].
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
abstract protected static function fieldLabels(): array;
|
||||
|
||||
/**
|
||||
* Map of [ propertyName => 'unit string or null' ].
|
||||
*
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
abstract protected static function fieldUnits(): array;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
/** @var array<string, float|string|null> $result */
|
||||
$result = [];
|
||||
|
||||
foreach (get_object_vars($this) as $name => $value) {
|
||||
if (\is_float($value) || \is_string($value) || $value === null) {
|
||||
$result[(string) $name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function humanReadableValues(): array
|
||||
{
|
||||
$values = $this->toArray();
|
||||
$out = [];
|
||||
|
||||
foreach (static::fieldLabels() as $property => $label) {
|
||||
$out[$label] = $values[$property] ?? null;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function units(): array
|
||||
{
|
||||
return static::fieldUnits();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public static function humanReadableUnits(): array
|
||||
{
|
||||
$out = [];
|
||||
|
||||
foreach (static::fieldLabels() as $property => $label) {
|
||||
$units = static::fieldUnits();
|
||||
if (\array_key_exists($property, $units)) {
|
||||
$out[$label] = $units[$property];
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array<string, float|string|null>
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return array_filter(
|
||||
$this->toArray(),
|
||||
static fn (mixed $value): bool => $value !== null,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
$values = $this->humanReadableValues();
|
||||
$units = static::humanReadableUnits();
|
||||
|
||||
$lines = [];
|
||||
foreach ($values as $label => $value) {
|
||||
$unit = $units[$label] ?? null;
|
||||
$lines[] = $label . ' : ' . $value . ($unit !== null ? ' ' . $unit : '');
|
||||
}
|
||||
|
||||
return \implode("\n", $lines);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class AcideGras extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
// Lipides généraux
|
||||
public string $nutFd,
|
||||
public string $nutChol,
|
||||
public string $nutAgt,
|
||||
// AGS - chaîne courte à longue (classés par nb de carbones)
|
||||
public string $nutFformiq,
|
||||
public string $nutFacetiq,
|
||||
public string $nutFpropioniq,
|
||||
public string $nutFbutyriq,
|
||||
public string $nutFvaleriq,
|
||||
public string $nutFcaproiq,
|
||||
public string $nutFenatiq,
|
||||
public string $nutFcapryliq,
|
||||
public string $nutFpelargoniq,
|
||||
public string $nutFcapriq,
|
||||
public string $nutFundecyliq,
|
||||
public string $nutFlauriq,
|
||||
public string $nutFtridecyliq,
|
||||
public string $nutFmyristiq,
|
||||
public string $nutFpentadecyliq,
|
||||
public string $nutF16d0,
|
||||
public string $nutFmargariq,
|
||||
public string $nutFsteariq,
|
||||
public string $nutFarachidiq,
|
||||
public string $nutFheneicosyliq,
|
||||
public string $nutFbeheniq,
|
||||
public string $nutFtricosiliq,
|
||||
public string $nutFlignoceriq,
|
||||
public string $nutFcarboceriq,
|
||||
public string $nutFcerotiq,
|
||||
public string $nutFceroplastiq,
|
||||
public string $nutFmontaniq,
|
||||
public string $nutFnonacosyliq,
|
||||
public string $nutFmelissiq,
|
||||
public string $nutFlaceroiq,
|
||||
public string $nutFhentriacontyliq,
|
||||
public string $nutFhextriacontyliq,
|
||||
public string $nutFheptatriacontyliq,
|
||||
public string $nutFoctatriacontyliq,
|
||||
public string $nutFnonadylique,
|
||||
public string $nutFpsylliq,
|
||||
// AGMI
|
||||
public string $nutFpalm,
|
||||
public string $nutFoleiq,
|
||||
public string $nutF18d,
|
||||
public string $nutF19d,
|
||||
public string $nutF11eico,
|
||||
public string $nutFgeddiq,
|
||||
public string $nutFeruciq,
|
||||
public string $nutFnervoniq,
|
||||
public string $nutFhyeniq,
|
||||
public string $nutFamscis,
|
||||
// AGPI
|
||||
public string $nutF18d2cn6,
|
||||
public string $nutF18d3n3,
|
||||
public string $nutF20d4,
|
||||
public string $nutF20d5n3,
|
||||
public string $nutF22d6n3,
|
||||
public string $nutFapun3,
|
||||
public string $nutFapun6,
|
||||
public string $nutFapun9,
|
||||
public string $nutFapucis,
|
||||
public string $nutFapulc,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
// Lipides généraux
|
||||
'nutFd' => 'Lipides totaux',
|
||||
'nutChol' => 'Cholestérol',
|
||||
'nutAgt' => 'Acides gras trans',
|
||||
// AGS
|
||||
'nutFformiq' => 'Acide formique (C1:0)',
|
||||
'nutFacetiq' => 'Acide acétique (C2:0)',
|
||||
'nutFpropioniq' => 'Acide propionique (C3:0)',
|
||||
'nutFbutyriq' => 'Acide butyrique (C4:0)',
|
||||
'nutFvaleriq' => 'Acide valérique (C5:0)',
|
||||
'nutFcaproiq' => 'Acide caproïque (C6:0)',
|
||||
'nutFenatiq' => 'Acide énanthique (C7:0)',
|
||||
'nutFcapryliq' => 'Acide caprylique (C8:0)',
|
||||
'nutFpelargoniq' => 'Acide pélargonique (C9:0)',
|
||||
'nutFcapriq' => 'Acide caprique (C10:0)',
|
||||
'nutFundecyliq' => 'Acide undécylique (C11:0)',
|
||||
'nutFlauriq' => 'Acide laurique (C12:0)',
|
||||
'nutFtridecyliq' => 'Acide tridécylique (C13:0)',
|
||||
'nutFmyristiq' => 'Acide myristique (C14:0)',
|
||||
'nutFpentadecyliq' => 'Acide pentadécylique (C15:0)',
|
||||
'nutF16d0' => 'Acide palmitique (C16:0)',
|
||||
'nutFmargariq' => 'Acide margarique (C17:0)',
|
||||
'nutFsteariq' => 'Acide stéarique (C18:0)',
|
||||
'nutFarachidiq' => 'Acide arachidique (C20:0)',
|
||||
'nutFheneicosyliq' => 'Acide héneicosylique (C21:0)',
|
||||
'nutFbeheniq' => 'Acide béhénique (C22:0)',
|
||||
'nutFtricosiliq' => 'Acide tétracosanoïque (C23:0)',
|
||||
'nutFlignoceriq' => 'Acide lignocérique (C24:0)',
|
||||
'nutFcarboceriq' => 'Acide carbocérique (C26:0)',
|
||||
'nutFcerotiq' => 'Acide cérotique (C26:0 alt.)',
|
||||
'nutFceroplastiq' => 'Acide céroplastique',
|
||||
'nutFmontaniq' => 'Acide montanique (C28:0)',
|
||||
'nutFnonacosyliq' => 'Acide nonacosylique (C29:0)',
|
||||
'nutFmelissiq' => 'Acide mélissique (C30:0)',
|
||||
'nutFlaceroiq' => 'Acide lacéroïque (C32:0)',
|
||||
'nutFhentriacontyliq' => 'Acide hentriacontylique (C31:0)',
|
||||
'nutFhextriacontyliq' => 'Acide hexatriacontylique (C36:0)',
|
||||
'nutFheptatriacontyliq' => 'Acide heptatriacontylique (C37:0)',
|
||||
'nutFoctatriacontyliq' => 'Acide octariacontylique (C38:0)',
|
||||
'nutFnonadylique' => 'Acide nonadylique',
|
||||
'nutFpsylliq' => 'Acide psyllique',
|
||||
// AGMI
|
||||
'nutFpalm' => 'Acide palmitoléique (C16:1)',
|
||||
'nutFoleiq' => 'Acide oléique (C18:1 ω-9)',
|
||||
'nutF18d' => 'C18 insaturé (générique)',
|
||||
'nutF19d' => 'C19 insaturé',
|
||||
'nutF11eico' => 'Acide gondoïque (C20:1)',
|
||||
'nutFgeddiq' => 'Acide geddique (C20:1 alt.)',
|
||||
'nutFeruciq' => 'Acide érucique (C22:1)',
|
||||
'nutFnervoniq' => 'Acide nervonique (C24:1)',
|
||||
'nutFhyeniq' => 'Acide hyénique',
|
||||
'nutFamscis' => 'AG monoinsaturés cis (total)',
|
||||
// AGPI
|
||||
'nutF18d2cn6' => 'Acide linoléique (C18:2 ω-6)',
|
||||
'nutF18d3n3' => 'Acide α-linolénique (C18:3 ω-3)',
|
||||
'nutF20d4' => 'Acide arachidonique (C20:4)',
|
||||
'nutF20d5n3' => 'EPA (C20:5 ω-3)',
|
||||
'nutF22d6n3' => 'DHA (C22:6 ω-3)',
|
||||
'nutFapun3' => 'AGPI ω-3 (total)',
|
||||
'nutFapun6' => 'AGPI ω-6 (total)',
|
||||
'nutFapun9' => 'AGPI ω-9 (total)',
|
||||
'nutFapucis' => 'AGPI cis (total)',
|
||||
'nutFapulc' => 'AGPI longue chaîne (total)',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
// All fatty acid values are in g / 100g
|
||||
$fields = \array_keys(self::fieldLabels());
|
||||
|
||||
return \array_combine(
|
||||
$fields,
|
||||
\array_map(static fn (string $field): string => match ($field) {
|
||||
'nutChol' => 'mg',
|
||||
default => 'g',
|
||||
}, $fields),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class AcidesAmines extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $nutAae,
|
||||
public string $nutCasn,
|
||||
public string $nutWhey,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'nutAae' => 'Acides aminés essentiels (total)',
|
||||
'nutCasn' => 'Caséines',
|
||||
'nutWhey' => 'Protéines de lactosérum (whey)',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'nutAae' => 'g',
|
||||
'nutCasn' => 'g',
|
||||
'nutWhey' => 'g',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class ApportsReference extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $pourcentApportRefE14P,
|
||||
public string $pourcentApportRefKjoP,
|
||||
public string $pourcentApportRefFatP,
|
||||
public string $pourcentApportRefFatsatP,
|
||||
public string $pourcentApportRefChovalP,
|
||||
public string $pourcentApportRefSugarP,
|
||||
public string $pourcentApportRefProP,
|
||||
public string $pourcentApportRefSalteqP,
|
||||
public string $apportPort,
|
||||
public string $pSurL,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'pourcentApportRefE14P' => '% AR Énergie E14 / portion',
|
||||
'pourcentApportRefKjoP' => '% AR Énergie kJ / portion',
|
||||
'pourcentApportRefFatP' => '% AR Matières grasses / portion',
|
||||
'pourcentApportRefFatsatP' => '% AR Graisses saturées / portion',
|
||||
'pourcentApportRefChovalP' => '% AR Glucides / portion',
|
||||
'pourcentApportRefSugarP' => '% AR Sucres / portion',
|
||||
'pourcentApportRefProP' => '% AR Protéines / portion',
|
||||
'pourcentApportRefSalteqP' => '% AR Sel / portion',
|
||||
'apportPort' => 'Apport par portion',
|
||||
'pSurL' => 'Ratio P/L',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'pourcentApportRefE14P' => '%',
|
||||
'pourcentApportRefKjoP' => '%',
|
||||
'pourcentApportRefFatP' => '%',
|
||||
'pourcentApportRefFatsatP' => '%',
|
||||
'pourcentApportRefChovalP' => '%',
|
||||
'pourcentApportRefSugarP' => '%',
|
||||
'pourcentApportRefProP' => '%',
|
||||
'pourcentApportRefSalteqP' => '%',
|
||||
'apportPort' => 'g',
|
||||
'pSurL' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class ComposesSpeciaux extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $nutCholn,
|
||||
public string $nutCarnitine,
|
||||
public string $nutTau,
|
||||
public string $nutNucleotide,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'nutCholn' => 'Choline',
|
||||
'nutCarnitine' => 'Carnitine',
|
||||
'nutTau' => 'Taurine',
|
||||
'nutNucleotide' => 'Nucléotides',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'nutCholn' => 'mg',
|
||||
'nutCarnitine' => 'mg',
|
||||
'nutTau' => 'mg',
|
||||
'nutNucleotide' => 'mg',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class Energie extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
// Par 100g/ml (référence)
|
||||
public string $tailleReference,
|
||||
public string $valEnergKjReference,
|
||||
public string $valEnergKcalReference,
|
||||
// Par portion
|
||||
public string $taillePortion,
|
||||
public string $valEnergKjPortion,
|
||||
public string $valEnergKcalPortion,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'tailleReference' => 'Taille de référence',
|
||||
'valEnergKjReference' => 'Énergie (kJ) / 100g',
|
||||
'valEnergKcalReference' => 'Énergie (kcal) / 100g',
|
||||
'taillePortion' => 'Taille de la portion',
|
||||
'valEnergKjPortion' => 'Énergie (kJ) / portion',
|
||||
'valEnergKcalPortion' => 'Énergie (kcal) / portion',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'tailleReference' => 'g',
|
||||
'valEnergKjReference' => 'kJ',
|
||||
'valEnergKcalReference' => 'kcal',
|
||||
'taillePortion' => 'g',
|
||||
'valEnergKjPortion' => 'kJ',
|
||||
'valEnergKcalPortion' => 'kcal',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class FruitLegumeNoix extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $fruitsReference,
|
||||
public string $fruitsCoqueSechReference,
|
||||
public string $fruitsPortion,
|
||||
public string $fruitsCoqueSechPortion,
|
||||
public string $fruitLegumeNoixFrais,
|
||||
public string $fruitLegumeNoixFraisPortion,
|
||||
public string $pourcentFruitsLegumesNoixFrais,
|
||||
public string $pourcentFruits,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'fruitsReference' => 'Fruits / 100g',
|
||||
'fruitsCoqueSechReference' => 'Fruits à coque et séchés / 100g',
|
||||
'fruitsPortion' => 'Fruits / portion',
|
||||
'fruitsCoqueSechPortion' => 'Fruits à coque et séchés / portion',
|
||||
'fruitLegumeNoixFrais' => 'FNL frais / 100g',
|
||||
'fruitLegumeNoixFraisPortion' => 'FNL frais / portion',
|
||||
'pourcentFruitsLegumesNoixFrais' => '% FNL frais',
|
||||
'pourcentFruits' => '% Fruits',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'fruitsReference' => 'g',
|
||||
'fruitsCoqueSechReference' => 'g',
|
||||
'fruitsPortion' => 'g',
|
||||
'fruitsCoqueSechPortion' => 'g',
|
||||
'fruitLegumeNoixFrais' => 'g',
|
||||
'fruitLegumeNoixFraisPortion' => 'g',
|
||||
'pourcentFruitsLegumesNoixFrais' => '%',
|
||||
'pourcentFruits' => '%',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class GlucidesDetail extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $nutStarch,
|
||||
public string $nutMaltdex,
|
||||
public string $nutSucs,
|
||||
public string $nutGlus,
|
||||
public string $nutLacs,
|
||||
public string $nutMals,
|
||||
public string $nutGalfb,
|
||||
public string $nutGHc,
|
||||
public string $nutFrufb,
|
||||
public string $nutFrus,
|
||||
public string $nutOlsac,
|
||||
public string $nutPolyl,
|
||||
public string $nutFibsol,
|
||||
public string $nutInotl,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'nutStarch' => 'Amidon',
|
||||
'nutMaltdex' => 'Maltodextrines',
|
||||
'nutSucs' => 'Saccharose',
|
||||
'nutGlus' => 'Glucose',
|
||||
'nutLacs' => 'Lactose',
|
||||
'nutMals' => 'Maltose',
|
||||
'nutGalfb' => 'Galactose',
|
||||
'nutGHc' => 'Glucides hydrates de carbone',
|
||||
'nutFrufb' => 'Fructose lié',
|
||||
'nutFrus' => 'Fructose libre',
|
||||
'nutOlsac' => 'Oligosaccharides',
|
||||
'nutPolyl' => 'Polyols',
|
||||
'nutFibsol' => 'Fibres solubles',
|
||||
'nutInotl' => 'Inositol',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'nutStarch' => 'g',
|
||||
'nutMaltdex' => 'g',
|
||||
'nutSucs' => 'g',
|
||||
'nutGlus' => 'g',
|
||||
'nutLacs' => 'g',
|
||||
'nutMals' => 'g',
|
||||
'nutGalfb' => 'g',
|
||||
'nutGHc' => 'g',
|
||||
'nutFrufb' => 'g',
|
||||
'nutFrus' => 'g',
|
||||
'nutOlsac' => 'g',
|
||||
'nutPolyl' => 'g',
|
||||
'nutFibsol' => 'g',
|
||||
'nutInotl' => 'mg',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class Identification extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $nom,
|
||||
public string $numero,
|
||||
public string $source,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'nom' => 'Nom',
|
||||
'numero' => 'Numéro',
|
||||
'source' => 'Source',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'nom' => null,
|
||||
'numero' => null,
|
||||
'source' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
/**
|
||||
* Interface for nutritional DTOs that can provide units, human-readable labels,
|
||||
* and be serialized to string / JSON.
|
||||
*/
|
||||
interface IncoCompoPartDto extends Dto
|
||||
{
|
||||
/**
|
||||
* Returns field values indexed by their internal property name.
|
||||
*
|
||||
* @return array<string, float|string|null>
|
||||
*/
|
||||
public function toArray(): array;
|
||||
|
||||
/**
|
||||
* Returns field values indexed by their human-readable label.
|
||||
*
|
||||
* @return array<string, float|string|null>
|
||||
*/
|
||||
public function humanReadableValues(): array;
|
||||
|
||||
/**
|
||||
* Returns units indexed by internal property name.
|
||||
*
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
public static function units(): array;
|
||||
|
||||
/**
|
||||
* Returns units indexed by human-readable label.
|
||||
*
|
||||
* @return array<string, string|null>
|
||||
*/
|
||||
public static function humanReadableUnits(): array;
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class MacronutrimentsPortion extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $matGrassPortion,
|
||||
public string $matGrassPortionPrec,
|
||||
public string $acideGrasSaturePortion,
|
||||
public string $acideGrasSaturePortionPrec,
|
||||
public string $glucidesPortion,
|
||||
public string $glucidesPortionPrec,
|
||||
public string $sucrePortion,
|
||||
public string $sucrePortionPrec,
|
||||
public string $fibAlimPortion,
|
||||
public string $fibAlimPortionPrec,
|
||||
public string $proteinePortion,
|
||||
public string $proteinePortionPrec,
|
||||
public string $selPortion,
|
||||
public string $selPortionPrec,
|
||||
public string $sodiumPortion,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'matGrassPortion' => 'Matières grasses / portion',
|
||||
'matGrassPortionPrec' => 'Matières grasses précision / portion',
|
||||
'acideGrasSaturePortion' => 'Acides gras saturés / portion',
|
||||
'acideGrasSaturePortionPrec' => 'Acides gras saturés précision / portion',
|
||||
'glucidesPortion' => 'Glucides / portion',
|
||||
'glucidesPortionPrec' => 'Glucides précision / portion',
|
||||
'sucrePortion' => 'Sucres / portion',
|
||||
'sucrePortionPrec' => 'Sucres précision / portion',
|
||||
'fibAlimPortion' => 'Fibres alimentaires / portion',
|
||||
'fibAlimPortionPrec' => 'Fibres alimentaires précision / portion',
|
||||
'proteinePortion' => 'Protéines / portion',
|
||||
'proteinePortionPrec' => 'Protéines précision / portion',
|
||||
'selPortion' => 'Sel / portion',
|
||||
'selPortionPrec' => 'Sel précision / portion',
|
||||
'sodiumPortion' => 'Sodium / portion',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'matGrassPortion' => 'g',
|
||||
'matGrassPortionPrec' => null,
|
||||
'acideGrasSaturePortion' => 'g',
|
||||
'acideGrasSaturePortionPrec' => null,
|
||||
'glucidesPortion' => 'g',
|
||||
'glucidesPortionPrec' => null,
|
||||
'sucrePortion' => 'g',
|
||||
'sucrePortionPrec' => null,
|
||||
'fibAlimPortion' => 'g',
|
||||
'fibAlimPortionPrec' => null,
|
||||
'proteinePortion' => 'g',
|
||||
'proteinePortionPrec' => null,
|
||||
'selPortion' => 'g',
|
||||
'selPortionPrec' => null,
|
||||
'sodiumPortion' => 'mg',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class MacronutrimentsRef extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $matGrassReference,
|
||||
public string $matGrassReferencePrec,
|
||||
public string $acideGrasSatureReference,
|
||||
public string $acideGrasSatureReferencePrec,
|
||||
public string $glucidesReference,
|
||||
public string $glucidesReferencePrec,
|
||||
public string $sucreReference,
|
||||
public string $sucreReferencePrec,
|
||||
public string $fibAlimReference,
|
||||
public string $fibAlimReferencePrec,
|
||||
public string $proteineReference,
|
||||
public string $proteineReferencePrec,
|
||||
public string $selReference,
|
||||
public string $selReferencePrec,
|
||||
public string $sodiumReference,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'matGrassReference' => 'Matières grasses / 100g',
|
||||
'matGrassReferencePrec' => 'Matières grasses précision / 100g',
|
||||
'acideGrasSatureReference' => 'Acides gras saturés / 100g',
|
||||
'acideGrasSatureReferencePrec' => 'Acides gras saturés précision / 100g',
|
||||
'glucidesReference' => 'Glucides / 100g',
|
||||
'glucidesReferencePrec' => 'Glucides précision / 100g',
|
||||
'sucreReference' => 'Sucres / 100g',
|
||||
'sucreReferencePrec' => 'Sucres précision / 100g',
|
||||
'fibAlimReference' => 'Fibres alimentaires / 100g',
|
||||
'fibAlimReferencePrec' => 'Fibres alimentaires précision / 100g',
|
||||
'proteineReference' => 'Protéines / 100g',
|
||||
'proteineReferencePrec' => 'Protéines précision / 100g',
|
||||
'selReference' => 'Sel / 100g',
|
||||
'selReferencePrec' => 'Sel précision / 100g',
|
||||
'sodiumReference' => 'Sodium / 100g',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'matGrassReference' => 'g',
|
||||
'matGrassReferencePrec' => null,
|
||||
'acideGrasSatureReference' => 'g',
|
||||
'acideGrasSatureReferencePrec' => null,
|
||||
'glucidesReference' => 'g',
|
||||
'glucidesReferencePrec' => null,
|
||||
'sucreReference' => 'g',
|
||||
'sucreReferencePrec' => null,
|
||||
'fibAlimReference' => 'g',
|
||||
'fibAlimReferencePrec' => null,
|
||||
'proteineReference' => 'g',
|
||||
'proteineReferencePrec' => null,
|
||||
'selReference' => 'g',
|
||||
'selReferencePrec' => null,
|
||||
'sodiumReference' => 'mg',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class Mineraux extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $nutCa,
|
||||
public string $nutMg,
|
||||
public string $nutK,
|
||||
public string $nutP,
|
||||
public string $nutCld,
|
||||
public string $nutFe,
|
||||
public string $nutZn,
|
||||
public string $nutCu,
|
||||
public string $nutMn,
|
||||
public string $nutSe,
|
||||
public string $nutId,
|
||||
public string $nutCr,
|
||||
public string $nutMo,
|
||||
public string $nutS6,
|
||||
public string $nutNitra,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'nutCa' => 'Calcium',
|
||||
'nutMg' => 'Magnésium',
|
||||
'nutK' => 'Potassium',
|
||||
'nutP' => 'Phosphore',
|
||||
'nutCld' => 'Chlorure',
|
||||
'nutFe' => 'Fer',
|
||||
'nutZn' => 'Zinc',
|
||||
'nutCu' => 'Cuivre',
|
||||
'nutMn' => 'Manganèse',
|
||||
'nutSe' => 'Sélénium',
|
||||
'nutId' => 'Iode',
|
||||
'nutCr' => 'Chrome',
|
||||
'nutMo' => 'Molybdène',
|
||||
'nutS6' => 'Soufre',
|
||||
'nutNitra' => 'Nitrates',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'nutCa' => 'mg',
|
||||
'nutMg' => 'mg',
|
||||
'nutK' => 'mg',
|
||||
'nutP' => 'mg',
|
||||
'nutCld' => 'mg',
|
||||
'nutFe' => 'mg',
|
||||
'nutZn' => 'mg',
|
||||
'nutCu' => 'mg',
|
||||
'nutMn' => 'mg',
|
||||
'nutSe' => 'µg',
|
||||
'nutId' => 'µg',
|
||||
'nutCr' => 'µg',
|
||||
'nutMo' => 'µg',
|
||||
'nutS6' => 'mg',
|
||||
'nutNitra' => 'mg',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class NutriScore extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $composanteN,
|
||||
public string $composanteN2022,
|
||||
public string $composanteP,
|
||||
public string $composanteP2022,
|
||||
public string $scoreNutri,
|
||||
public string $scoreNutri2022,
|
||||
public string $classement,
|
||||
public string $nutriScore2022,
|
||||
public string $nutriAArt,
|
||||
public string $viandeRouge,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'composanteN' => 'Composante négative',
|
||||
'composanteN2022' => 'Composante négative 2022',
|
||||
'composanteP' => 'Composante positive',
|
||||
'composanteP2022' => 'Composante positive 2022',
|
||||
'scoreNutri' => 'Score Nutri-Score',
|
||||
'scoreNutri2022' => 'Score Nutri-Score 2022',
|
||||
'classement' => 'Classement (A-E)',
|
||||
'nutriScore2022' => 'Nutri-Score 2022 (lettre)',
|
||||
'nutriAArt' => 'Article Nutri-Score A',
|
||||
'viandeRouge' => 'Viande rouge',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'composanteN' => null,
|
||||
'composanteN2022' => null,
|
||||
'composanteP' => null,
|
||||
'composanteP2022' => null,
|
||||
'scoreNutri' => null,
|
||||
'scoreNutri2022' => null,
|
||||
'classement' => null,
|
||||
'nutriScore2022' => null,
|
||||
'nutriAArt' => null,
|
||||
'viandeRouge' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class PointsNutriScore extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $pointsEnergie,
|
||||
public string $pointsAcideGrasSature,
|
||||
public string $pointsSucres,
|
||||
public string $pointsSel,
|
||||
public string $pointsFibres,
|
||||
public string $pointsProteines,
|
||||
public string $pointsFruitsLegumesNoixFrais,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'pointsEnergie' => 'Points énergie',
|
||||
'pointsAcideGrasSature' => 'Points acides gras saturés',
|
||||
'pointsSucres' => 'Points sucres',
|
||||
'pointsSel' => 'Points sel',
|
||||
'pointsFibres' => 'Points fibres',
|
||||
'pointsProteines' => 'Points protéines',
|
||||
'pointsFruitsLegumesNoixFrais' => 'Points FNL frais',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'pointsEnergie' => null,
|
||||
'pointsAcideGrasSature' => null,
|
||||
'pointsSucres' => null,
|
||||
'pointsSel' => null,
|
||||
'pointsFibres' => null,
|
||||
'pointsProteines' => null,
|
||||
'pointsFruitsLegumesNoixFrais' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\IncoCompo;
|
||||
|
||||
final readonly class Vitamines extends AbstractIncoCompoPartDto
|
||||
{
|
||||
public function __construct(
|
||||
public string $nutVita,
|
||||
public string $nutVitb6,
|
||||
public string $nutVitb12,
|
||||
public string $nutVitc,
|
||||
public string $nutVitd,
|
||||
public string $nutVite,
|
||||
public string $nutVitk,
|
||||
public string $nutChocal,
|
||||
public string $nutNia,
|
||||
public string $nutRibf,
|
||||
public string $nutThia,
|
||||
public string $nutPantac,
|
||||
public string $nutBiot,
|
||||
public string $nutFoldfe,
|
||||
) {
|
||||
}
|
||||
|
||||
protected static function fieldLabels(): array
|
||||
{
|
||||
return [
|
||||
'nutVita' => 'Vitamine A',
|
||||
'nutVitb6' => 'Vitamine B6',
|
||||
'nutVitb12' => 'Vitamine B12',
|
||||
'nutVitc' => 'Vitamine C',
|
||||
'nutVitd' => 'Vitamine D',
|
||||
'nutVite' => 'Vitamine E',
|
||||
'nutVitk' => 'Vitamine K',
|
||||
'nutChocal' => 'Cholécalciférol (D3)',
|
||||
'nutNia' => 'Niacine (B3)',
|
||||
'nutRibf' => 'Riboflavine (B2)',
|
||||
'nutThia' => 'Thiamine (B1)',
|
||||
'nutPantac' => 'Acide pantothénique (B5)',
|
||||
'nutBiot' => 'Biotine (B8)',
|
||||
'nutFoldfe' => 'Folates équivalents (B9)',
|
||||
];
|
||||
}
|
||||
|
||||
protected static function fieldUnits(): array
|
||||
{
|
||||
return [
|
||||
'nutVita' => 'µg',
|
||||
'nutVitb6' => 'mg',
|
||||
'nutVitb12' => 'µg',
|
||||
'nutVitc' => 'mg',
|
||||
'nutVitd' => 'µg',
|
||||
'nutVite' => 'mg',
|
||||
'nutVitk' => 'µg',
|
||||
'nutChocal' => 'µg',
|
||||
'nutNia' => 'mg',
|
||||
'nutRibf' => 'mg',
|
||||
'nutThia' => 'mg',
|
||||
'nutPantac' => 'mg',
|
||||
'nutBiot' => 'µg',
|
||||
'nutFoldfe' => 'µg',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\IncoId;
|
||||
|
||||
/**
|
||||
* Represents an Inco sheet resumed info
|
||||
*/
|
||||
final readonly class IncoShort implements Dto
|
||||
{
|
||||
public function __construct(
|
||||
public IncoId $id,
|
||||
public string $name,
|
||||
public string $libelle,
|
||||
public string $description,
|
||||
public ?\DateTimeInterface $created_at,
|
||||
public ?\DateTimeInterface $updated_at,
|
||||
) {
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'id' => $this->id->value,
|
||||
'name' => $this->name,
|
||||
'libelle' => $this->libelle,
|
||||
'description' => $this->description,
|
||||
'created_at' => $this->created_at,
|
||||
'updated_at' => $this->updated_at,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
/**
|
||||
* A page of items with pagination metadata.
|
||||
*
|
||||
* @template T
|
||||
*/
|
||||
final readonly class PageOf implements Dto
|
||||
{
|
||||
/** @var array<int,T> */
|
||||
private array $items;
|
||||
|
||||
/**
|
||||
* @param array<int, T> $items
|
||||
*/
|
||||
public function __construct(
|
||||
private PaginationDto $pagination,
|
||||
array $items
|
||||
) {
|
||||
// * Shallow copy
|
||||
$this->items = array_values($items);
|
||||
}
|
||||
|
||||
/** @return array<int, T> $items */
|
||||
public function items(): array
|
||||
{
|
||||
return [...$this->items];
|
||||
}
|
||||
|
||||
public function pagination(): PaginationDto
|
||||
{
|
||||
return $this->pagination;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'pagination' => $this->pagination,
|
||||
'items' => $this->items,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto;
|
||||
|
||||
final readonly class PaginationDto implements Dto
|
||||
{
|
||||
public function __construct(
|
||||
public int $page,
|
||||
public int $pageSize,
|
||||
public int $totalItems,
|
||||
public int $totalPages,
|
||||
) {
|
||||
}
|
||||
|
||||
public function hasNextPage(): bool
|
||||
{
|
||||
return $this->page < $this->totalPages;
|
||||
}
|
||||
|
||||
public function hasPreviousPage(): bool
|
||||
{
|
||||
return $this->page > 1;
|
||||
}
|
||||
|
||||
public function nextPage(): ?int
|
||||
{
|
||||
return $this->hasNextPage() ? $this->page + 1 : null;
|
||||
}
|
||||
|
||||
public function previousPage(): ?int
|
||||
{
|
||||
return $this->hasPreviousPage() ? $this->page - 1 : null;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return [
|
||||
'page' => $this->page,
|
||||
'pageSize' => $this->pageSize,
|
||||
'totalItems' => $this->totalItems,
|
||||
'totalPages' => $this->totalPages,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiSorter;
|
||||
|
||||
enum EmployeeSortColumn implements SortColumn
|
||||
{
|
||||
case arcadiaId;
|
||||
case startDate;
|
||||
case endDate;
|
||||
case plannedEndDate;
|
||||
case createdAt;
|
||||
case updatedAt;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiSorter;
|
||||
|
||||
final readonly class EmployeeSorter
|
||||
{
|
||||
public function __construct(
|
||||
public EmployeeSortColumn $sortBy,
|
||||
public \SortDirection $direction = \SortDirection::Descending
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiSorter;
|
||||
|
||||
enum IncoSortColumn implements SortColumn
|
||||
{
|
||||
case id;
|
||||
case createdAt;
|
||||
case updatedAt;
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiSorter;
|
||||
|
||||
final readonly class IncoSorter
|
||||
{
|
||||
public function __construct(
|
||||
public IncoSortColumn $sortBy,
|
||||
public \SortDirection $direction = \SortDirection::Descending
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiSorter;
|
||||
|
||||
interface SortColumn extends \UnitEnum
|
||||
{
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
||||
|
||||
final readonly class ArcadiaEmployeeId implements ValueObjectId
|
||||
{
|
||||
private const PATTERN = '/^\d{7,8}$/';
|
||||
|
||||
public function __construct(
|
||||
public readonly string $value
|
||||
) {
|
||||
if (! preg_match(self::PATTERN, $value)) {
|
||||
throw new \DomainException(
|
||||
"Invalid value '{$value}' does not match pattern " . self::PATTERN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
||||
|
||||
/**
|
||||
* Unique public brand code (e.g. "LGL01", "ARC01").
|
||||
*/
|
||||
final readonly class BrandCode implements ValueObjectId
|
||||
{
|
||||
public function __construct(
|
||||
public string $value
|
||||
) {
|
||||
if (! preg_match('/^[A-Z]{3}\d{2}$/', $value)) {
|
||||
throw new \DomainException(
|
||||
"Invalid code '{$value}' does not match pattern /^[A-Z]{3}\d{2}$/"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
||||
|
||||
final readonly class EstablishmentCode implements ValueObjectId
|
||||
{
|
||||
private const PATTERN = '/^[\dA-Z]{2,}$/';
|
||||
|
||||
public function __construct(
|
||||
public readonly string $value
|
||||
) {
|
||||
if (! preg_match(self::PATTERN, $value)) {
|
||||
throw new \DomainException(
|
||||
"Invalid value '{$value}' does not match pattern " . self::PATTERN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
||||
|
||||
final readonly class IncoCompoId implements ValueObjectId
|
||||
{
|
||||
private const PATTERN = '/^\d{1,}$/';
|
||||
|
||||
public function __construct(
|
||||
public readonly string $value
|
||||
) {
|
||||
if (! preg_match(self::PATTERN, $value)) {
|
||||
throw new \DomainException(
|
||||
"Invalid value '{$value}' does not match pattern " . self::PATTERN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
||||
|
||||
final readonly class IncoId implements ValueObjectId
|
||||
{
|
||||
private const PATTERN = '/^\d{1,}$/';
|
||||
|
||||
public function __construct(
|
||||
public readonly string $value
|
||||
) {
|
||||
if (! preg_match(self::PATTERN, $value)) {
|
||||
throw new \DomainException(
|
||||
"Invalid value '{$value}' does not match pattern " . self::PATTERN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
||||
|
||||
/**
|
||||
* eg: 097
|
||||
*/
|
||||
final readonly class ServiceId implements ValueObjectId
|
||||
{
|
||||
private const PATTERN = '/^\d{1,}$/';
|
||||
|
||||
public function __construct(
|
||||
public readonly string $value
|
||||
) {
|
||||
if (! preg_match(self::PATTERN, $value)) {
|
||||
throw new \DomainException(
|
||||
"Invalid value '{$value}' does not match pattern " . self::PATTERN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ValueObjectId;
|
||||
|
||||
/**
|
||||
* eg: 'LDCSER', 'SLELA', 'DDE'
|
||||
*/
|
||||
final readonly class SiteId implements ValueObjectId
|
||||
{
|
||||
private const PATTERN = '/^[A-Z]{2,}$/';
|
||||
|
||||
public function __construct(
|
||||
public readonly string $value
|
||||
) {
|
||||
if (! preg_match(self::PATTERN, $value)) {
|
||||
throw new \DomainException(
|
||||
"Invalid value '{$value}' does not match pattern " . self::PATTERN
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib;
|
||||
|
||||
interface Equatable
|
||||
{
|
||||
public function equals(mixed $other): bool;
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib;
|
||||
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface LibMiddleware
|
||||
{
|
||||
/** @param array<string, mixed> $options */
|
||||
public function __invoke(RequestInterface $request, array $options): PromiseInterface;
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
|
||||
final readonly class LibProperties
|
||||
{
|
||||
public const string PACKAGE_NAME = 'uwil/arcadia-api-php-client';
|
||||
|
||||
public string $productName;
|
||||
|
||||
public string $productVersion;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$version = InstalledVersions::getPrettyVersion(self::PACKAGE_NAME);
|
||||
|
||||
if ($version === null) {
|
||||
throw new \UnexpectedValueException(
|
||||
sprintf("Unable to determine the version for package '%s'.", self::PACKAGE_NAME)
|
||||
);
|
||||
}
|
||||
|
||||
$this->productName = self::PACKAGE_NAME;
|
||||
$this->productVersion = $version;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\Pagination;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\Equatable;
|
||||
|
||||
/**
|
||||
* Value object representing a page number in pagination (1-indexed).
|
||||
*/
|
||||
final readonly class PageNumber implements \JsonSerializable, \Stringable, Equatable
|
||||
{
|
||||
public const int DEFAULT_PAGE = 1;
|
||||
|
||||
public function __construct(
|
||||
public int $value = self::DEFAULT_PAGE,
|
||||
) {
|
||||
if ($this->value <= 0) {
|
||||
throw new \InvalidArgumentException('Page number must be greater than 0');
|
||||
}
|
||||
}
|
||||
|
||||
public static function of(int $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public static function default(): self
|
||||
{
|
||||
return new self(self::DEFAULT_PAGE);
|
||||
}
|
||||
|
||||
public function value(): int
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function equals(mixed $other): bool
|
||||
{
|
||||
return $other instanceof self && $this->value === $other->value;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string) $this->value;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): int
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\Pagination;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\Equatable;
|
||||
|
||||
/**
|
||||
* Value object representing a page size in pagination.
|
||||
*/
|
||||
final readonly class PageSize implements \JsonSerializable, \Stringable, Equatable
|
||||
{
|
||||
public const int DEFAULT_SIZE = 50;
|
||||
|
||||
/** ERR_TOO_MANY_RECORDS fixed to 10k */
|
||||
public const int MAX_PAGE_SIZE = 10000;
|
||||
|
||||
public function __construct(
|
||||
public int $value = self::DEFAULT_SIZE,
|
||||
) {
|
||||
if ($this->value <= 0) {
|
||||
throw new \InvalidArgumentException('Page size must be greater than 0');
|
||||
}
|
||||
|
||||
if ($this->value > self::MAX_PAGE_SIZE) {
|
||||
throw new \InvalidArgumentException('Page size must be less than or equal to ' . self::MAX_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
public static function of(int $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public static function default(): self
|
||||
{
|
||||
return new self(self::DEFAULT_SIZE);
|
||||
}
|
||||
|
||||
public function value(): int
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function equals(mixed $other): bool
|
||||
{
|
||||
return $other instanceof self && $this->value === $other->value;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string) $this->value;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): int
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\Pagination;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\Equatable;
|
||||
|
||||
/**
|
||||
* Immutable value object representing a pagination range.
|
||||
*/
|
||||
final class Pagination implements Equatable
|
||||
{
|
||||
/** ERR_TOO_MANY_RECORDS fixed to 10k */
|
||||
public const int MAX_PAGE_SIZE = PageSize::MAX_PAGE_SIZE;
|
||||
|
||||
public function __construct(
|
||||
private readonly int $from,
|
||||
private readonly int $to,
|
||||
) {
|
||||
if ($from < 0) {
|
||||
throw new \InvalidArgumentException('From index must be non-negative');
|
||||
}
|
||||
if ($to < 0) {
|
||||
throw new \InvalidArgumentException('To index must be non-negative');
|
||||
}
|
||||
if ($to <= $from) {
|
||||
throw new \InvalidArgumentException('To index must be greater than from index');
|
||||
}
|
||||
if (($to - $from + 1) > self::MAX_PAGE_SIZE) {
|
||||
throw new \InvalidArgumentException('Page size must be less than or equal to ' . self::MAX_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
public function from(): int
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
public function to(): int
|
||||
{
|
||||
return $this->to;
|
||||
}
|
||||
|
||||
public function pageSize(): int
|
||||
{
|
||||
return $this->to - $this->from + 1;
|
||||
}
|
||||
|
||||
public function pageNumber(): int
|
||||
{
|
||||
return (int) ($this->from / $this->pageSize()) + 1;
|
||||
}
|
||||
|
||||
public function pageSizeVo(): PageSize
|
||||
{
|
||||
return new PageSize($this->pageSize());
|
||||
}
|
||||
|
||||
public function pageNumberVo(): PageNumber
|
||||
{
|
||||
return new PageNumber($this->pageNumber());
|
||||
}
|
||||
|
||||
public function equals(mixed $other): bool
|
||||
{
|
||||
return $other instanceof self && $this->from === $other->from && $this->to === $other->to;
|
||||
}
|
||||
|
||||
public static function ofIndexes(int $from, int $to): self
|
||||
{
|
||||
return new self($from, $to);
|
||||
}
|
||||
|
||||
public static function ofPage(PageNumber $pageNumber, PageSize $pageSize = new PageSize()): self
|
||||
{
|
||||
$fromIndex = ($pageNumber->value - 1) * $pageSize->value;
|
||||
$toIndex = max($pageNumber->value * $pageSize->value - 1, 1);
|
||||
|
||||
return new self($fromIndex, $toIndex);
|
||||
}
|
||||
|
||||
public static function ofOffsetAndLimit(int $offset, int $limit): self
|
||||
{
|
||||
if ($offset < 0) {
|
||||
throw new \InvalidArgumentException('Offset must be non-negative');
|
||||
}
|
||||
if ($limit <= 0) {
|
||||
throw new \InvalidArgumentException('Limit must be positive and non-zero');
|
||||
}
|
||||
|
||||
$fromIndex = $offset;
|
||||
$toIndex = max($offset + $limit - 1, 1);
|
||||
|
||||
return new self($fromIndex, $toIndex);
|
||||
}
|
||||
|
||||
public static function firstPage(PageSize $pageSize = new PageSize()): self
|
||||
{
|
||||
return self::ofPage(new PageNumber(1), $pageSize);
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return sprintf('Pagination{from=%d, to=%d}', $this->from, $this->to);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib\Pagination;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Error\ApiError;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PaginationDto;
|
||||
|
||||
/**
|
||||
* Lazy stream / cursor iterator over paginated API resources.
|
||||
*
|
||||
* Provides continuous O(1) memory iteration across all pages.
|
||||
*
|
||||
* @template T
|
||||
*
|
||||
* @implements \IteratorAggregate<int, T>
|
||||
*/
|
||||
final class StreamOf implements \IteratorAggregate
|
||||
{
|
||||
private ?ApiError $lastError = null;
|
||||
|
||||
private ?PaginationDto $firstPagePagination = null;
|
||||
|
||||
/**
|
||||
* @param \Closure(Pagination):(\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PageOf<T>|ApiError) $pageFetcher
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly \Closure $pageFetcher,
|
||||
private readonly PageSize $pageSize = new PageSize(),
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over individual items of type T.
|
||||
*
|
||||
* @return \Generator<int, T, mixed, ?ApiError>
|
||||
*/
|
||||
public function getIterator(): \Generator
|
||||
{
|
||||
foreach ($this->pages() as $page) {
|
||||
foreach ($page->items() as $item) {
|
||||
yield $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate page by page (useful for batching operations).
|
||||
*
|
||||
* @return \Generator<int,\Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PageOf<T>, mixed, ?ApiError>
|
||||
*/
|
||||
public function pages(): \Generator
|
||||
{
|
||||
$this->lastError = null;
|
||||
$pageNumber = 1;
|
||||
|
||||
do {
|
||||
$pagination = Pagination::ofPage(new PageNumber($pageNumber), $this->pageSize);
|
||||
$result = ($this->pageFetcher)($pagination);
|
||||
|
||||
if ($result instanceof ApiError) {
|
||||
$this->lastError = $result;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($this->firstPagePagination === null) {
|
||||
$this->firstPagePagination = $result->pagination();
|
||||
}
|
||||
|
||||
yield $result;
|
||||
|
||||
if (! $result->pagination()->hasNextPage()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$pageNumber++;
|
||||
} while (true);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a callback on each item.
|
||||
* Stops and returns the ApiError if an error occurs, or stops if the callback returns false.
|
||||
*
|
||||
* @param callable(T): (bool|void) $callback
|
||||
*/
|
||||
public function each(callable $callback): ?ApiError
|
||||
{
|
||||
foreach ($this->getIterator() as $item) {
|
||||
if ($callback($item) === false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->lastError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Yield at most $limit items without requesting subsequent pages unnecessarily.
|
||||
*
|
||||
* @return \Generator<int, T>
|
||||
*/
|
||||
public function take(int $limit): \Generator
|
||||
{
|
||||
if ($limit <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$count = 0;
|
||||
foreach ($this->getIterator() as $item) {
|
||||
yield $item;
|
||||
$count++;
|
||||
if ($count >= $limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first item of the stream, or null if empty, or ApiError on failure.
|
||||
*
|
||||
* @return T|ApiError|null
|
||||
*/
|
||||
public function first(): mixed
|
||||
{
|
||||
foreach ($this->take(1) as $item) {
|
||||
return $item;
|
||||
}
|
||||
|
||||
return $this->lastError;
|
||||
}
|
||||
|
||||
public function lastError(): ?ApiError
|
||||
{
|
||||
return $this->lastError;
|
||||
}
|
||||
|
||||
public function hasError(): bool
|
||||
{
|
||||
return $this->lastError !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of items reported by the API (available once at least one page is fetched).
|
||||
*/
|
||||
public function totalCount(): ?int
|
||||
{
|
||||
return $this->firstPagePagination?->totalItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of pages reported by the API (available once at least one page is fetched).
|
||||
*/
|
||||
public function totalPages(): ?int
|
||||
{
|
||||
return $this->firstPagePagination?->totalPages;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Lib;
|
||||
|
||||
interface ValueObjectId
|
||||
{
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Logger;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use GuzzleHttp\Promise\Create;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Uwil\ArcadiaApiPhpClient\Auth\ApiMandatoryHeadersMiddleware;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\LibMiddleware;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ApiLoggerMiddleware implements LibMiddleware
|
||||
{
|
||||
public const string HANDLER_NAME = 'Arcadia API Logger';
|
||||
|
||||
public const string HEADER_AUTHORIZATION = 'Authorization';
|
||||
|
||||
/** @var string[] */
|
||||
public const array SENSITIVE_HEADERS = [
|
||||
self::HEADER_AUTHORIZATION,
|
||||
ApiMandatoryHeadersMiddleware::HEADER_SIGNATURE,
|
||||
ApiMandatoryHeadersMiddleware::HEADER_CLIENT_IDENTIFIER,
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates the middleware handler closure.
|
||||
*/
|
||||
public static function log(LoggerInterface $logger): callable
|
||||
{
|
||||
return static function (callable $handler) use ($logger): self {
|
||||
return new self($handler, $logger);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(RequestInterface, array<string, mixed>): PromiseInterface $nextHandler Next handler to invoke.
|
||||
*/
|
||||
public function __construct(
|
||||
private $nextHandler,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {
|
||||
}
|
||||
|
||||
/** @param array<string, mixed> $options */
|
||||
public function __invoke(RequestInterface $request, array $options): PromiseInterface
|
||||
{
|
||||
$fn = $this->nextHandler;
|
||||
$startTime = hrtime(true);
|
||||
|
||||
return $fn($request, $options)->then(
|
||||
function (ResponseInterface $response) use ($request, $startTime): ResponseInterface {
|
||||
$durationMs = (hrtime(true) - $startTime) / 1_000_000;
|
||||
$this->logResponse($request, $response, $durationMs);
|
||||
|
||||
return $response;
|
||||
},
|
||||
function (\Throwable $reason) use ($request, $startTime): PromiseInterface {
|
||||
$durationMs = (hrtime(true) - $startTime) / 1_000_000;
|
||||
$this->logFailure($request, $reason, $durationMs);
|
||||
|
||||
return Create::rejectionFor($reason);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private function logResponse(RequestInterface $request, ResponseInterface $response, float $durationMs): void
|
||||
{
|
||||
$statusCode = $response->getStatusCode();
|
||||
$roundedDuration = round($durationMs, 2);
|
||||
|
||||
$context = [
|
||||
'method' => $request->getMethod(),
|
||||
'uri' => (string) $request->getUri(),
|
||||
'status_code' => $statusCode,
|
||||
'duration_ms' => $roundedDuration,
|
||||
'headers' => $this->sanitizeHeaders($request->getHeaders()),
|
||||
];
|
||||
|
||||
$message = sprintf(
|
||||
'Arcadia API [%s] %s -> %d %s (%.2fms)',
|
||||
$request->getMethod(),
|
||||
(string) $request->getUri(),
|
||||
$statusCode,
|
||||
$response->getReasonPhrase(),
|
||||
$roundedDuration
|
||||
);
|
||||
|
||||
if ($statusCode >= 500) {
|
||||
$this->logger->error($message, $context);
|
||||
} elseif ($statusCode >= 400) {
|
||||
$this->logger->warning($message, $context);
|
||||
} else {
|
||||
$this->logger->info($message, $context);
|
||||
}
|
||||
}
|
||||
|
||||
private function logFailure(RequestInterface $request, \Throwable $reason, float $durationMs): void
|
||||
{
|
||||
$roundedDuration = round($durationMs, 2);
|
||||
|
||||
$context = [
|
||||
'method' => $request->getMethod(),
|
||||
'uri' => (string) $request->getUri(),
|
||||
'duration_ms' => $roundedDuration,
|
||||
'headers' => $this->sanitizeHeaders($request->getHeaders()),
|
||||
'error_class' => $reason::class,
|
||||
'error_message' => $reason->getMessage(),
|
||||
];
|
||||
|
||||
if ($reason instanceof RequestException && $reason->getResponse() !== null) {
|
||||
$response = $reason->getResponse();
|
||||
$statusCode = $response->getStatusCode();
|
||||
$context['status_code'] = $statusCode;
|
||||
|
||||
$message = sprintf(
|
||||
'Arcadia API [%s] %s -> %d %s (%.2fms)',
|
||||
$request->getMethod(),
|
||||
(string) $request->getUri(),
|
||||
$statusCode,
|
||||
$response->getReasonPhrase(),
|
||||
$roundedDuration
|
||||
);
|
||||
|
||||
if ($statusCode >= 500) {
|
||||
$this->logger->error($message, $context);
|
||||
} else {
|
||||
$this->logger->warning($message, $context);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
'Arcadia API [%s] %s -> Failed: %s (%.2fms)',
|
||||
$request->getMethod(),
|
||||
(string) $request->getUri(),
|
||||
$reason->getMessage(),
|
||||
$roundedDuration
|
||||
);
|
||||
|
||||
$this->logger->error($message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<array-key, array<string>> $headers
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function sanitizeHeaders(array $headers): array
|
||||
{
|
||||
$sanitized = [];
|
||||
|
||||
foreach ($headers as $name => $values) {
|
||||
$stringName = (string) $name;
|
||||
$value = implode(', ', $values);
|
||||
|
||||
if ($this->isSensitiveHeader($stringName)) {
|
||||
$sanitized[$stringName] = strlen($value) > 8
|
||||
? substr($value, 0, 4) . '***[REDACTED]'
|
||||
: '[REDACTED]';
|
||||
} else {
|
||||
$sanitized[$stringName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
private function isSensitiveHeader(string $headerName): bool
|
||||
{
|
||||
foreach (self::SENSITIVE_HEADERS as $sensitive) {
|
||||
if (strcasecmp($headerName, $sensitive) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Mapper;
|
||||
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Brand;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PageOf;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PaginationDto;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\BrandCode;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\Pagination\Pagination;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\Models\Brand as KiotaBrand;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\Models\BrandListResponse;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\Models\PaginationMeta;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class BrandMapper
|
||||
{
|
||||
public function toDto(KiotaBrand $model): Brand
|
||||
{
|
||||
$code = $model->getCode() ?? '';
|
||||
$name = $model->getName() ?? '';
|
||||
$label = mb_trim($name) !== '' ? $name : $code;
|
||||
|
||||
return new Brand(
|
||||
code: new BrandCode($code),
|
||||
label: $label,
|
||||
arcadiaId: $model->getArcadiaId(),
|
||||
createdAt: $model->getCreatedAt(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PageOf<Brand>
|
||||
*/
|
||||
public function toPage(BrandListResponse $response, Pagination $pagination): PageOf
|
||||
{
|
||||
$items = [];
|
||||
foreach ($response->getBrands() ?? [] as $brand) {
|
||||
$items[] = $this->toDto($brand);
|
||||
}
|
||||
|
||||
$paginationDto = $this->paginationDto(
|
||||
$response->getPagination(),
|
||||
$pagination
|
||||
);
|
||||
|
||||
return new PageOf(
|
||||
$paginationDto,
|
||||
$items
|
||||
);
|
||||
}
|
||||
|
||||
private function paginationDto(?PaginationMeta $meta, Pagination $pagination): PaginationDto
|
||||
{
|
||||
return new PaginationDto(
|
||||
page: $meta?->getPage() ?? $pagination->pageNumber(),
|
||||
pageSize: $meta?->getPageSize() ?? $pagination->pageSize(),
|
||||
totalItems: $meta?->getTotalItems() ?? 0,
|
||||
totalPages: $meta?->getTotalPages() ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Uwil\ArcadiaApiPhpClient\Mapper;
|
||||
|
||||
use Microsoft\Kiota\Abstractions\Types\Date;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\Employee;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PageOf;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiDto\Dto\PaginationDto;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\ArcadiaEmployeeId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\EstablishmentCode;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\ServiceId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\ApiValueObject\SiteId;
|
||||
use Uwil\ArcadiaApiPhpClient\Lib\Pagination\Pagination;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\Models\Employee as KiotaEmployee;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\Models\EmployeeListResponse;
|
||||
use Uwil\ArcadiaKiotaOpenApiClient\Models\PaginationMeta;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class EmployeeMapper
|
||||
{
|
||||
public function toDto(KiotaEmployee $model): Employee
|
||||
{
|
||||
$matricule = $model->getMatricule() ?? '';
|
||||
$establishment = $model->getEstablishment() ?? '';
|
||||
$site = $model->getSite() ?? '';
|
||||
$service = $model->getService() ?? '';
|
||||
|
||||
$startDate = $this->dateToDateTime($model->getStartDate()) ?? new \DateTimeImmutable('today');
|
||||
$createdAt = $model->getCreatedAt() ?? new \DateTimeImmutable();
|
||||
|
||||
return new Employee(
|
||||
matriculePublique: new ArcadiaEmployeeId($matricule),
|
||||
lastname: $model->getLastName() ?? '',
|
||||
firstname: $model->getFirstName() ?? '',
|
||||
start_date: $startDate,
|
||||
end_date: $this->dateToDateTime($model->getEndDate()),
|
||||
planned_end_date: $this->dateToDateTime($model->getPlannedEndDate()),
|
||||
establishment: new EstablishmentCode($establishment),
|
||||
site: new SiteId($site),
|
||||
service: new ServiceId($service),
|
||||
created_at: $createdAt,
|
||||
updated_at: $model->getUpdatedAt(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PageOf<Employee>
|
||||
*/
|
||||
public function toPage(EmployeeListResponse $response, Pagination $pagination): PageOf
|
||||
{
|
||||
$items = [];
|
||||
foreach ($response->getEmployees() ?? [] as $employeeModel) {
|
||||
$items[] = $this->toDto($employeeModel);
|
||||
}
|
||||
|
||||
$paginationDto = $this->paginationDto(
|
||||
$response->getPagination(),
|
||||
$pagination
|
||||
);
|
||||
|
||||
return new PageOf(
|
||||
$paginationDto,
|
||||
$items
|
||||
);
|
||||
}
|
||||
|
||||
private function paginationDto(?PaginationMeta $meta, Pagination $pagination): PaginationDto
|
||||
{
|
||||
return new PaginationDto(
|
||||
page: $meta?->getPage() ?? $pagination->pageNumber(),
|
||||
pageSize: $meta?->getPageSize() ?? $pagination->pageSize(),
|
||||
totalItems: $meta?->getTotalItems() ?? 0,
|
||||
totalPages: $meta?->getTotalPages() ?? 0,
|
||||
);
|
||||
}
|
||||
|
||||
private function dateToDateTime(?Date $date): ?\DateTimeInterface
|
||||
{
|
||||
if ($date === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new \DateTimeImmutable((string) $date);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue