Aller au contenu principal

Afficher vos prestations sur votre site

Ce guide vous montre comment récupérer et afficher vos prestations téo sur votre site internet.

Cas d'usage

Vous souhaitez :

  • Afficher votre catalogue de formations sur votre site
  • Synchroniser automatiquement les informations (tarifs, descriptions, dates)
  • Créer une page "Nos formations" dynamique

Prérequis

  • Vos identifiants API (voir Authentification)
  • Un site web avec capacité de faire des appels API (backend ou SSR)

Récupérer les prestations

Endpoint

GET https://teoapp.fr/api/production/services
Authorization: Bearer {access_token}
Accept: application/ld+json

Exemple de réponse

{
"@context": "/api/contexts/Service",
"@id": "/api/production/services",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/api/production/services/abc123-...",
"@type": "Service",
"id": "abc123-def456-...",
"name": "Bilan de compétences",
"description": "Accompagnement personnalisé pour faire le point...",
"duration": 24,
"price": 2500.00,
"isActive": true
},
{
"@id": "/api/production/services/def456-...",
"@type": "Service",
"id": "def456-ghi789-...",
"name": "Formation Excel Avancé",
"description": "Maîtrisez les fonctions avancées d'Excel...",
"duration": 14,
"price": 1200.00,
"isActive": true
}
],
"hydra:totalItems": 15
}

Implémentation

Avec cache (recommandé)

Les prestations changent rarement. Mettez-les en cache pour optimiser les performances.

// services/teo.js
const CACHE_DURATION = 3600 * 1000; // 1 heure
let servicesCache = { data: null, expiresAt: 0 };

async function getServices() {
// Retourner le cache si valide
if (servicesCache.data && Date.now() < servicesCache.expiresAt) {
return servicesCache.data;
}

const token = await getAccessToken();

const response = await fetch('https://teoapp.fr/api/production/services', {
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/ld+json'
}
});

const data = await response.json();
const services = data['hydra:member'].filter(s => s.isActive);

// Mettre en cache
servicesCache = {
data: services,
expiresAt: Date.now() + CACHE_DURATION
};

return services;
}

Affichage React

import { useState, useEffect } from 'react';

function ServicesList() {
const [services, setServices] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetch('/api/services')
.then(res => res.json())
.then(data => {
setServices(data);
setLoading(false);
});
}, []);

if (loading) return <div>Chargement...</div>;

return (
<div className="services-grid">
{services.map(service => (
<article key={service.id} className="service-card">
<h3>{service.name}</h3>
<p>{service.description}</p>
<div className="service-meta">
<span>{service.duration}h</span>
<span>{service.price.toLocaleString('fr-FR')}</span>
</div>
<a href={`/inscription?service=${service.id}`}>
S'inscrire
</a>
</article>
))}
</div>
);
}

Affichage PHP

<?php
// Récupérer les services (avec cache fichier)
function getServices(): array
{
$cacheFile = sys_get_temp_dir() . '/teo_services.json';
$cacheTime = 3600; // 1 heure

if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTime) {
return json_decode(file_get_contents($cacheFile), true);
}

$token = getAccessToken();

$ch = curl_init('https://teoapp.fr/api/production/services');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer $token",
'Accept: application/ld+json'
]
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

$services = array_filter(
$response['hydra:member'],
fn($s) => $s['isActive']
);

file_put_contents($cacheFile, json_encode($services));

return $services;
}
?>

<div class="services-grid">
<?php foreach (getServices() as $service): ?>
<article class="service-card">
<h3><?= htmlspecialchars($service['name']) ?></h3>
<p><?= htmlspecialchars($service['description']) ?></p>
<div class="service-meta">
<span><?= $service['duration'] ?>h</span>
<span><?= number_format($service['price'], 2, ',', ' ') ?></span>
</div>
<a href="/inscription?service=<?= $service['id'] ?>">
S'inscrire
</a>
</article>
<?php endforeach; ?>
</div>

Filtrer les prestations

Par type/catégorie

L'API supporte les filtres via query parameters :

GET /api/production/services?isActive=true&category=formation

Côté serveur

const services = await getServices();

// Filtrer par durée
const shortServices = services.filter(s => s.duration <= 14);

// Filtrer par prix
const affordableServices = services.filter(s => s.price < 1000);

// Trier par nom
services.sort((a, b) => a.name.localeCompare(b.name));

Afficher les prochaines sessions

Pour afficher les prochaines dates de formation, récupérez les groupes associés :

async function getUpcomingSessions(serviceId) {
const token = await getAccessToken();

const response = await fetch(
`https://teoapp.fr/api/production/groups?service=${serviceId}&status=OPENED`,
{
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/ld+json'
}
}
);

const data = await response.json();

return data['hydra:member']
.filter(g => new Date(g.startDate) > new Date())
.sort((a, b) => new Date(a.startDate) - new Date(b.startDate));
}

Affichage avec sessions

function ServiceCard({ service }) {
const [sessions, setSessions] = useState([]);

useEffect(() => {
fetch(`/api/services/${service.id}/sessions`)
.then(res => res.json())
.then(setSessions);
}, [service.id]);

return (
<article className="service-card">
<h3>{service.name}</h3>
<p>{service.description}</p>

{sessions.length > 0 && (
<div className="upcoming-sessions">
<h4>Prochaines sessions</h4>
<ul>
{sessions.slice(0, 3).map(session => (
<li key={session.id}>
{new Date(session.startDate).toLocaleDateString('fr-FR')}
{session.location && ` - ${session.location}`}
</li>
))}
</ul>
</div>
)}

<a href={`/inscription?service=${service.id}`}>
S'inscrire
</a>
</article>
);
}

SEO et performances

Génération statique (SSG)

Pour un meilleur SEO, générez les pages au build plutôt qu'au runtime :

// pages/formations/[id].js (Next.js)
export async function getStaticPaths() {
const services = await getServices();

return {
paths: services.map(s => ({ params: { id: s.id } })),
fallback: 'blocking'
};
}

export async function getStaticProps({ params }) {
const service = await getService(params.id);
const sessions = await getUpcomingSessions(params.id);

return {
props: { service, sessions },
revalidate: 3600 // Régénérer toutes les heures
};
}

Données structurées

Ajoutez des données structurées pour améliorer le référencement :

<script type="application/ld+json">
{JSON.stringify({
"@context": "https://schema.org",
"@type": "Course",
"name": service.name,
"description": service.description,
"provider": {
"@type": "Organization",
"name": "Votre organisme",
"url": "https://votre-site.fr"
},
"offers": {
"@type": "Offer",
"price": service.price,
"priceCurrency": "EUR"
}
})}
</script>

Bonnes pratiques

  1. Cachez agressivement - Les prestations changent rarement, mettez-les en cache côté serveur
  2. Filtrez côté serveur - N'exposez que les prestations publiques
  3. Optimisez les images - Si vous affichez des images, utilisez un CDN
  4. Prévoyez le fallback - Affichez un message d'erreur si l'API est indisponible
  5. Invalidez le cache - Prévoyez un moyen de rafraîchir les données manuellement

Aller plus loin