Kalender mit MySql - Anbindung

ASchwiedy

Mitglied
Hi,

ich habe mit PHP einen Kalender gebastelt. Dieser wird mit der Funktion getCalender aufgerufen. In der Variablen $date ist der aktuelle timestamp gespeichert.

Hier die Funktion:
Code:
//AB HIER DIE FUNKTION getCalender
//------------------------------------------------------------------------------------------------

function getCalender($date,$headline) {
	$sum_days = date('t',$date); //$sum_days enthält die Anzahl Tage des aktuellen Monates ($date ist der aktuelle übergebene Timestamp)
	$LastMonthSum = date('t',mktime(0,0,0,(date('m',$date)-1),0,date('Y',$date)));  //$LastMonthSum enthält die Anzahl Tage des letzten Monates. Es wird nur -1 von m abgezogen.

foreach( $headline as $key => $value ) { 
	echo "<div class=\"day headline\">".$value."</div>"; 
	} echo "<div class=\"clear\"></div>";


		
for( $i = 1; $i <= $sum_days; $i++ ) { //Schleife läuft sooft wie der Monat Tage hat z.B. 31 ($sum_days)
        $day_name = date('D',mktime(0,0,0,date('m',$date),$i,date('Y',$date))); //$day_name enthält den aktuellen Tagnamen in Englisch                	
	
		
		if( $i == date('d',$date) AND date('m') == date('m',$date)) {
            echo "<div class=\"day current\">".sprintf("%02d",$i)."</div>\n"; //aktueller Tag
        } else {
            echo "<div class=\"day normal\">".sprintf("%02d",$i)."</div>\n"; //normaler Tag
        } 									 

    }
}
//ENDE getCalender
//------------------------------------------------------------------------------------------------

Nicht über das Layout wundern. Die Tage des Monats werden komplett nebeneinander in einer Zeile angezeigt :).
Nun das Problem. Es existiert eine MySql-DB mit den Felder "id", "von" und "bis". Diese sind im Format DATE (außer id natürlich). Wie würde nun am besten die MySQL - Abfrage aussehen, damit die jeweilgen Zeiträume farbig markiert werden? Der aktueller Tag wird bereits markiert wie im Code zu sehen.

Gruss
Andi
 
Danke für Deine schnelle Antwort******!!
Ja das Umwandeln stellt in der Tat kein Problem dar. Allerdings bekomm ich es einfach nicht in meinen Schädel, wie ich die mysql_query in meinem Fall gestalten muß, damit die Zeiträume auch im Kalender markiert werden...********************? :confused:
 
Also wenn $date das aktuelle Datum enthält, dann weiß die Funktion doch gar nicht welchen Zeitraum deine Abfrage liefert. Oder verstehe ich das was falsch.

Erweitere doch die Parameterliste der Funktion noch um die Werte von und bis. Damit kannst du dann den jeweiligen Zeitraum kennzeichnen.
 
$date wird bereits vor der Funktion per $_GET gesetzt. Und wenn dieses nicht gesetzt ist, dann wird einfach das aktuelle Datum gesetzt. Sorry hab meinen Code mal dahingehend erweitert, damit dieses ersichtlich ist:

Code:
<?php
if( isset($_GET['timestamp']) && !empty($_GET['timestamp'])){
    $date = $_GET['timestamp'];
} else {
    $date = time();
}

//AB HIER DIE FUNKTION getCalender
//------------------------------------------------------------------------------------------------

