ITSkillsCenter
WordPress

Tutoriel : Créer un site de petites annonces avec WordPress

11 دقائق للقراءة

Créer un site de petites annonces avec WordPress : guide technique complet

Un site de petites annonces est l’un des projets WordPress les plus rentables en Afrique de l’Ouest. Que ce soit pour l’immobilier à Dakar, les véhicules d’occasion, l’emploi ou les services, le modèle fonctionne parce que les gens cherchent activement ces plateformes. Ce guide vous montre comment construire un site type Expat-Dakar ou CoinAfrique avec WordPress, étape par étape.

1. Choisir le bon plugin de petites annonces

Plugin Prix Points forts Limites
ClassiPress (thème AppThemes) ~69 $ Solution tout-en-un, paiements intégrés, système de prix par catégorie Design daté, mises à jour lentes
Flavor flavor flavor flavor flavor flavor flavor flavor Gratuit + Pro 149 $/an Moderne, compatible Elementor, système de paquets d’annonces Version gratuite limitée
AWP Classifieds Gratuit + modules payants Léger, modulaire, bon pour démarrer Moins de fonctionnalités natives
Solution Custom (CPT + ACF) Gratuit Contrôle total, performance optimale Requiert du développement

Recommandation pour le Sénégal : la solution custom avec CPT + ACF Pro vous donne le contrôle total et les meilleures performances sur les connexions mobiles. C’est ce que nous allons construire.

2. Créer le Custom Post Type « Annonce »

Ajoutez dans functions.php de votre thème enfant :

// Enregistrer le CPT Annonce
add_action('init', function() {
    register_post_type('annonce', [
        'labels' => [
            'name' => 'Annonces',
            'singular_name' => 'Annonce',
            'add_new' => 'Publier une annonce',
            'add_new_item' => 'Nouvelle annonce',
            'edit_item' => 'Modifier l\'annonce',
            'view_item' => 'Voir l\'annonce',
            'search_items' => 'Rechercher une annonce',
        ],
        'public' => true,
        'has_archive' => true,
        'rewrite' => ['slug' => 'annonces'],
        'supports' => ['title', 'editor', 'thumbnail', 'author'],
        'menu_icon' => 'dashicons-megaphone',
        'show_in_rest' => true,
    ]);

    // Taxonomie : Catégorie d'annonce
    register_taxonomy('categorie_annonce', 'annonce', [
        'labels' => [
            'name' => 'Catégories',
            'singular_name' => 'Catégorie',
        ],
        'hierarchical' => true,
        'rewrite' => ['slug' => 'catégorie'],
        'show_in_rest' => true,
    ]);

    // Taxonomie : Localisation
    register_taxonomy('localisation', 'annonce', [
        'labels' => [
            'name' => 'Localisations',
            'singular_name' => 'Localisation',
        ],
        'hierarchical' => true,
        'rewrite' => ['slug' => 'lieu'],
        'show_in_rest' => true,
    ]);
});

// Catégories par défaut
add_action('init', function() {
    $catégories = [
        'Immobilier' => ['Appartements', 'Maisons', 'Terrains', 'Bureaux'],
        'Véhicules' => ['Voitures', 'Motos', 'Pièces détachées'],
        'Emploi' => ['Offres d\'emploi', 'Demandes d\'emploi', 'Stages'],
        'Électronique' => ['Téléphones', 'Ordinateurs', 'TV & Audio'],
        'Services' => ['Cours particuliers', 'Artisans', 'Transport'],
    ];
    
    foreach ($catégories as $parent => $children) {
        if (!term_exists($parent, 'categorie_annonce')) {
            $term = wp_insert_term($parent, 'categorie_annonce');
            $parent_id = $term['term_id'];
            foreach ($children as $child) {
                if (!term_exists($child, 'categorie_annonce')) {
                    wp_insert_term($child, 'categorie_annonce', 
                      ['parent' => $parent_id]);
                }
            }
        }
    }
    
    // Localisations Sénégal
    $villes = ['Dakar' => ['Plateau', 'Almadies', 'Parcelles Assainies', 
      'Guédiawaye', 'Pikine', 'Rufisque'], 'Thiès' => [], 
      'Saint-Louis' => [], 'Ziguinchor' => [], 'Kaolack' => []];
    
    foreach ($villes as $ville => $quartiers) {
        if (!term_exists($ville, 'localisation')) {
            $term = wp_insert_term($ville, 'localisation');
            foreach ($quartiers as $q) {
                if (!term_exists($q, 'localisation')) {
                    wp_insert_term($q, 'localisation', 
                      ['parent' => $term['term_id']]);
                }
            }
        }
    }
}, 20);

