BCard - Documentation technique

ContactApiController extends AbstractController
in package

Contrôleur d'API du carnet de contacts personnel.

Il gère le cycle de vie complet des contacts appartenant à l'utilisateur authentifié (liste paginée, recherche, création, mise à jour, suppression) ainsi que deux modes d'acquisition automatisés : la conversion d'une carte de visite numérique BCard en contact, et le scan d'une carte de visite papier analysée par AIAnalysisService. Aucun attribut #[IsGranted] n'est utilisé : chaque action vérifie elle-même la présence d'un utilisateur authentifié et la propriété du contact ciblé, un contact appartenant à autrui étant masqué derrière une 404.

Préfixe de route : /api/contacts. Aucun rôle exigé au niveau de la classe.

Attributes
#[Route]
'/api/contacts'

Table of Contents

Properties

$contactRepository  : ContactRepository
$entityManager  : EntityManagerInterface
$serializer  : SerializerInterface
$validator  : ValidatorInterface

Methods

__construct()  : mixed
create()  : JsonResponse
Crée un nouveau contact
createFromBusinessCard()  : JsonResponse
Crée automatiquement un contact à partir d'une business card
delete()  : JsonResponse
Supprime un contact
list()  : JsonResponse
Récupère la liste paginée des contacts de l'utilisateur connecté
scanPaperCard()  : JsonResponse
Scanner une carte de visite papier avec l'IA
search()  : JsonResponse
Recherche des contacts
show()  : JsonResponse
Récupère un contact spécifique
update()  : JsonResponse
Met à jour un contact existant

Properties

Methods

__construct()

public __construct(EntityManagerInterface $entityManager, ContactRepository $contactRepository, SerializerInterface $serializer, ValidatorInterface $validator) : mixed
Parameters
$entityManager : EntityManagerInterface

Gestionnaire d'entités Doctrine, utilisé pour persister et supprimer les contacts.

$contactRepository : ContactRepository

Dépôt des contacts (recherche, comptage, contrôles de doublon).

$serializer : SerializerInterface

Sérialiseur utilisé pour désérialiser le corps JSON en entité Contact.

$validator : ValidatorInterface

Validateur appliqué aux contacts avant persistance.

create()

Crée un nouveau contact

public create(Request $request) : JsonResponse

Désérialise le corps JSON directement en entité Contact, puis applique deux contrôles d'unicité limités au carnet de l'utilisateur courant : un contact portant le même email, puis un contact portant le même téléphone. Les deux contrôles ne sont effectués que si la valeur correspondante est renseignée. Le contact est ensuite rattaché à l'utilisateur authentifié, validé, puis persisté.

Route : /api/contacts (POST), nom api_contacts_create.

Corps attendu : objet JSON dont les propriétés correspondent aux champs de l'entité Contact, par exemple {"firstname": "string", "lastname": "string", "email": "string — contrôlé en unicité", "phone": "string — contrôlé en unicité", "company": "string", "jobTitle": "string", "note": "string"}. Toutes les clés sont optionnelles côté désérialisation ; les obligations réelles proviennent des contraintes de validation de l'entité.

Réponses : 201 contact créé, 400 corps JSON invalide ({"error": "Invalid JSON data"}), 400 violations de contraintes ({"errors": {propriété: message}}), 409 email déjà présent dans le carnet, 409 téléphone déjà présent, 401 appelant non authentifié.

Parameters
$request : Request

Requête HTTP dont le corps porte le contact à créer.

Attributes
#[Route]
''
$name: 'api_contacts_create'
$methods: ['POST']
Return values
JsonResponse

Contact créé, sérialisé avec le groupe contact:read, ou erreurs.

createFromBusinessCard()

Crée automatiquement un contact à partir d'une business card

public createFromBusinessCard(string $hash, BusinessCardAizeRepository $businessCardRepository) : JsonResponse

