Story 3.4 - Page projet individuelle: - Breadcrumb, header avec badges technologies - Boutons "Voir en ligne" / "GitHub" - Sections: Contexte, Solution, Travail d'équipe - Galerie screenshots, sidebar durée - Navigation retour + CTA contact Story 3.5 - Projets secondaires: - Section "Autres projets" sur /projets - Template project-card-compact.php - Format liste avec lien externe direct Story 3.6 - Optimisation images: - Fonction projectImage() avec <picture> WebP + fallback JPG - Dimensions explicites (400x225, 800x450, 1200x675) - Lazy loading configurable Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
144 lines
3.8 KiB
PHP
144 lines
3.8 KiB
PHP
<?php
|
|
/**
|
|
* Fonctions helpers du portfolio
|
|
*/
|
|
|
|
/**
|
|
* Inclut un template avec des données
|
|
* @param string $name Nom du template (sans .php)
|
|
* @param array $data Variables à passer au template
|
|
*/
|
|
function include_template(string $name, array $data = []): void
|
|
{
|
|
extract($data);
|
|
include __DIR__ . "/../templates/{$name}.php";
|
|
}
|
|
|
|
/**
|
|
* Charge et parse un fichier JSON
|
|
* @param string $filename Nom du fichier dans le dossier data/
|
|
* @return array Données décodées ou tableau vide en cas d'erreur
|
|
*/
|
|
function loadJsonData(string $filename): array
|
|
{
|
|
$path = __DIR__ . "/../data/{$filename}";
|
|
|
|
if (!file_exists($path)) {
|
|
error_log("JSON file not found: {$filename}");
|
|
return [];
|
|
}
|
|
|
|
$content = file_get_contents($path);
|
|
$data = json_decode($content, true);
|
|
|
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
error_log("JSON parse error in {$filename}: " . json_last_error_msg());
|
|
return [];
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Récupère tous les projets
|
|
* @return array Liste des projets
|
|
*/
|
|
function getProjects(): array
|
|
{
|
|
$data = loadJsonData('projects.json');
|
|
return $data['projects'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Récupère les projets par catégorie
|
|
* @param string $category Catégorie (vedette|secondaire)
|
|
* @return array Projets filtrés
|
|
*/
|
|
function getProjectsByCategory(string $category): array
|
|
{
|
|
return array_filter(getProjects(), fn($p) => $p['category'] === $category);
|
|
}
|
|
|
|
/**
|
|
* Récupère un projet par son slug
|
|
* @param string $slug Slug du projet
|
|
* @return array|null Projet ou null si non trouvé
|
|
*/
|
|
function getProjectBySlug(string $slug): ?array
|
|
{
|
|
$projects = getProjects();
|
|
foreach ($projects as $project) {
|
|
if ($project['slug'] === $slug) {
|
|
return $project;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Récupère les technologies uniques de tous les projets
|
|
* @return array Liste triée des technologies
|
|
*/
|
|
function getAllTechnologies(): array
|
|
{
|
|
$technologies = [];
|
|
foreach (getProjects() as $project) {
|
|
foreach ($project['technologies'] ?? [] as $tech) {
|
|
if (!in_array($tech, $technologies)) {
|
|
$technologies[] = $tech;
|
|
}
|
|
}
|
|
}
|
|
sort($technologies);
|
|
return $technologies;
|
|
}
|
|
|
|
/**
|
|
* Génère le HTML pour une image projet optimisée
|
|
* Utilise <picture> pour WebP avec fallback JPG
|
|
*
|
|
* @param string $filename Nom du fichier image (ex: project-thumb.webp)
|
|
* @param string $alt Texte alternatif
|
|
* @param int $width Largeur en pixels
|
|
* @param int $height Hauteur en pixels
|
|
* @param bool $lazy Activer le lazy loading (défaut: true)
|
|
* @param string $class Classes CSS additionnelles
|
|
* @return string HTML de l'image
|
|
*/
|
|
function projectImage(string $filename, string $alt, int $width, int $height, bool $lazy = true, string $class = ''): string
|
|
{
|
|
$alt = htmlspecialchars($alt, ENT_QUOTES, 'UTF-8');
|
|
$class = htmlspecialchars($class, ENT_QUOTES, 'UTF-8');
|
|
$lazyAttr = $lazy ? 'loading="lazy"' : '';
|
|
|
|
// Détermine les chemins WebP et fallback
|
|
$basePath = '/assets/img/projects/';
|
|
$webpFile = $filename;
|
|
|
|
// Si le fichier n'est pas .webp, on essaie de trouver la version .webp
|
|
if (!str_ends_with($filename, '.webp')) {
|
|
$webpFile = preg_replace('/\.(jpg|jpeg|png)$/i', '.webp', $filename);
|
|
}
|
|
|
|
// Fallback: remplace .webp par .jpg
|
|
$fallbackFile = str_replace('.webp', '.jpg', $webpFile);
|
|
|
|
// Image par défaut si fichier manquant
|
|
$defaultImage = $basePath . 'default-project.svg';
|
|
|
|
return <<<HTML
|
|
<picture>
|
|
<source srcset="{$basePath}{$webpFile}" type="image/webp">
|
|
<img
|
|
src="{$basePath}{$fallbackFile}"
|
|
alt="{$alt}"
|
|
width="{$width}"
|
|
height="{$height}"
|
|
{$lazyAttr}
|
|
class="{$class}"
|
|
onerror="this.onerror=null; this.src='{$defaultImage}';"
|
|
>
|
|
</picture>
|
|
HTML;
|
|
}
|