Algorithme pour affichage d'événements dans un calendrier

Y a-t'il un autre moyen de ne pas boucler sur tous les jours puis tous les événements "restants" ?

a marqué ce sujet comme résolu.

Bonjour à tous !

Aujourd’hui je m’intéresse à un point d’algorithmique qui est probablement courant, et dont ma solution actuelle (en PHP) ne me paraît pas satisfaisante.

J’ai une liste classique d’événements (ici des locations) avec dates et heures de début et de fin. Je la récupère depuis ma base de données triée par date de début ascendante et date de fin descendante (histoire d’avoir "les plus longs" en premier sur un même jour de début). Vient donc le moment de les afficher dans le calendrier, et un visuel proche de ce que propose iCal pour un mois serait mon but à atteindre.

Actuellement, je détermine la date de début et la date de fin de ma période sans trop de souci. Ensuite de quoi, j’ai deux boucles imbriquées pour afficher mes jours : une globale pour couvrir toute la période, et une autre pour couvrir chaque semaine — je pourrais probablement faire avec une seule, maintenant que j’y pense, mais bref). Pour chaque jour, je parcours ma liste à la recherche d’événements qui commencent, ont commencé et pas encore terminé, finissent ce moment-là. Histoire de ne pas toujours boucler sur l’ensemble des événements, ceux qui se terminent sont enlevés du tableau, et j’arrête de compulser ma liste d’événements si j’en rencontre un pour le jour suivant.

Au final, j’ai donc ceci :

<table class="table table-condensed" style="table-layout: fixed">
    <thead>
        <tr>
            <th>lu</th>
            <th>ma</th>
            <th>me</th>
            <th>je</th>
            <th>ve</th>
            <th>sa</th>
            <th>di</th>
        </tr>
    </thead>
    <tbody>
    <?php
    $currentWeek = $start->format('W');
    $currentMonth = $start->format('n');
    $monthBackground = ($start->format('w') == 1 ? '' : 'disabled');
    for ($date = new DateTime($start->format(\DateTime::W3C)); $date < $end;): ?>
        <tr>
        <?php for (; $date->format('W') == $currentWeek; $date->modify('+1 day')):
            if ($date->format('n') != $currentMonth) {
                $monthBackground = ($monthBackground ? '' : 'disabled');
                $currentMonth = $date->format('n');
            } ?>
            <td<?php if ($monthBackground): ?> class="<?= $currentMonth; ?>"<?php endif; ?>>
                <?php if ($date->format('w') == 1): ?>
                <small class="label label-primary"><?= $date->format('W'); ?></small>
                <?php endif; ?>
                <small class="label label-default"><?= $date->format('j'); ?></small>
                <?php foreach ($rentals as $key => &$rental):
                    if ($rental->spansOver($date)): ?>
                <a class="<?= ($rental->startsOn($date) ? 'event-start' : ($rental->endsOn($date) ? 'event-end' : 'event-span')); ?>"><?= $rental->getItem(); ?></a>
                    <?php elseif ($rental->isPast($date)):
                        unset($rentals[$key]);
                    elseif ($rental->isFuture($date)):
                        break;
                    endif; ?>
                <?php endforeach; ?>
            </td>
        <?php endfor;
        $currentWeek = $date->format('W'); ?>
        </tr>
    <?php endfor; ?>
    </tbody>
</table>
Génération d’un calendrier hebdomadaire

Mon questionnement porte sur la chose suivante :

  • Est-ce qu’un "bête" tableau trié reste une bonne structure ? J’ai vu notamment une solution où un arbre était proposé, donc quelque chose comme SplHeap
  • Sachant que je ne peux pas à ma connaissance récupérer mes objets directement dans un SplHeap si c’était l’idéal, est-ce que ce ne serait pas overkill  de reconstruire ce genre d’objet depuis mon tableau initial ?

Merci d’avance :)

Edit

Version avec une seule boucle
<table class="table table-condensed" style="table-layout: fixed">
    <thead>
        <tr>
            <th>lu</th>
            <th>ma</th>
            <th>me</th>
            <th>je</th>
            <th>ve</th>
            <th>sa</th>
            <th>di</th>
        </tr>
    </thead>
    <tbody>
    <?php
    $currentMonth = $start->format('n');
    $monthBackground = ($start->format('w') == 1 ? '' : 'disabled');
    for ($date = new DateTime($start->format('o-\WW-1')); $date < $end; $date->modify('+1 day')):
        if ($date->format('N') == 1): ?>
        <tr>
        <?php endif;
        if ($date->format('n') != $currentMonth) {
                $monthBackground = ($monthBackground ? '' : 'disabled');
                $currentMonth = $date->format('n');
            } ?>
            <td<?php if ($monthBackground): ?> class="<?= $currentMonth; ?>"<?php endif; ?>>
                <?php if ($date->format('w') == 1): ?>
                <small class="label label-primary"><?= $date->format('W'); ?></small>
                <?php endif; ?>
                <small class="label label-default"><?= $date->format('j'); ?></small>
                <?php foreach ($rentals as $key => &$rental):
                    if ($rental->spansOver($date)): ?>
                <a class="<?= ($rental->startsOn($date) ? 'event-start' : ($rental->endsOn($date) ? 'event-end' : 'event-span')); ?>"><?= $rental->getItem(); ?></a>
                    <?php elseif ($rental->isPast($date)):
                        unset($rentals[$key]);
                    elseif ($rental->isFuture($date)):
                        break;
                    endif;
                endforeach; ?>
            </td>
        <?php if ($date->format('N') == 7): ?>
        </tr>
        <?php endif;
    endfor; ?>
    </tbody>
</table>
Une boucle en moins pour générer les semaines
+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte