Freelance Expert Technique Magento - PHP + JS : Créer un calendrier interactif avec jQuery

CONTACT
RSS

PHP + JS : Créer un calendrier interactif avec jQuery

Nous allons ici voir comment réaliser un calendrier interactif dans le type de celui proposé par Google Calendar. Je vous préviens quand même, ça va être relativement long et complexe. J’ai réalisé ceci dans le cadre d’un projet et je vous propose ici un turoriel sur une version “Lite”. Le but est d’obtenir un calendrier interactif répondant aux actions suivantes : déplacer des événements, redimensionner des événements (par le bas), Cliquer sur un événement (Popup d’info) et double cliquer dans un espace vide (Créer un événement). Chacune de ces actions entrainera également un enregistrement des modifications dans la base de données.

Vous pouvez ci-dessous voir une démonstration de ce que vous allons obtenir. La démonstration est uniquement composée de Javascript, css et Html, l’enregistrement des actions effectuées sur les événements n’est pas effective. Les sources du Contrôleur, de la vue associée et du javascript sont disponibles tout en bas du tutoriel (Les sources ci-dessous sont celles de la démo).

Ce tutoriel se décompose en 5 étapes :

  1. Principes de base
  2. Base de données
  3. Contrôleur
  4. Vue
  5. Ajax

Étape 1. Les principes de base

Voici comment fonctionne la bête. Nous allons créer un tableau avec une seule ligne et 7 colonnes (sans compter la 1ere ligne de titre)

<table>
  <th>
    <!-- Les en-têtes -->
  </th>
  <tr>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</table>

Dans chacune des colonnes, nous allons afficher les événement de la journée y correspondant. Le positionnement des événements se fera en respectant les principes suivants:

  1. 10px = 15 minutes
  2. Taillé d’un élément = 10px * sa durée en quart d’heure
  3. Positionnement initial d’un élément = 10px * ( heure de départ en minute à partir de 0h00 * 15)

Dans un premier temps, nous allons attaquer la construction du tableau de données qui nous servira à construire le rendu visuel de nos événements au sein de notre Calendrier.

Étape 2. La partie BDD

Pour stocker nos événements, nous allons utiliser la table suivante :