function getCalender($date,$headline) {
	$sum_days = date('t',$date); //$sum_days enthält die Anzahl Tage des aktuellen Monates ($date ist der aktuelle übergebene Timestamp)
	$LastMonthSum = date('t',mktime(0,0,0,(date('m',$date)-1),0,date('Y',$date)));  //$LastMonthSum enthält die Anzahl Tage des letzten Monates. Es wird nur -1 von m abgezogen.

foreach( $headline as $key => $value ) { 
	echo "<div class=\"day headline\">".$value."</div>"; 
	} echo "<div class=\"clear\"></div>";


		
for( $i = 1; $i <= $sum_days; $i++ ) { //Schleife läuft sooft wie der Monat Tage hat z.B. 31 ($sum_days)
        $day_name = date('D',mktime(0,0,0,date('m',$date),$i,date('Y',$date))); //$day_name enthält den aktuellen Tagnamen in Englisch	
           
	
		
         if( $i == date('d',$date) AND date('m') == date('m',$date)) {
            echo "<div class=\"day current\">".sprintf("%02d",$i)."</div>\n"; //aktueller Tag
        } else {
            echo "<div class=\"day normal\">".sprintf("%02d",$i)."</div>\n"; //normaler Tag
        } 
    }
}
//ENDE getCalender
//------------------------------------------------------------------------------------------------

Es soll der komplette aktuell in $date abgespeicherte Monat abgefragt werden. Wenn man nun die Mysql-Abfrage entsprechend setzt, dann müsste man die Werte von und bis doch gar nicht erst an die Funktion übergeben oder?
 
Zuletzt bearbeitet:
Wenn ich dich richtig verstehe, willst du jeden Tag markeiren, der in irgend einer Periode in der DB gespeichert ist.

Da gibt es mehrere Ansätze

Ein Ansatz um die Termine in MySQL zu konsilidieren, damit du nur noch die Perioden hast, in denen ein Termin vorhanden ist. Also dass überscheidende Termine zusammengefasst werden

Beispiel. Meine Tabelle Termine
Code:
[termine]
id | name | von        | bis
-----------------------------------
1  | T1   | 2011-06-05 | 2011-06-07
2  | T2   | 2011-06-03 | 2011-06-06
3  | T3   | 2011-05-06 | 2011-05-18

T1 und T2 überscheniden sich. Diese zusammenfassen.

gewünschtes Resultat
Code:
von        | bis        | von_timestamp | bis_timestamp
-------------------------------------------------------
2011-05-06 | 2011-05-18 | 1304632800    | 1305669600
2011-06-03 | 2011-06-07 | 1307052000    | 1307397600
Dies erreicht man mit dem folgenden einfachen Query
SQL:
SELECT
	von,
	MAX(bis) AS bis,
	UNIX_TIMESTAMP(von) AS von_timestamp,
	UNIX_TIMESTAMP(MAX(bis)) AS bis_timestamp
FROM
	(
		-- überlappende Termine auf dasselbe vo-Datum bringen
		SELECT
			-- Vergleichen on das `von` grösser las das letzte `bis` ist.
			-- Wenn ja, nimm das neue `von` als `von`, ansosnten das Alte
			@last_von := IF(von > @last_bis, von, @last_von) as von,
			-- letzes `bis`setzen 
			@last_bis := bis AS bis
		FROM
			-- tiefste von und bis auslesen und als Initialwert verwenden
			(SELECT @last_von := MIN(von), @last_bis := MIN(bis) FROM termine) AS vars,
			-- Alle Termine sortieren
			(SELECT * FROM termine ORDER BY von, bis) AS ordered_data
	) AS calculatet_periodes
GROUP BY
	von

Nun kann man im PHP einfach noch prüfen, ob ein Datum innerhalb dieser Timestamps liegt.

2ter Ansatz
Die andere möglichkeit ist, den Kalender gleich ganz in MySQL zu erstellen.
Als Grundlage dazu dient eine Virtuelle Tabelle für die Tage des Jahres: MySQL Virtuelle Tabelle mit allen Datums im Jahr
Nun kann man diese Tabelle mit den Terminen kreuzen

