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
- Cachez agressivement - Les prestations changent rarement, mettez-les en cache côté serveur
- Filtrez côté serveur - N'exposez que les prestations publiques
- Optimisez les images - Si vous affichez des images, utilisez un CDN
- Prévoyez le fallback - Affichez un message d'erreur si l'API est indisponible
- Invalidez le cache - Prévoyez un moyen de rafraîchir les données manuellement
Aller plus loin
- Créer un dossier - Permettre l'inscription en ligne
- Pagination - Gérer un grand nombre de prestations
- Référence API - Documentation complète