CREATE TABLE IF NOT EXISTS `evenement` (
  `evenement_id` int(30) NOT NULL AUTO_INCREMENT,
  `evenement_titre` varchar(255) COLLATE utf8_bin NOT NULL,
  `evenement_contenu` text COLLATE utf8_bin NOT NULL,
  `evenement_debut` int(30) NOT NULL,
  `evenement_fin` int(30) NOT NULL,
  `evenement_lieu` varchar(255) COLLATE utf8_bin NOT NULL,
  `evenement_idUser` int(30) NOT NULL,
  PRIMARY KEY (`evenement_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=16 ;

Je ne pense pas qu’il soit nécessaire de préciser l’utilité de chacun des champs, leurs noms sont assez explicites. Attaquons maintenant la partie PHP !

Étape 3. La partie Contrôleur

J’ai développé ce module sous Zend Framework, je vais donc ici également respecter le modèle MVC pour construire mon tableau de données. Le but est d’obtenir un tableau de la forme suivante :

Array
(
    [0] => Array
        (
            [class] => other_day
            [day_start] => 1261954800
            [nb] => 0
            [tsu] => 0
        )

    [1] => Array
        (
            [class] => other_day
            [day_start] => 1262041200
            [events] => Array
                (
                    [0] => Array
                        (
                            [id] => 6
                            [titre] => (Sans titre)
                            [lieu] => (Inconnu)
                            [debut] => 1262055600
                            [fin] => 1262062800
                            [qhoccured] => 8
                            [size] => 80
                            [qhstart] => 16
                        )

                )

            [nb] => 1
            [tsu] => 80
        )
)

[Index du tableau] (de 0 à 6) : Les jours de la semaine

  • [class] : current_day ou other_day (Jour actuel ou non)
  • [day_start] : Timestamp de départ du jour
  • [events]: Array() regroupant les événements de la journée
    • [id] : Identifiant de l’événement
    • [titre] : Titre de l’événement
    • [lieu] : Lieu
    • [debut] : Timestamp de départ
    • [fin] : Timestamp de fin
    • [qhoccurend] : Durée de l’événement en Quart d’heure
    • [size] : Taille de l’événement en Pixels
    • [qhstart] : Quart d’heure de départ
  • [nb] : Nombre d’événements dans la journée
  • [tsu] (Total size used) : Taille (en Pixel) totale utilisée par les événements

Voilà pour le tableau à obtenir, je détaillerai l’utilité de chacun de ces éléments quand nous traiterons la construction visuelle du Calendrier. Maintenant, attelons nous à la construction de ce fameux tableau. Voici comment nous allons procéder :

  1. L’Action (la fonction) reçoit en paramètre la date d’une journée (Sous forme de 3 paramètres d,m et y). si aucune date n’est envoyée, on prend la date actuelle.
  2. On construit le timestamp de cette date qu’on envoie à une fonction.
  3. La fonction nous retourne le timestamp de départ (0h00) du premier jour de la semaine contenant la journée.
  4. Nous calculons le timestamp de fin de la semaine.
  5. Nous récupérons les événements dont la date de début est supérieure ou égale à celle du début de la semaine cible et dont la date de fin est inférieure ou égale à celle de fin de la semaine.
  6. Nous construisons un premier tableau, contenant les en-têtes qui serviront aux th du table
    de notre Calendrier.
  7. On construit notre tableau de données (structure vue précédemment).
  8. On envoie les deux tableaux à notre vue.

Allez hop, on met les mains dans le cambouis, on attaque le code en lui-même !

- Réception des paramètres et envoie à la fonction :

function indexAction() {
       /*calcul du timestamp équivalent à 7 jours (moins 1 sec, pour ne pas déborder sur la semaine suivate)
        $sept_jours=(7*24*60*60)-1;

        if(!$this->_request->getParam('d')) {
            $jour=date("d",time());
            $mois=date("m",time());
            $an=date("Y",time());
        }else {
            $jour=$this->_request->getParam('d');
            $mois=$this->_request->getParam('m');
            $an=$this->_request->getParam('y');
        }

        $timestamp_concerne=mktime(0, 0, 0, $mois, $jour, $an);
        $semaine_debut = $this->find_first_day_of_week($timestamp_concerne);

Comme vous le voyez, si je n’ai pas reçu de paramètres spécifiant la volonté d’afficher une autre semaine que celle actuelle, je prend la date actuelle. Puis j’envoie le tout à ma fonction.

- Récupérer le début de la semaine

Pour récupérer le début de la semaine, j’ai construit une petite fonction.

</span>
<pre>function find_first_day_of_week($timestamp, $start_of_week='monday') {
    $day=date('D',$target_week);
    switch(strtoupper($day)) {
        case "MON":$day_left=0;
            break;
        case "TUE":$day_left=1;
            break;
        case "WED":$day_left=2;
            break;
        case "THU":$day_left=3;
            break;
        case "FRI":$day_left=4;
            break;
        case "SAT":$day_left=5;
            break;
        case "SUN":$day_left=6;
            break;
        default:$day_left=0;
            break;
    }
    $one_day=24*60*60;
    $first_day = $target_week - ($one_day * $day_left);
    return $first_day;
}

La fonction reçoit en paramètre le timestamp,  elle prend le chiffre correspondant au jour, puis y retire de 0 à 6 jour pour obtenir le premier jour de la semaine.

- Calcul fin de la semaine et récupération des événements

Maintenant que nous avons le jour de début de la semaine, nous allons pouvoir trouver celui de fin et ainsi récupérer tous les événement compris dans cet espace temps. Mais voyons ceci avant :

$semaine_fin = $semaine_debut + $sept_jours;
$this->view->next_week=$semaine_debut+(7*24*60*60);
$this->view->next_week_start=date("d/m/Y",$semaine_debut+(7*24*60*60));
$this->view->next_week_end=date("d/m/Y",$semaine_debut+(7*24*60*60)+(7*24*60*60)-1);

$this->view->bef_week=$semaine_debut-(7*24*60*60);
$this->view->bef_week_start=date("d/m/Y",$semaine_debut-(7*24*60*60));
$this->view->bef_week_end=date("d/m/Y",$semaine_debut-1);
$this->view->semaine_debut=$semaine_debut;
$this->view->semaine_fin=$semaine_fin;

Comme vous le voyez, je récupère la fin de ma semaine et j’envoie à ma vue une série de variables. Ces dernières serviront en fait à construire des liens renvoyant vers cette même page pour accéder à la semaine suivante et à la semaine précédente.
Maintenant, la requête pour obtenir les événements :

/*ma table evenement */
$table_evenement = new Admin_Model_Db_Evenement();
$select = $table_evenement->select()
    ->where('evenement_debut BETWEEN '.$semaine_debut.' AND '.$semaine_fin)
    ->order('evenement_debut');

$row = $table_evenement->fetchAll($select);

Je récupère tous mes événements dans la variable $row.

- Construction des en-têtes du tableau

Pour les en-têtes, je prend juste le libellé du jour ainsi que la date en version numérique (jour/mois).

$thead=array(
    "1" => array("1" => "Lu. ".date("j/m",$semaine_debut),
    "2" => $semaine_debut
    ),
    "2" => array("1" => "Ma. ".date("j/m",$semaine_debut+(1*24*60*60)),
    "2" => $semaine_debut+(1*24*60*60)
    ),
    "3" => array("1" => "Me. ".date("j/m",$semaine_debut+(2*24*60*60)),
    "2" => $semaine_debut+(2*24*60*60)
    ),
    "4" => array("1" => "Je. ".date("j/m",$semaine_debut+(3*24*60*60)),
    "2" => $semaine_debut+(3*24*60*60)
    ),
    "5" => array("1" => "Ve. ".date("j/m",$semaine_debut+(4*24*60*60)),
    "2" => $semaine_debut+(4*24*60*60)
    ),
    "6" => array("1" => "Sa. ".date("j/m",$semaine_debut+(5*24*60*60)),
    "2" => $semaine_debut+(5*24*60*60)
    ),
    "7" => array("1" => "Di. ".date("j/m",$semaine_fin),
    "2" => $semaine_fin
    )
);
/*j'envoie le tout à ma vue*/
$this->view->thead=$thead;

- Construction du tableau de contenu

Nous y voilà ! Maintenant nous allons construire notre tableau de données pour l’affichage de notre calendrier. Mais avant ça, nous allons construire, également pour l’affichage, un autre tableau comprenant les horaires de la journée (j’aurais très bien pu faire ça dans la vue, mais je le fais ici parce que je suis un ouf).

$jour_actuel_debut=mktime(0,0,0,date("m",time()),date("d",time()),date("Y",time()));
$horaires=array();
for($i=0;$i<24;$i++) {
    if($i<10)
        $horaire="0".$i."h00";
    else
        $horaire=$i."h00";

    $horaires[$i]=$horaire;
}
/*j'envoie à ma vue*/
$this->view->horaires=$horaires;

Allez, voyons de plus près la construction du tableau du contenu :

$table_content=array();
for($i=0;$i<7;$i++) {
    $debut_jour=$semaine_debut+($i*24*60*60);
    $fin_jour=$debut_jour+((1*24*60*60)-1);
    $info="";
    if($debut_jour==$jour_actuel_debut)
        $class="current_day";
    else
        $class="other_day";
    $titre="";
    $debut="";
    $fin="";
    $table_content[$i]['class']=$class;
    $table_content[$i]['day_start']=$debut_jour;
    $count=0;
    $total_size_used=0;
    foreach($row as $event) {
        if(($event->evenement_debut >= $debut_jour)
            &&($event->evenement_fin <= $fin_jour)) {
            $table_content[$i]['events'][$count]['id']=$event->evenement_id;
            $table_content[$i]['events'][$count]['titre']=$event->evenement_titre;
            $table_content[$i]['events'][$count]['lieu']=$event->evenement_lieu;
            $table_content[$i]['events'][$count]['debut']=$event->evenement_debut;
            $table_content[$i]['events'][$count]['fin']=$event->evenement_fin;
            $duree_secondes=$event->evenement_fin - $event->evenement_debut;
            $duree_heure=$duree_secondes/60/60;
            $duree_quart_heure=$duree_heure*4;
            $table_content[$i]['events'][$count]['qhoccured']=ceil($duree_quart_heure);
            $size=ceil($duree_quart_heure)*10;//10px par quart d'heure pour l'affichage
            $table_content[$i]['events'][$count]['size']=$size;
$quart_heure_journee=(date('H',$event->evenement_debut)*4)+(date('i',$event->evenement_debut)/15);
            $table_content[$i]['events'][$count]['qhstart']=ceil($quart_heure_journee);
            $count+=1;
            $total_size_used+=$size;
        }
    }
    $table_content[$i]["nb"]=$count;
    $table_content[$i]["tsu"]=$total_size_used;
}
$this->view->table_content=$table_content;

Dlénondédiou ! Qu’est ce que c’est que ce truc ? Allez, décortiquons un peu le tout.

$table_content=array();
for($i=0;$i<7;$i++) {
    $debut_jour=$semaine_debut+($i*24*60*60);
    $fin_jour=$debut_jour+((1*24*60*60)-1);
    $info="";

    if($debut_jour==$jour_actuel_debut)
        $class="current_day";
    else
        $class="other_day";

    $titre="";
    $debut="";
    $fin="";

    $table_content[$i]['class']=$class;
    $table_content[$i]['day_start']=$debut_jour;

    $count=0;
    $total_size_used=0;

Au départ, j’instancie mon tableau, puis je boucle sur les jours de la semaine, de sorte que chaque entrée principale du tableau corresponde à un jour de semaine (je n’aurais donc plus qu’à créer une colonne pour chaque entrée du tableau dans ma vue. Donc, pour chacun des jours, je récupère le timestamp de début et de fin de ce dernier. Si ce jour correspond au jour actuel, je le spécifie en le stockant dans une sous-entrée class de l’entrée du jour. De même, je stocke le timestamp du jour dans une autre sous-entrée day_start. Enfin, j’initialise mon compteur de nombre d’événement et celui de la taille totale utilisée.

foreach($row as $event) {
    if(($event->evenement_debut >= $debut_jour)
        &&($event->evenement_fin <= $fin_jour)) {

        /*je remplis les informations */
        $table_content[$i]['events'][$count]['id']=$event->evenement_id;
        $table_content[$i]['events'][$count]['titre']=$event->evenement_titre;
        $table_content[$i]['events'][$count]['lieu']=$event->evenement_lieu;
        $table_content[$i]['events'][$count]['debut']=$event->evenement_debut;
        $table_content[$i]['events'][$count]['fin']=$event->evenement_fin;

        /* je stocke le nombre de quart d'heure */
        $duree_secondes=$event->evenement_fin - $event->evenement_debut;
        $duree_heure=$duree_secondes/60/60;
        $duree_quart_heure=$duree_heure*4;
        $table_content[$i]['events'][$count]['qhoccured']=ceil($duree_quart_heure);

        /* ceil() -> arrondir entier supérieur*/
        $size=ceil($duree_quart_heure)*10;//10px par quart d'heure pour l'affichage
        /*je stocke la taille en pixel de l'événement */
        $table_content[$i]['events'][$count]['size']=$size;

        /*je recupere le quart d'heure de la journée correspondant
         * au départ de l'evenement */
        $quart_heure_journee=(date('H',$event->evenement_debut)*4)+(date('i',$event->evenement_debut)/15);
        $table_content[$i]['events'][$count]['qhstart']=ceil($quart_heure_journee);

        /* j'incrémente mes compteurs*/
        $count+=1;
        $total_size_used+=$size;
    }
}

Ensuite, je boucle sur mes événements. Pour chacun d’entre eux, je regarde si il se trouve dans l’espace temps cible. Si il s’y trouve, je stocke un certain nombre d’informations qui serviront plus tard, ainsi que le nombre de quart d’heure de la durée de l’événement, sa taille en pixel, le quart d’heure de départ, puis j’incrémente mes compteurs.

$table_content[$i]["nb"]=$count;
$table_content[$i]["tsu"]=$total_size_used;

Finalement, je stocke dans l’entrée principale du tableau en cours (la journée), le nombre d’événements s’y trouvant ainsi que la taille en pixel qu’ils occupent à eux tous.

$this->view->table_content=$table_content;

Puis j’envoie le tout à ma vue.
Tadaaaam ! Voilà, c’en est fini de la partie Php ! Je vous invite fortement à aller vous servir un café, manger un truc, fumer une clope, peu importe, car c’est loin d’être terminé ! (Je dois avouer que je prend un certain plaisir à vous torturer avec ce tutoriel). Allez, on envoie la partie traitant de la vue !

Étape 4. La partie VUE

Maintenant que nous avons généré toutes les informations nécessaires, nous allons mettre en place l’affichage de notre calendrier.

<div id="gen_new_content" title="Nouvel événement">
    <form action="">
        <label class="label_evenement" for="new_event_title">Objet : </label><input type="text" class="lab" name="new_event_title" id="new_event_title" /><br />
        <label class="label_evenement" for="new_event_lieu">Lieu : </label><input type="text" class="lab" name="new_event_lieu" id="new_event_lieu" />
    </form>
</div>
<div id="create_event"></div>
<div id="ajax_load"></div>
<div id="dialog" title="Suppression">Veuillez confirmer la suppression</div>

Dans un premier temps, nous créons des emplacements qui vont servir à générer du contenu via le Javascrit (ce que nous verrons dans l’étape 5, je ne détaille donc pas ceci pour l’instant).

<div id="switcher_agenda_options">
    <div id="choix_plage_horaire">
        <?php
        $next_week_start=$this->next_week;
        $bef_week_start=$this->bef_week;

        $day_next=date('d',$next_week_start);
        $month_next=date('m',$next_week_start);
        $year_next=date('Y',$next_week_start);

        $day_bef=date('d',$bef_week_start);
        $month_bef=date('m',$bef_week_start);
        $year_bef=date('Y',$bef_week_start);
        ?>
        <a class="info" href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","d"=>"$day_bef","m"=>"$month_bef","y"=>"$year_bef"),'default','true'); ?>"><img src="<?php echo $this->baseUrl()."/public/themes/admin/img/icons/bef_week.png";?>" alt="before" /><span><?php echo $this->bef_week_start." au ".$this->bef_week_end; ?></span></a>
        <a class="info" href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","d"=>"$day_next","m"=>"$month_next","y"=>"$year_next"),'default','true'); ?>"><img src="<?php echo $this->baseUrl()."/public/themes/admin/img/icons/next_week.png";?>" alt="next" /><span><?php echo $this->next_week_start." au ".$this->next_week_end; ?></span></a>
        <span class="semaine_en_cours">Semaine du <?php echo date('d/m/Y',$this->semaine_debut)." au ".date('d/m/Y',$this->semaine_fin);?></span>

    </div>
    <div id="switcher_agenda">
        <span class="switcher_agenda_inside selected_switch_agenda"><a href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"index"),'default','true'); ?>">Semaine</a></span>
        <span class="switcher_agenda_inside"><a href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"journee"),'default','true'); ?>">Journée</a></span>
        <span class="switcher_agenda_inside"><a href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"planning"),'default','true'); ?>">Planning</a></span>
    </div>
</div>

Nous mettons ensuite en place ce que j’appelle le switcher agenda. Il s’agit en fait de liens regroupés dans deux parties. La première partie comprend un lien vers la semaine suivante ainsi que la précédente. L’autre partie comprend des liens vers la visualisation type Journée et la visualisation type Planning. Ces deux visualisations ne sont pas traitées dans ce tutoriel, je ne vais pas non plus tout vous donner :)


<div id="calendrier">
<table id="calendar_table">
<thead>
<tr>
<th></th>
<?php foreach($this->thead as $thead):
$day_link=date('d',$thead[2]);
$month_link=date('m',$thead[2]);
$year_link=date('Y',$thead[2]);
$class_th="";
if(mktime(0,0,0,$month_link,$day_link,$year_link) == mktime(0,0,0,date('m',time()),date('d',time()),date('Y',time())))
$class_th=" style='color: #D96666;font-weight:bolder;' ";
?>
<th><a <?php echo $class_th;?> href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"journee","d"=>"$day_link","m"=>"$month_link","y"=>"$year_link"),'default','true'); ?>"><?php echo $thead[1];?></a></th>
<?php endforeach; ?>
</tr>
</thead>
<div id="calendrier">    <table id="calendar_table">        <thead>            <tr>                <th></th>                <?php foreach($this->thead as $thead):                    $day_link=date('d',$thead[2]);                    $month_link=date('m',$thead[2]);                    $year_link=date('Y',$thead[2]);
$class_th="";
if(mktime(0,0,0,$month_link,$day_link,$year_link) == mktime(0,0,0,date('m',time()),date('d',time()),date('Y',time())))                        $class_th=" style='color: #D96666;font-weight:bolder;' ";                    ?>                <th><a <?php echo $class_th;?> href="<?php echo $this->url(array("module"=>"admin","controller"=>"evenements","action"=>"journee","d"=>"$day_link","m"=>"$month_link","y"=>"$year_link"),'default','true'); ?>"><?php echo $thead[1];?></a></th>                <?php endforeach; ?>            </tr>        </thead>

Nous attaquons maintenant la mise en place du calendrier en lui-même. Nous créons d’abord son emplacement, puis le tableau. Nous mettons en place la head du tableau via notre array() de th passé préalablement à notre vue (voir Partie Contrôleur). Au passage, vous remarquerez que j’applique un style différent au th si il correspond à date actuelle.

<tbody>
    <tr>
        <td class="info_horaires">
            <?php foreach($this->horaires as $horaire):?>

                <?php echo "<div class='info_horaires_content'>".$horaire."</div>"; ?>
            <?php endforeach; ?>
        </td>

J’ouvre le corps de mon tableau ainsi qu’une première et unique ligne. Dans la première colonne, j’utilise le array() passé à ma vue pour afficher les différentes plages horaires. Nous pouvons maintenant passer à l’affichage des événements !

for($i=0;$i<$content['nb'];$i++) {
            $content_current_debut=$content['events'][$i]['debut'];
            $content_current_fin=$content['events'][$i]['fin'];
            $content_current_size=$content['events'][$i]['size'];
            $content_current_id=$content['events'][$i]['id'];
            $padding_top=$content['events'][$i]['qhstart']*10;

            echo'
<div class="calendar_event" id="'.$content_current_id.'" style="height:'.$content_current_size.'px; margin-top:'.$padding_top.'px;">
<div class="calendar_event_date" id="'.$content_current_id.'_date" >
<span id="'.$content_current_id.'_date_debut_heure">'.date('H',$content_current_debut).'</span>:
<span id="'.$content_current_id.'_date_debut_minute">'.date('i',$content_current_debut).'</span> -
<span id="'.$content_current_id.'_date_fin_heure">'.date('H',$content_current_fin).'</span>:
<span id="'.$content_current_id.'_date_fin_minute">'.date('i',$content_current_fin).'</span>
</div>
<div class="calendar_event_title" id="'.$content_current_id.'_title">'.$content['events'][$i]['titre'].'</div>
<div class="calendar_event_lieu" id="'.$content_current_id.'_lieu" style="display:none;">'.$content['events'][$i]['lieu'].'</div>
</div>';
            //$qh=intval( $content['events'][$i]['qhstart'] + $content['events'][$i]['qhoccured'] -1);
        }

Je boucle donc sur chacune des entrées de mon array() regroupant les journées auxquelles sont associés les événements (voir partie Contrôleur). Pour chaque journée, je crée donc une nouvelle colonne. Je lui affecte sa classe css (définie dans le contrôleur, si il s’agit d’un jour normal ou du jour actuel). Je boucle ensuite sur les événements contenus dans la journée.  Pour chacun d’entre eux, je leur applique une height et un margin-top en css. Je vous invite à aller jeter un coup d’oeil au css de la demo, vous verrez que les événements sont en absolute. A titre indicatif, voici le css des événements :

div.calendar_event{
    background-color:rgb(217, 102, 102);
    color:#fff;
    font-weight:bolder;
    font-size:0.8em;
    position:absolute;
    text-align:center;
}

Il ne me reste plus qu’à fermer ma ligne et mon tableau.

            </tr>
        </tbody>
    </table>
</div>

Et voilà pour l’étape traitant de la vue. Le rendu est maintenant actif, on voit nos magnifique petits événements mais on ne peut pas encore les bouger. C’est justement ce que nous allons voir dans l’étape suivante.

Etape 5. La partie AJAX

Alors, avant d’attaquer le javascript en lui-même, nous allons devoir nous retaper un peu de php. En effet, pour enregistrer les modifications effectuées sur notre calendrier, nous allons devoir charger des fonctions php dans des balises Html (De l’ajax quoi). Nous allons donc tout d’abord créer un contrôleur qui comprendra toutes les fonctions que nous appellerons par la suite.

class AjaxController extends Zend_Controller_Action {

	function init() {
		$layout = Zend_Layout::getMvcInstance();
		$layout->disableLayout();
	}

Je met donc en place mon contrôleur. Dans le init(), je desactive le layout pour éviter les doublons dans le #ajax_load du design du site. Je vais en effet, après exécution d’une fonction, afficher un petit message pour indiquer que tout s’est bien passé (comme vous l’avez surement vu dans la démo, le cadre au fond jaune). Voyons maintenant les différentes Actions de mon contrôleur.

function evenementAction() {
        $heure_debut = $this->_request->getParam('d');
        $heure_fin = $this->_request->getParam('f');
        $id = $this->_request->getParam('id');

        $lesEvents = new Admin_Model_Db_Evenement();
        $row = $lesEvents->fetchRow('evenement_id='.$id);

        $debut = mktime(date('H', $heure_debut), date('i', $heure_debut), 0, date('m', $row->evenement_debut), date('d', $row->evenement_debut), date('Y', $row->evenement_debut));

        $fin = mktime(date('H', $heure_fin), date('i', $heure_fin), 0, date('m', $row->evenement_fin), date('d', $row->evenement_fin), date('Y', $row->evenement_fin));

        $row->evenement_debut = $debut;
        $row->evenement_fin = $fin;
        $row->save();

        $this->view->info = "Modifications enregistrées.";
}

L’Action evenement sera appelée quand un événement sera déplacé. Elle reçoit en paramètre la nouvelle heure de début et la nouvelle heure de fin, converti le tout en timestamp et enregistre le tout.

function evenementresizeAction() {
        $duree = $this->_request->getParam('dur');
        $id = $this->_request->getParam('id');

        $lesEvents = new Admin_Model_Db_Evenement();
        $row = $lesEvents->fetchRow('evenement_id='.$id);
        $row->evenement_fin = $row->evenement_debut + $duree;
        $row->save();

        $this->view->info = "Horaire de fin modifiée.";
}

evenementresize gère, comme son nom l’indique, le redimensionnement d’un événement (qui ne pourra être redimensionné que par le bas). Elle reçoit donc la nouvelle durée, calcule la nouvelle heure de fin et enregistre le tout.

function evenementcreationAction() {
        $evenement = new Admin_Model_Db_Evenement();
        $row = $evenement->createRow();
        $start = $this->_request->getParam('ds');
        $end = $this->_request->getParam('de');

        $row->evenement_titre = "(Sans titre)";
        $row->evenement_contenu = "";
        $row->evenement_lieu = "(Inconnu)";
        $row->evenement_debut = $start;
        $row->evenement_fin = $end;
        $idEve = $row->save();

        $this->view->info = $idEve;
}

evenementcreation crée un événement et nous renvoie son id (que nous récupérerons pour générer le rendu visuel).

function specifyeventAction() {
        $titre = $this->_request->getParam('titre');
        $lieu = $this->_request->getParam('lieu');
        $id = $this->_request->getParam('id');

        $lesEvents = new Admin_Model_Db_Evenement();
        $row = $lesEvents->fetchRow('evenement_id='.$id);
        if (! empty($titre))
                $row->evenement_titre = $titre;
        if (! empty($lieu))
                $row->evenement_lieu = $lieu;
        $row->save();
        $this->view->info = "événement enregistré.";
}

Quand nous allons créer double-cliquer dans un espace vide, cela créera automatiquement un événement (grâce à l’action vu juste avant). Ensuite une alert javascript personnalisée apparaîtra nous permettant de spécifier un nouveau nom et lieu pour notre événement.

function supprimereventAction() {
        $id = $this->_request->getParam('id');
        $evenement = new Admin_Model_Db_Evenement();
        $evenement->delete('evenement_id='.$id);
        $this->view->info = "événement supprimé.";
}

Et enfin, la suppression de notre événement.
N’oubliez pas que pour chacune de ces actions, il vous faudra créer la vue associée. Voici comment j’ai code chacune des vues associées à l’AjaxController (De toutes façons, vous les avez dans les sources à la fin du tutoriel).

<?php if(isset($this->error)) echo $this->error;  ?>
<?php if(isset($this->info)) echo "<div id='info_modification_ini'>".$this->info."</div>";  ?>

Voilà, maintenant nous pouvons enfin attaquer le javascript en lui-même ! Nous allons commencer par créer une petite fonction qui retournera l’url de base. J’ai fait un petit article là dessus ya pas longtemps d’ailleurs. Vous pouvez très bien spécifier l’adresse de votre site manuellement à la place de cette fonction.

function getBaseURL() {
    var url = location.href;
    var baseURL = url.substring(0, url.indexOf('/', 14));

    if (baseURL.indexOf('http://localhost') != -1) {
        var pathname = location.pathname;
        var index1 = url.indexOf(pathname);
        var index2 = url.indexOf("/", index1 + 1);
        var baseLocalUrl = url.substr(0, index2);

        return baseLocalUrl;
    }
    else {
        return baseURL;
    }

}

En local ça marche niquel, à vérifier en ligne car je n’ai pas pu tester (j’ai du la modifier par rapport à la mienne). Ensuite, nous allons écrire quelques lignes qui sont nous permettre d’adapter la taille des événements proportionnellemnt à la taille des td :

/* Taille des events */
var td_width=$(".calendar_td").width();
$(".calendar_event").css({
    "width" : td_width*0.85,
    "margin-left" : (td_width-(td_width*0.85))/2
});

Encore quelques touches de design avant d’attaquer la programmation des événements, histoire d’avoir des beaux arrondis :) (J’utilise jQuery Corner)

/*------------------   ARRONDIS  --------------------*/
$('.lab_form').corner();
$('.identifiant').corner("round 9px");
$('.input_form').corner();
$('.form_list').corner();
$('.switcher_content_content').corner("left");
$('.calendar_event').corner("cc:#fff");
$('.calendar_event_date').corner("top cc:#fff");
$(".switcher_agenda_inside").corner("top");
$("div#select_agenda").corner("top");
$(".select_agenda_selector").corner("round 4px");
});

Et voilà, nous sommes maintenant prêts à attaquer le jQuery en lui-même. On va commencer par le Draggable(), permettant le déplacement des éléments.

/*  Déplacement event */
$(".calendar_event").draggable({
    containment: "parent",
    grid: [10, 10],
    delay: 100,
    drag: function(event, ui) {
        /*placement de l'événement*/
        /*changement affichage des horaires*/
    },
    stop: function(event, ui) {
        /*placement de l'evenement*/
        /*changement affichage horaire*/
        /*Enregistrement des modifications */
    }
});

D’abord un petit aperçu sans le code associé au drag et au stop. On voit plusieurs choses, la première est que l’événement ne peut être déplacé que dans son parent (le TD). Ensuite il ne peut se déplacer que de 10px en 10px (de Quart d’heure en quart d’heure donc) et qu’il y a un délai de 0.1 sec avant de commencer le drag. Le délai est là car si je n’en met pas, parfois le navigateur croit que je viens d’effectuer un Click au lieu du commencement d’un Drag. Nous allons maintenant voir le code associé au drag et au stop.

drag: function(event, ui) {
    var object_drop = $(this);
    var object_position=object_drop.position();
    var this_position=$(this).parent().position();
    current_position=object_position.top - this_position.top - 1;

    /*placement de l'evenement*/
    var marg_css=object_drop.css("margin-top");
    var marg_css_value=parseInt(marg_css.replace(".px",""));
    var margin_top=marg_css_value+current_position;
    if(margin_top<0)margin_top=0;
    margin_top=parseInt(margin_top);

    /*changement affichage horaire*/
    var id_event=object_drop.attr("id");

    var depart_en_sec=(((margin_top/10)/4-1)*60)*60;
    var depart_en_millisec=depart_en_sec*1000;
    var height_css=object_drop.css("height");
    var height_css_value=parseInt(height_css.replace(".px",""));
    var duree_en_sec=(((height_css_value/10)/4)*60)*60;
    var duree_en_millisec=duree_en_sec*1000;
    var fin_en_sec=depart_en_sec + duree_en_sec;
    var fin_en_millisec=depart_en_millisec + duree_en_millisec;
    nouvelle_heure_depart = new Date();
    nouvelle_heure_depart.setTime(depart_en_millisec);
    nouvelle_heure_fin = new Date();
    nouvelle_heure_fin.setTime(fin_en_millisec);
    $("#"+id_event+"_date_debut_heure").html(nouvelle_heure_depart.getHours());
    $("#"+id_event+"_date_debut_minute").html(nouvelle_heure_depart.getMinutes());
    $("#"+id_event+"_date_fin_heure").html(nouvelle_heure_fin.getHours());
    $("#"+id_event+"_date_fin_minute").html(nouvelle_heure_fin.getMinutes());
},

Voici comment ça se passe : Je récupère la position de mon objet ainsi que celle de son parent (le TD). Je calcule sa position avec ces informations, suite à quoi je calcule sa nouvelle plage horaire en récupérant l’ancienne et j’applique la nouvelle horaire. Je n’oublie bien sur pas de modifier le margin-top.

stop: function(event, ui) {
    var object_drop = $(this);
    var object_position=object_drop.position();
    var this_position=$(this).parent().position();
    current_position=object_position.top - this_position.top - 1;

    /*placement de l'evenement*/
    var marg_css=object_drop.css("margin-top");
    var marg_css_value=parseInt(marg_css.replace(".px",""));
    var margin_top=marg_css_value+current_position;
    if(margin_top<0)margin_top=0;
    margin_top=parseInt(margin_top);

    /*changement affichage horaire*/
    var id_event=object_drop.attr("id");

    var depart_en_sec=(((margin_top/10)/4-1)*60)*60;
    var depart_en_millisec=depart_en_sec*1000;
    var height_css=object_drop.css("height");
    var height_css_value=parseInt(height_css.replace(".px",""));
    var duree_en_sec=(((height_css_value/10)/4)*60)*60;
    var duree_en_millisec=duree_en_sec*1000;
    var fin_en_sec=depart_en_sec + duree_en_sec;
    var fin_en_millisec=depart_en_millisec + duree_en_millisec;
    nouvelle_heure_depart = new Date();
    nouvelle_heure_depart.setTime(depart_en_millisec);
    nouvelle_heure_fin = new Date();
    nouvelle_heure_fin.setTime(fin_en_millisec);
    $("#"+id_event+"_date_debut_heure").html(nouvelle_heure_depart.getHours());
    $("#"+id_event+"_date_debut_minute").html(nouvelle_heure_depart.getMinutes());
    $("#"+id_event+"_date_fin_heure").html(nouvelle_heure_fin.getHours());
    $("#"+id_event+"_date_fin_minute").html(nouvelle_heure_fin.getMinutes());
    $("#ajax_load").load(getBaseURL()+"/admin/ajax/evenement/id/"+id_event+"/d/"+depart_en_sec+"/f/"+fin_en_sec);
    $("#ajax_load").html("Modifications enregistr&eacute;es.");
}

Pour le stop, le principe est exactement le même, sauf qu’en plus je vais charger mon action evenement de mon AjaxController en lui passant les paramètres qui vont bien. Notre événement peut donc maintenant être déplacé et chaque déplacement engendre une modification de sa place horaire qui est enregistrée dans la base de données.
Nous allons maintenant nous attaquer au redimensionnement de l’événement :

$(".calendar_event").resizable({
    handles: 's',
    grid: [0, 10],
    stop: function(event, ui) {
        var object_drop = $(this);
        var id_event=object_drop.attr("id");
        var height_css=object_drop.css("height");
        var height_css_value=parseInt(height_css.replace(".px",""));
        var duree_en_sec=(((height_css_value/10)/4)*60)*60;
        $("#ajax_load").load(getBaseURL()+"/admin/ajax/evenementresize/id/"+id_event+"/dur/"+duree_en_sec);
    },
    resize: function(event,ui) {
        var object_drop = $(this);
        var id_event=object_drop.attr("id");
        var heure_depart=parseInt($("#"+id_event+"_date_debut_heure").html());
        var min_depart=parseInt($("#"+id_event+"_date_debut_minute").html());
        var heure_ref = new Date();
        heure_ref.setHours(heure_depart, min_depart, 0, 0);
        var timestamp=heure_ref.getTime();
        var height_css=object_drop.css("height");
        var height_css_value=parseInt(height_css.replace(".px",""));
        var duree_en_milli=((((height_css_value/10)/4)*60)*60)*1000;

        var new_heure = new Date();
        new_heure.setTime(timestamp+duree_en_milli);

        $("#"+id_event+"_date_fin_heure").html(new_heure.getHours());
        $("#"+id_event+"_date_fin_minute").html(new_heure.getMinutes());
    }
});

Il n’y a rien de bien compliqué dans le redimensionnement. On voit juste que dans les paramètres de resizable je spécifie que ce dernier ne peut être redimensionné que par le bas (handles : 's'), et que par “pic” de 10 px (soit 15 minutes). Ensuite le principe est exactement le même que pour le déplacement sauf que cette fois-ci, je récupère la propriété height et non sa position.
Maintenant, le dernier point avant la création : l’affichage de l’alert permettant de supprimer, modifier, ou voir les détails de l’événement.

/*info event*/
$(".calendar_event").click(function(e){
    var object_clicked = $(this);
    var id_event=object_clicked.attr("id");

    $("#dialog").dialog({
        bgiframe: true,
        resizable: true,
        height:140,
        width:500,
        modal: true,
        overlay: {
            backgroundColor: '#000',
            opacity: 0.5
        },
        beforeclose: function(event, ui) {
            $(this).dialog('destroy');
        },
        open: function(event, ui) {
            var heure_depart=$("#"+id_event+"_date_debut_heure").html();
            var min_depart=$("#"+id_event+"_date_debut_minute").html();
            var heure_fin=$("#"+id_event+"_date_fin_heure").html();
            var min_fin=$("#"+id_event+"_date_fin_minute").html();
            var titre_eve=$("#"+id_event+"_title").html();
            var lieu_eve=$("#"+id_event+"_lieu").html();

            var contenu = "<p>";
            contenu += "<b>Durée : </b>"+heure_depart+":"+min_depart+" à ";
            contenu += heure_fin+":"+min_fin+"<br />";
            contenu += "<b>Lieu : </b>"+lieu_eve+"<br />";
            contenu += "</p>";
            $("#ui-dialog-title-dialog").html(titre_eve);
            $("#dialog").html(contenu);
        },
        buttons: {
            'Voir détails': function() {
                $(this).dialog('destroy');
                var url_details=getBaseURL()+"/admin/evenements/voir/id/"+id_event;
                $(location).attr('href',url_details);
            },
            'Modifier': function() {
                $(this).dialog('destroy');
            },
            'Supprimer': function() {
                $(this).html("Veuillez Confirmer la suppression");
                $("#dialog").dialog('destroy');
                $("#dialog").dialog({
                    bgiframe: true,
                    resizable: true,
                    height:140,
                    modal: true,
                    beforeclose: function(event, ui) {
                        $(this).dialog('destroy');
                    },
                    buttons: {
                        'Supprimer': function() {
                            $(this).dialog('destroy');
                            $("#ajax_load").load(getBaseURL()+"/admin/ajax/supprimerevent/id/"+id_event);
                            $("#"+id_event).hide("highlight",{
                                direction: "vertical",
                                color: "#A60000"
                            },2000);
                        },
                        'Annuler': function() {
                            $(this).dialog('destroy');
                        }
                    }
                });

            }
        }

    });
});

Alors, j’utilise en fait ici un Widget de jQuery : le Dialog. C’est pour cela qu’au début de l’étape 4 (la Vue), nous avons créé un div #Dialog. En fait, le contenu va être appelé par jQuery pour avoir un rendu super chouette (qui change des alert on ne peut plus moche). Ici je personnalise donc cette fonction permettant d’afficher le Dialog. Quand je clique sur l’objet, la première chose que je fais est de récupérer l’id de l’événement ainsi qu’un pointeur vers mon objet, car si je fais $(this) dans les fonctions du Dialog, ça pointera vers le dialog et non vers mon Objet (j’ai cherché toute une aprem pourquoi j’arrivais pas à le récupérer…).
Je crée ensuite mon Dialog, qui m’affiche le contenu dessiné en html dans la vue. Modifier et Voir sont ici vides, il faut juste y mettre une redirection vers la page d’affichage et vers la page de modification d’un événement. Attention, vous voyez ici que j’ai mis des $(this).dialog('destroy'); partout. C’est parce que si je ne le met pas, quand je quitte un dialog, je ne peux pas en réouvrir un autre, ce qui est assez embettant.
Pour la suppression, je transforme mon dialog en un autre dont le bouton Supprimer me charge en Ajax l’action supprimerevent.
Passons maintenant au plus gros du tutoriel, la création d’un nouvel événement via double-clique :

$(".calendar_td").dblclick(function(e){
    /*
    a- gif de chargement
    b- recuperation de la journée cible et de la position de depart
    c- creation de l'event et recuperation de son id
    d- ouverture d'un Dialog jQuery pour spécifier nom et lieu de l'event (si on clique sur annuler ça supprime l'event créé)
    e- creation d'un evenement (en html, pour la visualisation)
    f- application des effets de Drag, Resize et Click à l'event
    */
});

Le fait qu’on doive rappliquer les fonctions Draggable,Click et Resizable au nouvel événement se justifie par le fait qu’il n’était pas présent au chargement de la page. Le javascript se chargeant au chargement, il ne peut donc pas y être associé et nous devons le refaire pour chaque événement créé. Allez, voyons le tout plus en détail :

a) Gif de chargement

$("#ajax_load").html('<p align="center"><img src="loading.gif" /></p>');

Je remplis mon div de chargement avec un Gif (non fourni dans les sources). Par la suite, je vais créer un nouvel événement dont je vais récupérer l’id (retourné par la fonction) pour construire mon événement visuellement. Cette fonction sera chargée de manière synchrone et peut donc, selon l’état de surcharge de la base de données, prendre jusqu’à deux secondes. Le gif est là pour rassurer l’utilisateur que la page n’a pas planté et qu’il ne cherche donc pas à spammer le tout de double-clic.
b) Récupération des informations

var position_choisie=e.pageY-$(this).position().top;

var to_round=Math.round(position_choisie);
var to_string=String(to_round);
var str_length=to_string.length-1;
var to_substr=to_string.substr(0,str_length);
var to_int=parseInt(to_substr);
if(!to_int){
    to_int=0;
}
var margin_top=to_int*10;

var depart_en_sec=(((margin_top/10)/4-1)*60)*60;
var depart_en_millisec=depart_en_sec*1000;
var height_css_value=parseInt(80);
var duree_en_sec=(((height_css_value/10)/4)*60)*60;
var duree_en_millisec=duree_en_sec*1000;
var fin_en_sec=depart_en_sec + duree_en_sec;
var fin_en_millisec=depart_en_millisec + duree_en_millisec;
nouvelle_heure_depart = new Date();
nouvelle_heure_depart.setTime(depart_en_millisec);
nouvelle_heure_fin = new Date();
nouvelle_heure_fin.setTime(fin_en_millisec);

var day_choose=(parseInt($(this).attr("id")));
var day_start=day_choose + ( nouvelle_heure_depart.getHours() * 60 * 60 ) + ( nouvelle_heure_depart.getMinutes() * 60 );
var day_end=day_choose + ( nouvelle_heure_fin.getHours() * 60 * 60 ) + ( nouvelle_heure_fin.getMinutes() * 60 );

Je récupère donc la position, à partir de laquelle je calcule l’heure de départ (j’y ajoute 2h, j’ai choisi qu’un événement par défaut ferait 2h de durée lors de sa création). Ensuite je récupère la journée (donc le timestamp a été stocké dans l’id de chaque TD, puis je charge ma fonction de manière synchrone.

c) creation de l’event et recuperation de son id

/*creation de l'event dans la bdd*/
var url_create=getBaseURL()+"/admin/ajax/evenementcreation/ds/"+day_start+"/de/"+day_end;
var event_id = $.ajax({
    url: url_create,
    async: false
}).responseText;

d) Ouverture du Dialog

/*dialog de remplissage*/
$("#gen_new_content").dialog({
    bgiframe: true,
    resizable: true,
    height:200,
    width:500,
    modal: true,
    beforeclose: function(event, ui) {
        $(this).dialog('destroy');
        $("#new_event_title").val("");
        $("#new_event_lieu").val("");
    },
    buttons: {
        'Enregistrer': function() {
            $(this).dialog('destroy');
            var new_titre=$("#new_event_title").val();
            var new_lieu=$("#new_event_lieu").val();
            agenda_id=$("#agenda_id").val();
            if(new_titre!=""){
                $("#"+event_id+'_title').html(new_titre);
            }
            if(new_lieu!=""){
                $("#"+event_id+'_lieu').html(new_lieu);
            }
            new_ti=new_titre.replace(/ /gi,"&nbsp;");
            new_li=new_lieu.replace(/ /gi,"&nbsp;");
            $("#"+event_id).removeClass("select_agenda_red");
            class_color_agenda=$("#"+agenda_id+"_agenda_id").html();
            $("#"+event_id).addClass(class_color_agenda);
            $("#ajax_load").load(getBaseURL()+"/admin/ajax/specifyevent/id/"+event_id+"/titre/"+new_ti+"/lieu/"+new_li+"/ag/"+agenda_id);
            $("#ajax_load").html("Evenement cr&eacute&eacute..");
            $("#new_event_title").val("");
            $("#new_event_lieu").val("");
        },
        'Annuler': function() {
            $(this).dialog('destroy');
            $("#ajax_load").load(getBaseURL()+"/admin/ajax/supprimerevent/id/"+event_id);
            $("#ajax_load").html("Cr&eacute;ation de l'&eacute;v&eacute;nement annul&eacute;e.");
            $("#"+event_id).hide("highlight",{
                direction: "vertical",
                color: "#A60000"
            },1000);
            $("#new_event_title").val("");
            $("#new_event_lieu").val("");
        }

    }
});

Un dialog comme tous les autres. Il appelle l’action specifyevent en lui passant les paramètres qui vont bien si on choisi de valider l’événement, sinon il appelle supprimerevent si on choisir d’annuler.

e) Création visuelle de l’événement

$("#ajax_load").html("");
var event = $('<div></div>')
.appendTo($(this))
.attr('class','calendar_event select_agenda_red')
.attr('id',event_id)
.css({
    height:"80px",
    marginTop:margin_top+"px"
});

var event_date = $('<div></div>')
.appendTo(event)
.attr('class','calendar_event_date')
.attr('div',event_id+'_calendar_event_date');

var event_date_heure_debut = $('<span>'+nouvelle_heure_depart.getHours()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_debut_heure');

$('<span>:</span>')
.appendTo(event_date);

var event_date_minute_debut = $('<span>'+nouvelle_heure_depart.getMinutes()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_debut_minute');

$('<span> - </span>')
.appendTo(event_date);

var event_date_heure_fin = $('<span>'+nouvelle_heure_fin.getHours()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_fin_heure');

$('<span>:</span>')
.appendTo(event_date);

var event_date_heure_fin = $('<span>'+nouvelle_heure_fin.getMinutes()+'</span>')
.appendTo(event_date)
.attr('id',event_id+'_date_fin_minute');

var event_date = $('<div>(Sans titre)</div>')
.appendTo(event)
.attr('class','calendar_event_title')
.attr('id',event_id+'_title');

var event_date = $('<div>(Inconnu)</div>')
.appendTo(event)
.attr('class','calendar_event_lieu')
.attr('id',event_id+'_lieu');

event.corner();
$(".calendar_event_date").corner("top cc:#fff");
var td_width=$(".calendar_td").width();
event.css({
    "width" : td_width*0.85,
    "margin-left" : (td_width-(td_width*0.85))/2
});

Comme vous le voyez, je vide d’abord ma div #ajax_load pour retirer l’effet de chargement (car pour arriver à cette étape, c’est forcément que l’id a été retourné et que le Dialog est apparu). Ensuite il s’agit d’une construction toute bête de html, pas la peine de s’y attarder.
f) Application des effets sur l’objet.

event.draggable();
event.resizable();
event.click();

Je ne met pas le détail des fonctions, car il s’agit d’un copié-collé des fonctions précédentes.

THE END

Et voilà, ce tutoriel est enfin terminé ! J’espère que personne n’est mort au cours de sa lecture, même si je pense que la plupart préféreront juste prendre les sources et se démerder pour comprendre. Je vous met ci-dessous les sources finales, elles comprennent les contrôleurs, les vues, le javascript et les backgrounds, à vous d’arranger le tout si vous voulez l’intégrer quelque part. Au fait, si j’ai vous voyez des choses qui, à-priori, ne servent à rien, c’est que j’ai oublié des les enlever. J’ai allégé au maximum mon Calendrier pour vous le proposer sous forme de tutoriel.

CATÉGORIES :

Zend

, jQuery

    • Schmidt.J
    • March 27th, 2010

    Très bon tutoriel, même si parfois c’est pas bien expliquer.

    • SFEZ
    • March 29th, 2010

    Certes, ce n’est pas l’endroit pour le dire mais je trouve ce site magnifique et très bien fait. Merci pour le TUTO du calendrier ca fait un moment que j’en cherche un dans le genre.

    • Lesny
    • March 30th, 2010

    merci :D

    • dimitri
    • April 8th, 2010

    tres bon tutoriel, mais j’aimerais avoir quelques infos plus pécifique : j’essaie d’integrer votre code avec Symfony, sans succes, auriez-vous déja essayer cette manoeuvre ?@Lesny

    • Lesny
    • April 9th, 2010

    Bonjour Dimitri,
    Désolé mais je n’ai jamais essayé d’intégrer mon code sous Symfony. De plus, je n’ai jamais travaillé sous ce framework, je ne pourrais donc t’être une grande aide sur ton problème.

    En revanche, Symfony est il également basé sur un design pattern MVC ? je suppose que oui. Ne faisant ici appel à aucune fonction spécifique de Zend, je ne vois pas pourquoi ce dernier ne fonctionnerait pas sous Symfony.

    Quel est ton problème exactement? Quelle est l’erreur générée ? Je t’aiderai dans la mesure du possible.

    • Ferdinand AMOI
    • April 15th, 2010

    Salut!
    Bon tuto sur le calendrier.
    Mais étant donné que vous utilisez la bibliothèque jquery, voici un plugin jquery pour le calendrier : http://arshaw.com/fullcalendar/
    Maintenant reste à créer une base de données et d’enregistrer les événements dans le calendrier avec le json ou autre.
    facilement utilisable avec le zend framework et symfony.

    • Lesny
    • April 15th, 2010

    Ha, je ne connaissais pas ce plug in.
    Et dire que j’ai passé 2 jours à coder comme un autiste pour développer le Calendrier ^^

    Mais pour maintenant, je continuerai d’utiliser le mien. C’est quand même nettement plus la classe d’utiliser un outil qu’on a soi-même développé :p

    • DrRevolte
    • May 4th, 2010

    Salut lesny,

    Pour t’avouer, je ne comprends pas ton tuto !

    pourquoi ne pas avoir donner le fichier php dans le .rar “source” ?

    et tout tes petits bouts de code en haut ce met tous dans le même fichier, qui est un fichier php ?

    Petit blocage pour ce bout de code :

    $table_evenement = new Admin_Model_Db_Evenement();

    la classe : Admin_Model_Db_Evenement (ou est-elle situé ?)

    Merci

    • Lesny
    • May 4th, 2010

    Bonjour DrRevolte,
    Je pense que ton problème principal réside dans le fait que tu ne connaisse pas le design pattern “MVC” ou même ce qu’est un design pattern.

    Ce calendrier a pour vocation d’être directement implémenté dans une application basée sous le ZendFramework, utilisant le design pattern MVC. (Controlleur -> Vue).

    Je ne vais pas ici t’expliquer comment tout ceci fonctionne, tu trouveras toutes ces informations sur le Web et même sur notre site ;) Glenn nous a fait de super Tutos pour apprendre à maîtriser le Zend Framework. (A la conquête de Zend Framework).

    Pour faire simple, les Controlleurs balancent le code aux vues.
    La classe Admin_Model_Db_Evenement n’est autre qu’une classe héritant d’une classe native de ZF permettant la liaison directe avec la table Evenement de la base de données.

    Maintenant, libre à toi de bidouiller ce code pour l’intégrer dans un script procédural, tu en fais ce que tu veux ^^
    Mais tu ne pourras pas le claquer tel qu’il est actuellement agencé, ce qui était je pense, ton objectif.

    J’espère avoir pu t’éclairer.
    Charles.

    • DrRevolte
    • May 4th, 2010

    Bonjour,

    Merci de se renseignement rapide !

    vue que je dois créer un agenda en ASP/Ajax ! Cela me sera impossible étant donné que Zend est un framework PHP, donc oui j’avais pas comprit au début qu’on aurait obligatoirement besoin de ZF !

    Merci beaucoup de votre rapidité, je vais me tourner vers FullCalendar alors !

    Bonne Journée

    • ornythorink
    • May 17th, 2010

    Bonjour Nanane

    Du beau boulot ! ;)
    Ok pour les critiques sur le MVC ..bon. Mais j’ai a peu près la même chose MVC (Symfony + ZF + un plugin de Rob Monie pour intégrer le JSON de google agenda dans le calendrier). Le “plus” de ce que je viens de lire rapidement, c’est ton intégration rapide et légère de la partie JSON/liaison évènement calendrier. C’est surement plus souple comme ça. En fait c’est dommage que je n’ai pas fait de recherche avant sur l’aspect coin arrondi sinon j’aurai sûrement adopté directement ta solution.

    Félicitation et bon courage.

    • Lesny
    • May 17th, 2010

    Merci :)

  1. C’est Bon, J’aime votre site web!

    • Lesny
    • July 6th, 2010

    :p
    Heureux que ça te plaise ^^
    Dommage qu’en ce moment nous n’ayons pas le temps de le remettre au gout du jour.
    J’ai encore une tripotée de tutos à pondre avec ce que j’ai fait ces 4 derniers mois, je prendrai du temps sous peu pour les rédiger.

    • flashnet
    • July 6th, 2010

    Salut,

    Tu tuto est super, il va me permettre de reprendre des idées pour réaliser la même chose avec le framework scriptacoulous & prototype.

    Je vois qu’on ne peut pas déplacer les rendez-vous à droite ou à gauche et je bloque sur le même problème, aurait-tu une solution ? Merci

    • Lesny
    • July 7th, 2010

    Salut Flashnet.
    Pour permettre le déplacement entre colonnes des Div, tu peux définir utiliser la fonction sortable() de jQuery.

    En revanche, j’ai une question :
    A moins d’y être contraint du fait d’une structure l’utilisant, pourquoi utilises tu Proto/Scriptacu ? Les classes ne sont plus vraiment mises à jour et les perspectives d’évolution quasiment nulles. Autant utiliser jQuery qui regroupe actuellement une communauté de développeurs bien plus importante que celle restant autour de prototype/scriptacu

    • Marcelinos
    • November 29th, 2010

    merci pour le tuto !

    c’est exactement ce que je cherche à faire depuis longtemps
    mais je ne sais pas comment faire?

    Mais tout mon problème est que je ne connais franchement rien sur le framework Jquery ! et donc je veux l’apprendre! !

    Alors ce serait très sympas de m’indiquer ou de me donner un bon cours sur ça !

    merci infiniment d’avance…

    Bravo pour ton tuto

  2. Merci pour ces infos, je vais éplucher tout ça et m’en inspirer pour développer un calendrier avec Yii framework. A bientôt!

    • devdevydev
    • July 19th, 2011

    Je déconseille ce planning,qui n’est pas du tout évolutif et adaptable.
    Je recommande le JQuery weekcalendar, qui est tout a fait modifiable et adaptable

    • Guillaume
    • February 10th, 2012

    C’est vraiment super bien fait ton truc mais je ne peux pas l’utiliser car il n’y a pas les noms de fichiers dans lesquels tu places tes bouts de code. Soi-disant parce que c’est fait avec ton framework. Moi qui ne l’utilise pas, je suis bloqué. C’est bien dommage…

    • Netounet
    • April 16th, 2012

    Sympas ce tuto il m’a bien servi pour gérer mes plannings. J’ai tout de même rencontré quelques soucis avec le draggable quand je voulais remonter un rdv, le margin-top me bloquait, pas trouvé pk mais comme j’étais dans un layout js je me suis arrangé en transférant le margin-top sur du top à l’initialisation de la grille et ça a résolu mon problème.

    Autre suggestion : rajouter une petite méthode dans la récupération des infos horaires par rapport à la position de l’élément en ajustant la valeur récupérée par rapport à la grille pour éviter d’avoir des horaires 10h03 …

    /**
    * fix top value to grid
    */
    get_fixed_top: function(subject, timeIntervalHeight) {
    var top = jQuery(subject).position().top – jQuery(subject).parent().position().top;
    top = top = timeIntervalHeight/2 ? timeIntervalHeight : 0);
    }

    Sachant que je ne travaille qu’avec du top dans mon cas je ne prend plus en compte le margin-top mais l’idée est là

    Autrement en rajoutant le droppable on peut facilement gérer le déplacement entre les jours

    Implémentation réalisée sur symfony2

    Merci

    PS : +1 pour le webdesgin :)

    • Netounet
    • April 16th, 2012

    Oula il m’a mangé pas mal de code de mon comment voici une version escape

    /**
    * fix top value to grid
    */
    get_fixed_top: function(subject, timeIntervalHeight) {
    var top = jQuery(subject).position().top – jQuery(subject).parent().position().top;
    top = top <= 0 ? 0 : top;
    return parseInt(top / timeIntervalHeight) * timeIntervalHeight + parseInt(top % timeIntervalHeight >= timeIntervalHeight/2 ? timeIntervalHeight : 0);
    }

  3. Very interesting info !Perfect just what I was looking for!

    • major200322
    • February 27th, 2013

    j’est pas l’intégrer dans les fichier de zend framework
    plz qui peux m’aider stp!!!!

    • motii
    • May 4th, 2013

    salut lesny
    j’ai trop bien aimer ton tutoriel, même si malheureusement j’ai rien compris, je suis un étudiant en génie indus et je veux bien concevoir un calendrier le même que vous avez fait, c’est pour ça que j’aimerai très bien avoir votre mail afin de vous expliqué mon problème, merci trop :)

  1. No trackbacks yet.