La carte est d'abord recherchée par son publicHash. En cas d'échec, un repli historique tente une recherche par identifiant numérique, uniquement si la valeur est numérique et fait moins de 20 caractères — précaution destinée à éviter qu'un hash entièrement numérique ne soit interprété comme un identifiant. Un contrôle d'unicité sur l'email est ensuite appliqué au carnet de l'appelant. Le contact créé reprend nom, prénom, email, entreprise et fonction de la carte ; le téléphone retenu est le premier numéro visible, avec repli sur le numéro principal. Le contact est marqué isBCard = true et reçoit une note mentionnant l'identifiant de la BCard d'origine. Aucune validation explicite n'est exécutée avant la persistance.

Route : /api/contacts/from-bcard/{hash} (POST), nom api_contacts_create_from_bcard.

Corps attendu : aucun ; la source est identifiée par le segment {hash}.

Réponses : 201 contact créé, 404 business card introuvable par hash ou par identifiant, 409 contact déjà présent avec le même email, 401 appelant non authentifié.

Parameters
$hash : string

Hash public de la carte, ou identifiant numérique en mode de compatibilité.

$businessCardRepository : BusinessCardAizeRepository

Dépôt utilisé pour retrouver la carte source.

Attributes
#[Post]
$path: '/api/contacts/from-bcard/{hash}'
$description: 'Crée automatiquement un contact à partir d\'une business card existante'
$summary: 'Créer un contact depuis une BCard'
$tags: ['Contacts']
$parameters: [new OA\Parameter(name: 'hash', description: 'Hash public de la business card à convertir en contact', in: 'path', required: true, schema: new OA\Schema(type: 'string'))]
$responses: [new OA\Response(response: 201, description: 'Contact créé avec succès'), new OA\Response(response: 404, description: 'Business card non trouvée'), new OA\Response(response: 409, description: 'Un contact avec cet email existe déjà'), new OA\Response(response: 401, description: 'Non authentifié')]
#[Route]
'/from-bcard/{hash}'
$name: 'api_contacts_create_from_bcard'
$methods: ['POST']
Return values
JsonResponse

Contact créé, sérialisé avec le groupe contact:read, ou message d'erreur.

delete()

Supprime un contact

public delete(Contact $contact) : JsonResponse

Après contrôle de propriété, l'entité est retirée définitivement de la base (suppression physique, sans archivage ni corbeille). Aucun corps n'est renvoyé.

Route : /api/contacts/{id} (DELETE), nom api_contacts_delete.

Réponses : 204 contact supprimé (corps vide), 404 contact inexistant, appartenant à autrui, ou appelant non authentifié.

Parameters
$contact : Contact

Contact résolu depuis le paramètre de route {id}.

Attributes
#[Route]
'/{id}'
$name: 'api_contacts_delete'
$methods: ['DELETE']
Return values
JsonResponse

Réponse sans contenu, ou message d'erreur.

list()

Récupère la liste paginée des contacts de l'utilisateur connecté

public list(Request $request) : JsonResponse

Délègue la pagination et le filtrage textuel au dépôt via findByUserWithSearch(), puis recompte le total avec countByUser() en appliquant le même terme de recherche afin que la pagination reste cohérente. Les paramètres page et limit sont lus comme entiers sans borne haute ; la sérialisation utilise le groupe contact:read.

Route : /api/contacts (GET), nom api_contacts_list.

Paramètres de requête : page (optionnel, défaut 1), limit (optionnel, défaut 20), search (optionnel, défaut chaîne vide).

Réponses : 200 liste renvoyée, 401 aucun utilisateur authentifié ({"error": "Authentication required"}).

Parameters
$request : Request

Requête HTTP portant les paramètres de pagination et de recherche.

Attributes
#[Route]
''
$name: 'api_contacts_list'
$methods: ['GET']
Return values
JsonResponse

Objet JSON {contacts: list<Contact>, pagination: array<string, int|float>}.

scanPaperCard()

Scanner une carte de visite papier avec l'IA

public scanPaperCard(Request $request, AIAnalysisService $aiAnalysisService, UploaderService $uploaderService) : JsonResponse

