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
$entityManager read-only
private
EntityManagerInterface
$entityManager
$serializer
private
SerializerInterface
$serializer
$validator read-only
private
ValidatorInterface
$validator
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.