BCard - Documentation technique

OrderApiController extends AbstractController
in package

Contrôleur d'API des commandes de cartes physiques.

Il couvre la consultation des commandes de l'utilisateur authentifié (liste paginée et filtrable par statut, détail incluant le suivi de livraison), la création d'une commande à partir d'un panier d'articles référencés au catalogue, et l'annulation d'une commande encore en attente. Chaque ligne de commande est renvoyée avec un détail de prix recalculé à la volée (prix de base de la carte, options de couleur et d'impression, prix unitaire et total de ligne).

Préfixe de route : /api/orders. Toutes les actions exigent IS_AUTHENTICATED_FULLY (jeton JWT bearerAuth), avec dérogation ROLE_ADMIN sur les contrôles de propriété.

Attributes
#[Route]
'/api/orders'
$name: 'api_orders_'
#[Tag]
$name: 'Orders'
$description: 'Gestion des commandes'

Table of Contents

Properties

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

Methods

__construct()  : mixed
cancel()  : JsonResponse
Annule une commande encore en attente.
create()  : JsonResponse
Crée une commande pour l'utilisateur authentifié.
list()  : JsonResponse
Retourne la liste paginée des commandes de l'utilisateur authentifié.
show()  : JsonResponse
Retourne le détail d'une commande.
formatPrice()  : string
Met en forme un montant pour la charge utile JSON.
priceToFloat()  : float
Convertit un prix stocké sous forme de chaîne en nombre flottant.

Properties

Methods

__construct()

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

Gestionnaire d'entités Doctrine, utilisé pour persister les commandes et leurs modifications.

$serializer : SerializerInterface

Sérialiseur Symfony (injecté ; les charges utiles sont construites à la main).

$validator : ValidatorInterface

Validateur appliqué à l'entité Order avant persistance.

cancel()

Annule une commande encore en attente.

public cancel(int $id, Request $request, OrderRepository $orderRepository) : JsonResponse

Après contrôle de propriété (avec dérogation ROLE_ADMIN), l'annulation n'est autorisée que si le statut courant est Order::EN_ATTENTE : tout autre statut est refusé. Le motif est repris du corps JSON s'il est fourni, sinon il vaut « Annulation demandée par le client ». Le statut passe à Order::ANNULER, le motif et l'horodatage d'annulation sont enregistrés. Aucun remboursement n'est déclenché par cette action.

Route : /api/orders/{id}/cancel (POST), nom api_orders_cancel. Requiert IS_AUTHENTICATED_FULLY.

Corps attendu (optionnel) : {"reason": "string — motif d'annulation"}

Réponses : 200 commande annulée, 400 commande dont le statut n'est pas « en attente », 403 commande appartenant à un autre utilisateur (hors administrateur), 404 commande inexistante, 401 requête non authentifiée.

Parameters
$id : int

Identifiant de la commande à annuler.

$request : Request

Requête HTTP dont le corps JSON peut porter le motif.

$orderRepository : OrderRepository

Dépôt utilisé pour retrouver la commande.

Attributes
#[IsGranted]
'IS_AUTHENTICATED_FULLY'
#[Post]
$path: '/api/orders/{id}/cancel'
$description: 'Annule une commande en attente'
$summary: 'Annule une commande'
$security: [['bearerAuth' => []]]
$requestBody: new OA\RequestBody(required: false, content: new OA\JsonContent(properties: [new OA\Property(property: 'reason', type: 'string', example: 'Changement d\'avis')]))
$tags: ['Orders']
$parameters: [new OA\Parameter(name: 'id', description: 'ID de la commande à annuler', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
$responses: [new OA\Response(response: 200, description: 'Commande annulée avec succès', content: new OA\JsonContent(properties: [new OA\Property(property: 'message', type: 'string', example: 'Commande annulée avec succès')])), new OA\Response(response: 400, description: 'Impossible d\'annuler cette commande'), new OA\Response(response: 404, description: 'Commande non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
#[Route]
'/{id}/cancel'
$name: 'cancel'
$methods: ['POST']
Return values
JsonResponse

Message de confirmation, ou message d'erreur sous la clé error.

create()

Crée une commande pour l'utilisateur authentifié.

public create(Request $request, CardRepository $cardRepository) : JsonResponse

Contrôle d'abord la présence non vide des champs firstname, lastname, email, phone et items (le message d'erreur produit contient une accolade parasite), puis vérifie que items est bien un tableau non vide. Chaque article doit porter card_id et quantity ; la carte est chargée au catalogue et rejetée si elle est inexistante ou masquée. La quantité est ramenée à 1 au minimum. Le total est calculé à partir du seul prix de la carte : les options de couleur et d'impression ne sont ni lues ni facturées à la création, alors qu'elles sont prises en compte à la lecture. La commande est validée avant persistance, les lignes étant écrites en cascade avec elle.

Route : /api/orders (POST), nom api_orders_create. Requiert IS_AUTHENTICATED_FULLY.

Corps attendu : {"firstname": "string — obligatoire", "lastname": "string — obligatoire", "email": "string — obligatoire", "phone": "string — obligatoire", "items": "list<{card_id: int, quantity: int}> — obligatoire, au moins un élément", "compagny_name": "string — optionnel", "address_to_delivery": "string — optionnel", "order_notes": "string — optionnel"}

Réponses : 201 commande créée ({message, order_id, total}), 400 champ obligatoire manquant ou vide, 400 items absent ou non tableau, 400 card_id ou quantity manquant sur un article, 400 carte inexistante ou masquée, 400 violations de contraintes ({"errors": [...]}), 401 requête non authentifiée.

Parameters
$request : Request

Requête HTTP dont le corps JSON décrit la commande et ses articles.

$cardRepository : CardRepository

Dépôt utilisé pour résoudre et valider les cartes commandées.

Attributes
#[IsGranted]
'IS_AUTHENTICATED_FULLY'
#[Post]
$path: '/api/orders'
$description: 'Crée une nouvelle commande pour l\'utilisateur connecté'
$summary: 'Crée une nouvelle commande'
$security: [['bearerAuth' => []]]
$requestBody: new OA\RequestBody(required: true, content: new OA\JsonContent(required: ['firstname', 'lastname', 'email', 'phone', 'items'], 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: 'phone', type: 'string', example: '+33123456789'), new OA\Property(property: 'compagny_name', type: 'string', example: 'Ma Société'), new OA\Property(property: 'address_to_delivery', type: 'string', example: '123 Rue Example'), new OA\Property(property: 'order_notes', type: 'string', example: 'Notes spéciales'), new OA\Property(property: 'items', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'card_id', type: 'integer', example: 1), new OA\Property(property: 'quantity', type: 'integer', example: 2)]))]))
$tags: ['Orders']
$responses: [new OA\Response(response: 201, description: 'Commande créée avec succès', content: new OA\JsonContent(properties: [new OA\Property(property: 'message', type: 'string', example: 'Commande créée avec succès'), new OA\Property(property: 'order_id', type: 'integer', example: 1), new OA\Property(property: 'total', type: 'string', example: '99.99')])), new OA\Response(response: 400, description: 'Données invalides'), new OA\Response(response: 401, description: 'Non authentifié')]
#[Route]
''
$name: 'create'
$methods: ['POST']
Return values
JsonResponse