Reçoit une photo en multipart/form-data, vérifie qu'elle est présente, valide et non vide, puis la soumet à AIAnalysisService::analyzeBusinessCard() avant d'extraire les champs structurés via extractContactData(). Si l'email extrait correspond à un contact déjà présent dans le carnet, l'opération est interrompue et l'identifiant du contact existant est renvoyé. Sinon un contact est créé avec les données extraites, chaque champ manquant étant remplacé par la chaîne « Non spécifié » (y compris email et téléphone), activitySector et country étant systématiquement forcés à cette même valeur. Les informations d'adresse, de ville, de code postal, de pays et de site web sont concaténées dans la note. La photo est enfin stockée dans contact_photo_directory et rattachée au contact. Toute exception est convertie en réponse 400 ; le message est spécialisé lorsque l'erreur mentionne un problème de JSON valide (photo jugée trop peu nette).

Route : /api/contacts/scan/paper (POST), nom api_contacts_scan_paper.

Corps attendu (multipart/form-data) : {"photo": "file — image de la carte de visite (obligatoire)"}

Réponses : 200 carte analysée et contact créé ({success: true, message, contact}), 400 photo absente, 400 contact déjà existant pour l'email extrait ({success: false, message, existing_contact_id}), 400 photo invalide ou vide, analyse IA en échec ou upload en erreur ({success: false, message, error}), 401 appelant non authentifié.

Parameters
$request : Request

Requête HTTP portant le fichier photo.

$aiAnalysisService : AIAnalysisService

Service d'analyse IA de la carte et d'extraction des champs.

$uploaderService : UploaderService

Service d'upload utilisé pour archiver la photo scannée.

Attributes
#[Post]
$path: '/api/contacts/scan/paper'
$description: 'Scanner une carte de visite papier avec l\'IA pour extraire les informations de contact'
$summary: 'Scanner une carte de visite papier'
$requestBody: new OA\RequestBody(description: 'Photo de la carte de visite à analyser', required: true, content: new OA\MediaType(mediaType: 'multipart/form-data', schema: new OA\Schema(properties: [new OA\Property(property: 'photo', description: 'Fichier image de la carte de visite', type: 'string', format: 'binary')], type: 'object')))
$tags: ['Contacts']
$responses: [new OA\Response(response: 200, description: 'Carte analysée avec succès et contact créé', content: new OA\JsonContent(properties: [new OA\Property(property: 'success', type: 'boolean', example: true), new OA\Property(property: 'message', type: 'string', example: 'Contact papier analysé et ajouté avec succès !'), new OA\Property(property: 'contact', type: 'string', example: 'Entité contact serait retourné ici')])), new OA\Response(response: 400, description: 'Erreur lors de l\'analyse', content: new OA\JsonContent(properties: [new OA\Property(property: 'success', type: 'boolean', example: false), new OA\Property(property: 'message', type: 'string', example: 'Erreur lors de l\'analyse de la carte de visite'), new OA\Property(property: 'error', type: 'string', example: 'Détails de l\'erreur')])), new OA\Response(response: 401, description: 'Non authentifié'), new OA\Response(response: 403, description: 'Accès refusé')]
#[Route]
'/scan/paper'
$name: 'api_contacts_scan_paper'
$methods: ['POST']
Return values
JsonResponse

Objet JSON de succès contenant les données extraites, ou objet d'erreur.

Recherche des contacts

public search(Request $request) : JsonResponse

Recherche rapide restreinte au carnet de l'utilisateur authentifié, déléguée à ContactRepository::searchByUser(). Le terme q est obligatoire : une chaîne vide ou absente est rejetée. Le paramètre limit, non documenté dans l'attribut OpenApi, borne le nombre de résultats (défaut 10). La réponse encapsule les résultats sous la clé contacts, alors que le schéma OpenApi déclare un tableau de premier niveau.

Attention : la route /{id} étant déclarée avant celle-ci, ce point d'entrée peut être court-circuité par ContactApiController::show() lors du routage.

