Kalender bauen mit PHP
5. September 2007
Das Ziel dieses Tutorials ist es zu zeigen, wie man einen hübschen Kalender bauen kann, der auch eingetragene Events anzeigen kann.
Zum Design des Kalenders: Er wird ein Monatskalender der in nach Wochentagen sortierten Spalten geordnet ist. Dazu muss man am Anfang und Ende leere Zellen einfügen. Ich habe mich für eine Tabellen-Version mit CSS-Formatierung entschieden, reines CSS wäre ein bisschen viel Aufwand gewesen
Ich habe mich vor kurzer Zeit mal damit befasst aber selbst keinen Ansatz gefunden. Nachdem eine Google-Recherche alles brachte außer dem ich suchte musste ich mir was von größeren Kalendern abschauen
Los geht’s!
Das ganze verpacken wir später in eine Klasse um den Kalender einfach wiederverwenden zu können.
Zuerst einmal benötigen wir die Informationen, welches Datum angezeigt werden soll.
1 2 3 4 5 | public function Generate($year = 0, $month = 0, $day = 0) { if(empty($day)) $day = date("d"); if(empty($month)) $month = date("m"); if(empty($year)) $year = date("Y"); |
Als nächstes brauchen wir den ersten Tag des Monats und welcher Wochentag dies ist sowie die Gesamtzahl der Tage in diesem Monat.
1 2 3 4 5 6 | // Ermitteln wir den UNIX-Timestamp vom ersten Tag des Monats $first_day = mktime(0, 0, 0, $month, 1, $year); // Auf welchen Wochentag fällt dieser? $day_of_week = date("D", $first_day); // Wieviel Tage hat der Monat? $days_in_month = cal_days_in_month(0, $month, $year); |
Nun können wir herausfinden, wieviele Zellen wir in der ersten Zeile leerlassen müssen.
1 2 3 4 5 6 7 8 9 10 | switch($day_of_week) { case "Sun": $blank = 0; break; case "Mon": $blank = 1; break; case "Tue": $blank = 2; break; case "Wed": $blank = 3; break; case "Thu": $blank = 4; break; case "Fri": $blank = 5; break; case "Sat": $blank = 6; break; } |
Nun kommt der Kopf der Tabelle mit den Überschriften
1 2 3 4 5 6 7 8 9 10 11 | $calendar = '<table class="c_table"> <tr> <th><strong>'.$this->weekday_names[0].'</strong></td> <th><strong>'.$this->weekday_names[1].'</strong></td> <th><strong>'.$this->weekday_names[2].'</strong></td> <th><strong>'.$this->weekday_names[3].'</strong></td> <th><strong>'.$this->weekday_names[4].'</strong></td> <th><strong>'.$this->weekday_names[5].'</strong></td> <th><strong>'.$this->weekday_names[6].'</strong></td> </tr> <tr>'."\n"; |
Die Namen der Tage habe ich im Kopf der Klasse definiert, um den Kalender einfach lokalisieren zu können.
1 2 3 4 5 6 7 8 9 | public $weekday_names = array( 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag', ); |
Nun initalisieren wir noch 2 Variablen bevor es richtig losgehen kann. $day_number wird später in jedem Durchlauf für jeden neuen Tag um eins erhöht. $day_count auch bei den leeren Zellen.
1 2 | $day_count = 1; $day_number = 1; |
Die vorhin definierten leeren Zellen können wir nun in den Kalender schreiben:
1 2 3 4 5 6 | while($blank > 1) { $calendar .= " <td></td>\n"; $blank--; $day_count++; } |
Solange wir noch nicht alle Tage durch haben tu was:
1 2 | while($day_number <= $days_in_month) { |
Erst einmal an die Werte für Tage und Monate eine führende Null hängen damit wir ein einheitliches Bild haben bei der Ausgabe.
1 2 3 4 | // Add 0 begin if necessary $dm = ( $month < 10 && substr($month, 0, 1) != '0' ) ? '0' . $month : $month; $dn = ( $day_number < 10 && substr($day_number, 0, 1) != '0' ) ? '0' . $day_number : $day_number; $date = "{$dn}.{$dm}.{$year}"; |
Jetzt kommt der Teil mit den Events. Die Events werden in einem internen Array nach Datum geordnet gespeichert.
1 2 3 4 5 | // Im Kopf der Klasse definieren: // Die Events, nur Zugriffe der Klasse und vererbten Klassen erlauben: protected $events; // Nur die Zeiten oder auch die Beschreibungen anzeigen? public $only_status = false; |
Zum Hinzufügen und entfernen der Events sind diese beiden Funktionen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public function AddEvent($date, $from_time, $to_time, $name) { // Führende Null hinzufügen list($day_number, $month, $year) = explode('.', $date); $dm = ( $month < 10 && substr($month, 0, 1) != '0' ) ? '0' . $month : $month; $dn = ( $day_number < 10 && substr($day_number, 0, 1) != '0' ) ? '0' . $day_number : $day_number; $date = "{$dn}.{$dm}.{$year}"; // Die ID die zurück gegeben wird $id = (isset($this->events[$date])) ? count($this->events[$date]) + 1 : 0; // Event schreiben $this->events[$date][$id] = array( 'id' => $id, 'from_time' => $from_time, 'to_time' => $to_time, 'name' => $name ); // ID zurück geben return $id; } public function RemoveEvent($date, $id) { // Gibt es ein Event mit der ID? Nein? False zurückgeben if (!isset($this->events[$date][$id])) return false; // Event entfernen und true zurückgeben unset($this->events[$date][$id]); return true; } |
Zurück zu Generate(): Nun können wir auslesen ob es für den aktuellen Tag Events gibt oder nicht. Falls ja werden diese ausgelesen und in die Variable $cal_add gespeichert welche später in die Tageszelle mit aufgenommen wird. Wenn es keine Events gibt ist die Variable leer und es wird nichts hinzugefügt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Variable leeren $cal_add = ''; // Haben wir heute ein Event? if ( isset($this->events[$date]) ) { // Haben wir, also holen wir die foreach ( $this->events[$date] as $event ) { // Nur Zeit oder auch Beschreibung anzeigen? if ($this->only_status) { $cal_add .= "<span class=\"c_date\">{$event['from_time']} - {$event['to_time']}</span><br />"; } else { $cal_add .= "<span class=\"c_date\">{$event['from_time']} - {$event['to_time']}</span><span class=\"c_name\">{$event['name']}</span><br />"; } } } |
Nun kommen wir zum Hauptteil, den Tagen. Wir haben 3 Typen: Vergangenheit, Heute und Zukunft. Da ich meine Formatierung mit CSS mache, ändert sich dann nur die CSS-Klasse der Tabellenzelle.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Tage in der Vergangenheit if($day_number < $day) { $calendar .= " <td class=\"c_past\">{$day_number}{$cal_add}</td>\n"; } // Heute elseif($day_number == $day) { $calendar .= " <td class=\"c_current\">{$day_number}{$cal_add}</td>\n"; } // Tage in der Zukunft else { $calendar .= " <td class=\"c_future\">{$day_number}{$cal_add}</td>\n"; } |
… und den internen Counter erhöhen damit wir vorwärts kommen
1 2 | $day_number++; $day_count++; |
$day_count höher als 7? Dann haben wir das Ende der Woche erreicht und müssen eine neue Zeile schreiben:
1 2 3 4 5 | if($day_count > 7) { $calendar .= " </tr>\n <tr>\n"; $day_count = 1; } |
Hier ist dann das Ende der vorher definierten While-Schleife. Wenn jetzt kein } kommt kann es Probleme haben
Jetzt noch die fehlenden Zellen am Ende der letzten Zeile hinzufügen…
1 2 3 4 5 | while($day_count > 1 && $day_count <= 7) { $calendar .= " <td></td>\n"; $day_count++; } |
… und den Kalender vollenden
1 | $calendar .= " </tr>\n</table>\n"; |
Damit der Kalender auch ausgegeben werden kann müssen wir ihn auch rausgeben:
1 | return $calendar; |
Ende der Funktion Generate()
Wenn die Klasse komplett ist kann man sie nun testen:
1 2 3 4 5 6 | $Calendar = new Calendar; $Calendar->AddEvent((date("d") -1) . '.' . date("m") . '.' . date("Y"), '16:43', '18:00', 'Gestern'); $Calendar->AddEvent(date("d").'.'.date("m").'.'.date("Y"), '16:43', '18:00', 'Heute'); $Calendar->AddEvent((date("d")+1).'.'.date("m").'.'.date("Y"), '18:43', '19:00', 'Morgen'); echo($Calendar->Generate()); |
Nun sieht der Kalender jedoch noch sehr langweilig aus. Mit ein bisschen CSS kann man ihn jedoch wesentlich verschönern:
1 2 3 4 5 6 7 8 9 | .c_table { width: 100%; border: 1px solid black;} .c_table th { text-decoration: underline; } .c_table td { border: 1px solid silver; height: 5em; padding-left: 3px; } .c_table td:hover { border: 1px solid grey; height: 5em; padding-left: 3px; } .c_past { font-style: italic; text-decoration: line-through; } .c_current { font-weight: bold; border: 1px solid black !important; } .c_future {} .c_date { display: block; font-weight: normal; } .c_name { font-weight: normal; font-style: italic; } |
Genug getippt für heute
Kompletter Kalender
Online-Demo