3. Champs personnalisés avec ACF

Installez ACF (Advanced Custom Fields) et créez ces champs pour le CPT « annonce » :

Champ Type ACF Options
Prix Number Suffixe : « FCFA », minimum : 0
Type d’annonce Select Vente, Location, Don, Échange
État Select Neuf, Occasion – Bon état, Occasion – État moyen
Téléphone Text Placeholder : « +221 7X XXX XX XX »
WhatsApp True/False Joignable sur WhatsApp
Galerie photos Gallery Max 8 images, taille max 2 Mo
Urgent True/False Annonce urgente (mise en avant)

4. Template d’affichage des annonces

CSS pour les cartes d’annonces

/* Grille d'annonces responsive */
.annonces-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
  padding: 20px 0;
}

.annonce-card {
  background: #fff;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(0,0,0,0.08);
  transition: transform 0.2s, box-shadow 0.2s;
  position: relative;
}

.annonce-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 8px 25px rgba(0,0,0,0.12);
}

.annonce-card__image {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.annonce-card__badge {
  position: absolute;
  top: 12px;
  left: 12px;
  background: #e94560;
  color: #fff;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 12px;
  font-weight: 600;
}

.annonce-card__badge--urgent {
  background: #ff6b35;
  animation: pulse 2s infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}

.annonce-card__body {
  padding: 15px;
}

.annonce-card__title {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 8px;
  color: #1a1a2e;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.annonce-card__price {
  font-size: 20px;
  font-weight: 700;
  color: #e94560;
  margin: 0 0 8px;
}

.annonce-card__meta {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  color: #666;
}

.annonce-card__location::before {
  content: "📍 ";
}

.annonce-card__whatsapp {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: #25D366;
  color: #fff;
  padding: 10px 20px;
  border-radius: 8px;
  text-decoration: none;
  font-weight: 600;
  width: 100%;
  justify-content: center;
  margin-top: 12px;
}

/* Mobile : 1 colonne */
@media (max-width: 480px) {
  .annonces-grid {
    grid-template-columns: 1fr;
    gap: 15px;
    padding: 10px;
  }
  .annonce-card__image { height: 180px; }
}

Template PHP (archive-annonce.php)

<?php get_header(); ?>

<div class="annonces-container" style="max-width:1200px; margin:0 auto; padding:20px;">
  <h1>Petites Annonces</h1>
  
  <!-- Barre de recherche -->
  <form class="annonces-search" method="GET" action="<?php echo get_post_type_archive_link('annonce'); ?>">
    <input type="text" name="s" placeholder="Que recherchez-vous ?" 
      value="<?php echo esc_attr(get_search_query()); ?>"
      style="flex:1; padding:12px; border:2px solid #ddd; border-radius:8px;">
    <input type="hidden" name="post_type" value="annonce">
    
    <?php wp_dropdown_categories([
      'taxonomy' => 'categorie_annonce',
      'name' => 'catégorie',
      'show_option_all' => 'Toutes les catégories',
      'hide_empty' => false,
    ]); ?>
    
    <?php wp_dropdown_categories([
      'taxonomy' => 'localisation',
      'name' => 'lieu',
      'show_option_all' => 'Toute localisation',
      'hide_empty' => false,
    ]); ?>
    
    <button type="submit">Rechercher</button>
  </form>

  <div class="annonces-grid">
    <?php while (have_posts()) : the_post(); 
      $prix = get_field('prix');
      $type = get_field('type_annonce');
      $urgent = get_field('urgent');
      $location = wp_get_post_terms(get_the_ID(), 'localisation');
      $whatsapp = get_field('whatsapp');
      $téléphone = get_field('téléphone');
    ?>
    <article class="annonce-card">
      <?php if ($urgent) : ?>
        <span class="annonce-card__badge annonce-card__badge--urgent">URGENT</span>
      <?php elseif ($type) : ?>
        <span class="annonce-card__badge"><?php echo esc_html($type); ?></span>
      <?php endif; ?>
      
      <?php if (has_post_thumbnail()) : ?>
        <img class="annonce-card__image" src="<?php the_post_thumbnail_url('medium_large'); ?>" 
          alt="<?php the_title_attribute(); ?>" loading="lazy">
      <?php endif; ?>
      
      <div class="annonce-card__body">
        <h3 class="annonce-card__title">
          <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
        </h3>
        <?php if ($prix) : ?>
          <p class="annonce-card__price"><?php echo number_format($prix, 0, ',', ' '); ?> FCFA</p>
        <?php endif; ?>
        <div class="annonce-card__meta">
          <span class="annonce-card__location">
            <?php echo $location ? esc_html($location[0]->name) : 'Sénégal'; ?>
          </span>
          <span><?php echo human_time_diff(get_the_time('U'), current_time('timestamp')) . ' ago'; ?></span>
        </div>
        <?php if ($whatsapp && $téléphone) : ?>
          <a class="annonce-card__whatsapp" 
            href="https://wa.me/<?php echo preg_replace('/[^0-9]/', '', $téléphone); ?>?text=Bonjour, je suis intéressé par : <?php echo urlencode(get_the_title()); ?>">
            Contacter via WhatsApp
          </a>
        <?php endif; ?>
      </div>
    </article>
    <?php endwhile; ?>
  </div>
  
  <?php the_posts_pagination(['mid_size' => 2]); ?>
</div>

<?php get_footer(); ?>

5. Soumission d’annonces en front-end

Permettez aux utilisateurs de publier des annonces sans accéder au back-office :

// Shortcode [formulaire_annonce]
add_shortcode('formulaire_annonce', function() {
    if (!is_user_logged_in()) {
        return '<div class="alert">
          <a href="' . wp_login_url(get_permalink()) . '">Connectez-vous</a> 
          pour publier une annonce.</div>';
    }
    
    // Traitement du formulaire
    if (isset($_POST['annonce_submit']) && wp_verify_nonce($_POST['annonce_nonce'], 'publier_annonce')) {
        $post_id = wp_insert_post([
            'post_title' => sanitize_text_field($_POST['titre']),
            'post_content' => wp_kses_post($_POST['description']),
            'post_type' => 'annonce',
            'post_status' => 'pending', // Modération avant publication
            'post_author' => get_current_user_id(),
        ]);
        
        if ($post_id) {
            update_field('prix', intval($_POST['prix']), $post_id);
            update_field('type_annonce', sanitize_text_field($_POST['type']), $post_id);
            update_field('téléphone', sanitize_text_field($_POST['téléphone']), $post_id);
            update_field('whatsapp', isset($_POST['whatsapp']), $post_id);
            
            // Gérer l'upload de la photo
            if (!empty($_FILES['photo']['name'])) {
                require_once ABSPATH . 'wp-admin/includes/image.php';
                require_once ABSPATH . 'wp-admin/includes/file.php';
                require_once ABSPATH . 'wp-admin/includes/media.php';
                $attach_id = media_handle_upload('photo', $post_id);
                if (!is_wp_error($attach_id)) {
                    set_post_thumbnail($post_id, $attach_id);
                }
            }
            
            wp_set_object_terms($post_id, intval($_POST['catégorie']), 'categorie_annonce');
            wp_set_object_terms($post_id, intval($_POST['localisation']), 'localisation');
            
            return '<div class="alert-success">Annonce soumise ! 
              Elle sera visible après validation par notre équipe.</div>';
        }
    }
    
    ob_start(); ?>
    <form method="post" enctype="multipart/form-data" class="form-annonce">
      <?php wp_nonce_field('publier_annonce', 'annonce_nonce'); ?>
      
      <div class="form-group">
        <label>Titre de l'annonce *</label>
        <input type="text" name="titre" required maxlength="100" 
          placeholder="Ex: iPhone 13 Pro 128Go">
      </div>
      
      <div class="form-row">
        <div class="form-group">
          <label>Prix (FCFA) *</label>
          <input type="number" name="prix" required min="0" 
            placeholder="Ex: 350000">
        </div>
        <div class="form-group">
          <label>Type</label>
          <select name="type">
            <option value="Vente">Vente</option>
            <option value="Location">Location</option>
            <option value="Don">Don</option>
          </select>
        </div>
      </div>
      
      <div class="form-group">
        <label>Description *</label>
        <textarea name="description" required rows="5" 
          placeholder="Décrivez votre article en détail..."></textarea>
      </div>
      
      <div class="form-group">
        <label>Photo principale</label>
        <input type="file" name="photo" accept="image/*">
        <small>Max 2 Mo. Formats : JPG, PNG, WebP</small>
      </div>
      
      <div class="form-row">
        <div class="form-group">
          <label>Téléphone *</label>
          <input type="tel" name="téléphone" required placeholder="+221 7X XXX XX XX">
        </div>
        <div class="form-group">
          <label><input type="checkbox" name="whatsapp"> Joignable sur WhatsApp</label>
        </div>
      </div>
      
      <button type="submit" name="annonce_submit" class="btn-submit">
        Publier mon annonce
      </button>
    </form>
    <?php return ob_get_clean();
});

6. Monétisation du site d’annonces

Les modèles économiques qui fonctionnent au Sénégal :

Annonces premium payantes

// Système de mise en avant payante via PayDunya
add_action('wp_ajax_promouvoir_annonce', function() {
    $post_id = intval($_POST['post_id']);
    $duree = intval($_POST['duree']); // jours
    
    $tarifs = [
        7 => 2000,   // 2 000 FCFA / semaine
        30 => 5000,  // 5 000 FCFA / mois
        90 => 12000, // 12 000 FCFA / trimestre
    ];
    
    $montant = $tarifs[$duree] ?? $tarifs[7];
    
    // Créer la facture PayDunya
    $invoice = [
        'amount' => $montant,
        'description' => 'Mise en avant annonce #' . $post_id . ' - ' . $duree . ' jours',
        'callback_url' => home_url('/paydunya-callback/'),
        'return_url' => get_permalink($post_id),
        'custom_data' => ['post_id' => $post_id, 'duree' => $duree],
    ];
    
    // Après paiement confirmé via callback :
    // update_post_meta($post_id, 'annonce_premium', true);
    // update_post_meta($post_id, 'premium_expiry', date('Y-m-d', strtotime("+$duree days")));
});

Modèle de revenus recommandé

  • Annonces gratuites : publication basique, visibilité normale
  • Annonce Urgente : 1 000 FCFA — badge « URGENT » + mise en avant 3 jours
  • Annonce Premium : 2 000 FCFA/semaine — affichée en tête des résultats
  • Pack Pro : 15 000 FCFA/mois — 10 annonces premium + logo vendeur
  • Bannières publicitaires : vendez des espaces aux entreprises locales

7. Optimisations pour le marché sénégalais

  • WhatsApp partout : le bouton WhatsApp est le principal canal de contact. Intégrez-le sur chaque annonce
  • Images compressées : limitez les uploads à 2 Mo et convertissez en WebP automatiquement (plugin ShortPixel ou Imagify)
  • Lazy loading : chargez les images uniquement quand elles apparaissent à l’écran (attribut loading="lazy")
  • Cache agressif : les pages de listing changent souvent, mais les pages d’annonces individuelles peuvent être mises en cache 1h
  • SMS de notification : utilisez l’API Orange SMS pour notifier les vendeurs quand quelqu’un contacte via le formulaire
  • Paiement mobile : intégrez Wave et Orange Money pour les annonces premium (via PayDunya ou PayTech)

8. SEO pour les petites annonces

// Schema.org pour chaque annonce
add_action('wp_head', function() {
    if (is_singular('annonce')) {
        $prix = get_field('prix');
        $location = wp_get_post_terms(get_the_ID(), 'localisation');
        $schema = [
            '@context' => 'https://schema.org',
            '@type' => 'Product',
            'name' => get_the_title(),
            'description' => wp_strip_all_tags(get_the_excerpt()),
            'image' => get_the_post_thumbnail_url(get_the_ID(), 'large'),
            'offers' => [
                '@type' => 'Offer',
                'price' => $prix ?: 0,
                'priceCurrency' => 'XOF',
                'availability' => 'https://schema.org/InStock',
                'areaServed' => $location ? $location[0]->name : 'Dakar, Sénégal',
            ],
        ];
        echo '<script type="application/ld+json">' . 
          json_encode($schema, JSON_UNESCAPED_UNICODE) . '</script>';
    }
});

Avec cette architecture, vous avez un site de petites annonces complet, monétisable et adapté au marché sénégalais. Le front-end est mobile-first, les paiements passent par le mobile money, et WhatsApp sert de canal de communication principal entre acheteurs et vendeurs.

Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité