Door
Mark Hogeveen
op 21-02-2022 20:29
gewijzigd op 21-02-2022 20:51
4.881 views
Ik maak een applicatie met een agenda functie.
De agenda is een soort grid waarin je de dagen van de maand ziet, zoals een typische kalender.
De afspraken tabel in MySQL: agenda_id In de applicatie kan een gebruiker eventueel meerdere agenda's maken. user_id Gebruiker die de afspraak heeft gemaakt. title Korte tekst afspraak descriptionRuimte voor langere tekst. start_date Begindatum/tijd van afspraak end_date Einddatum/tijd van afspraak location Veld voor locatie/adres van afspraak.
Als ik een specifieke agenda wil weergeven, en het weergavebereik is bijvoorbeeld een maand, haal ik alle afspraken op uit de database die een begin- of einddatum hebben binnen het weergavebereik.
Ik krijg dan een resultset uit de database met de afspraken die in het huidige relevante "bereik" liggen dat ik nodig heb om de agenda te tonen aan de gebruiker.
Vervolgens maak ik een array van AgendaDay objecten (is een class), met het aantal elementen gelijk aan het aantal dagen in de weer te geven periode.
De AgendaDay class heeft op zijn beurt een array voor de afspraken die op die dag plaatsvinden.
Ik pak dus de afspraken uit de resultset van de database, en zet deze in de juiste AgendaDay objecten die corresponderen met de dag waarop de afspraak valt.
De data structuur bestaat dus uit een array van AgendaDay objecten, die op hun beurt de afspraken in zich hebben.
Nu heb ik meerdere bedenkingen:
- Het is mogelijk dat het beginmoment en eindmoment van een afspraak op verschillende dagen ligt. Ik zou dan bijvoorbeeld dezelfde afspraak in twee AgendaDay objecten kunnen zetten om dit te tonen in de browser.
- Het kan zelfs zo zijn dat een afspraak meerdere dagen duurt (zoals een vakantie). Dat zou betekenen dat nog veel meer AgendaDay objecten dezelfde afspraak kunnen bevatten om deze te kunnen tonen.
- Het kan zijn, dat een afspraak de grens tussen maanden overscreidt.
Ik vraag me dus af of dit wel de juiste aanpak is, en of het wel handig is.
Ook wil ik in de toekomst een functie implementeren waarmee de gebruiker een herhaalde afspraak kan toevoegen aan zijn agenda. Dus een afspraak die structureel elke week/maand in de agenda staat.
Het blijkt nog best ingewikkeld te zijn soms, als je er goed over nadenkt.
Heeft iemand ideeën of tips misschien?
?
Onbekende gebruiker
21-02-2022 20:44
gewijzigd op 21-02-2022 20:46
Zoals ik het zie heeft de kalender drie technische lagen in het kader van MVC:
- de database is het model
- PHP verzorgt de 'business logic'
- de browser is de view
Om de kalender weer te geven is CSS grid een goed idee. Ik zou in PHP dan wel 1 kalender-object maken per afspraak en niet meerdere. Bij het tekenen van de HTML en de CSS kan je het opsplitsen in meerdere dagen. Als een afspraak langer duurt dan kan worden weergegeven kan je er een icoontje bij zetten, een beetje als 'z.o.z.', zoals een pijl naar rechts.
Afspraken herhalen is lastiger. Dan moet je je database goed op orde hebben. Herhaalpatronen worden door eindgebruikers vaak aangegeven in dagen (zie bijvoorbeeld Microsoft Outlook), dus ziet het er logischer uit wanneer afspraken niet langer dan een dag kunnen duren. Voor de meeste eindgebruikers is dat geen probleem. In een papieren agenda moet je ook op meerdere aparte dagen invullen dat je vakantie hebt.
Of je de afspraken opslaat in dagen is aan jou. Het hoeft niet, je kan ook een herhaalpatroon gebruiken voor het inplannen vanuit de startdatum van een afspraak.
De andere manier is om in de database je te beperken tot 1 afspraak per dag, en als afspraken langer duren plan je meerdere dagen in en raap je ze in 1 query weer bij elkaar.
Het is in mijn ogen lood om oud-ijzer want op het scherm moet je het toch opsplitsen. Je kunt zelfs afspraken aan elkaar rijgen met een FK-kolom met een UNIQUE index, als een linked list.
>> Ik pak dus de afspraken uit de resultset van de database, en zet deze in de juiste AgendaDay objecten die corresponderen met de dag waarop de afspraak valt.
Hiervoor zijn objecten uitgevonden. (en een instantie van een class is een object).
Goed..
Je maakt een afspraak object. Deze heeft niets met een bepaalde AgendaDay te maken.
$appointment = new Appointment();
$appointment->setStart($row['start']);
$appointment->setEnd($row['end']);
$appointment->setTitle($row['title']);
}
En dan maak je (bijvoorbeeld) twee "dag" objecten aan:
$day1 = new AgendaDay();
$day1->setDate('21-2-2022');
$day1->addAppointment($appointment);
$day2 = new AgendaDay();
$day2->setDate('22-2-2022');
$day2->addAppointment($appointment);
Zoals je ziet voeg ik aan twee verschillende dagen dezelfde afspraak toe. Maar dit zijn niet twee verschillende afspraken. Dit zijn slechts twee variabelen (pointers) die naar hetzelfde object wijzen. Dus voeg je afspraak aan zoveel dagen toe als je wilt. Test het maar uit door de afspraak te wijzigen en laat dan die twee dagen op het scher m zien, de wijzigingen zijn voor beiden dagen doorgevoerd.
Stapje verder:
Op een html pagina laat je een kalender maand zien. Maak dan ook een object AgendaMonth. Geef die AgendaMonth een array met AgendaDagen met daarin alle dagen van de maand. Om het aantal database queries klein te houden laat je de AgendaMonth alle afspraken die in die maand vallen ophalen uit de database. Vervolgens bouw je in AgendaMonth ook logica zodat je AgendaDay object zijn afspraken bij AgendaMonth kan opvragen..
Abstractie
Je zou er nog voor kunnen kiezen om een class AgendaPeriod te maken ipv AgendaMonth met een onbepaald aantal dagen. Deze is dan inzetbaar voor een jaarweergave, een maandweergave, een weekweergave of een dagweergave.
<?php
$appointment = new Appointment();
$appointment->setStart($row['start']);
$appointment->setEnd($row['end']);
$day1 = new AgendaDay();
$day1->setDate('21-2-2022');
$day1->addAppointment($appointment);
?>
Niet de AgendaDay maar de Appointment bepaalt namelijk op welke dag de afspraak valt. Je zou dus op zijn minst een exception verwachten bij AgendaDay::addAppointment als de afspraak niet valt op de datum die je voor die dag hebt ingesteld met AgendaDay::setDay.
Als je doorredeneert, lijkt het sowieso niet logisch om twee keer dezelfde datum in te stellen. Je zou ook kunnen zeggen dat de agenda slechts een view is van een queue met opeenvolgende afspraken, waarbij de afspraken meteen de agendadagen en agenda-uren bepalen.
Of simpeler gezegd: uit alleen een lijstje met afspraken kun je altijd afleiden hoe je agenda eruitziet.
Bedankt voor de reacties tot nu toe.
Het gaat me vooral om de software structuur (OO-design).
Dan heb ik nog een vraag.
Ik wil ook een weergave maken waarin je de week ziet, met dagen als kolommen zoals bij bijvoorbeeld de Outlook agenda.
Elke dag-kolom is dan eigenlijk een soort verticale tijdlijn.
Zou dit nog dingen met zich meebrengen waar ik nu al rekening mee moet houden?
>> Als je doorredeneert, lijkt het sowieso niet logisch om twee keer dezelfde datum in te stellen. Je zou ook kunnen zeggen dat de agenda slechts een view is van een queue met opeenvolgende afspraken, waarbij de afspraken meteen de agendadagen en agenda-uren bepalen.
Dat spreekt mij wel aan Ward. Maar het enigste waar ik nog even aan zit te denken is als je in de lijst de eerste afspraak om 2 maart hebt staan en de laatste op 4 maart, wil je dan een maandkalender of een weekkalender zien? Je zou denk ik op zijn minst wel moeten aangeven of je
(een) maand of week overzicht(en) wilt.
?
Onbekende gebruiker
24-02-2022 13:29
Mark Hogeveen op 23/02/2022 19:18:36
Bedankt voor de reacties tot nu toe.
Het gaat me vooral om de software structuur (OO-design).
Dan heb ik nog een vraag.
Ik wil ook een weergave maken waarin je de week ziet, met dagen als kolommen zoals bij bijvoorbeeld de Outlook agenda.
Elke dag-kolom is dan eigenlijk een soort verticale tijdlijn.
Zou dit nog dingen met zich meebrengen waar ik nu al rekening mee moet houden?
Volgens mij ging het toch over opslaan? (Zie titel).
Maargoed, ik denk dat ik ook al gereageerd heb op het rekening houden met een tijdlijn: CSS Grid.
Je zou denk ik op zijn minst wel moeten aangeven of je (een) maand of week overzicht(en) wilt.
Eens. Ik zou het configureerbaar maken, zodat de gebruiker zelf kan bepalen of er een view per dag, per week of per maand nodig is. (Of een andere view, bijvoorbeeld: toon me alle afspraken in Amsterdam, dan kan ik aansluitend even langs bij een oude vriend.)
Om bij het opslaan te blijven, want daar hangt dit mee samen: elke afspraak heeft drie variabelen, waarvan er altijd één afhankelijk is van de andere twee. Je hebt een begintijd t1, een eindtijd t2 en een duur d. Daarbij geldt:
t1 + d = t2
En dus ook:
d = t2 - t1
Je moet dus minimaal t1 en d óf t1 en t2 opslaan. (Minimaal, ik zou ze persoonlijk alledrie opslaan omdat ze zelden veranderen en omdat berekeningen en rekenfouten meer kosten dan opslag.)
Voor het weergeven van een tijdvak zoals een week of een maand heb je alleen een query nodig die controleert of t1 en t2 van een afspraak binnen het opgegeven tijdvak vallen.
>> Het gaat me vooral om de software structuur (OO-design).
Zoiets?
De controller (het begin)
<?php
class AgendaController extends AbstractController
{
public function monthView(AppointmentRepository $appointmentRepository)
{
// initialiseer benodigde variabelen
$start = new \DateTime('first day of this month');
$end = new \DateTime('last day of this month');
$start->setTime(0,0,0);
$end->setTime(23, 59, 59);
// Haal de afspraken op uit de database
$appointments = $appointmentRepository->findOverlap($start, $end);
// render de view en geef de benodigde data mee aan de view
return $this->render('agenda/month.html', [
'start' => $start,
'end' => $end,
'appointments' => $appointments,
]);
}
}
?>
De repository haalt de informatie uit de database
<?php
class AppointmentRepository extends ServiceEntityRepository
{
public function findOverlap(\DateTime $start, \DateTime $end)
{
return $this->createQueryBuilder('a')
->andWhere('start < :end AND :start < end')
->setParameter('start', $start)
->setParameter('end', $end)
->orderBy('a.start', 'ASC')
->getQuery()
->getResult()
;
}
}
?>