1. Aufbau des Nachrichtensystems
Da wir nun ein funktionierenden Login haben können wir diesen nun Testweise verwenden. Dazu schreiben wir ein Nachrichtensystem mit den wir anderen Benutzern Nachrichten schreiben können. Dies ist üblicherweise in Internetforen bekannt als PM-System (von private message).
Die MySQL-Tabelle muss für eine Nachricht folgende Daten speichern.
-
ID
- Die typische Identifikationsspalte mit den typischen Attributen INT, UNSIGNED, AUTO_INCREMENT und PRIMARY KEY. -
Quelle
- Die UserID der die Nachricht geschrieben hat. Daher auch der Typ INT mit den Attributen UNSIGNED und NOT NULL. -
Ziel
- Die UserID für die die Nachricht bestimmt ist. Auch vom Typ INT mit UNSIGNED und NOT NULL. -
Datum
- Speichert den Zeitpunkt wann die Nachricht geschrieben bzw. gesendet wurde. Typ ist DATETIME mit NOT NULL. -
Gelesen
- Speichert den Zeitpunkt wann der Empfänger sich die Nachricht angeguckt hat. Der Typ ist auch DATETIME jedoch muss das Attribut NULL verwendet werden weil der Empfänger die Nachricht auch erst dann gelesen hat wenn er die Nachricht öffnet. Beim öffnen wird diese Spalte mit dem aktuellen Zeitpunkt gefüllt und somit gesetzt. -
Betreff
- Selbsterklärend, vom Typ VARCHAR(100) mit dem Attribut NOT NULL. -
Inhalt
- Der Inhalt der Nachricht, vom Typ TEXT mit dem Attribut NOT NULL.
Die MySQL-Anfrage sieht dann wie folgt aus.
CREATE TABLE Nachricht ( ID INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, Quelle INT UNSIGNED NOT NULL, Ziel INT UNSIGNED NOT NULL, Datum DATETIME NOT NULL, Gelesen DATETIME NULL, Betreff VARCHAR(100) NOT NULL, Inhalt TEXT NOT NULL );
Analog kann man auch die Tabelle in phpMyAdmin hinzufügen.
2. Einbindung in unser Templatesystem
Für unser Templatesystem verwenden wir das $dateien
-Array mit dem wir
angeben welche Dateien geladen werden können. Bei dem Templatesystem gibt
es jedoch mehrere Aktionen die auftreten können.
-
Einmal kann man sich in einer Übersicht alle Nachrichten angucken die man geschrieben hat und die man empfangen hat. Insbesondere können hier auch Nachrichten gelöscht werden.
-
Des Weiteren kann man sich den Inhalt einer Nachricht angucken. Darauf folgend kann man z.B. die Nachricht löschen oder auf dieser Antworten.
-
Dann kann man auch einfach so eine neue Nachricht schreiben. Interessant wird auch wie man die Benutzer findet, für die eine Nachricht bestimmt ist.
All diese Aktionen müssen irgendwie in unsere Datei eingebunden werden und wir brauchen entsprechende Template-Dateien mit den wir den entsprechenden HTML-Code anzeigen. Hier sollte man ein wenig vorher überlegen welche Bereiche wie aufgerufen werden, mit welcher HTTP-Methode und mit welchen Variablen.
Um die ganzen Teilbereiche zu erreichen verwenden wir eine neu GET-Variable action
in der wir die Aktion angeben die wir machen wollen. Für jeden dieser Bereiche legen
wir ein PHP-Skript und ein HTML-Template an. Die Bereiche laden wir mit einem
Arrayinclude ähnlich der in der index.php
-Datei.
<?php
if (!$UserID = GetUserID($db)) {
return NOT_LOGGED_IN;
}
$actions = array();
$actions['view'] = 'pm_view.php';
$actions['delete'] = 'pm_delete.php';
$actions['reply'] = 'pm_reply.php';
$actions['find'] = 'pm_find.php';
$actions['new'] = 'pm_new.php';
if (isset($_GET['action'], $actions[$_GET['action']])) {
return include $actions[$_GET['action']];
}
return include 'pm_overview.php';
?>
Einmal sehen wir das abgefragt wurde ob man eingeloggt ist. Des Weiteren
definieren wir unser Arrayinclude und rufen als Fallback die
pm_overview.php
-Datei auf. Die pm.php
-Datei definiert
selbst nicht das Return-Array sondern liefert das zurück was
die Include-Dateien generieren. Natürlich darf nicht vergessen
diese Datei auch in unser
$section
-Array einzutragen.
Zuerst schreiben wir die Template-Datei damit wir wissen was wir alles für Daten brauchen.
<?php /* * Daten: * neu - Ein Array mit Arrays für jede ungelesen Nachricht. Jedes Array * für eine Nachricht hat die folgende Werte: * ID - Die ID der Nachricht * Betreff - Den Betreff der Nachricht * Autor - Der Name vom Benutzer der die Nachricht geschrieben hat. * Datum - Der Zeitpunkt wann die Nachricht gesendet wurde. * alt - Ein Array mit Arrays für jede bereits gelesene Nachricht. Der * Aufbau des Arrays ist der selbe wie oben, nur das es ein * weiteres Feld enthält. * Gelesen - Der Zeitpunkt wann die Nachricht angeguckt wurde. */ ?><form action="index.php?section=pm&action=delete" method="post"> <table class="messages new"> <caption>Ungelesene Nachrichten</caption> <thead> <tr> <th>Betreff</th> <th>Autor</th> <th>Datum</th> <th>Markieren</th> </tr> </thead> <tfoot> <tr> <td colspan="3">Markierte Nachrichten löschen</td> <td><input type="submit" name="formaction" value="Löschen" /></td> </tr> </tfoot> <tbody> <?php foreach ($data['new'] as $message) { echo " <tr>\n"; echo ' <td><a href="index.php?section=pm&action=view&ID='.$message['ID'].'">'. htmlspecialchars($message['Betreff'])."</a></td>\n"; echo ' <td>'.htmlspecialchars($message['Autor'])."</td>\n"; echo ' <td>'.htmlspecialchars($message['Datum'])."</td>\n"; echo ' <td><input type="checkbox" name="ID[]" value="'.$message['ID'].'" /></td>'."\n"; echo " </tr>\n"; } ?> </tbody> </table> <table class="messages old"> <caption>Gelesene Nachrichten</caption> <thead> <tr> <th>Betreff</th> <th>Autor</th> <th>Datum</th> <th>Gelesen</th> <th>Markieren</th> </tr> </thead> <tfoot> <tr> <td colspan="4">Markierte Nachrichten löschen</td> <td><input type="submit" name="formaction" value="Löschen" /></td> </tr> </tfoot> <tbody> <?php foreach ($data['old'] as $message) { echo " <tr>\n"; echo ' <td><a href="index.php?section=pm&action=view&ID='.$message['ID'].'">'. htmlspecialchars($message['Betreff'])."</a></td>\n"; echo ' <td>'.htmlspecialchars($message['Autor'])."</td>\n"; echo ' <td>'.htmlspecialchars($message['Datum'])."</td>\n"; echo ' <td>'.htmlspecialchars($message['Gelesen'])."</td>\n"; echo ' <td><input type="checkbox" name="ID[]" value="'.$message['ID'].'" /></td>'."\n"; echo " </tr>\n"; } ?> </tbody> </table> </form> <p> <a href="index.php?section=pm&action=new">Neue Nachricht schreiben</a> </p>
Dazu schreiben wir die pm_overview.php
-Datei. Diese
wird wie alle anderen Dateien in
includes/
abgelegt. Problematisch wird es jedoch
den Benutzernamen des Autors auszulesen, da in der
Nachrichten-Tabelle nur die ID des Autors steht, nicht dessen
Name. Eine Möglichkeit wäre es
mit eine While-Schleife die Nachrichten auszulesen und innerhalb
dieser While-Schleife verschachtelt eine SQL-Anfrage zu schicken
die den Benutzernamen des aktuellen Autors ausliest. Die
elegantere Lösung ist jedoch den Namen gleichzeitig mit den
Nachrichten auszulesen. Dazu wird eine JOIN-Anfrage
verwendet.
3. JOIN-Anfragen
In unserer relationalen Datenbank werden wir auch weiterhin mit IDs arbeiten. Dies entspricht in etwa der Dritten Normalform, die auf jedenfall angestrebt werden sollte. Daher brauchen wir eine Möglichkeit von einer Tabelle zur anderen Tabelle zu gelangen. In MySQL existiert dafür das Schlüsselwort JOIN.
SELECT ... FROM Tabelle_A JOIN Tabelle_B
Dieser Query würde zuerst eine Art Kreuzprodukt mit allen
Datensätze aus Tabelle A und den Datensätzen aus Tabelle B bilden.
Bei 100 Datensätze in Tabelle A und 20 Datensätzen in Tabelle B sind
dies 2000 Datensätze die rauskommen. Dies ist meistens unerwünscht,
insbesondere weil einige Datensätze aus Tabelle A nicht mit Datensätzen
aus Tabelle B semantisch zusammenpassen. Daher besteht bei
JOIN
die Möglichkeit die Art der Verkettung anzugeben. Dies
wird mit dem auf JOIN tbl
folgenden Schlüsselwort
ON angegeben. Dabei gibt man dort eine Bedingung an
wann Datensätze aus Tabelle A mit Datensätzen aus Tabelle B kombiniert
werden.
SELECT ... FROM Tabelle_A JOIN Tabelle_B ON Tabelle_A.ID = Tabelle_B.ObjektID
Diese Bedingung dient nur für die Verknüpfung der Tabellen. Weitere
Semantische Einschränkungen wie Username = 'Foobar'
wird
normal im WHERE-Teil verarbeitet. Wenn wir aus
zwei oder mehr Tabellen auslesen kann es sein dass eine Spaltenangabe
wie z.B. ID
nicht mehr eindeutig ist. Daher werden
bei JOIN-Anfragen die Spalten in der Form
Tabelle.Spalte
angegeben.
SELECT Tabelle_A.ID, Tabelle_A.Beschreibung, Tabelle_B.Name, Tabelle_B.Datum FROM Tabelle_A JOIN Tabelle_B ON Tabelle_A.ID = Tabelle_B.ObjektID
In PHP wird der Spaltenname jedoch weiterhin nur ID
,
Beschreibung
, usw. heißen. Falls das einem nicht passt
kann der Namen durch AS neuer_name
verändert werden.
SELECT Tabelle_A.ID, Tabelle_A.Beschreibung, Tabelle_B.Name AS Username, Tabelle_B.Datum FROM Tabelle_A JOIN Tabelle_B ON Tabelle_A.ID = Tabelle_B.ObjektID
4. Anzeigen der Nachrichten
Da wir nun wissen wie wir den Benutzernamen aus der anderen Tabelle laden können wir nun unsere MySQL-Anfrage abschicken.
<?php
$ret = array();
$ret['filename'] = 'pm_overview.tpl';
$ret['data'] = array();
$sql = 'SELECT
Nachricht.ID,
Nachricht.Betreff,
User.Username AS Autor,
Nachricht.Datum
FROM
Nachricht
JOIN
User
ON
Nachricht.Quelle = User.ID
WHERE
Nachricht.Ziel = ? AND
Nachricht.Gelesen IS NULL
ORDER BY
Nachricht.Datum DESC';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $UserID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ID, $Betreff, $Autor, $Datum);
$neu = array();
while ($stmt->fetch()) {
$neu[] = array('ID' => $ID,
'Betreff' => $Betreff,
'Autor' => $Autor,
'Datum' => $Datum);
}
$stmt->close();
$ret['data']['new'] = $neu;
// und das selbe für gelesene Nachrichten.
$sql = 'SELECT
Nachricht.ID,
Nachricht.Betreff,
User.Username AS Autor,
Nachricht.Datum,
Nachricht.Gelesen
FROM
Nachricht
JOIN
User
ON
Nachricht.Quelle = User.ID
WHERE
Nachricht.Ziel = ? AND
Nachricht.Gelesen IS NOT NULL
ORDER BY
Nachricht.Datum DESC';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $UserID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ID, $Betreff, $Autor, $Datum, $Gelesen);
$alt = array();
while ($stmt->fetch()) {
$alt[] = array('ID' => $ID,
'Betreff' => $Betreff,
'Autor' => $Autor,
'Datum' => $Datum,
'Gelesen' => $Gelesen);
}
$stmt->close();
$ret['data']['old'] = $alt;
return $ret;
?>
Wie man sieht wählen wir mit Nachricht.Ziel = ?
nur die Nachrichten aus
die für uns bestimmt sind und mit Nachricht.Gelesen IS (NOT) NULL
nur
die Nachrichten die wir nicht gelesen bzw. gelesen haben. Um den
Benutzernamen des Autors rauszukriegen verwenden wir ein JOIN auf die Tabelle
User mit der Bedingung Nachricht.Quelle = User.ID
.
5. Schreiben neuer Nachrichten
Damit wir für die späteren Programmteile auch Testnachrichten haben schreiben
wir nun den Teil für das Schreiben neuer Nachrichten. Jedoch wissen wir
noch nicht wie wir den Empfänger der Nachricht angeben. Die einfachste
Möglichkeit wäre ein Eingabefeld in der wir die UserID des Empfängers eingeben.
Dies ist jedoch nicht grad Benutzerfreundlich wenn wir Nachrichten an
User 557324
oder 2496235
schicken. Besser wäre es doch
wenn wir eine Benutzernamen eingeben können. Da der Benutzername nur einmal
vorkommen kann könnte dies sogar klappen, könnte aber auch daran scheitern wenn
der Benutzernamen aus kryptischen Zeichen besteht. Wir werden jedoch
diesen Weg nehmen, auch wenn damit einige Problem verbunden sind.
Zuerst schreiben wir die Template-Datei. Diese braucht keine Daten aus der (noch zu schreibenen) PHP-Datei.
<?php /* Daten: * keine */ ?><form action="index.php?section=pm&action=new" method="post"> <fieldset> <legend>Neue Nachricht schreiben</legend> <label>Empfänger: <input type="text" name="Empfaenger" /></label> <label>Betreff: <input type="text" name="Betreff" /></label> <label>Nachricht: <textarea name="Nachricht" cols="40" rows="10"></textarea></label> <input type="submit" name="formaction" value="Nachricht senden" /> </fieldset> </form>
In unserer PHP-Datei zeigen wir dieses Template an, solange die Seite nicht mit der POST-Methode aufgerufen wurde.
<?php
$ret = array();
$ret['filename'] = 'pm_new.tpl';
$ret['data'] = array();
if ('POST' == $_SERVER['REQUEST_METHOD']) {
}
return $ret;
?>
Wie üblich prüfen wir ob alle Daten vorhanden und ausgefüllt sind.
<?php
$ret = array();
$ret['filename'] = 'pm_new.tpl';
$ret['data'] = array();
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['Empfaenger'], $_POST['Betreff'], $_POST['Nachricht'], $_POST['formaction'])) {
return INVALID_FORM;
}
if (($Empfänger = trim($_POST['Empfaenger'])) == '' OR
($Betreff = trim($_POST['Betreff'])) == '' OR
($Nachricht = trim($_POST['Nachricht'])) == '') {
return EMPTY_FORM;
}
}
return $ret;
?>
Nun lesen wir die UserID vom Empfänger aus und gegen eine Fehlermeldung aus wenn kein Benutzer gefunden wurde.
<?php
// [...]
if (($Empfänger = trim($_POST['Empfaenger'])) == '' OR
($Betreff = trim($_POST['Betreff'])) == '' OR
($Nachricht = trim($_POST['Nachricht'])) == '') {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
User
WHERE
Username = ? AND
ID != ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('si', $Empfänger, $UserID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ZielID);
if (!$stmt->fetch()) {
return 'Es wurde kein Benutzer mit diesem Namen gefunden.';
}
$stmt->close();
// [...]
?>
Die Bedingung ID != ?
dient wieder dazu dass wir uns nicht
selbst finden und somit auch keine Nachrichten an uns selbst schreiben können.
Da wir nun die ID haben können wir die Nachricht in unserer Tabelle speichern.
<?php
// [...]
$stmt->close();
$sql = 'INSERT INTO
Nachricht(Quelle, Ziel, Datum, Betreff, Inhalt)
VALUES
(?, ?, NOW(), ?, ?)';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('iiss', $UserID, $ZielID, $Betreff, $Nachricht);
if (!$stmt->execute()) {
return $stmt->error;
}
return showInfo('Die Nachricht wurde gesendet.');
// [...]
?>
Dies wars schon. Die Spalte Gelesen wird standardmäßig mit
NULL
gefüllt und somit wird die Nachricht als ungelesen
interpretiert.
6. Nachricht anzeigen
Wenn eine Nachricht angezeigt werden soll müssen wir mehrere Dinge beachten. Einmal könnte jemand versuche eine Nachricht anzuzeigen die nicht für ihn bestimmt ist. Des Weiteren müssen wir beim Anzeigen ggf. darauf achten das in dem Gelesen-Feld der aktuelle Zeitpunkt gespeichert wird weil derjenige Benutzer ja schließlich die Nachricht gelesen hat bzw. zumindest geöffnet hat. Und dann müssen wir auf HTML-Code und Spielerein in den Nachricht achten, wie wir es im Gästebuch gemacht haben.
Zuerst schreiben wir wieder die Template-Datei damit wir wissen welche Daten wir brauchen.
<?php /* Daten: * ID - Die ID der Nachricht * Quelle - Der Name desjenigen der die Nachricht geschrieben hat * Ziel - Der Name desjenigen für den die Nachricht bestimmt ist. * Datum - Der Zeitpunkt wann die Nachricht gesendet/geschrieben wurde * Gelesen - Der Zeitpunkt wann die Nachricht gelesen/betrachtet wurde, kann NULL sein * Betreff - Der Betreff der Nachricht * Inhalt - Der eigentliche Inhalt der Nachricht. */ ?> <table id="nachricht"> <caption>Nachricht von <?php echo htmlspecialchars($data['Quelle']); ?></caption> <tr> <th>ID</th> <td><?php echo $data['ID']; ?></td> </tr> <tr> <th>Sender</th> <td><?php echo htmlspecialchars($data['Quelle']); ?></td> </tr> <tr> <th>Empfänger</th> <td><?php echo htmlspecialchars($data['Ziel']); ?></td> </tr> <tr> <th>Gesendet</th> <td><?php echo htmlspecialchars($data['Datum']); ?></td> </tr> <tr> <th>Gelesen</th> <td><?php if (is_null($data['Gelesen'])) { echo 'Noch nicht gelesen'; } else { echo htmlspecialchars($data['Gelesen']); } ?></td> </tr> <tr> <th>Betreff</th> <td><?php echo htmlspecialchars($data['Betreff']); ?> </tr> <tr> <th>Nachricht</th> <td><?php echo nl2br(htmlspecialchars(preg_replace('~\S{30}~', '\0 ', $data['Inhalt']))); ?></td> </tr> </table> <p> <a href="index.php?section=pm&action=reply&ID=<?php echo $data['ID']; ?>">Auf diese Nachricht antworten</a> </p>
Da wir nun die Template-Datei haben und wissen was wir übergeben müssen können
wir die Include-Datei schreiben. In der gucken wir ob die GET-Variable
ID
vorhanden ist die die ID der Nachricht angibt.
<?php
$ret = array();
$ret['filename'] = 'pm_view.tpl';
$ret['data'] = array();
if (!isset($_GET['ID'])) {
return INVALID_LINK;
}
if (($ID = trim($_GET['ID'])) == '') {
return 'Bitte geben sie eine gültige ID an.'; // oder irgendwas passendes schreiben
}
return $ret;
Die Konstante INVALID_LINK
muss dann entsprechend in der
constants.php
-Datei definiert werden. Als Fehlermeldung
könnte man z.B. Bitte benutzen sie nur Links von der Homepage.
verwenden.
Wir müssen nun folgende Daten prüfen. Einmal müssen wir gucken ob es eine Nachricht mit der ID gibt, dann gucken ob wir die Nachricht lesen dürfen und dann die Daten der Nachricht an das Template geben. Dies könnte man z.B. mit drei SQL-Anfragen bewerkstelligen. Zuerst liefern wir uns die ID zurück, dann die UserIDs von Quelle und Ziel und dann die restlichen Daten von der Nachricht. Dies machen wir alles jedoch in einer einzigen SQL-Anfrage. Für die Anzeige im Template benötigen wir jedoch die Benutzernamen der Benutzer. Diese stehen, wie wir wissen, jedoch in der User-Tabelle. Also müssen wir ein JOIN verwenden. Und da wir zwei (unterschiedliche) UserIDs haben brauchen wir auch zwei JOINs. Wir entwickeln die SQL-Anfrage Schritt für Schritt.
SELECT ... FROM Nachricht
Wir beginnen von der Nachrichten-Tabelle und lassen den
SELECT-Teil erstmal weg. Nun beginnen wir
den ersten JOIN auf die User-Tabelle. Da
wir zwei mal auf diese Tabelle zugreifen werden müssen
wir hier ein Alias mit AS ...
verwenden.
SELECT ... FROM Nachricht JOIN User AS Sender ON Nachricht.Quelle = Sender.ID
Nun folgt der zweite JOIN auf die selbe Tabelle nur
verbinden wir die Datensätze mit der ID im
Nachricht.Ziel
-Feld.
SELECT ... FROM Nachricht JOIN User AS Sender ON Nachricht.Quelle = Sender.ID JOIN User AS Empfaenger ON Nachricht.Ziel = Empfaenger.ID
Nun können wir die Spalten angeben die wir auslesen wollen.
SELECT Nachricht.ID, Nachricht.Quelle, Sender.Username AS QuelleName, Nachricht.Ziel, Empfaenger.Username AS ZielName, Nachricht.Datum, Nachricht.Gelesen, Nachricht.Betreff, Nachricht.Inhalt FROM Nachricht JOIN User AS Sender ON Nachricht.Quelle = Sender.ID JOIN User AS Empfaenger ON Nachricht.Ziel = Empfaenger.ID
Obwohl wir im Template die IDs aus Nachricht.Quelle
und
Nachricht.Ziel
nicht brauchen brauchen wir sie jedoch in
unserem PHP-Skript um zu gucken ob wir die Nachricht lesen dürfen.
Nun sehen wir auch wie wichtig die ON-Bedingungen
sind. Wenn wir 100 User hätten und 50 Nachrichten gespeichert sind würden
wir ohne die ON-Bedingungen 100*100*50 = 500.000 Datensätze
auslesen, wobei die meisten Datensätze aus der Usertabelle nicht zum
Datensatz der Nachricht passt. Durch die ON-Bedingungen
lesen wir jedoch nur die Nachrichten mit den Datensätzen aus der
User-Tabelle aus die auch Sinn ergeben. Da wir
nicht alle Nachrichten brauchen sondern nur eine schränken
wir die Wahl der Datensätze mit der WHERE-Bedingung ein.
SELECT Nachricht.ID, Nachricht.Quelle, Sender.Username AS QuelleName, Nachricht.Ziel, Empfaenger.Username AS ZielName, Nachricht.Datum, Nachricht.Gelesen, Nachricht.Betreff, Nachricht.Inhalt FROM Nachricht JOIN User AS Sender ON Nachricht.Quelle = Sender.ID JOIN User AS Empfaenger ON Nachricht.Ziel = Empfaenger.ID WHERE Nachricht.ID = ?
Nun ist unsere SQL-Anfrage fertig und verwenden sie in unserem PHP-Skript.
<?php
$ret = array();
$ret['filename'] = 'pm_view.tpl';
$ret['data'] = array();
if (!isset($_GET['ID'])) {
return INVALID_LINK;
}
if (($ID = trim($_GET['ID'])) == '') {
return 'Bitte geben sie eine gültige ID an.'; // oder irgendwas passendes schreiben
}
$sql = 'SELECT
Nachricht.ID,
Nachricht.Quelle,
Sender.Username AS QuelleName,
Nachricht.Ziel,
Empfaenger.Username AS ZielName,
Nachricht.Datum,
Nachricht.Gelesen,
Nachricht.Betreff,
Nachricht.Inhalt
FROM
Nachricht
JOIN
User AS Sender
ON
Nachricht.Quelle = Sender.ID
JOIN
User AS Empfaenger
ON
Nachricht.Ziel = Empfaenger.ID
WHERE
Nachricht.ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $ID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ID, $Quelle, $QuelleName, $Ziel, $ZielName, $Datum, $Gelesen, $Betreff, $Inhalt);
if (!$stmt->fetch()) {
return 'Es wurde keine Nachricht mit der angegebenen ID gefunden.';
}
if ($UserID != $Quelle AND
$UserID != $Ziel) { // weder Quelle noch Ziel (erinnert ihr euch noch an De Morgan aus einen früheren Kapitel?...)
return 'Sie dürfen diese Nachricht nicht betrachten das wie weder von Ihnen ist noch für sie bestimmt ist.';
}
$stmt->close();
$ret['data']['ID'] = $ID;
$ret['data']['Quelle'] = $QuelleName;
$ret['data']['Ziel'] = $ZielName;
$ret['data']['Datum'] = $Datum;
$ret['data']['Gelesen'] = $Gelesen;
$ret['data']['Betreff'] = $Betreff;
$ret['data']['Inhalt'] = $Inhalt;
return $ret;
Zuerst prüfen wir ob es überhaupt eine Nachricht gibt, dann ob wir sie lesen dürfen und dann zeigen wie die Nachricht ggf. an, wenn alles in Ordnung ist. Danach setzen wir den Zeitpunkt wann der Empfänger die Nachricht gelesen hat.
<?php
// [...]
$ret['data']['ID'] = $ID;
$ret['data']['Quelle'] = $QuelleName;
$ret['data']['Ziel'] = $ZielName;
$ret['data']['Datum'] = $Datum;
$ret['data']['Gelesen'] = $Gelesen;
$ret['data']['Betreff'] = $Betreff;
$ret['data']['Inhalt'] = $Inhalt;
if (is_null($Gelesen) AND $Ziel == $UserID) {
$sql = 'UPDATE
Nachricht
SET
Gelesen = NOW()
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $ID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
}
return $ret;
Mit der is_null-Funktion prüfen wir ob das Gelesen-Feld gesetzt ist und prüfen mit einem einfachen Vergleich ob wir auch derjenige sind für den die Nachricht bestimmt ist. Wenn dies der Fall ist speichern wir den aktuellen Zeitpunkt in das Gelesen-Feld und markieren so das wir die Nachricht gelesen haben.
7. Auf eine Nachricht antworten
Der Programmteil zum Antworten auf einer Nachricht ist vergleichbar mit dem Programmteil zum Schreiben einer Nachricht nur das der Empfänger schon feststeht und der Betreff schon vorhanden ist.
Die Template-Datei sieht wie folgt aus.
<?php /* Daten: * ID - Die ID der Nachricht * Ziel - Der Empfänger der Nachricht * Betreff - Der alte Betreff der Nachricht */ ?><form action="index.php?section=pm&action=reply&ID=<?php echo $data['ID']; ?>" method="post"> <fieldset> <legend>Auf Nachricht antworten</legend> <label>Empfänger: <?php echo htmlspecialchars($data['Ziel']); ?></label> <label>Betreff: <input type="text" name="Betreff" value="Re: <?php echo htmlspecialchars($data['Betreff']); ?>" /></label> <label>Nachricht: <textarea name="Inhalt" cols="40" rows="10"></textarea></label> <input type="submit" name="formaction" value="Nachricht senden" /> </fieldset> </form>
Im PHP-Skript müssen wir nun zuerst die GET-Variable ID prüfen und die alten Nachricht auslesen.
<?php
$ret = array();
$ret['filename'] = 'pm_reply.tpl';
$ret['data'] = array();
if (!isset($_GET['ID'])) {
return INVALID_LINK;
}
if (($ID = trim($_GET['ID'])) == '') {
return 'Bitte geben sie eine gültige ID an.';
}
$sql = 'SELECT
Nachricht.ID,
Nachricht.Quelle,
User.Username,
Nachricht.Betreff
FROM
Nachricht
JOIN
User
ON
Nachricht.Quelle = User.ID
WHERE
Nachricht.ID = ? AND
Nachricht.Ziel = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('ii', $ID, $UserID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ID, $Quelle, $Username, $Betreff);
if (!$stmt->fetch()) {
return 'Die Nachricht existiert nicht oder sie haben keine Berechtigung die Nachricht zu betrachten.';
}
$stmt->close();
$ret['data']['ID'] = $ID;
$ret['data']['Ziel'] = $Username;
$ret['data']['Betreff'] = $Betreff;
return $ret;
?>
Wenn das Skript mit der POST-Methode aufgerufen wird fügen wir die Daten für eine neue Nachricht in die Datenbank ein.
<?php
// [...]
if (!$stmt->fetch()) {
return 'Die Nachricht existiert nicht oder sie haben keine Berechtigung die Nachricht zu betrachten.';
}
$stmt->close();
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['Betreff'], $_POST['Inhalt'])) {
return INVALID_FORM;
}
if (($Betreff = trim($_POST['Betreff'])) == '' OR
($Inhalt = trim($_POST['Inhalt'])) == '') {
return EMPTY_FORM;
}
$sql = 'INSERT INTO
Nachricht(Quelle,Ziel,Datum,Betreff,Inhalt)
VALUES
(?,?,NOW(),?,?)';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('iiss', $UserID, $Quelle, $Betreff, $Inhalt);
if (!$stmt->execute()) {
return $stmt->error;
}
return showInfo('Die Antwort wurde verschickt.');
}
$ret['data']['ID'] = $ID;
$ret['data']['Ziel'] = $Username;
$ret['data']['Betreff'] = $Betreff;
return $ret;
?>
Zur Bestätigung wird zum Schluss noch eine Meldung angezeigt. Nicht wundern
dass die Variable $Quelle
den Wert für die Spalte
Ziel enthält, da wir ja eine Antwort schreiben und
die ID vom Sender übernehmen müssen.
8. Nachrichten löschen
Zuletzt fügen wir noch den Quellcode hinzu um Nachrichten zu löschen.
In der Übersicht finden wir schon Formularelemente mit denen wir
Nachrichten auswählen und löschen können. Wenn wir das Formular jedoch
abschicken erhalten wir momentan die Fehlermeldung
Warning: include(pm_delete.php) [function.include]: failed to open
stream: No such file or directory in /.../pm.php on line 12
.
Diese schreiben wir nun. Die IDs der zu löschenden Nachrichten werden
als Array übertragen. Jedoch prüfen wir zuerst ob die Daten vorhanden
sind.
<?php
if (!isset($_POST['ID'])) {
return 'Bitte wählen sie mindestens eine Nachricht zum löschen aus.';
}
if (!is_array($_POST['ID'])) {
return INVALID_FORM;
}
foreach ($_POST['ID'] as $ID) {
// [...]
}
return showInfo('Alle markierten Nachrichten wurden gelöscht');
?>
Für jede ID müssen wir überprüfen ob die Nachricht existiert und ob es unsere Nachricht ist. Wir definieren vor der Schleife das MySQL-Statement und füllen es innerhalb der Schleife mit Daten.
<?php
if (!isset($_POST['ID'])) {
return 'Bitte wählen sie mindestens eine Nachricht zum löschen aus.';
}
if (!is_array($_POST['ID'])) {
return INVALID_FORM;
}
$sql = 'DELETE FROM
Nachricht
WHERE
ID = ? AND
Ziel = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
foreach ($_POST['ID'] as $ID) {
$stmt->bind_param('ii', $ID, $UserID);
if (!$stmt->execute()) {
return $stmt->error;
}
}
return showInfo('Alle markierten Nachrichten wurden gelöscht');
?>
Und somit sind wir mit allem Fertig. Wir können Nachrichten schreiben, lesen und löschen, sowie auf Nachrichten antworten (auch wenn sie ganz normale Nachrichten sind).