gewnünschte Ausgabe
Code:
mydate     | besetzt | heute
----------------------------
...
2011-06-01 | 0       | 0
2011-06-02 | 0       | 0
2011-06-03 | 1       | 0
2011-06-04 | 1       | 0
2011-06-05 | 1       | 0
2011-06-06 | 1       | 1
2011-06-07 | 1       | 0
2011-06-08 | 0       | 0
2011-06-09 | 0       | 0
2011-06-10 | 0       | 0
...

Das SQL dazu ist eigentlich relativ einfach. Ich gehe nicht weiter auf die Virtuelle Tabelle für die Tage pro Jahr ein, denn das hab ich weiter oben Verlinkt.
SQL:
SELECT
	mydate,
	--Vergleichn ob das Datum innerhalb eines Termines ist
	MAX(mydate between von AND bis) AS besetzt,
	--Vergleichn ob das Datum heute ist
	MAX(mydate = DATE(SYSDATE())) AS heute
FROM
	termine,
   --Virtuelle Tabelle mit allen Daten vom letzten Jahr bis und mit in 2 Jahren
	(
        SELECT
            makedate(myYear, day_in_year) AS mydate
        FROM
            -- Zahlen von 1 bis 400 liefern.
				(
                SELECT
                    @day_in_year := @day_in_year +1 AS day_in_year
                FROM
                    (SELECT @day_in_year :=0) AS vars,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS d1,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS d2,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) AS d3,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) AS d4
            ) AS daysOfYear,
            -- Alle Jahre vom letzten Jahr bis und mit in 2 Jahren definieren
            -- Pro weiteres Jahr ein weiteren UNION setzen
            (
                SELECT YEAR(SYSDATE())-1 AS myYear 
					 UNION SELECT YEAR(SYSDATE())
                UNION SELECT YEAR(SYSDATE()) +1
					 UNION SELECT YEAR(SYSDATE()) +2) AS years
        WHERE
        		--Nur die Einträge nehmen, dessen Jahr auch mit dem Jahr übereinstimmt 
        		--(also bei den 400 Einträgen die Überflüssigen wegschmeissen)
            YEAR(makedate(myYear, day_in_year)) = myYear
	) AS daysOfYears
GROUP BY
	mydate;

Nun kann man noch ein Filter mit BETEWEEN da drauf setzen um aunzuchränken welche daten man ansehen will
SQL:
..
	) AS daysOfYears
--Hier der Filter
WHERE
	mydate BETWEEN FROM_UNIXTIME({$anzeigeVonPhpTimestamp)) AND FROM_UNIXTIME({$anzeigeBisPhpTimestamp))
GROUP BY
	mydate;

Jetzt kann der ganze Kalender aus der DB geladen werden. Die 2 Flags `besetzt`und `heute`geben dabei auskunft darüber, wie es in PHP formatiert werden muss.
Und so müsste die angepasste Funktion in etwa aussehen
PHP:
function getCalender($date,$headline) {
	//TODO: Deine Header-Geschichte

	//SQL definieren
$sql = <<<SQL

SELECT
	mydate,
	--Vergleichn ob das Datum innerhalb eines Termines ist
	MAX(mydate between von AND bis) AS besetzt,
	--Vergleichn ob das Datum heute ist
	MAX(mydate = DATE(SYSDATE())) AS heute
FROM
	termine,
   --Virtuelle Tabelle mit allen Daten vom letzten Jahr bis und mit in 2 Jahren
	(
        SELECT
            makedate(myYear, day_in_year) AS mydate
        FROM
            -- Zahlen von 1 bis 400 liefern.
				(
                SELECT
                    @day_in_year := @day_in_year +1 AS day_in_year
                FROM
                    (SELECT @day_in_year :=0) AS vars,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS d1,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5) AS d2,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) AS d3,
                    (SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4) AS d4
            ) AS daysOfYear,
            -- Alle Jahre vom letzten Jahr bis und mit in 2 Jahren definieren
            -- Pro weiteres Jahr ein weiteren UNION setzen
            (
                SELECT YEAR(SYSDATE())-1 AS myYear 
					 UNION SELECT YEAR(SYSDATE())
                UNION SELECT YEAR(SYSDATE()) +1
					 UNION SELECT YEAR(SYSDATE()) +2) AS years
        WHERE
        		--Nur die Einträge nehmen, dessen Jahr auch mit dem Jahr übereinstimmt 
        		--(also bei den 400 Einträgen die Überflüssigen wegschmeissen)
            YEAR(makedate(myYear, day_in_year)) = myYear
	) AS daysOfYears
