Hallo Bernd,
ich bin jetzt durch und möchte Dir meinen Stand vorstellen. Er ist NICHT objektorientiert, damit er zum Rest deines Codes passt. Vielleicht könnte man über eine Hybridlösung nachdenken, d.h. prozedurale Programmierung mit Klassenunterstützung. Ich habe aber den Eindruck, dass dein Know How an diesem Punkt noch nicht ist. Wenn doch, kann man dahingehend weiter machen.
Deinen Code ersetze ich zunächst einmal im Wesentlichen durch zwei Funktionsaufrufe. Den Teil, mit dem ich nichts anfangen konnte, habe ich stehen gelassen 😂
$admin_kalenderarten = admin_kalenderarten($mysqli);
// Monate auf Deutsch
$monate = array(1=>"Januar", 2=>"Februar", 3=>"März", 4=>"April", 5=>"Mai", 6=>"Juni", 7=>"Juli", 8=>"August", 9=>"September",
10=>"Oktober", 11=>"November", 12=>"Dezember");
// Anzuzeigenden Monat aus $_GET["ym"] laden und auf 1. des Monats einstellen
$timestamp = get_anzeigemonat_timestamp();
$showYear = date('Y', $timestamp);
$showMonth = date('m', $timestamp);
// H3 Title
$html_title = sprintf("%04d / %02d", $showYear, $showMonth);
// Prev & Next / Monat Link
$nextMonth = date('Y-m', mktime(0, 0, 0, $showMonth+1, 1, $showYear));
$prevMonth = date('Y-m', mktime(0, 0, 0, $showMonth-1, 1, $showYear));
// <tr>...</tr> Zeilen für die Kalenderausgabe konstruieren
$weeks = erzeuge_kalenderwochen($timestamp, $mysqli, $teile25);
$timestamp hätte ich gern umbenannt, weiß aber nicht wo Du die Variable sonst noch brauchst.
Die Funktionen sind selbsterklären benannt und zusätzlich ist der Aufruf kommentiert.
$showMonth und $showYear sind im Prinzip temporäre Werte. Würde dieser Code in einer Klasse stehen, wären es lokale Variablen einer allgemeinen Initialisierungsfunktion.
$mysqli und $teile25 sind Variablen, die Du vorher setzt (Dein Code setzt sie ebenfalls voraus) und die in erzeuge_kalenderwochen benötigt werden. Daher reiche ich sie durch.
Die Implementierung von get_anzeigemonat_timestamp und erzeuge_kalenderwochen könnte nun ganz woanders stehen. Solche Funktionen SOLLTE man gemäß PSR-1 aus einer anderen Datei includen (die dann NUR Funktionen enthält). Oder Du sammelst sie am Ende deiner PHP Datei (was dem Basic Coding Standard widerspricht)
Nun zu den Funktionen, Stück für Stück.
Beachte den PHPDocs Kommentar vor den Funktionen. Das ist eine Standardschreibweise für Funktions-Selbstbeschreibungen, die von guten IDEs verstanden wird und Dir Codierungshilfe (a.k.a. Intellisense) geben kann.
/**
* Ermittle den Timestamp für den 1. des anzuzeigenden Monats. Der Monat wird
* im Query-Parameter ym übergeben. Fehlt der oder hat falsches Format, wird
* der aktuelle Monat verwendet.
*
* @return int Unix-Timestamp des ersten Tages im Anzeigemonat
*/
function get_anzeigemonat_timestamp() {
$ym = filter_input(INPUT_GET, "ym",
FILTER_VALIDATE_REGEXP,
[ 'options' => [ 'regexp'=>'/\d{4}-\d{1,2}/' ] ]);
// Parameter fehlt oder die Validierung ist gescheitert
if (is_null($ym) || $ym === false)
{
$ym = date('Y-m');
}
// $ym ist jetzt immer im Format yyyy-mm.
// Entweder durch Regex bestätigt, oder von date geliefert.
$parts = explode("-", $ym);
// Monat Jahr
return mktime(0,0,0,intval($parts[1]),1,intval($parts[0]));
}
Bei Eingaben aus $_GET sollte man immer vorsichtig sein, daher filter_input als Schirm davor, zusammen mit einer Regex als Überprüfung auf das richtige Format. Die Funktion sollte zusammen mit den PHPDocs selbsterklärend sein!
/**
* Generiert Table Rows mit Kalendereinträgen. Daten werden automatisch beschafft.
*
* @return array Ein Array mit fertigen HTML Fragmenten pro Row
*/
function erzeuge_kalenderwochen($firstOfMonth, $mysqli, $teile25)
{
$thisMonth = date("Y-m", $firstOfMonth);
$showYear = intval(date('Y', $firstOfMonth));
$showMonth = intval(date('m', $firstOfMonth));
// date('w') liefert 0:So 1:Mo 2:Di usw...
// Finde Wochentag des Monatsersten, +6, %7, ergibt Wochentag des Tages zuvor.
// Diese Tagesnummer ist die Anzahl von leeren Feldern vor dem 1. des Monats
// $day wird die auszugebende Tagesnummer sein, d.h. um N Felder leerzulassen,
// wird $day auf 1-N initialisiert.
$weekDayBeforeFirst = (6 + intval(date('w', $firstOfMonth))) % 7;
$day = 1 - $weekDayBeforeFirst;
// Kalender zusammenbauen
// Tagesdatum und YYYY-MM Form des aktuellen Monats ermitteln
$today = date('Y-m-d', time());
// Anzahl der Tage im Anzeigemonat ermitteln
$day_count = intval(date('t', $firstOfMonth));
// $weeks: Ein Eintrag pro Anzeigewoche
$weeks = [];
while ($day <= $day_count)
{
// Negative Werte oder zu große Werte für $day sind kein Problem, die werden
// von mktime in den passenden Monat verschoben.
$weekNr = date('W', mktime(0,0,0,$showMonth, $day, $showYear));
$weekParts = [ "<td class='kw'>$weekNr</td>" ];
// Die 7 Tage einer Woche aufbereiten
for ($i=0; $i<7; $i++)
{
$weekParts[] = formatiere_tag($thisMonth, $day_count, $day, $today, $mysqli, $teile25);
$day++;
}
$weeks[] = "<tr>".implode('', $weekParts)."</tr>";
}
return $weeks;
}
$showMonth und $showYear berechne ich hier erneut. Das verschwendet ein paar Taktzyklen, aber um das zu sparen hätte ich eine Klasse erzeugen müssen und die Variablen dort als private Werte speichern. Vielleicht später. Jedenfalls wollte ich keine Hilfswerte als Parameter durchschleusen.
Die Variable $day enthält den aufbereiteten Tag im Monat. Die Logik zu Beginn setzt sie so, dass sie immer auf dem Montag steht, der vor dem Monatsersten liegt, bzw. auf dem Monatsersten, wenn der ein Montag ist. Wenn z.B. der Monatserste ein Donnerstag ist, beginnt $day mit -2.
Vor der Hauptschleife werden noch ein paar erforderliche Werte vorbereitet, und dann geht's los. Laufe so lange, bis $day größer ist als der letzte Tag im Monat.
Der Code ist analog zum Kalender strukturiert, d.h. er bereitet immer eine ganze Woche auf. Solange $day außerhalb des Monats liegt, werden Leerzellen geschrieben. Das erzeugt die Füllung vor der ersten und nach der letzten Woche.
Zu Beginn einer Woche wird die KW ermittelt und an den Anfang der Row gesetzt. Danach folgt die Aufbereitung der 7 Tage. Wie ein Tag formatiert wird, entscheidet allein die aufgerufene Funktion. Bei Verwendung von Klassen könnte man auf ein paar Parameter verzichten...
Bisher hatten wir nur Formatierung und Delegation. Nun geht's tiefer.
/**
* Formatiere das HTML Fragment für einen Kalendertag
*
* @param \mysqli $mysqli mysqli-Objekt für Datenbeschaffung
* @param string $date Anfragedatum, yyyy-mm-tt
* @param bool $aktuellerTag True um Tag als aktuelles Datum zu formatieren
* @return string
*/
function formatiere_tag($thisMonth, $day_count, $day, $today, $mysqli, $teile25)
{
if ($day < 1 || $day > $day_count)
{
return "<td class='emptyDay'></td>";
}
$date = $thisMonth . sprintf('-%02d', $day);
// Daten beschaffen
$datumTermine = kalender_termine($mysqli, $date, $teile25, 3);
$anzTermineGesamt = count_alle_kalender_termine($mysqli, $date, $teile25);
$dayClasses = ($date == $today ? "today " : "");
$dayHtml = "<td class='$dayClasses calendarDay'>"
. formatiere_tag_header($date, $day, $anzTermineGesamt);
foreach ($datumTermine as $termin)
{
$dayHtml .= formatiere_termin($termin);
}
return $dayHtml . '</td>';
}
Diese Funktion steuert die Aufbereitung eines Tages. Um Kalenderwochen und sonstigen Firlefanz kümmert sie sich nicht. Sie holt die Daten aus der DB, und bereitet sie als Tabellenzelle auf.
Ich verwende hier an einigen Stellen String Parsing, also das Ersetzen von Variablen in Strings. Man braucht dann nicht so viele Verkettungsschritte.
Im nächsten Posting mache ich weiter.
Rolf
sumpsi - posui - clusi