Page 1 sur 2

Générer un planning sous forme de tableau HTML

Publié : 09 août 2009, 00:22
par ouckileou
Edit 04/04/2013: mise à jour avec gestion de plusieurs salles: post405966.html#p405966

Hello

j'avais besoin de ça pour un outil de gestion de cours, et j'ai beau avoir bien cherché, je n'ai rien trouvé sur le net. Juste un truc qui à partir d'une liste de cours, avec heure début/ heure fin vous génère un beau tableau HTML avec les colspan qui vont bien, et des couleurs de fond de cellule.

Donc j'ai fait une petite classe rapidos, que je partage avec vous. C'est fait un peu à la va-vite, ça pourrait être carrément plus paramétrable et mieux construit (là une classe Planning qui fait toutes les manips, une classe de contenu de cellule et une classe Mapper qui fait le lien entre votre objet cours et mon objet contenu de cellule) mais bon déjà ça marche et ça fait ce que je voulais. Ah, c'est pas trop documenté non plus (mais bon ça va, y'a que 3 fichiers dont 1 presque inutile) et il reste des bouts d'Anglais dedans (mais pas beaucoup). Peut-être que c'est pas bien écrit aussi, parceque moi vous savez le PHP, j'en fais jamais :lol:

Voici le code, si vous voyez des petits bugs, des petites améliorations possibles et que vous voulez les partager, n'hésitez pas. Par exemple, il faudrait gérer les formats d'heures, si possible dans le mapper pas dans l'objet de contenu. Mais bon, ça si c'est vous êtes motivés. Allez c'est parti.

Planning.php
<?php
class Planning {
	private	$joursFr = Array(0=>'Dimanche', 1=>'Lundi', 2=>'Mardi', 3=>'Mercredi', 4=>'Jeudi', 5=>'Vendredi', 6=>'Samedi');
	
	private $jourDebut;		// jour de début du planning (0 à 6)
	private $jourFin;		// jour de fin du planning

	private $heureDebut; 	// heure de début de chaque jour (en minutes)
	private $heureFin;		// heure de fin de chaque jour (en minutes)

	private $pas; 			// durée d'une case (en minutes)
	private $minutesKeys;
	
	private $contenu;		// contenu général du planning (tableau de PlanningCellule)

	private $tabSemaine;	// stockage des données (tableau initialisé avec des cellules vides)

	const htmlSpace = '&nbsp;';
	const htmlEmptyCell = '<td>&nbsp;</td>';
	const htmlCellOpen = '<td>';
	const htmlCellClose = '</td>';
	const htmlRowOpen = '<tr>';
	const htmlRowClose = '</tr>';
	const htmlTableOpen = '<table class="tabPlanning">';
	const htmlTableClose = '</table>';
	
	const separateurHeure = 'h';

	public function __construct($jourDebut=1, $jourFin=5, $heureDebut=540, $heureFin=1260, $pas=30, $contenu = Array()){
		$this->jourDebut = $jourDebut;
		$this->jourFin = $jourFin;
		$this->heureDebut = $heureDebut;
		$this->heureFin = $heureFin;
		$this->pas = $pas;
		$this->contenu = $contenu;
		
		$this->initTableauSemaine($this->contenu);
		// $this->debugPHPArrays();
		$this->insererContenus($contenu);
	}
	
	/**
	 * Génère un tableau dont les clés sont les heures de début de chaque case (en minutes)
	 * Serviront à identifier facilement chaque case du planning
	 * @return unknown_type
	 */
	private function genererMinutesKeys() {
		$keys = Array();
		for ($key=$this->heureDebut; $key<=$this->heureFin; $key+=$this->pas) {
			$keys[] = $key;
		}
		$this->keys = $keys;
		return $keys;
	}

	/**
	 * Génère un tableau correspondant à un jour
	 * @return unknown_type
	 */
	private function initTableauJour() {
		if ($this->pas != 0) {
			$numCells = ($this->heureDebut - $this->heureFin) / $this->pas;
		} else {
			echo 'pas == 0 !!';
		}
		$keys = $this->genererMinutesKeys();
		$tabJour = array_fill_keys($keys, self::htmlEmptyCell);
		return $tabJour;
	}

