# Une billetterie affichée en Query Loop, pour un club sportif sans plugin

> Lister les prochains matchs et leurs places disponibles avec une Query Loop et des champs personnalisés, pour un club qui gère lui-même sa billetterie physique.

- Auteur : WordPress Développement
- Publié le : 2022-08-08
- Mis à jour le : 2022-08-08
- Catégorie : Éditeur de site (FSE)
- URL : https://www.wpmoderne.fr/fse/billetterie-query-loop-club-sportif/

## L’essentiel

- Un type de contenu « match » avec des champs dédiés
- Query Loop triée par date la plus proche
- Affichage conditionnel selon le stock restant

Douze matchs par saison, une buvette qui vend les billets le soir même, et un président de club qui refuse catégoriquement d'installer « encore un plugin » sur le site. C'est le point de départ de ce cas : un club de volley amateur qui veut simplement afficher, sur sa page d'accueil, la liste des prochaines rencontres avec le nombre de places encore disponibles, sans jamais gérer de paiement en ligne.

La contrainte du président est en réalité une bonne nouvelle technique. Puisqu'il n'y a pas de transaction à sécuriser, tout peut tenir dans un type de contenu personnalisé, quelques champs et une Query Loop bien réglée dans l'éditeur de site. Pas de base de données externe, pas de service tiers, juste des données que le club met lui-même à jour depuis l'administration.

## Structurer un type de contenu « match »

Le point de départ est un type de contenu personnalisé enregistré via `register_post_type()`, appelons-le `match`. Il porte quatre métadonnées suffisantes pour tout le reste :

- `match_adversaire` : le nom de l'équipe visiteuse.
- `match_date_heure` : la date et l'heure de la rencontre.
- `match_places_totales` : la capacité de la salle.
- `match_places_vendues` : le nombre de billets déjà écoulés à la buvette.

Ces champs sont déclarés avec `register_post_meta()`, en réglant `show_in_rest` à `true` pour qu'ils soient disponibles côté éditeur et, plus tard, dans les blocs de liaison si besoin.

```
register_post_type( 'match', array(
    'label'        => 'Matchs',
    'public'       => true,
    'show_in_rest' => true,
    'supports'     => array( 'title', 'custom-fields' ),
    'menu_icon'    => 'dashicons-groups',
) );

foreach ( array( 'match_adversaire', 'match_date_heure', 'match_places_totales', 'match_places_vendues' ) as $champ ) {
    register_post_meta( 'match', $champ, array(
        'show_in_rest' => true,
        'single'       => true,
        'type'         => 'string',
    ) );
}
```

## Construire la Query Loop triée par proximité

> L'essentiel à retenir : Un type de contenu « match » avec des champs dédiés ; Query Loop triée par date la plus proche ; Affichage conditionnel selon le stock restant

Dans l'éditeur de site, un gabarit d'archive dédié au type `match` accueille le bloc Query Loop. Le réglage clé se trouve dans le panneau latéral : le tri doit se faire sur la date la plus proche, ce qui suppose de trier sur la métadonnée `match_date_heure` plutôt que sur la date de publication de l'article, souvent sans rapport avec la date réelle du match.

Comme l'interface native ne propose pas nativement de tri par métadonnée personnalisée dans le panneau du bloc Query Loop, il faut passer par le filtre `pre_get_posts`, appliqué uniquement sur ce gabarit d'archive :

```
add_action( 'pre_get_posts', function( $query ) {
    if ( is_admin() || ! $query->is_main_query() ) {
        return;
    }
    if ( is_post_type_archive( 'match' ) ) {
        $query->set( 'meta_key', 'match_date_heure' );
        $query->set( 'orderby', 'meta_value' );
        $query->set( 'order', 'ASC' );
    }
} );
```

## Afficher les places restantes dans le bloc Post Template

À l'intérieur du bloc Post Template de la Query Loop, on ajoute un bloc de champ personnalisé (ou une liaison de bloc, selon la version de WordPress utilisée) qui calcule les places restantes. Le calcul lui-même, une simple soustraction entre places totales et places vendues, peut être exposé via un filtre qui transforme l'affichage brut de la métadonnée :

```
add_filter( 'render_block_core/post-terms', function( $contenu, $bloc ) {
    // Exemple simplifié : on suppose un bloc dédié « places restantes »
    return $contenu;
}, 10, 2 );
```

En pratique, la solution la plus simple pour un club qui ne veut aucune dépendance reste un court-code minimal ou un bloc dynamique enregistré côté thème, qui lit les deux métadonnées et affiche un badge « complet » lorsque la différence atteint zéro.

### Gérer visuellement le cas « complet »

Un match affiché sans indication de disponibilité pousse les spectateurs à se déplacer inutilement. Une condition simple dans le rendu du bloc dynamique change la couleur du badge selon le nombre de places restantes, en s'appuyant sur les classes utilitaires déjà présentes dans le fichier `theme.json` plutôt que sur du CSS ad hoc.

## Ce que cette approche ne couvre pas

Il faut être honnête avec le club : cette solution n'inclut aucun paiement en ligne, aucune réservation nominative, aucune génération de billet avec code-barres. C'est une vitrine d'information, pas une billetterie complète. Si le club souhaite un jour vendre en ligne, il faudra basculer vers une solution e-commerce dédiée, ce qui sort du périmètre traité ici.

| Besoin | Couvert ici |
| --- | --- |
| Afficher la liste des matchs | Oui |
| Afficher les places restantes | Oui |
| Vendre un billet en ligne | Non |
| Générer un billet nominatif | Non |

## En résumé

Un club qui refuse d'installer une extension supplémentaire peut tout à fait obtenir une vitrine fonctionnelle avec seulement un type de contenu, quatre champs personnalisés et une Query Loop triée finement. La limite est claire : dès qu'une transaction financière entre en jeu, cette architecture atteint son plafond et il faut changer d'outil.