Message de confirmation avec l'identifiant et le total de la commande, ou erreurs.

list()

Retourne la liste paginée des commandes de l'utilisateur authentifié.

public list(Request $request, OrderRepository $orderRepository) : JsonResponse

Les résultats sont restreints à l'utilisateur courant et triés par created_at décroissant. page est ramené à 1 au minimum et limit est borné à l'intervalle [1, 50] (défaut 10) ; le filtre status n'est appliqué que s'il est fourni et non vide, sans validation de la valeur. Chaque ligne de commande est recalculée à l'affichage via OrderApiController::priceToFloat() et OrderApiController::formatPrice() : le prix unitaire additionne le prix de la carte et ceux des options de couleur et d'impression, le total de ligne multipliant ce résultat par la quantité. Les champs address_to_delivery et order_notes sont renvoyés bien qu'absents du schéma OpenApi déclaré, et le champ price documenté au niveau des lignes n'est pas présent ici.

Route : /api/orders (GET), nom api_orders_list. Requiert IS_AUTHENTICATED_FULLY.

Paramètres de requête : status (optionnel), page (optionnel, défaut 1), limit (optionnel, défaut 10, plafonné à 50).

Réponses : 200 liste renvoyée (éventuellement vide), 401 requête non authentifiée.

Parameters
$request : Request

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

$orderRepository : OrderRepository

Dépôt utilisé pour la recherche et le comptage.