WHERE 
-- Den Monat und das Jahr vergleichen
	EXTRACT(YEAR_MONTH FROM mydate) = EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME({$date}))
GROUP BY
	mydate;

SQL;
	$result = mysql_query($sql);
	while($dayRow = mysql_fetch_assoc($result)){} 
			
        $day_name = date('D', $dayRow['mydate']); //$day_name enthält den aktuellen Tagnamen in Englisch  
        if($dayRow['heute']==1) {
            echo "<div class=\"day current\">".sprintf("%02d",$i)."</div>\n"; //aktueller Tag
        } elseif ($dayRow['besetzt']==1) {
            echo "<div class=\"day besetzt\">".sprintf("%02d",$i)."</div>\n"; //besetzter Tag
        } else {
            echo "<div class=\"day normal\">".sprintf("%02d",$i)."</div>\n"; //freier Tag
        } 
    }
}
 
Zuletzt bearbeitet von einem Moderator:
Wow, danke für Deine beiden ausführlichen Vorschläge. Der Einfachkeit halber setze ich auf Idee 1.

Nun kann man im PHP einfach noch prüfen, ob ein Datum innerhalb dieser Timestamps liegt.

Genau da liegt der Hase im Pfeffer. Das bekomm ich bei meinem Skript nicht zum laufen
 
Naja, wenn du die etrste einfacher findest - ich merke gerade, dass die erste eigentlich überflüssig ist und man direkt die ganzen Termine abrufen kann.
PHP:
function getCalender($date,$headline) {
    //TODO: Deine Header-Geschichte

    //SQL definieren
    //Nur die Termine auslesen, die innerhalb des Jahr-Monats von $date liegen
$sql = <<<SQL
SELECT
    UNIX_TIMESTAMP(von) AS von_timestamp,
    UNIX_TIMESTAMP(bis) AS bis_timestamp
FROM
    termine
WHERE
    EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME({$date})) 
        BETWEEN EXTRACT(YEAR_MONTH FROM von) 
        AND EXTRACT(YEAR_MONTH FROM bis);
    
SQL;
    //Alle Termine auslesen
    $result = mysql_query($sql);
    while($termin = mysql_fetch_assoc($result)){
        $termine[] = $termin;
    }

    $sum_days = date('t',$date);


    for( $i = 1; $i <= $sum_days; $i++ ) { //Schleife läuft sooft wie der Monat Tage hat z.B. 31 ($sum_days)
        $calDate = mktime(0,0,0,date('m',$date),$i,date('Y',$date));
        $day_name = date('D',$calDate); //$day_name enthält den aktuellen Tagnamen in Englisch                  
    
        //Datum gegen alle Termine prüfen
        $flagBesetzt = false;
        foreach($termine as $termin){
            if($termin['von_timestamp'] <= $calDate && $calDate <= $termin['bis_timestamp']){
                $flagBesetzt = true;
                break;  //im True-fall, kann die Schliefe abgebrochen werden
            }
        }
        
        if( $i == date('d',$date) AND date('m') == date('m',$date)) {
            echo "<div class=\"day current\">".sprintf("%02d",$i)."</div>\n"; //aktueller Tag
        } elseif($flagBesetzt) {
            echo "<div class=\"day besetzt\">".sprintf("%02d",$i)."</div>\n"; //besetzter Tag
        } else {
            echo "<div class=\"day normal\">".sprintf("%02d",$i)."</div>\n"; //normaler Tag
        }                                    
 
    }
}
 