	private  function initTableauSemaine() {
		$this->tabSemaine = Array();
		$tabJour = $this->initTableauJour();
		for($i=$this->jourDebut; $i<=$this->jourFin; $i++) {
			$this->tabSemaine[$i] = $tabJour;
		}
	}
	
	private function getNumeroCellule($minutesDebut, $minutesFin) {
		return ($minutesFin - $minutesDebut) / $this->pas;
	}
	
	/**
	 * Insère tous les contenus de cellulés envoyés
	 * @param $contenuPlanning
	 * @return unknown_type
	 */
	private function insererContenus($contenuPlanning) {
		foreach ($contenuPlanning as $contenuCellule) {
			$this->insererContenu($contenuCellule);
		}
	}
	
	/**
	 * Insère le contenu d'une cellule précise
	 * @param $contenuCellule
	 * @return unknown_type
	 */
	private function insererContenu($contenuCellule) {
		// ajout de la cellule fusionnée
		$duree = $this->getNumeroCellule($contenuCellule->heureDebut, $contenuCellule->heureFin);
		$contenu = $contenuCellule->contenu.'<br />';
		$contenu .= $this->convertMinutesEnHeuresMinutes($contenuCellule->heureDebut);
		$contenu .= ' - '.$this->convertMinutesEnHeuresMinutes($contenuCellule->heureFin);
		
		$this->tabSemaine[$contenuCellule->numJour][$contenuCellule->heureDebut] = $this->genererCelluleHTML($contenu, $duree, '', $contenuCellule->bgColor);
		
		// suppression du contenu suivant
		$key = $contenuCellule->heureDebut;
		for ($cpt = $duree-1; $cpt>0; $cpt--) {
			$key += $this->pas;
			$this->tabSemaine[$contenuCellule->numJour][$key] = '';			
		}
	}
	
	/* Affichage */	
	public function debugPHPArrays() {
		echo '<pre>';
		print_r($this->tabSemaine);
		echo '</pre>';		
	}
	
	public function genererHtmlTable() {
		$htmlTable = self::htmlTableOpen;

		$htmlTable .= $this->genererBandeauJours();
		
		$key = $this->heureDebut;
		$keyEnd = $this->heureFin;
		for ($key; $key <= $keyEnd; $key+=$this->pas) {
			$htmlTable .= self::htmlRowOpen;
			$htmlTable .= '<td class="cellHour">'.$this->convertMinutesEnHeuresMinutes($key).'</td>';
			foreach ($this->tabSemaine as $tabHeures) {
				$htmlTable .= $tabHeures[$key];
			}
			$htmlTable .= self::htmlRowClose;
		}
		
		$htmlTable .= self::htmlTableClose;
		return $htmlTable;
	}
	
	public function afficherHtmlTable() {
		echo $this->genererHtmlTable();
	}
	
	private function genererBandeauJours() {
		$daysLine = self::htmlRowOpen;
		$daysLine .= $this->genererCelluleHTML(self::htmlSpace);
		$day = $this->jourDebut;
		while ($day <= $this->jourFin) {
			$daysLine .= $this->genererCelluleHTML($this->jourFr($day), '', 'cellDay');
			$day++;
		}
		$daysLine .= self::htmlRowClose;
		return $daysLine;
	}
	
	/**
	 * Génère une ligne HTML contenant le libellé des jours utilisés dans le planning
	 * @param $contenuCellule
	 * @param $colspan
	 * @param $class
	 * @param $bgColor
	 * @return unknown_type
	 */
	private function genererCelluleHTML($contenuCellule, $colspan = '', $class = '', $bgColor = '') {
		$celluleHTML = '<td';
		if (!empty($colspan)) 
			$celluleHTML .= ' rowspan="'.$colspan.'"';			
		if (!empty($class)) 
			$celluleHTML .= ' class="'.$class.'"';
		if (!empty($bgColor)) 
			$celluleHTML .= ' bgcolor="'.$bgColor.'"';
		$celluleHTML .= '/>';
		$celluleHTML .= $contenuCellule;
		$celluleHTML .= '</td>';
		return $celluleHTML;
	}
	
