BusinessCardApiController
extends AbstractController
in package
Contrôleur REST des cartes de visite numériques (BusinessCardAize).
Expose sous le préfixe /api/business-cards (préfixe de nom de route
api_business_cards_) les opérations CRUD sur les cartes de visite, la
recherche dans le réseau BCard, la consultation publique par hash, les
transitions de statut (publication, brouillon, désactivation, marquage de
l'email comme envoyé) et l'upload du logo personnalisé.
Sauf pour l'endpoint public par hash, l'accès est protégé par JWT
(#[IsGranted('IS_AUTHENTICATED')] ou IS_AUTHENTICATED_FULLY) et chaque
action sur une carte existante vérifie que l'appelant en est le propriétaire
ou possède ROLE_ADMIN. Certaines fonctionnalités (numéros multiples,
secteur d'activité, type d'organisation, compétences, logo personnalisé,
réseau BCard) sont réservées aux offres Pro/Entreprise via
SubscriptionService.
Les corps de requête des endpoints d'écriture sont en multipart/form-data
(et non en JSON) car ils transportent des fichiers ; la sérialisation des
réponses est déléguée à BusinessCardSerializerService.
Attributes
- #[Route]
- '/api/business-cards'
- $name: 'api_business_cards_'
- #[Tag]
- $name: 'Business Cards'
- $description: 'Gestion des cartes de visite numériques'
Table of Contents
Properties
- $businessCardSerializer : BusinessCardSerializerService
- $entityManager : EntityManagerInterface
- $logoManagementService : LogoManagementService
- $qrCodeService : QRCodeService
- $subscriptionService : SubscriptionService
- $uploaderService : UploaderService
- $validator : ValidatorInterface
Methods
- __construct() : mixed
- create() : JsonResponse
- Crée une carte de visite pour l'utilisateur connecté à partir d'un envoi multipart.
- delete() : JsonResponse
- Supprime une carte de visite ainsi que ses fichiers associés.
- disable() : JsonResponse
- Désactive une carte de visite.
- list() : JsonResponse
- Renvoie la liste paginée des cartes de visite de l'utilisateur connecté.
- markEmailAsSent() : JsonResponse
- Marque comme envoyé l'email associé à une carte de visite.
- networkSearch() : JsonResponse
- Recherche des cartes de visite partagées dans le réseau BCard.
- publicShow() : JsonResponse
- Expose publiquement une carte de visite identifiée par son hash.
- publish() : JsonResponse
- Fait passer une carte de visite au statut publié.
- setAsDraft() : JsonResponse
- Repasse une carte de visite au statut brouillon.
- show() : JsonResponse
- Renvoie le détail d'une carte de visite appartenant à l'utilisateur connecté.
- update() : JsonResponse
- Met à jour partiellement une carte de visite existante.
- uploadCustomLogo() : JsonResponse
- Téléverse le logo personnalisé d'une carte de visite.
- applyActivities() : void
- Synchronise les activités d'une carte de visite avec les données fournies.
- applyActivitiesFromJson() : void
- Décode une charge utile JSON d'activités et l'applique à une carte de visite.
- getFileSizeLimit() : int
- Détermine la limite de taille de fichier selon l'offre de l'utilisateur
- normalizePhoneNumber() : string
- Normalise un numéro de téléphone au format international.
- normalizePhoneNumbersPayload() : array<int, array{number: string, visible: bool}>
- Normalise chaque numéro d'une liste déjà structurée.
- parseMultipartPut() : array<string|int, mixed>
- Parser manuel pour récupérer les champs/fichiers en PUT multipart
- parsePhoneNumbersPayload() : array<int, array{number: string, visible: bool}>
- Convertit une charge utile de numéros de téléphone en liste structurée.
- setNestedValue() : void
- Écrit une valeur dans un tableau à partir d'un nom de champ HTML éventuellement indexé.
- validateFile() : void
- Valide un fichier uploadé
Properties
$businessCardSerializer read-only
private
BusinessCardSerializerService
$businessCardSerializer
$entityManager read-only
private
EntityManagerInterface
$entityManager
$logoManagementService read-only
private
LogoManagementService
$logoManagementService
$qrCodeService read-only
private
QRCodeService
$qrCodeService
$subscriptionService read-only
private
SubscriptionService
$subscriptionService
$uploaderService read-only
private
UploaderService
$uploaderService
$validator read-only
private
ValidatorInterface
$validator
Methods
__construct()
public
__construct(EntityManagerInterface $entityManager, ValidatorInterface $validator, UploaderService $uploaderService, QRCodeService $qrCodeService, SubscriptionService $subscriptionService, BusinessCardSerializerService $businessCardSerializer, LogoManagementService $logoManagementService) : mixed
Parameters
- $entityManager : EntityManagerInterface
-
Gestionnaire Doctrine utilisé pour persister, supprimer et flusher les cartes et leurs activités.
- $validator : ValidatorInterface
-
Valideur Symfony appliqué à l'entité BusinessCardAize avant enregistrement.
- $uploaderService : UploaderService
-
Service d'upload des fichiers (CV, photo de profil, brochures) et de suppression des fichiers associés à une carte.
- $qrCodeService : QRCodeService
-
Service de génération du QR code de la carte à partir de son hash public.
- $subscriptionService : SubscriptionService
-
Service d'abonnement : création de l'abonnement gratuit, quota de cartes, accès aux fonctionnalités premium.
- $businessCardSerializer : BusinessCardSerializerService
-
Service de sérialisation d'une carte ou d'une collection de cartes en tableaux JSON.
- $logoManagementService : LogoManagementService
-
Service de gestion et d'upload du logo personnalisé d'une carte.
create()
Crée une carte de visite pour l'utilisateur connecté à partir d'un envoi multipart.
public
create(Request $request, EmployeeRepository $employeeRepository) : JsonResponse
Enchaîne les contrôles suivants : interdiction faite aux comptes
ROLE_ADMIN, refus des comptes d'entreprise non activés, création
automatique d'un abonnement gratuit si l'utilisateur n'en a aucun, puis
vérification du quota de cartes du plan (canCreateCard). Les champs
phoneNumbers, industry, organizationType et skills sont réservés
aux offres Pro/Entreprise. skills doit être un tableau JSON ; ses
entrées sont converties en chaînes et les valeurs vides écartées.
firstname, lastname et email sont obligatoires ; en l'absence de
phoneNumbers, phoneNumber devient obligatoire et est normalisé, sinon
le premier numéro normalisé de la liste alimente aussi phoneNumber. Si
employee_id est fourni, l'employé doit exister, appartenir à la même
entreprise que l'utilisateur et avoir le même email que la carte. Les
fichiers cv (PDF, 5 Mo max) et image (JPEG/PNG, 2 Mo max) sont validés
selon les limites du plan puis uploadés, et les activités décrites par le
JSON activities sont créées avec leurs brochures activity_brochures[i].
Après le premier flush, un publicHash est calculé par
hash_hmac('sha256', id, APP_SECRET) s'il est absent, puis le QR code est
généré ; un échec de génération du QR code est seulement journalisé via
error_log() et n'interrompt pas la création.
Route : /api/business-cards (POST), nom api_business_cards_create.
Authentification complète requise (IS_AUTHENTICATED_FULLY).
Corps attendu (multipart/form-data) : {"firstname": "string — requis", "lastname": "string — requis", "email": "string — requis", "phoneNumber": "string — requis si phoneNumbers absent", "phoneNumbers": "string JSON — liste de numéros, Pro/Entreprise uniquement", "skills": "string JSON — tableau de compétences, Pro/Entreprise", "industry": "string — Pro/Entreprise", "organizationType": "string — Pro/Entreprise", "shareInNetwork": "bool — partage dans le réseau BCard", "company"|"compagny": "string — raison sociale", "jobTitle": "string", "website": "string", "address": "string", "linkedin": "string", "twitter": "string", "facebook": "string", "instagram": "string", "bio": "string", "additionalInfo": "string", "employee_id": "int — employé associé", "cv": "fichier PDF", "image": "fichier JPEG/PNG", "activities": "string JSON — [{\"name\": \"...\"}]", "activity_brochures": "fichiers indexés alignés sur activities"}.
Réponses : 201 carte créée ; 400 champ requis manquant, skills non
tableau JSON, liste de numéros vide, employé introuvable ou non autorisé,
email différent de celui de l'employé, ou erreur d'upload/validation de
l'entité ; 401 si le jeton JWT est absent ou invalide ; 403 pour un
administrateur, un compte entreprise non activé, un quota de cartes
atteint ou l'usage d'un champ réservé aux offres Pro/Entreprise.
Parameters
- $request : Request
-
Requête multipart contenant les champs et fichiers de la carte.
- $employeeRepository : EmployeeRepository
-
Repository utilisé pour résoudre
employee_id.
Attributes
- #[IsGranted]
- 'IS_AUTHENTICATED_FULLY'
- #[Post]
- $path: '/api/business-cards'
- $description: 'Crée une nouvelle carte de visite numérique avec support des fichiers (CV, photo, activités)'
- $summary: 'Crée une nouvelle carte de visite numérique'
- $security: [['bearerAuth' => []]]
- $requestBody: new OA\RequestBody(required: true, content: new OA\MediaType(mediaType: 'multipart/form-data', schema: new OA\Schema(required: ['firstname', 'lastname', 'email', 'compagny'], properties: [new OA\Property(property: 'firstname', type: 'string', example: 'John'), new OA\Property(property: 'lastname', type: 'string', example: 'Doe'), new OA\Property(property: 'email', type: 'string', example: 'john@example.com'), new OA\Property(property: 'phoneNumber', description: 'Numéro principal (utilisateurs Free)', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumbers', description: 'JSON des numéros de téléphone (utilisateurs Pro/Entreprise uniquement)', type: 'string', example: '[{"number": "+33123456789", "visible": true}]'), new OA\Property(property: 'jobTitle', type: 'string', example: 'Développeur'), new OA\Property(property: 'organizationType', type: 'string', example: 'Entreprise'), new OA\Property(property: 'industry', type: 'string', example: 'Logiciels informatiques'), new OA\Property(property: 'skills', description: 'JSON des compétences', type: 'string', example: '["PHP", "Symfony", "React"]'), new OA\Property(property: 'shareInNetwork', type: 'boolean', example: "true or false"), new OA\Property(property: 'compagny', type: 'string', example: 'Ma Société'), new OA\Property(property: 'website', type: 'string', example: 'https://example.com'), new OA\Property(property: 'address', type: 'string', example: '123 Rue Example'), new OA\Property(property: 'linkedin', type: 'string', example: 'https://linkedin.com/in/johndoe'), new OA\Property(property: 'twitter', type: 'string', example: 'https://twitter.com/johndoe'), new OA\Property(property: 'facebook', type: 'string', example: 'https://facebook.com/johndoe'), new OA\Property(property: 'instagram', type: 'string', example: 'https://instagram.com/johndoe'), new OA\Property(property: 'bio', type: 'string', example: 'Développeur passionné...'), new OA\Property(property: 'additionalInfo', type: 'string', example: 'Informations supplémentaires'), new OA\Property(property: 'employee_id', description: 'ID de l\'employé associé (optionnel)', type: 'integer', example: 1), new OA\Property(property: 'cv', description: 'Fichier CV (PDF)', type: 'string', format: 'binary'), new OA\Property(property: 'image', description: 'Photo de profil', type: 'string', format: 'binary'), new OA\Property(property: 'activities', description: 'JSON des activités (ordre important pour les brochures) [{"name":"Activité 1"},{"name":"Activité 2"}]', type: 'string'), new OA\Property(property: 'activity_brochures', description: 'Fichiers brochures alignés par index (activity_brochures[0], activity_brochures[1], ...)', type: 'array', items: new OA\Items(type: 'string', format: 'binary'))])))
- $tags: ['Business Cards']
- $responses: [new OA\Response(response: 201, description: 'Carte de visite créée avec succès', content: new OA\JsonContent(properties: [new OA\Property(property: 'message', type: 'string', example: 'Carte de visite créée avec succès'), new OA\Property(property: 'business_card_id', type: 'integer', example: 1)])), new OA\Response(response: 400, description: 'Données invalides'), new OA\Response(response: 401, description: 'Non authentifié'), new OA\Response(response: 403, description: 'Accès refusé - Compte entreprise requis')]
- #[Route]
- ''
- $name: 'create'
- $methods: ['POST']
Return values
JsonResponse —Objet {"message", "business_card_id", "qr_code"} en cas de succès, sinon {"error"} ou {"errors"}.
delete()
Supprime une carte de visite ainsi que ses fichiers associés.
public
delete(int $id, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Charge la carte, retourne 404 si elle est introuvable, exige que
l'appelant en soit le propriétaire ou possède ROLE_ADMIN, puis appelle
deleteAssociatedFiles() (CV, image, brochures, QR code…) avant de
retirer l'entité et de flusher. Toute exception survenant pendant la
suppression est convertie en réponse 500 contenant le message d'origine.
Route : /api/business-cards/{id} (DELETE), nom
api_business_cards_delete. Authentification complète requise
(IS_AUTHENTICATED_FULLY).
Réponses : 200 suppression réussie ; 401 si le jeton JWT est absent ou invalide ; 403 carte appartenant à un autre utilisateur ; 404 carte introuvable ; 500 exception levée lors de la suppression des fichiers ou de l'entité.
Parameters
- $id : int
-
Identifiant de la carte à supprimer.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
Attributes
- #[Delete]
- $path: '/api/business-cards/{id}'
- $description: 'Supprime une carte de visite numérique'
- $summary: 'Supprime une carte de visite'
- $security: [['bearerAuth' => []]]
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite à supprimer', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Carte de visite supprimée avec succès', content: new OA\JsonContent(properties: [new OA\Property(property: 'message', type: 'string', example: 'Carte de visite supprimée avec succès')])), new OA\Response(response: 404, description: 'Carte de visite non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
- #[IsGranted]
- 'IS_AUTHENTICATED_FULLY'
- #[Route]
- '/{id}'
- $name: 'delete'
- $methods: ['DELETE']
Return values
JsonResponse —Objet {"message": "Carte de visite et tous les fichiers associés supprimés avec succès"}, sinon {"error"}.
disable()
Désactive une carte de visite.
public
disable(int $id, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Charge la carte, retourne 404 si elle est introuvable, exige que
l'appelant en soit le propriétaire ou possède ROLE_ADMIN, puis délègue
le changement d'état à BusinessCardAize::disable() avant de flusher.
Aucun corps de requête n'est lu et aucun fichier n'est supprimé.
Route : /api/business-cards/{id}/disable (POST), nom
api_business_cards_disable. Authentification complète requise
(IS_AUTHENTICATED_FULLY).
Réponses : 200 carte désactivée ; 401 si le jeton JWT est absent ou invalide ; 403 carte appartenant à un autre utilisateur ; 404 carte introuvable.
Parameters
- $id : int
-
Identifiant de la carte à désactiver.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
Attributes
- #[IsGranted]
- 'IS_AUTHENTICATED_FULLY'
- #[Post]
- $path: '/api/business-cards/{id}/disable'
- $summary: 'Désactiver une carte de visite numérique'
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Carte de visite désactivée avec succès', content: new OA\JsonContent(properties: ['message' => new OA\Property(property: 'message', type: 'string', example: 'Carte de visite désactivée avec succès')])), new OA\Response(response: 404, description: 'Carte de visite non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
- #[Route]
- '/{id}/disable'
- $name: 'disable'
- $methods: ['POST']
Return values
JsonResponse —Objet {"message": "Carte de visite désactivée avec succès"}, sinon {"error"}.
list()
Renvoie la liste paginée des cartes de visite de l'utilisateur connecté.
public
list(Request $request, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Refuse l'accès aux comptes rattachés à une entreprise non activée. Lit les
paramètres de requête page (borné à 1 minimum), limit (borné entre 1 et
50, 10 par défaut) et search, puis délègue au repository
(findByUserWithSearch / countByUserWithSearch) la recherche filtrée sur
les cartes du seul utilisateur courant. Les cartes sont sérialisées via
BusinessCardSerializerService et le nombre total de pages est calculé avec
ceil().
Route : /api/business-cards (GET), nom api_business_cards_list.
Authentification requise (IS_AUTHENTICATED).
Paramètres de requête : page (int, optionnel), limit (int, optionnel),
search (string, optionnel — nom, prénom, email, entreprise, poste, téléphone).
Réponses : 200 liste et bloc de pagination renvoyés ; 401 si le jeton JWT est absent ou invalide ; 403 si l'utilisateur appartient à une entreprise dont le compte n'est pas activé.
Parameters
- $request : Request
-
Requête HTTP portant les paramètres de pagination et de recherche.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository interrogé pour la recherche et le comptage.
Attributes
- #[Get]
- $path: '/api/business-cards'
- $description: 'Récupère la liste des cartes de visite numériques de l\'utilisateur connecté (utilisateurs normaux et entreprises)'
- $summary: 'Liste les cartes de visite numériques'
- $security: [['bearerAuth' => []]]
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'page', description: 'Numéro de page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)), new OA\Parameter(name: 'limit', description: 'Nombre d\'éléments par page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 10)), new OA\Parameter(name: 'search', description: 'Terme de recherche (nom, prénom, email, entreprise, poste, téléphone)', in: 'query', required: false, schema: new OA\Schema(type: 'string'))]
- $responses: [new OA\Response(response: 200, description: 'Liste des cartes de visite récupérée avec succès', content: new OA\JsonContent(properties: [new OA\Property(property: 'business_cards', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'firstname', type: 'string', example: 'John'), new OA\Property(property: 'lastname', type: 'string', example: 'Doe'), new OA\Property(property: 'email', type: 'string', example: 'john@example.com'), new OA\Property(property: 'phone', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumber', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumbers', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'number', type: 'string', example: '+33123456789'), new OA\Property(property: 'visible', type: 'boolean', example: true)])), new OA\Property(property: 'visiblePhoneNumbers', type: 'array', items: new OA\Items(type: 'string', example: '+33123456789')), new OA\Property(property: 'position', type: 'string', example: 'Développeur'), new OA\Property(property: 'organizationType', type: 'string', example: 'Entreprise'), new OA\Property(property: 'industry', type: 'string', example: 'Informatique'), new OA\Property(property: 'skills', type: 'array', items: new OA\Items(type: 'string', example: 'PHP')), new OA\Property(property: 'bio', type: 'string', example: 'bio'), new OA\Property(property: 'website', type: 'string', example: 'https://example.com'), new OA\Property(property: 'address', type: 'string', example: '123 Rue Example'), new OA\Property(property: 'image', type: 'string', example: 'profil.png'), new OA\Property(property: 'cv', type: 'string', example: 'cv.jpg '), new OA\Property(property: 'activities', properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'name', type: 'string', example: "BINN"), new OA\Property(property: 'brochure', type: 'string', example: "brochure.pdf")]), new OA\Property(property: 'status', type: 'string', example: "published"), new OA\Property(property: 'hash', type: 'string', example: "hash_code"), new OA\Property(property: 'created_at', type: 'string', format: 'date-time'), new OA\Property(property: 'employee', properties: [new OA\Property(property: 'id', type: 'integer'), new OA\Property(property: 'firstname', type: 'string'), new OA\Property(property: 'lastname', type: 'string')], type: 'object')])), new OA\Property(property: 'pagination', properties: [new OA\Property(property: 'current_page', type: 'integer'), new OA\Property(property: 'total_pages', type: 'integer'), new OA\Property(property: 'total_items', type: 'integer'), new OA\Property(property: 'items_per_page', type: 'integer')], type: 'object')])), new OA\Response(response: 401, description: 'Non authentifié'), new OA\Response(response: 403, description: 'Accès refusé - Compte entreprise non activé')]
- #[IsGranted]
- 'IS_AUTHENTICATED'
- #[Route]
- ''
- $name: 'list'
- $methods: ['GET']
Return values
JsonResponse —Objet {"business_cards": [...], "pagination": {"current_page", "total_pages", "total_items", "items_per_page"}}.
markEmailAsSent()
Marque comme envoyé l'email associé à une carte de visite.
public
markEmailAsSent(int $id, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Charge la carte, retourne 404 si elle est introuvable, exige que
l'appelant en soit le propriétaire ou possède ROLE_ADMIN, puis appelle
BusinessCardAize::markEmailAsSent() avant de flusher. Cette action ne
déclenche aucun envoi de message : elle ne fait qu'enregistrer l'état
d'envoi côté entité.
Route : /api/business-cards/{id}/mark-email-sent (POST), nom
api_business_cards_mark_email_sent. Authentification complète requise
(IS_AUTHENTICATED_FULLY).
Réponses : 200 marquage effectué ; 401 si le jeton JWT est absent ou invalide ; 403 carte appartenant à un autre utilisateur ; 404 carte introuvable.
Parameters
- $id : int
-
Identifiant de la carte concernée.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
Attributes
- #[IsGranted]
- 'IS_AUTHENTICATED_FULLY'
- #[Post]
- $path: '/api/business-cards/{id}/mark-email-sent'
- $summary: 'Marquer l\'email comme envoyé pour une carte de visite'
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Email marqué comme envoyé avec succès', content: new OA\JsonContent(properties: ['message' => new OA\Property(property: 'message', type: 'string', example: 'Email marqué comme envoyé avec succès')])), new OA\Response(response: 404, description: 'Carte de visite non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
- #[Route]
- '/{id}/mark-email-sent'
- $name: 'mark_email_sent'
- $methods: ['POST']
Return values
JsonResponse —Objet {"message": "Email marqué comme envoyé avec succès"}, sinon {"error"}.
networkSearch()
Recherche des cartes de visite partagées dans le réseau BCard.
public
networkSearch(Request $request, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Vérifie d'abord que l'utilisateur courant est bien une instance de User,
puis qu'il dispose des fonctionnalités premium (via SubscriptionService) ou
de l'un des rôles ROLE_EMPLOYEE / ROLE_MANAGER. Le terme q est
nettoyé (trim) et converti en null s'il est vide, la pagination est
bornée (page >= 1, limit entre 1 et 50, 20 par défaut) ; la sélection
des cartes visibles dans le réseau, dont l'exclusion de celles sans
consentement de partage, est déléguée à
findNetworkCardsWithSearch(), qui reçoit également l'utilisateur courant.
Route : /api/business-cards/network/search (GET), nom
api_business_cards_network_search. Authentification requise
(IS_AUTHENTICATED).
Paramètres de requête : q (string, optionnel), page (int, optionnel),
limit (int, optionnel).
Réponses : 200 résultats sérialisés ; 401 si aucun utilisateur User n'est authentifié ; 403 si l'utilisateur n'a ni offre Pro/Entreprise ni rôle employé/manager.
Parameters
- $request : Request
-
Requête HTTP portant
q,pageetlimit. - $businessCardRepository : BusinessCardAizeRepository
-
Repository interrogé pour la recherche réseau.
Attributes
- #[Get]
- $path: '/api/business-cards/network/search'
- $description: 'Liste/recherche de cartes de visite dans le réseau BCard (Pro/Entreprise). Les cartes sans consentement sont exclues.'
- $summary: 'Réseau BCard'
- $security: [['bearerAuth' => []]]
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'q', in: 'query', required: false, schema: new OA\Schema(type: 'string')), new OA\Parameter(name: 'page', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 1)), new OA\Parameter(name: 'limit', in: 'query', required: false, schema: new OA\Schema(type: 'integer', default: 20))]
- $responses: [new OA\Response(response: 200, description: 'Résultats de recherche'), new OA\Response(response: 400, description: 'Paramètres invalides'), new OA\Response(response: 403, description: 'Offre Pro/Entreprise requise')]
- #[IsGranted]
- 'IS_AUTHENTICATED'
- #[Route]
- '/network/search'
- $name: 'network_search'
- $methods: ['GET']
Return values
JsonResponse —Objet {"data": [...], "q": string|null, "page": int, "limit": int} avec le statut 200.
publicShow()
Expose publiquement une carte de visite identifiée par son hash.
public
publicShow(string $hash, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Recherche la carte dont la propriété publicHash correspond au segment
d'URL fourni, sans aucun contrôle d'authentification ni de propriété
(endpoint déclaré security: []), et la sérialise en mode détaillé
(serializeBusinessCard($card, true)). Aucun filtrage sur le statut de la
carte n'est effectué : une carte en brouillon ou désactivée reste
accessible si son hash est connu.
Route : /api/business-cards/public/{hash} (GET), nom
api_business_cards_public_show. Aucune authentification requise.
Réponses : 200 carte trouvée et sérialisée ; 404 {"error": "Carte de visite non trouvée"} si aucune carte ne porte ce hash.
Parameters
- $hash : string
-
Hash public de la carte, extrait de l'URL.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour retrouver la carte par son hash.
Attributes
- #[Get]
- $path: '/api/business-cards/public/{hash}'
- $description: 'Récupère les détails publics d\'une carte de visite par son hash'
- $summary: 'Récupère une carte de visite par son hash public'
- $security: []
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'hash', description: 'Hash public de la carte de visite', in: 'path', required: true, schema: new OA\Schema(type: 'string'))]
- $responses: [new OA\Response(response: 200, description: 'Détails publics de la carte de visite', content: new OA\JsonContent(properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'firstname', type: 'string', example: 'John'), new OA\Property(property: 'lastname', type: 'string', example: 'Doe'), new OA\Property(property: 'email', type: 'string', example: 'john@example.com'), new OA\Property(property: 'phone', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumber', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumbers', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'number', type: 'string', example: '+33123456789'), new OA\Property(property: 'visible', type: 'boolean', example: true)])), new OA\Property(property: 'visiblePhoneNumbers', type: 'array', items: new OA\Items(type: 'string', example: '+33123456789')), new OA\Property(property: 'jobTitle', type: 'string', example: 'Développeur'), new OA\Property(property: 'bio', type: 'string', example: 'Bio'), new OA\Property(property: 'company', type: 'string', example: 'Ma Société'), new OA\Property(property: 'website', type: 'string', example: 'https://example.com'), new OA\Property(property: 'address', type: 'string', example: '123 Rue Example'), new OA\Property(property: 'image', type: 'string', example: 'profil.png'), new OA\Property(property: 'cv', type: 'string', example: 'cv.jpg '), new OA\Property(property: 'activities', properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'name', type: 'string', example: "BINN"), new OA\Property(property: 'brochure', type: 'string', example: "brochure.pdf")]), new OA\Property(property: 'status', type: 'string', example: "published"), new OA\Property(property: 'linkedin', type: 'string', example: 'https://linkedin.com/in/johndoe'), new OA\Property(property: 'twitter', type: 'string', example: 'https://twitter.com/johndoe'), new OA\Property(property: 'facebook', type: 'string', example: 'https://facebook.com/johndoe'), new OA\Property(property: 'instagram', type: 'string', example: 'https://instagram.com/johndoe'), new OA\Property(property: 'created_at', type: 'string', format: 'date-time'), new OA\Property(property: 'employee', type: 'object')])), new OA\Response(response: 404, description: 'Carte de visite non trouvée')]
- #[Route]
- '/public/{hash}'
- $name: 'public_show'
- $methods: ['GET']
Return values
JsonResponse —Représentation JSON détaillée de la carte, ou message d'erreur.
publish()
Fait passer une carte de visite au statut publié.
public
publish(int $id, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Charge la carte, retourne 404 si elle est introuvable, exige que
l'appelant en soit le propriétaire ou possède ROLE_ADMIN, puis délègue
le changement d'état à BusinessCardAize::publish() avant de flusher.
Aucun corps de requête n'est lu.
Route : /api/business-cards/{id}/publish (POST), nom
api_business_cards_publish. Authentification complète requise
(IS_AUTHENTICATED_FULLY).
Réponses : 200 carte publiée ; 401 si le jeton JWT est absent ou invalide ; 403 carte appartenant à un autre utilisateur ; 404 carte introuvable.
Parameters
- $id : int
-
Identifiant de la carte à publier.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
Attributes
- #[IsGranted]
- 'IS_AUTHENTICATED_FULLY'
- #[Post]
- $path: '/api/business-cards/{id}/publish'
- $summary: 'Publier une carte de visite numérique'
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Carte de visite publiée avec succès', content: new OA\JsonContent(properties: ['message' => new OA\Property(property: 'message', type: 'string', example: 'Carte de visite publiée avec succès')])), new OA\Response(response: 404, description: 'Carte de visite non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
- #[Route]
- '/{id}/publish'
- $name: 'publish'
- $methods: ['POST']
Return values
JsonResponse —Objet {"message": "Carte de visite publiée avec succès"}, sinon {"error"}.
setAsDraft()
Repasse une carte de visite au statut brouillon.
public
setAsDraft(int $id, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Charge la carte, retourne 404 si elle est introuvable, exige que
l'appelant en soit le propriétaire ou possède ROLE_ADMIN, puis délègue
le changement d'état à BusinessCardAize::setAsDraft() avant de flusher.
Aucun corps de requête n'est lu.
Route : /api/business-cards/{id}/draft (POST), nom
api_business_cards_set_draft. Authentification complète requise
(IS_AUTHENTICATED_FULLY).
Réponses : 200 carte mise en brouillon ; 401 si le jeton JWT est absent ou invalide ; 403 carte appartenant à un autre utilisateur ; 404 carte introuvable.
Parameters
- $id : int
-
Identifiant de la carte à repasser en brouillon.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
Attributes
- #[IsGranted]
- 'IS_AUTHENTICATED_FULLY'
- #[Post]
- $path: '/api/business-cards/{id}/draft'
- $summary: 'Mettre une carte de visite en brouillon'
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Carte de visite mise en brouillon avec succès', content: new OA\JsonContent(properties: ['message' => new OA\Property(property: 'message', type: 'string', example: 'Carte de visite mise en brouillon avec succès')])), new OA\Response(response: 404, description: 'Carte de visite non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
- #[Route]
- '/{id}/draft'
- $name: 'set_draft'
- $methods: ['POST']
Return values
JsonResponse —Objet {"message": "Carte de visite mise en brouillon avec succès"}, sinon {"error"}.
show()
Renvoie le détail d'une carte de visite appartenant à l'utilisateur connecté.
public
show(int $id, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Charge la carte par son identifiant, retourne 404 si elle n'existe pas,
puis vérifie que l'utilisateur courant en est le propriétaire ou possède
ROLE_ADMIN avant de la sérialiser en mode détaillé
(serializeBusinessCard($card, true)).
Route : /api/business-cards/{id} (GET), nom api_business_cards_show.
Authentification requise (IS_AUTHENTICATED).
Réponses : 200 carte sérialisée ; 401 si le jeton JWT est absent ou
invalide ; 403 {"error": "Accès refusé"} si la carte appartient à un
autre utilisateur et que l'appelant n'est pas administrateur ; 404 si
aucune carte ne porte cet identifiant.
Parameters
- $id : int
-
Identifiant de la carte de visite.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
Attributes
- #[Get]
- $path: '/api/business-cards/{id}'
- $description: 'Récupère les détails d\'une carte de visite spécifique'
- $summary: 'Récupère une carte de visite par son ID'
- $security: [['bearerAuth' => []]]
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Détails de la carte de visite', content: new OA\JsonContent(properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'firstname', type: 'string', example: 'John'), new OA\Property(property: 'lastname', type: 'string', example: 'Doe'), new OA\Property(property: 'email', type: 'string', example: 'john@example.com'), new OA\Property(property: 'phone', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumber', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumbers', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'number', type: 'string', example: '+33123456789'), new OA\Property(property: 'visible', type: 'boolean', example: true)])), new OA\Property(property: 'visiblePhoneNumbers', type: 'array', items: new OA\Items(type: 'string', example: '+33123456789')), new OA\Property(property: 'jobTitle', type: 'string', example: 'Développeur'), new OA\Property(property: 'bio', type: 'string', example: 'bio'), new OA\Property(property: 'company', type: 'string', example: 'Ma Société'), new OA\Property(property: 'website', type: 'string', example: 'https://example.com'), new OA\Property(property: 'address', type: 'string', example: '123 Rue Example'), new OA\Property(property: 'image', type: 'string', example: 'profil.png'), new OA\Property(property: 'cv', type: 'string', example: 'cv.jpg '), new OA\Property(property: 'activities', properties: [new OA\Property(property: 'id', type: 'integer', example: 1), new OA\Property(property: 'name', type: 'string', example: "BINN"), new OA\Property(property: 'brochure', type: 'string', example: "brochure.pdf")]), new OA\Property(property: 'status', type: 'string', example: "published"), new OA\Property(property: 'linkedin', type: 'string', example: 'https://linkedin.com/in/johndoe'), new OA\Property(property: 'twitter', type: 'string', example: 'https://twitter.com/johndoe'), new OA\Property(property: 'facebook', type: 'string', example: 'https://facebook.com/johndoe'), new OA\Property(property: 'instagram', type: 'string', example: 'https://instagram.com/johndoe'), new OA\Property(property: 'created_at', type: 'string', format: 'date-time'), new OA\Property(property: 'employee', type: 'object')])), new OA\Response(response: 404, description: 'Carte de visite non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
- #[IsGranted]
- 'IS_AUTHENTICATED'
- #[Route]
- '/{id}'
- $name: 'show'
- $methods: ['GET']
Return values
JsonResponse —Représentation JSON détaillée de la carte, ou message d'erreur.
update()
Met à jour partiellement une carte de visite existante.
public
update(int $id, Request $request, BusinessCardAizeRepository $businessCardRepository, LoggerInterface $logger) : JsonResponse
Charge la carte, retourne 404 si elle est introuvable, puis exige que
l'appelant en soit le propriétaire ou possède ROLE_ADMIN. En méthode
PUT, le corps multipart est décodé manuellement par
parseMultipartPut() (PHP ne peuplant pas $_POST/$_FILES en PUT) ;
en POST, les champs et fichiers natifs de la requête sont utilisés. Seules
les clés présentes sont appliquées, via une table de correspondance
champ => setter (firstname, lastname, email, phoneNumber,
jobTitle, organizationType, industry, compagny/company,
website, address, linkedin, twitter, facebook, instagram,
bio, status, additionalInfo). phoneNumbers, industry,
organizationType et skills sont réservés aux offres Pro/Entreprise ;
phoneNumber est normalisé et, pour un compte premium, réplique la liste
phoneNumbers à un seul élément visible. Les nouveaux fichiers cv et
image sont validés puis uploadés après suppression du fichier
précédent sur le disque. Le JSON activities déclenche un upsert des
activités sans suppression des activités absentes (replace = false).
L'entité est validée avant flush(), et la mise à jour est journalisée.
Note : shareInNetwork est lu sans test de présence préalable, il est
donc supposé toujours transmis dans le corps.
Route : /api/business-cards/{id} (PUT et POST), nom
api_business_cards_update. Authentification complète requise
(IS_AUTHENTICATED_FULLY).
Corps attendu (multipart/form-data, tous les champs optionnels sauf
shareInNetwork qui est lu inconditionnellement) : {"firstname": "string", "lastname": "string", "email": "string", "phoneNumber": "string — normalisé", "phoneNumbers": "string JSON — Pro/Entreprise uniquement", "skills": "string JSON — tableau, Pro/Entreprise", "industry": "string — Pro/Entreprise", "organizationType": "string — Pro/Entreprise", "shareInNetwork": "bool", "company"|"compagny": "string", "jobTitle": "string", "website": "string", "address": "string", "linkedin": "string", "twitter": "string", "facebook": "string", "instagram": "string", "bio": "string", "status": "string", "additionalInfo": "string", "cv": "fichier PDF", "image": "fichier JPEG/PNG", "activities": "string JSON", "activity_brochures": "fichiers indexés"}.
Réponses : 200 mise à jour effectuée ; 400 liste de numéros vide, skills
non tableau JSON, erreur d'upload ou violations de contraintes de
validation ; 401 si le jeton JWT est absent ou invalide ; 403 carte
appartenant à un autre utilisateur, ou champ réservé aux offres
Pro/Entreprise employé sans droits ; 404 carte introuvable.
Parameters
- $id : int
-
Identifiant de la carte à modifier.
- $request : Request
-
Requête multipart (PUT ou POST) contenant les champs et fichiers.
- $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
- $logger : LoggerInterface
-
Journalise l'identifiant de la carte mise à jour.
Attributes
- #[IsGranted]
- 'IS_AUTHENTICATED_FULLY'
- #[Put]
- $path: '/api/business-cards/{id}'
- $description: 'Met à jour les informations d\'une carte de visite numérique avec support des fichiers'
- $summary: 'Met à jour une carte de visite'
- $security: [['bearerAuth' => []]]
- $requestBody: new OA\RequestBody(required: true, content: new OA\MediaType(mediaType: 'multipart/form-data', schema: new OA\Schema(properties: [new OA\Property(property: 'firstname', type: 'string', example: 'John'), new OA\Property(property: 'lastname', type: 'string', example: 'Doe'), new OA\Property(property: 'email', type: 'string', example: 'john@example.com'), new OA\Property(property: 'phoneNumber', description: 'Numéro principal (utilisateurs Free)', type: 'string', example: '+33123456789'), new OA\Property(property: 'phoneNumbers', description: 'JSON des numéros de téléphone (utilisateurs Pro/Entreprise uniquement)', type: 'string', example: '[{"number": "+33123456789", "visible": true}]'), new OA\Property(property: 'jobTitle', type: 'string', example: 'Développeur Senior'), new OA\Property(property: 'organizationType', type: 'string', example: 'Entreprise'), new OA\Property(property: 'industry', type: 'string', example: 'Logiciels informatiques'), new OA\Property(property: 'skills', description: 'JSON des compétences', type: 'string', example: '["PHP", "Symfony", "React"]'), new OA\Property(property: 'shareInNetwork', type: 'boolean', example: "true or false"), new OA\Property(property: 'compagny', type: 'string', example: 'Ma Société'), new OA\Property(property: 'website', type: 'string', example: 'https://example.com'), new OA\Property(property: 'address', type: 'string', example: '123 Rue Example'), new OA\Property(property: 'linkedin', type: 'string', example: 'https://linkedin.com/in/johndoe'), new OA\Property(property: 'twitter', type: 'string', example: 'https://twitter.com/johndoe'), new OA\Property(property: 'facebook', type: 'string', example: 'https://facebook.com/johndoe'), new OA\Property(property: 'instagram', type: 'string', example: 'https://instagram.com/johndoe'), new OA\Property(property: 'bio', type: 'string', example: 'Développeur passionné...'), new OA\Property(property: 'additionalInfo', type: 'string', example: 'Informations supplémentaires'), new OA\Property(property: 'cv', description: 'Nouveau fichier CV (PDF)', type: 'string', format: 'binary'), new OA\Property(property: 'status', type: 'string', example: "draft"), new OA\Property(property: 'image', description: 'Nouvelle photo de profil', type: 'string', format: 'binary'), new OA\Property(property: 'activities', description: 'JSON des activités à upsert (par id si fourni, sinon par nom).', type: 'string'), new OA\Property(property: 'activity_brochures', description: 'Nouveaux fichiers brochures alignés par index (activity_brochures[0], activity_brochures[1], ...)', type: 'array', items: new OA\Items(type: 'string', format: 'binary'))])))
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite à mettre à jour', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Carte de visite mise à jour avec succès', content: new OA\JsonContent(properties: [new OA\Property(property: 'message', type: 'string', example: 'Carte de visite mise à jour avec succès')])), new OA\Response(response: 400, description: 'Données invalides'), new OA\Response(response: 404, description: 'Carte de visite non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
- #[Route]
- '/{id}'
- $name: 'update'
- $methods: ['PUT', 'POST']
Return values
JsonResponse —Objet {"message": "Carte de visite mise à jour avec succès"}, sinon {"error"} ou {"errors"}.
uploadCustomLogo()
Téléverse le logo personnalisé d'une carte de visite.
public
uploadCustomLogo(int $id, Request $request, BusinessCardAizeRepository $businessCardRepository) : JsonResponse
Charge la carte, retourne 404 si elle est introuvable, exige que
l'appelant en soit le propriétaire ou possède ROLE_ADMIN, puis vérifie
que son abonnement donne accès aux fonctionnalités premium. Le fichier est
lu dans le champ logo de la requête ; sa validation (format, taille) et
son stockage sont entièrement délégués à
LogoManagementService::uploadCustomLogo(), dont le chemin retourné est
affecté à la carte avant flush(). Toute exception levée par le service
est renvoyée telle quelle au client en 400.
Route : /api/business-cards/{id}/custom-logo (POST), nom
api_business_cards_upload_custom_logo. Authentification requise
(IS_AUTHENTICATED).
Corps attendu (multipart/form-data) : {"logo": "fichier image (PNG, JPG, JPEG, SVG) — requis"}.
Réponses : 200 logo enregistré ; 400 aucun fichier logo fourni ou
exception d'upload ; 401 si le jeton JWT est absent ou invalide ; 403
carte appartenant à un autre utilisateur ou offre PRO absente ; 404 carte
introuvable.
Parameters
- $id : int
-
Identifiant de la carte concernée.
- $request : Request
-
Requête multipart contenant le fichier
logo. - $businessCardRepository : BusinessCardAizeRepository
-
Repository utilisé pour charger la carte.
Attributes
- #[IsGranted]
- 'IS_AUTHENTICATED'
- #[Post]
- $path: '/api/business-cards/{id}/custom-logo'
- $description: 'Upload le logo personnalisé pour une carte de visite (réservé aux utilisateurs PRO)'
- $summary: 'Upload du logo personnalisé pour une carte de visite'
- $security: [['bearerAuth' => []]]
- $requestBody: new OA\RequestBody(description: 'Fichier logo à uploader', required: true, content: new OA\MediaType(mediaType: 'multipart/form-data', schema: new OA\Schema(required: ['logo'], properties: ['logo' => new OA\Property(property: 'logo', description: 'Fichier image du logo (PNG, JPG, JPEG, SVG)', type: 'string', format: 'binary')])))
- $tags: ['Business Cards']
- $parameters: [new OA\Parameter(name: 'id', description: 'ID de la carte de visite', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
- $responses: [new OA\Response(response: 200, description: 'Logo d\'entreprise uploadé avec succès', content: new OA\JsonContent(properties: ['message' => new OA\Property(property: 'message', type: 'string'), 'logoPath' => new OA\Property(property: 'logoPath', type: 'string')])), new OA\Response(response: 400, description: 'Données invalides'), new OA\Response(response: 403, description: 'Accès refusé ou offre PRO requise'), new OA\Response(response: 404, description: 'Carte de visite non trouvée')]
- #[Route]
- '/{id}/custom-logo'
- $name: 'upload_custom_logo'
- $methods: ['POST']
Return values
JsonResponse —Objet {"message", "logoPath"} en cas de succès, sinon {"error"}.
applyActivities()
Synchronise les activités d'une carte de visite avec les données fournies.
private
applyActivities(BusinessCardAize $businessCard, array<int, mixed> $activitiesData, array<int, mixed> $brochureFiles, User $user, bool $replace) : void
Indexe d'abord les activités existantes par identifiant et par nom en
minuscules. Pour chaque entrée : ignore les valeurs non tabulaires et les
noms vides après trim(), puis recherche l'activité correspondante par
id (entier ou chaîne numérique), à défaut par nom insensible à la casse,
et en crée une nouvelle si aucune ne correspond. Le nom est ensuite
réaffecté. Si un fichier de brochure existe à l'index courant du tableau
$brochureFiles, il est validé (PDF, JPEG ou PNG, 5 Mo maximum plafonnés
par l'offre de l'utilisateur), l'ancienne brochure est supprimée du
disque, puis le nouveau fichier est uploadé dans brochure_directory.
L'alignement se fait par index : la clé du tableau d'activités doit
correspondre à celle du fichier. Enfin, si $replace vaut vrai, les
activités existantes non retenues sont supprimées de la carte et leurs
brochures effacées du disque ; sinon la méthode se termine sans rien
retirer.
Parameters
- $businessCard : BusinessCardAize
-
Carte dont la collection d'activités est modifiée.
- $activitiesData : array<int, mixed>
-
Données d'activités décodées, chaque entrée acceptée étant
array{id?: int|string, name: string}. - $brochureFiles : array<int, mixed>
-
Fichiers de brochures indexés par la même clé que
$activitiesData. - $user : User
-
Utilisateur courant, servant à déterminer la taille de fichier autorisée.
- $replace : bool
-
Si vrai, supprime les activités existantes absentes des données fournies.
applyActivitiesFromJson()
Décode une charge utile JSON d'activités et l'applique à une carte de visite.
private
applyActivitiesFromJson(BusinessCardAize $businessCard, string|null $activitiesJson, mixed $brochureFiles, User $user, bool $replace) : void
Sort silencieusement si la chaîne est nulle ou vide, ainsi que si le JSON
est mal formé ou ne décode pas en tableau : aucune exception n'est levée
et aucune activité n'est modifiée. Les fichiers de brochures reçus sont
normalisés en tableau (toute valeur non tableau devient un tableau vide)
avant d'être transmis à applyActivities().
Parameters
- $businessCard : BusinessCardAize
-
Carte dont les activités sont mises à jour.
- $activitiesJson : string|null
-
Chaîne JSON attendue sous la forme
[{"id": 1, "name": "..."}, ...]. - $brochureFiles : mixed
-
Fichiers de brochures indexés par position de l'activité ; ignoré s'il ne s'agit pas d'un tableau.
- $user : User
-
Utilisateur courant, utilisé pour appliquer les limites de taille de fichier de son offre.
- $replace : bool
-
Si vrai, les activités existantes absentes de la charge utile sont supprimées.
getFileSizeLimit()
Détermine la limite de taille de fichier selon l'offre de l'utilisateur
private
getFileSizeLimit(User $user, string $fileType) : int
Parameters
- $user : User
- $fileType : string
Return values
intnormalizePhoneNumber()
Normalise un numéro de téléphone au format international.
private
normalizePhoneNumber(string $phoneNumber) : string
Supprime tous les caractères autres que les chiffres et le signe +
(espaces, points, tirets, parenthèses). Si le résultat commence déjà par
+, il est renvoyé tel quel. Sinon les zéros de tête sont retirés, puis
un numéro de 9 chiffres est préfixé par l'indicatif guinéen +224 ; toute
autre longueur reçoit simplement un + en tête. Aucun contrôle de
validité n'est effectué : une chaîne vide reste + et un numéro déjà
précédé d'un indicatif sans + n'est pas corrigé.
Parameters
- $phoneNumber : string
-
Numéro brut saisi par l'utilisateur.
Return values
string —Numéro normalisé commençant par +.
normalizePhoneNumbersPayload()
Normalise chaque numéro d'une liste déjà structurée.
private
normalizePhoneNumbersPayload(array<int, array{number?: mixed, visible?: mixed}> $phoneNumbers) : array<int, array{number: string, visible: bool}>
Applique normalizePhoneNumber() à la clé number de chaque entrée (clé
absente traitée comme chaîne vide) et conserve la visibilité castée en
booléen, true par défaut. Les numéros dont la normalisation produit une
chaîne vide sont écartés ; en pratique le filtre ne se déclenche jamais,
normalizePhoneNumber() renvoyant au minimum +. La liste résultante est
réindexée à partir de zéro, ce qui permet aux appelants d'utiliser
[0]['number'] comme numéro principal.
Parameters
- $phoneNumbers : array<int, array{number?: mixed, visible?: mixed}>
-
Entrées issues de
parsePhoneNumbersPayload().
Return values
array<int, array{number: string, visible: bool}> —Liste des numéros normalisés au format international.
parseMultipartPut()
Parser manuel pour récupérer les champs/fichiers en PUT multipart
private
parseMultipartPut(Request $request) : array<string|int, mixed>
Parameters
- $request : Request
Return values
array<string|int, mixed>parsePhoneNumbersPayload()
Convertit une charge utile de numéros de téléphone en liste structurée.
private
parsePhoneNumbersPayload(mixed $payload) : array<int, array{number: string, visible: bool}>
Accepte soit une chaîne JSON (décodée après trim()), soit directement un
tableau. Toute entrée invalide est ignorée sans erreur : chaîne vide, JSON
mal formé ou ne décodant pas en tableau, payload non tabulaire, élément
ni chaîne ni tableau, ou numéro vide après trim(). Une entrée chaîne est
transformée en ['number' => ..., 'visible' => true] ; une entrée tableau
reprend sa clé number et sa clé visible castée en booléen, cette
dernière valant true par défaut. Les numéros ne sont pas normalisés à ce
stade (voir normalizePhoneNumbersPayload()).
Parameters
- $payload : mixed
-
Chaîne JSON ou tableau d'entrées
stringouarray{number: string, visible?: mixed}.
Return values
array<int, array{number: string, visible: bool}> —Liste réindexée des numéros retenus, éventuellement vide.
setNestedValue()
Écrit une valeur dans un tableau à partir d'un nom de champ HTML éventuellement indexé.
private
setNestedValue(array<string|int, mixed> &$target, string|null $name, mixed $value) : void
Ne fait rien si le nom est nul ou vide. Un nom sans crochet est affecté
directement à la racine du tableau. Sinon, les crochets fermants sont
retirés et le nom est éclaté sur [ pour obtenir la suite de clés :
activity_brochures[0] donne ['activity_brochures', '0']. Les clés
exclusivement numériques sont converties en entiers ; une clé vide
(notation champ[]) provoque un ajout en fin de tableau, terminal si
c'est le dernier segment, sinon création d'un sous-tableau parcouru par
référence. Les niveaux intermédiaires manquants ou non tabulaires sont
réinitialisés en tableaux, ce qui peut écraser une valeur scalaire déjà
présente à ce niveau.
Parameters
- $target : array<string|int, mixed>
-
Tableau modifié par référence (champs ou fichiers).
- $name : string|null
-
Nom du champ, éventuellement de la forme
a[b][0]oua[]. - $value : mixed
-
Valeur à écrire (chaîne du corps ou instance UploadedFile).
validateFile()
Valide un fichier uploadé
private
validateFile(UploadedFile $file, array<string|int, mixed> $allowedMimeTypes, int $maxSize, User $user, string $fileType) : void
Parameters
- $file : UploadedFile
- $allowedMimeTypes : array<string|int, mixed>
- $maxSize : int
- $user : User
- $fileType : string