Hab Deinen Vorschlag jetzt folgender Maßen eingebaut:

Code:
//AB HIER DIE FUNKTION getCalender
//------------------------------------------------------------------------------------------------

function getCalender($date,$headline) {
		$sum_days = date('t',$date); //$sum_days enthält die Anzahl Tage des aktuellen Monates ($date ist der aktuelle übergebene Timestamp)
	$LastMonthSum = date('t',mktime(0,0,0,(date('m',$date)-1),0,date('Y',$date)));  //$LastMonthSum enthält die Anzahl Tage des letzten Monates. Es wird nur -1 von m abgezogen.
	
	
foreach( $headline as $key => $value ) { 
	echo "<div class=\"day headline\">".$value."</div>"; 
	} echo "<div class=\"clear\"></div>";


//SQL definieren

$sql = mysql_query("SELECT UNIX_TIMESTAMP(von) AS von_timestamp, UNIX_TIMESTAMP(bis) AS bis_timestamp FROM reservations WHERE EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME({$date})) BETWEEN EXTRACT(YEAR_MONTH FROM von) AND EXTRACT(YEAR_MONTH FROM bis)");
		
		$result = mysql_query($sql);
    while($termin = mysql_fetch_assoc($result)){
        $termine[] = $termin;
    }		

		
  for( $i = 1; $i <= $sum_days; $i++ ) { //Schleife läuft sooft wie der Monat Tage hat z.B. 31 ($sum_days)
        $calDate = mktime(0,0,0,date('m',$date),$i,date('Y',$date));
        $day_name = date('D',$calDate); //$day_name enthält den aktuellen Tagnamen in Englisch                  
    
        //Datum gegen alle Termine prüfen
        $flagBesetzt = false;
        foreach($termine as $termin){
            if($termin['von_timestamp'] <= $calDate && $calDate <= $termin['bis_timestamp']){
                $flagBesetzt = true;
                break;  //im True-fall, kann die Schliefe abgebrochen werden
            }
        }
        
        if( $i == date('d',$date) AND date('m') == date('m',$date)) {
            echo "<div class=\"day current\">".sprintf("%02d",$i)."</div>\n"; //aktueller Tag
        } elseif($flagBesetzt) {
            echo "<div class=\"daybooked\">".sprintf("%02d",$i)."</div>\n"; //besetzter Tag
        } else {
            echo "<div class=\"day normal\">".sprintf("%02d",$i)."</div>\n"; //normaler Tag
        }                                    
 
    }
}  
//}
//ENDE getCalender
//------------------------------------------------------------------------------------------------

Der Kalender wird zwar ohne Fehlermeldung korrekt ausgegeben, jedoch werden die Zeiträume nicht markiert. Folgende 2 Zeiträume habe ich testhalber in der DB:

von | bis | von_timestamp | bis_timestamp
2011-06-08 2011-06-15 1307484000 1308088800
2011-06-25 2011-06-28 1308952800 1309212000

Der Kalender zeigt beim Aufruf ja automatisch den aktuellen Monat (Juni) an. Die beiden Zeiträume sollten also erscheinen, tun sie allerdings nicht :(
 
Warum verhunzt du meinen Code? ;)
Was soll das genau bewirken? Da ist einmal mysql_query() zuviel.
PHP:
$sql = mysql_query("SELECT UNIX_TIMESTAMP(von) AS von_timestamp, UNIX_TIMESTAMP(bis) AS bis_timestamp FROM reservations WHERE EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME({$date})) BETWEEN EXTRACT(YEAR_MONTH FROM von) AND EXTRACT(YEAR_MONTH FROM bis)");
        
        $result = mysql_query($sql);

$sql ist ein String. Um die SQL-Struktur zu behalten habe ich einfach die Heredoc-Schreibweise verwendet
 
Zurück