	/**
	 * Renvoie le libellé d'un jour en Français
	 * @param $dayNum
	 * @return unknown_type
	 */
	private function jourFr($dayNum) {
		return $this->joursFr[$dayNum];
	}

	private function convertMinutesEnHeuresMinutes($minutes) {
		$heure = floor($minutes / 60);
		$minutes = ($minutes % 60);
		$minutes = str_pad($minutes, 2, '0', STR_PAD_LEFT);
		return ($heure. self::separateurHeure .$minutes);
	}
}
?>
PlanningCellule
<?php
class PlanningCellule {
	private $data;
	
	public function __construct($numJour, $heureDebut, $heureFin, $bgColor, $contenu) {
		$this->numJour = $numJour;
		$this->heureDebut = $heureDebut;
       	$this->heureFin = $heureFin;
       	$this->bgColor = $bgColor;
       	$this->contenu = $contenu;
    }
    
    public function __set($name, $value) {
    	if ($name == 'heureDebut' || $name == 'heureFin') {
    		$tabHeure = explode(':', $value);
    		$value = (int)$tabHeure[0];
    		if ($tabHeure[1] == 30)
    			$value += 0.5;
    		$value = $value*60;
    	}
        $this->data[$name] = $value;
    }

    public function __get($name) {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }

        $trace = debug_backtrace();
        trigger_error(
            'Propriété non-définie via __get(): ' . $name .
            ' dans ' . $trace[0]['file'] .
            ' à la ligne ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }
    
    public function __toString() {
    	$str = 'heure début : '.$this->heureDebut."<br />\n";
    	$str .= 'heure fin : '.$this->heureFin."<br />\n";
    	$str .= 'couleur : '.$this->bgColor."<br />\n";
    	$str .= 'contenu : '.$this->contenu."<br />\n";
    	return $str;
    }
}
?>
PlanningMapper
<?php
Class PlanningMapper {
	// instance de la classe
	private static $instance;

	// Un constructeur privé ; empêche la création directe d'objet
	private function __construct() { }

	// La méthode singleton
	public static function getInstance() {
		if (!isset(self::$instance)) {
			$c = __CLASS__;
			self::$instance = new $c;
		}
		return self::$instance;
	}

	public function genererPlanningCellule($cours) {
		// $lienNiveau = UrlHelper::link_to($cours->getAlphaNiveaux()->getLibelleNiveau(), 'niveaux/listApprenantsParNiveau/idNiveau'.$cours->getAlphaNiveaux()->getIdNiveau());
		$contenuCellule = '<b>'.$cours->getAlphaFormateurs()->getPrenomFormateur().'</b><br />'
		.$cours->getAlphaNiveaux()->getLibelleNiveau();

		$planningContent = new PlanningCellule($cours->getJour(),
		$cours->getHeureDebut(),
		$cours->getHeureFin(),
		$cours->getAlphaNiveaux()->getCodeCouleur(),
		$contenuCellule);
		return $planningContent;
	}

	public function __clone() {
		trigger_error('Le clônage n\'est pas autorisé.', E_USER_ERROR);
	}
}

?>
Une démo rapide (qui ne fait pas appel au mapper, que j'utilise dans mon appli symfony):
<html>
<head>
	<title>Démo planning dynamique</title>
	<link rel="stylesheet" type="text/css" media="screen" href="planning.css" />
<body>
<div id="planning">
<?php
include('Planning.php');
include('PlanningCellule.php');
include('PlanningMapper.php');

$contenusCellules[] = new PlanningCellule(1,'17:30:00','19:00:00','#008000','<b>Zeus</b><br />B1/1');
$contenusCellules[] = new PlanningCellule(1,'19:30:00','21:00:00','#7CCAF4','<b>Ryle</b><br />A2');
$contenusCellules[] = new PlanningCellule(2,'12:30:00','14:00:00','#7CCAF4','<b>Mère-Térésa</b><br />A2');
$contenusCellules[] = new PlanningCellule(2,'14:00:00','15:30:00','#314E8C','<b>Truc</b><br />A1');
$contenusCellules[] = new PlanningCellule(2,'16:00:00','17:30:00','#314E8C','<b>Albat</b><br />A1');
$contenusCellules[] = new PlanningCellule(2,'17:30:00','19:00:00','#C080C0','<b>Calimero</b><br />B3');
$contenusCellules[] = new PlanningCellule(3,'10:30:00','12:00:00','#800080','<b>Damien</b><br />B2');
$contenusCellules[] = new PlanningCellule(3,'13:00:00','14:30:00','#314E8C','<b>Cobaye</b><br />A1');
$contenusCellules[] = new PlanningCellule(3,'14:30:00','16:00:00','#80C080','<b>Moosh</b><br />B1/2');
$contenusCellules[] = new PlanningCellule(3,'16:00:00','17:30:00','#008000','<b>@rthur</b><br />B1/1');
$contenusCellules[] = new PlanningCellule(3,'18:30:00','20:00:00','#80C080','<b>Dunbar</b><br />B1/2');
$contenusCellules[] = new PlanningCellule(3,'20:00:00','21:30:00','#7CCAF4','<b>Ryle</b><br />A2');
$contenusCellules[] = new PlanningCellule(4,'11:00:00','12:30:00','#314E8C','<b>AB</b><br />A1');
$contenusCellules[] = new PlanningCellule(4,'14:00:00','15:30:00','#7CCAF4','<b>Hywan</b><br />A2');
$contenusCellules[] = new PlanningCellule(4,'17:00:00','18:30:00','#7CCAF4','<b>Sekiltoyai</b><br />A2');
$contenusCellules[] = new PlanningCellule(4,'18:30:00','20:00:00','#800080','<b>Genova</b><br />B2');
$contenusCellules[] = new PlanningCellule(4,'20:00:00','21:30:00','#008000','<b>Ouckileou</b><br />B1/1');
$contenusCellules[] = new PlanningCellule(5,'09:00:00','10:30:00','#314E8C','<b>Nagol</b><br />A1');
$contenusCellules[] = new PlanningCellule(5,'18:00:00','19:30:00','#80C080','<b>Fab</b><br />B1/2');
$contenusCellules[] = new PlanningCellule(6,'10:30:00','12:00:00','#80C080','<b>Naholyr</b><br />B1/2');



$planning = new Planning(1, 6, 540, 1260, 30, $contenusCellules);
$planning->afficherHtmlTable();
?>
</div>
</body>
</html>
La CSS qui va avec :

Code : Tout sélectionner

/* PLANNING */ #planning { padding-left: 0; margin-left: 210px; overflow: auto; } #planning table, #planning tr, #planning td { text-align: center; border-width:1px; border-style:solid; border-color:black; } td.cellDay { width: 120px; font-weight: bold; } td.cellHour { width: 70px; vertical-align: top; height: 30px; font-weight: bold; }
J'attache une capture au message.
Voilà, c'est pas le grand art de la programmation, mais ça peut toujours aider ;)

Re: Générer un planning sous forme de tableau HTML

Publié : 09 août 2009, 00:35
par ouckileou
Hum... je vois que le Google Bot est dans ce forum juste après avoir posté mon message, donc je cherche "Planning+PHP" dessus, et là forcément je trouve plein de trucs, notamment un forum spécial "calendriers" sur developpez.com : http://www.developpez.net/forums/f572/p ... alendrier/


|*()

Re: Générer un planning sous forme de tableau HTML

Publié : 25 juil. 2010, 14:56
par texaff
Salut,

je me suis inscrit sur ce site juste pour te remercier... c'est exactement ce que je cherchais, tu me fais économiser beaucoup de temps!


MERCI !

Re: Générer un planning sous forme de tableau HTML

Publié : 22 sept. 2010, 11:15
par albat
<parenthèse>
Nagol à 9h du matin... J'en ris encore ! :lol: ;)
</parenthèse>

Re: Générer un planning sous forme de tableau HTML

Publié : 26 nov. 2010, 19:07
par eristyl
Salut,
Je suis entrain de me monter un petit site dans lequel j'ai inséré ton script et je voulais savoir s'il était possible que le planning commence a 8h du matin et termine a 22h. Car j'ai quand même essayé, mais je n'y arrive pas (même en modifiant les informations passé au constructeur :shock: ).
De plus j'aimerais savoir si il est possible de diviser les cases pas par quart d'heure mais par demi heure. :roll:

Merci d'avance :mrgreen:

Re: Générer un planning sous forme de tableau HTML

Publié : 27 nov. 2010, 16:12
par ouckileou
Salut,

ben étendre les jours si ça devrait être possible en changeant les paramètres, je peux voir comment tu fais ?

Pour diviser les cases par quart d'heure pareil ça doit être possible en changeant le $pas dans l'appel à Planning, et après peut-être qu'il y a des choses à chanegr dans le contenu, je ne sais plus. Il faudrait vraiment refaire ce truc un peu plus propre, j'avoue :)

Re: Générer un planning sous forme de tableau HTML

Publié : 30 nov. 2010, 13:09
par eristyl
Merci de ta réponse,

Premièrement j'ai essaye de modifier les information passe en paramètre au constructeur comme ci-dessous.

Code : Tout sélectionner

public function __construct($jourDebut=1, $jourFin=5, $heureDebut=480, $heureFin=1320, $pas=15, $contenu = Array()){
Puis en faisant un peu d'affichage dans le code je me suis rendu compte que la valeur de l'heure de début d'une journée se réinitialisé a 9h00 "tout seul" :shock: .
Ensuite pour le "pas" j'ai a peu près le même problème.

Pour moi le constructeur construit le tableau avec les paramètre qu'on lui fournis et non sont plus modifie par la suite (Si tout fonctionne bien :wink: ).

Du coup je suis un peu perdu :?

Re: Générer un planning sous forme de tableau HTML

Publié : 30 nov. 2010, 13:19
par devlop78
Peut-être que lors de l'instanciation, les arguments du constructeur sont redéfinies ... Ce qui serait assez logique vue sa conception

Re: Générer un planning sous forme de tableau HTML

Publié : 30 nov. 2010, 16:00
par ouckileou
Le constructeur a des valeurs par défaut, tu n'as pas besoin de le modifier, c'est lors de l'appel que tu passes ce que tu veux.
Par exemple en reprenant le code de mon message, avec ceci j'obtiens bien un tableau du dimanche au samedi, de 8h à 22h, avec des cases de 15 min:
$planning = new Planning(0, 6, 480, 1320, 15, $contenusCellules);

Re: Générer un planning sous forme de tableau HTML

Publié : 30 nov. 2010, 16:16
par devlop78
Nickel eristyl a sa réponse

Re: Générer un planning sous forme de tableau HTML

Publié : 30 nov. 2010, 16:17
par devlop78
Héhé je suis passé de éléphanteau à éléphant ... Trop fier !

=D>

:arrow:

Re: Générer un planning sous forme de tableau HTML

Publié : 30 nov. 2010, 20:40
par eristyl
Nikel ! :D merci beaucoup

Re: Générer un planning sous forme de tableau HTML

Publié : 08 déc. 2010, 17:19
par masss777
Salut a toi, je me demandais s'il était possible et surtout savoir comment faire pour pouvoir générer ce même emploi du temps sur plusieurs semaines voir année.
Du genre avoir deux onglets > semaine suivante, semaine précédentes .
Dans l'attente d'une reponse ;)

Re: Générer un planning sous forme de tableau HTML

Publié : 09 déc. 2010, 15:43
par ouckileou
À la base ce n'est pas prévu pour, c'est vraiment pour faire un planning hebdomadaire, en partant du principe qu'il ne change pas.

Après tu peux toujours faire plusieurs appels, dans ton affichage afficher date début/date fin, et faire les onglets toi-même, car après tout ça n'est que de la mise en forme ce truce.

Si l'envie te prend de récupérer le code, le modifier pour le rendre modulable n'hésite pas :)

Re: Générer un planning sous forme de tableau HTML

Publié : 29 déc. 2010, 16:34
par masss777
Re, j'ai utiliser la majeur partis de ton code mais je butte sur la classe planning cellule , je ne comprend pas comment sont employé les fonctions__get et __set et ce qu elles font ;)
Merci :p