Quakenet/#php Tutorial

Hinweis: Wenn sie diese Seite von einer externen URL aufgerufen haben achten sie darauf das alle Kapitel aufeinander aufbauen. Stellen sie daher sicher dass sie alle vorherigen Kapitel gelesen haben, da sie sonst relevante Informationen übersehen.

Nachrichtensystem

  1. Aufbau des Nachrichtensystems
  2. Einbindung in unser Templatesystem
  3. JOIN-Anfragen
  4. Anzeigen der Nachrichten
  5. Schreiben neuer Nachrichten
  6. Nachricht anzeigen
  7. Auf eine Nachricht antworten
  8. Nachrichten löschen

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.

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.

  1. 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.

  2. Des Weiteren kann man sich den Inhalt einer Nachricht angucken. Darauf folgend kann man z.B. die Nachricht löschen oder auf dieser Antworten.

  3. 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&amp;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&amp;action=view&amp;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&amp;action=view&amp;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&amp;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&amp;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&amp;action=reply&amp;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&amp;action=reply&amp;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).

Fragen zum Kapitel

Keine Fragen zum Kapitel vorhanden

Zurück zu Weiter zu
Copyright © bei den OPs von #php.de/QuakeNet Valid XHTML 1.0 Strict Valid CSS!