Route : /api/contacts/search (GET), nom api_contacts_search.

Paramètres de requête : q (obligatoire, terme recherché), limit (optionnel, défaut 10).

Réponses : 200 résultats renvoyés, 400 terme de recherche manquant ou vide, 401 appelant non authentifié.

Parameters
$request : Request

Requête HTTP portant le terme de recherche et la limite.

Attributes
#[Get]
$path: '/api/contacts/search'
$description: 'Recherche rapide de contacts par nom, prénom, email ou entreprise'
$summary: 'Recherche rapide de contacts'
$tags: ['Contacts']
$parameters: [new OA\Parameter(name: 'q', description: 'Terme de recherche', in: 'query', required: true, schema: new OA\Schema(type: 'string'))]
$responses: [new OA\Response(response: 200, description: 'Résultats de la recherche', content: new OA\JsonContent(type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'id', type: 'integer'), new OA\Property(property: 'firstname', type: 'string'), new OA\Property(property: 'lastname', type: 'string'), new OA\Property(property: 'email', type: 'string'), new OA\Property(property: 'company', type: 'string'), new OA\Property(property: 'jobTitle', type: 'string'), new OA\Property(property: 'phone', type: 'string')]))), new OA\Response(response: 400, description: 'Terme de recherche manquant'), new OA\Response(response: 401, description: 'Non authentifié'), new OA\Response(response: 403, description: 'Accès refusé')]
#[Route]
'/search'
$name: 'api_contacts_search'
$methods: ['GET']
Return values
JsonResponse

Objet JSON {contacts: list<Contact>} sérialisé avec le groupe contact:read.

show()

Récupère un contact spécifique

public show(Contact $contact) : JsonResponse

Le contact est résolu automatiquement par le ParamConverter à partir du segment {id} (un identifiant inconnu provoque donc une 404 émise par Symfony avant l'exécution de la méthode). Le contrôle de propriété qui suit renvoie également une 404 — et non une 403 — lorsque le contact appartient à un autre utilisateur ou que l'appelant n'est pas authentifié, afin de ne pas révéler son existence.

Route : /api/contacts/{id} (GET), nom api_contacts_show.

Réponses : 200 contact renvoyé, 404 contact inexistant, appartenant à autrui, ou appelant non authentifié.

Parameters
$contact : Contact

Contact résolu depuis le paramètre de route {id}.

Attributes
#[Route]
'/{id}'
$name: 'api_contacts_show'
$methods: ['GET']
Return values
JsonResponse

Contact sérialisé avec le groupe contact:read, ou message d'erreur.

update()

Met à jour un contact existant

public update(Contact $contact, Request $request) : JsonResponse

Après contrôle de propriété, le corps JSON est désérialisé par-dessus l'entité existante (object_to_populate) : seules les clés présentes sont écrasées, la requête se comporte donc comme une mise à jour partielle malgré le verbe PUT. Aucun contrôle d'unicité sur l'email ou le téléphone n'est effectué ici, contrairement à ContactApiController::create(). La date de mise à jour est repositionnée à l'instant courant avant validation et flush().

Route : /api/contacts/{id} (PUT), nom api_contacts_update.

Corps attendu : objet JSON partiel reprenant les champs de l'entité Contact (firstname, lastname, email, phone, company, jobTitle, note, …).

Réponses : 200 contact mis à jour, 400 corps JSON invalide ({"error": "Invalid JSON data"}), 400 violations de contraintes ({"errors": {propriété: message}}), 404 contact inexistant, appartenant à autrui, ou appelant non authentifié.

Parameters
$contact : Contact

Contact résolu depuis le paramètre de route {id}.

$request : Request

Requête HTTP dont le corps porte les champs à modifier.

Attributes
#[Route]
'/{id}'
$name: 'api_contacts_update'
$methods: ['PUT']
Return values
JsonResponse

Contact mis à jour, sérialisé avec le groupe contact:read, ou erreurs.


        
On this page

Search results