- Ymox,
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>
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
<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>