Attributes
#[Get]
$path: '/api/orders'
$description: 'Récupère la liste des commandes de l\'utilisateur connecté'
$summary: 'Liste les commandes de l\'utilisateur'
$security: [['bearerAuth' => []]]
$tags: ['Orders']
$parameters: [new OA\Parameter(name: 'status', description: 'Filtrer par statut de commande', in: 'query', required: false, schema: new OA\Schema(type: 'string', enum: ['En Attente', 'En traitement', 'Terminée', 'Remboursée', 'Annulée'])), 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))]
$responses: [new OA\Response(response: 200, description: 'Liste des commandes récupérée avec succès', content: new OA\JsonContent(properties: [new OA\Property(property: 'orders', 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: 'compagny_name', type: 'string', example: 'Ma Société'), new OA\Property(property: 'total', type: 'string', example: '99.99'), new OA\Property(property: 'status', type: 'string', example: 'En Attente'), new OA\Property(property: 'created_at', type: 'string', format: 'date-time'), new OA\Property(property: 'paymentMethod', type: 'string', example: 'CREDIT_CART'), new OA\Property(property: 'orderItems', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'id', type: 'integer'), new OA\Property(property: 'quantity', type: 'integer'), new OA\Property(property: 'price', type: 'string'), new OA\Property(property: 'card', properties: [new OA\Property(property: 'id', type: 'integer'), new OA\Property(property: 'name', type: 'string'), new OA\Property(property: 'price', 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é')]
#[IsGranted]
'IS_AUTHENTICATED_FULLY'
#[Route]
''
$name: 'list'
$methods: ['GET']
Return values
JsonResponse

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

show()

Retourne le détail d'une commande.

public show(int $id, OrderRepository $orderRepository) : JsonResponse

La commande est chargée par sa clé primaire, puis un contrôle de propriété est appliqué : seul le titulaire, ou un porteur de ROLE_ADMIN, peut la consulter. Par rapport à OrderApiController::list(), la charge utile ajoute le type de produit sur chaque carte, un champ price correspondant au prix unitaire formaté, ainsi que les informations d'annulation (cancellationReason, cancelledAt) et de livraison (tracking_number, shipping_carrier, shipped_at, delivered_at).

Attention : cette route est déclarée avec un {id} sans exigence numérique.

Route : /api/orders/{id} (GET), nom api_orders_show. Requiert IS_AUTHENTICATED_FULLY.

Réponses : 200 commande renvoyée, 403 commande appartenant à un autre utilisateur (hors administrateur), 404 commande inexistante, 401 requête non authentifiée.

Parameters
$id : int

Identifiant de la commande, issu du chemin.

$orderRepository : OrderRepository

Dépôt utilisé pour retrouver la commande.

Attributes
#[Get]
$path: '/api/orders/{id}'
$description: 'Récupère les détails d\'une commande spécifique'
$summary: 'Récupère une commande par son ID'
$security: [['bearerAuth' => []]]
$tags: ['Orders']
$parameters: [new OA\Parameter(name: 'id', description: 'ID de la commande', in: 'path', required: true, schema: new OA\Schema(type: 'integer'))]
$responses: [new OA\Response(response: 200, description: 'Détails de la commande', 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: 'compagny_name', type: 'string', example: 'Ma Société'), new OA\Property(property: 'total', type: 'string', example: '99.99'), new OA\Property(property: 'status', type: 'string', example: 'En Attente'), new OA\Property(property: 'created_at', type: 'string', format: 'date-time'), new OA\Property(property: 'paymentMethod', type: 'string', example: 'CREDIT_CART'), new OA\Property(property: 'orderItems', type: 'array', items: new OA\Items(properties: [new OA\Property(property: 'id', type: 'integer'), new OA\Property(property: 'quantity', type: 'integer'), new OA\Property(property: 'price', type: 'string'), new OA\Property(property: 'card', type: 'object')]))])), new OA\Response(response: 404, description: 'Commande non trouvée'), new OA\Response(response: 403, description: 'Accès refusé')]
#[IsGranted]
'IS_AUTHENTICATED_FULLY'
#[Route]
'/{id}'
$name: 'show'
$methods: ['GET']
Return values
JsonResponse

Objet JSON décrivant la commande, ses lignes et son suivi, ou message d'erreur.

formatPrice()

Met en forme un montant pour la charge utile JSON.

private formatPrice(float $amount) : string

Méthode utilitaire interne : le montant est formaté avec exactement deux décimales, un point comme séparateur décimal et aucun séparateur de milliers, afin de rester exploitable tel quel par les clients. Aucune route n'est associée.

Parameters
$amount : float

Montant à formater.

Return values
string

Représentation décimale du montant, par exemple « 99.99 ».

priceToFloat()

Convertit un prix stocké sous forme de chaîne en nombre flottant.

private priceToFloat(string|null $price) : float

Méthode utilitaire interne : la valeur est d'abord découpée de ses espaces de bordure et une chaîne vide (ou null) donne 0.0. Les espaces internes, utilisés comme séparateurs de milliers, sont supprimés et la virgule décimale est convertie en point avant le transtypage. Aucune route n'est associée.

Parameters
$price : string|null

Prix brut tel que stocké en base, éventuellement formaté à la française.

Return values
float

Montant exploitable pour les calculs, ou 0.0 si la valeur est absente ou vide.


        
On this page

Search results