1. Funktionalität des neuen Newsskripts
Da wir nun ein Loginsystem haben können wir ein neues Newsskript mit mehr Funktionalität schreiben. Neben der eigentlichen News wird es im Newssystem auch Links für Quellen, Kommentare für Newsbeiträge und Newskategorien geben.
2. Aufbaue der MySQL-Tabellen
Wir werden user Newssystem so schreiben das eine News zu einer
Kategorie gehört. Wenn man möchte das eine News zu mehreren
Kategorien gehört muss man ähnliche Tabellen wie für
unser Adminbereich anlegen. In unserem Fall erstellen wir
zuerst die Tabelle News_Kategorie
für die Kategorien.
-
ID
- Selbsterklärend, ist eh immer das selbe. -
Name
- Der Name der Kategorie, einVARCHAR(40)
mit NOT NULL sollte reichen.
Dann gibt es natürlich die Tabelle News
mit den Newseinträgen.
-
ID
- Klar. -
AutorID
- Die ID vom Benutzer der die News geschrieben hat. Daher ist der TypINT
mit NOT NULL. -
Titel
- Wie im einfachen NewsskriptVARCHAR(100)
mit NOT NULL. -
Datum
- Auch wie im einfachen Newsskript vom TypDATETIME
mit NOT NULL -
Inhalt
- Typ itTEXT
mit den Attribut NOT NULL. -
KatID
- Diese Spalte enthält die ID der Kategorie zu der die News gehört. Da alle unsere IDs den SpaltentypINT
haben bekommt auch diese Spalte den TypINT
mit NOT NULL.
Für die Kommentare der News legen wir die Tabelle News_Kommentar
an.
-
ID
- Wie immer. -
AutorID
- Die ID desjenigen der den Kommentar geschrieben hat. Typ istINT
mit NOT NULL. -
Inhalt
- Der Kommentar selbst, vom TypTEXT
mit NOT NULL. -
Datum
- Der Zeitpunkt wann der Kommentar verfasst wurde, Typ istDATETIME
mit NOT NULL. -
NewsID
- Die ID von der News zu der dieser Kommentar gehört. Daher vom TypINT
mit NOT NULL.
Falls es zu einer News Quellenangaben oder anderseitig Links gibt werden diese
in der Tabelle News_Link
gespeichert.
-
ID
- Siehe oben. -
NewsID
- Die News zu der der Link gehört. Typ istINT
mit NOT NULL. -
URL
- Die eigentlichen URL vom Link, der TypVARCHAR(150)
sollte reichen, wie überall mit dem Attribut NOT NULL. -
Beschreibung
- Ein aussagekräftiger Name für den Link, Typ istVARCHAR(100)
mit NOT NULL.
Diese Tabellen fügen wir nun mit phpMyAdmin in die Datenbank ein. Die
alte News
-Tabelle muss man ggf. vorher löschen.
3. Kategorie hinzufügen
Zuerst schreiben wir den Adminteil in der wir die Kategorien bearbeiten können
da es ohne Kategorien auch keine News geben kann. In der Rechteliste
-Tabelle
fügen wir dafür das neue Recht NewsKat
ein. Aufgrund des neuen
Recht werden wir die Dateien admin_NewsKat_add.php
, admin_NewsKat_edit.php
und admin_NewsKat_del.php
hinzufügen müssen. Aber zuerst der
Teil zum hinzufügen neuer Kategorien.
Zuerst schreiben wir die Template-Datei admin_NewsKat_add.tpl
.
<form action="index.php?section=admin&cat=NewsKat&action=add" method="post"> <fieldset> <legend>Newskategorie hinzufügen</legend> <label>Name: <input type="text" name="Name" /></label> <input type="submit" name="formaction" value="Kategorie hinzufügen" /> </fieldset> </form>
Die Include-Datei admin_NewsKat_add.php
sieht wie folgt aus.
<?php
$ret = array();
$ret['filename'] = 'admin_NewsKat_add.tpl';
$ret['data'] = array();
if ('POST' != $_SERVER['REQUEST_METHOD']) {
return $ret;
}
if (!isset($_POST['formaction'], $_POST['Name'])) {
return INVALID_FORM;
}
if ('' == $Name = trim($_POST['Name'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News_Kategorie
WHERE
Name = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('s', $Name);
if (!$stmt->execute()) {
return $stmt->error;
}
if ($stmt->fetch()) {
return 'Es ist bereits eine Kategorie mit diesen Namen vorhanden.';
}
$stmt->close();
$sql = 'INSERT INTO
News_Kategorie(Name)
VALUES
(?)';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('s', $Name);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
return showInfo('Die Kategorie wurde hinzugefügt.');
?>
4. Kategorie bearbeiten
Wir beginnen wieder zuerst mit den Template-Dateien. Zuerst schreiben wir
die Datei admin_NewsKat_edit_select.tpl
um eine Kategorie auszuwählen.
<?php /* Daten: * Kategorien - Ein Array mit allen bereits vorhandenen Kategorien. Bei einem Arrayelement * ist der Schlüssel die ID und der Wert der Name der Kategorie. */ ?><form action="index.php?section=admin&cat=NewsKat&action=edit" method="post"> <fieldset> <legend>Kategorie wählen</legend> <label>Kategorie <select name="KatID"> <option value="0">Bitte wählen</option> <?php foreach ($data['Kategorien'] as $key => $value) { ?> <option value="<?php echo $key; ?>"><?php echo htmlspecialchars($value); ?></option> <?php } ?> </select></label> <input type="submit" name="formaction" value="Kategorie wählen"> </fieldset> </form>
In der Include-Datei zeigen wir dieses Template an wenn die Datei nicht mit der POST-Methode aufgerufen wurde.
<?php
$ret = array();
$ret['filename'] = 'admin_NewsKat_edit_select.tpl';
$ret['data'] = array();
if ('POST' != $_SERVER['REQUEST_METHOD']) {
$sql = 'SELECT
ID,
Name
FROM
News_Kategorie
ORDER BY
Name ASC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$Kategorien = array();
while ($row = $result->fetch_assoc()) {
$Kategorien[$row['ID']] = $row['Name'];
}
$ret['data']['Kategorien'] = $Kategorien;
$result->close();
return $ret;
}
?>
Wenn wir nun eine Kategorie auswählen zeigen wir die Daten in einem
weiteren Template admin_NewsKat_edit_show.tpl
an.
<?php /* Daten: * KatID - Die ID von der Kategorie * Name - Der Name der Kategorie */ ?><form action="index.php?section=admin&cat=NewsKat&action=edit" method="post"> <fieldset> <legend>Kategorie bearbeiten</legend> <label>Name: <input type="text" name="Name" value="<?php echo htmlspecialchars($data['Name']); ?>" /></label> <input type="submit" name="formaction" value="Kategorie speichern" /> <input type="hidden" name="KatID" value="<?php echo $data['KatID']; ?>" /> </fieldset> </form>
Anhand der benötigten Variablen wissen wir wie unserer Include-Datei aussehen muss.
<?php
// [...]
$ret['data']['Kategorien'] = $Kategorien;
$result->close();
return $ret;
}
if (!isset($_POST['formaction'], $_POST['KatID'])) {
return INVALID_FORM;
}
if ('' == $KatID = trim($_POST['KatID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID,
Name
FROM
News_Kategorie
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($KatID, $Name);
if (!$stmt->fetch()) {
return 'Es wurde keine News mit der angegebenen ID gefunden.';
}
$stmt->close();
if ($_POST['formaction'] == 'Kategorie speichern') {
// todo
} else {
$ret['data']['KatID'] = $KatID;
$ret['data']['Name'] = $Name;
$ret['filename'] = 'admin_NewsKat_edit_show.tpl';
return $ret;
}
?>
Wenn der Admin auf Kategorie speichern
klickt speichern
wie den neuen Namen ab, solange er nicht doppel oder leer ist.
<?php
// [...]
if ($_POST['formaction'] == 'Kategorie speichern') {
if (!isset($_POST['Name'])) {
return INVALID_FORM;
}
if ('' == $Name = trim($_POST['Name'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News_Kategorie
WHERE
Name = ? AND
ID != ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('si', $Name, $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
if ($stmt->fetch()) {
return 'Es ist bereits eine Kategorie mit den Namen vorhanden.';
}
$stmt->close();
$sql = 'UPDATE
News_Kategorie
SET
Name = ?
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('si', $Name, $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
return showInfo('Die Kategorie wurde bearbeitet.');
} else {
// [...]
?>
5. Kategorie löschen
Wie beim Bearbeiten schreiben wir ein Template welches eine Auswahlliste aller Kategorien bereitstellt. Das Template ist fast identisch wie beim Bearbeiten.
<?php /* Daten: * Kategorien - Ein Array mit allen bereits vorhandenen Kategorien. Bei einem Arrayelement * ist der Schlüssel die ID und der Wert der Name der Kategorie. */ ?><form action="index.php?section=admin&cat=NewsKat&action=del" method="post"> <fieldset> <legend>Kategorie wählen</legend> <label>Kategorie <select name="KatID"> <option value="0">Bitte wählen</option> <?php foreach ($data['Kategorien'] as $key => $value) { ?> <option value="<?php echo $key; ?>"><?php echo htmlspecialchars($value); ?></option> <?php } ?> </select></label> <input type="submit" name="formaction" value="Kategorie löschen"> </fieldset> </form>
In der Include-Datei laden wir erstmal alle Kategorien in ein Array und zeigen das Template an.
<?php
$ret = array();
$ret['filename'] = 'admin_NewsKat_del.tpl';
$ret['data'] = array();
$sql = 'SELECT
ID,
Name
FROM
News_Kategorie
ORDER BY
Name ASC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$Kategorien = array();
while ($row = $result->fetch_assoc()) {
$Kategorien[$row['ID']] = $row['Name'];
}
$ret['data']['Kategorien'] = $Kategorien;
if ('POST' != $_SERVER['REQUEST_METHOD']) {
return $ret;
}
?>
Beim Löschen müssen wir aufpassen das wir keine Kategorie löschen zu denen noch Newsbeiträge gehören. Daher schreiben wir einige Abfragen ob wir die News auch wirklich löschen können.
<?php
// [...]
if ('POST' != $_SERVER['REQUEST_METHOD']) {
return $ret;
}
if (!isset($_POST['formaction'], $_POST['KatID'])) {
return INVALID_FORM;
}
if ('' == $KatID = trim($_POST['KatID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News_Kategorie
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($KatID);
if (!$stmt->fetch()) {
return 'Es wurde keine Kategorie mit der angegebenen ID gefunden.';
}
$stmt->close();
$sql = 'SELECT
ID
FROM
News
WHERE
KatID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
if ($stmt->fetch()) {
return 'Es gibt noch Newsbeiträge die diese Kategorie verwenden.';
}
$stmt->close();
$sql = 'DELETE FROM
News_Kategorie
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
return showInfo('Die Kategorie wurde gelöscht.');
?>
6. News hinzufügen
Beim hinzufügen (und bearbeiten) einer News müssen wir uns überlegen wie wir die Formulare aufbauen. Neben der eigentlichen News müssen z.B. auch die Newslinks angegeben werden. Es gibt verschiedene Möglichkeiten wie man das machen kann.
-
Wenn wir eine News hinzugefügt haben könnten wir die Newslinks über ein extra Adminbereich bearbeiten, also auch wieder mit links woe Hinzufügen, Bearbeiten und Löschen. Dies ist jedoch umständlich für jede News zwei Bereiche zu benutzen (einmal
News
und dann z.B. ein BereichNewsLink
). -
In der Eingabemaske der News bieten wir z.B. 5 Zeilen an in der wir die Links mit den Beschreibungen hinzufügen können. Wenn wir 6 Links hinzufügen wollen haben wir ein Problem. Wenn wir die Anzahl von 5 auf 6 erhöhen haben wir das selbe Problem wenn wir 7 Links hinzufügen. Egal wie groß wir
N
wählen, wir können nieN+1
Links hinzufügen. Und die vielen Eingabefelder würden stören wenn wir nur einen Link hinzufügen wollen. -
Wir könnten die Eingabefelder dynamisch mit Javascript hinzufügen. Javascript ist jedoch nicht bestandteil des Tutorials.
-
Innerhalb des Adminbereichs der News bieten wir ein Formular an um einen einzelnen Link hinzuzufügen. Diesen speichern wir schonmal in der Datenbank ab. Wenn wir dann die News hinzufügen ändern wir die IDs der Links bzw. übernehmen die Links aus einer Temporären Tabelle. Nur dumm wenn wir den Vorgang abbrechen, dann haben wir in der Datenbank Newslinks die zu keiner News gehören.
So hoffnungslos es auch scheinen mag, es gibt ne Lösung genannt Session. Wir bereiten die Newserstellung vor indem wir erst alle Daten in einer Session speichern und dann erst in die Datenbank einfügen wenn wir fertig sind. So wird die Datenbank nicht vorher mit ungenutzten Müll gefüttert sondern alle Daten liegen erstmal in der Session. Wenn der Vorgang abgebrochen wird verfällt die Session eh nach einiger Zeit.
Zur Vorbereitung der Session setzen wir die session.use_only_cookies-Einstellung
auf 1
, damit die Session-ID nur per Cookie übermittelt wird. Dies machen wir
in der index.php
-Datei nach dem einstellen des Error-Reportings.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('session.use_only_cookies', 1);
// [...]
?>
Die Template-Datei sieht nun sehr überladen aus.
<?php /* Daten: * Titel - Der Titel der News * Inhalt - Der Text der News * KatID - Die ID der aktuell gewählten Kategorie * Kategorien - Ein Array mit allen Kategorien. Bei einem Arrayelement ist der Schlüssel * die ID und der Wert der Name der Kategorie. * Links - Ein Array mit den Links die zu der News gehört. Bei einem Arrayelement ist der * Schlüssel eine fortlaufende Nummer, der Wert entspricht einem assoziativen * Array mit den Schlüssel 'URL' und 'Beschreibung' mit den entsprechenden Werten. */ ?><form action="index.php?section=admin&cat=News&action=add" method="post"> <fieldset> <legend>News hinzufügen</legend> <label>Titel <input type="text" name="Titel" value="<?php echo htmlspecialchars($data['Titel']); ?>" /></label> <label>Inhalt <textarea name="Inhalt" cols="40" rows="10"><?php echo htmlspecialchars($data['Inhalt']); ?></textarea></label> <label>Kategorie <select name="KatID"> <option value="0">Bitte wählen</option> <?php foreach ($data['Kategorien'] as $key => $value) { if ($data['KatID'] == $key) { ?> <option value="<?php echo $key; ?>" selected="selected"><?php echo htmlspecialchars($value); ?></option> <?php } else { ?> <option value="<?php echo $key; ?>"><?php echo htmlspecialchars($value); ?></option> <?php } } ?> </select> </label> <fieldset> <legend>Newslinks</legend> <?php if (count($data['Links'])) { ?> <ul> <?php foreach ($data['Links'] as $key => $value) { ?> <li><label> <input type="checkbox" name="LinkID[]" value="<?php echo $key; ?>"/> <?php echo htmlspecialchars($value['Beschreibung']); ?></label> - <a href="<?php echo htmlspecialchars($value['URL']); ?>"><?php echo htmlspecialchars($value['URL']); ?></a> </li> <?php } ?> </ul> <input type="submit" name="formaction" value="Markierte Links löschen" /> <?php } else { ?> <p class="info"> Es sind momentan keine Links hinzugefügt. </p> <?php } ?> <fieldset> <legend>Neuen Link hinzufügen</legend> <label>URL: <input type="text" name="URL" /></label> <label>Beschreibung: <input type="text" name="Beschreibung" /></label> <input type="submit" name="formaction" value="Link hinzufügen" /> </fieldset> </fieldset> <input type="submit" name="formaction" value="News hinzufügen" /> </fieldset> </form>
Wie man sieht wird das Template mit diversen Werten gefüllt und das Template selbst enthält mehrere Submit-Buttons mit unterschiedlichen Beschriftungen für entsprechend unterschiedliche Aktionen. All diese Aktionen müssen wir in unserem PHP-Skript abfragen. Außerdem müssen wir eine Session starten und alle Felder vorinitialisieren.
<?php
$ret = array();
$ret['filename'] = 'admin_News_add.tpl';
$ret['data'] = array();
$sql = 'SELECT
ID,
Name
FROM
News_Kategorie
ORDER BY
Name ASC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$Kategorien = array();
while ($row = $result->fetch_assoc()) {
$Kategorien[$row['ID']] = $row['Name'];
}
$ret['data']['Kategorien'] = $Kategorien;
session_start();
if (!isset($_SESSION['News']) OR 'POST' != $_SERVER['REQUEST_METHOD']) {
$_SESSION['News'] = array();
$_SESSION['News']['Titel'] = '';
$_SESSION['News']['Inhalt'] = '';
$_SESSION['News']['KatID'] = 0;
$_SESSION['News']['Links'] = array();
} else {
// [...]
}
$ret['data']['Titel'] = $_SESSION['News']['Titel'];
$ret['data']['Inhalt'] = $_SESSION['News']['Inhalt'];
$ret['data']['KatID'] = $_SESSION['News']['KatID'];
$ret['data']['Links'] = $_SESSION['News']['Links'];
return $ret;
?>
Falls ein Formularknopf verwendet wird prüfen wir zuerst
ob alle Formularfelder vorhanden ist, egal ob wir sie in diesem
Aufruf verwenden oder nicht. Danach speichern wir stehts die
Felder Titel
, Inhalt
und
KatID
in der Session.
<?php
// [...]
} else {
// Aufruf der POST und Session vorinitialisiert
if (!isset($_POST['formaction'], $_POST['Titel'], $_POST['Inhalt'],
$_POST['KatID'], $_POST['URL'], $_POST['Beschreibung'])) {
return INVALID_FORM;
}
// speichern der allgemeinen newsdaten
$_SESSION['News']['Titel'] = $_POST['Titel'];
$_SESSION['News']['Inhalt'] = $_POST['Inhalt'];
$_SESSION['News']['KatID'] = $_POST['KatID'];
}
// [...]
?>
Wenn wir einen Link hinzufügen wollen prüfen wir die
beiden Felder URL
und Beschreibung
und erweitern das Array.
<?php
// [...]
// speichern der allgemeinen newsdaten
$_SESSION['News']['Titel'] = $_POST['Titel'];
$_SESSION['News']['Inhalt'] = $_POST['Inhalt'];
$_SESSION['News']['KatID'] = $_POST['KatID'];
if ('Link hinzufügen' == $_POST['formaction']) {
if ('' == $URL = trim($_POST['URL']) OR
'' == $Beschreibung = trim($_POST['Beschreibung'])) {
return EMPTY_FORM;
}
$_SESSION['News']['Links'][] = array('URL' => $URL, 'Beschreibung' => $Beschreibung);
}
}
// [...]
?>
Wenn wir Links löschen wollen durchlaufen wir das Array und löschen die entsprechenden Arrayelemente aus der Liste.
<?php
// [...]
$_SESSION['News']['Links'][] = array('URL' => $URL, 'Beschreibung' => $Beschreibung);
}
if ('Markierte Links löschen' == $_POST['formaction']) {
if (!isset($_POST['LinkID'])) {
return EMPTY_FORM;
}
if (!is_array($_POST['LinkID'])) {
return INVALID_FORM;
}
foreach ($_POST['LinkID'] as $IDs) {
unset($_SESSION['News']['Links'][$IDs]);
}
}
}
// [...]
?>
Der spaßige Teil ist die Aktion News hinzufügen
. Zuerst
prüfen wir ob alle Texte eingegeben sind, dann ob die gewählte
Kategorie auch existiert und fügen dann die News und die Links
in die Datenbank ein.
<?php
// [...]
foreach ($_POST['LinkID'] as $IDs) {
unset($_SESSION['News']['Links'][$IDs]);
}
}
if ('News hinzufügen' == $_POST['formaction']) {
if ('' == $Titel = trim($_SESSION['News']['Titel']) OR
'' == $Inhalt = trim($_SESSION['News']['Inhalt']) OR
'' == $KatID = trim($_SESSION['News']['KatID'])) {
return EMPTY_FORM;
}
$Links = $_SESSION['News']['Links'];
// gucken ob es die Kategorie gibt
$sql = 'SELECT
ID
FROM
News_Kategorie
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($KatID);
if (!$stmt->fetch()) {
return 'Es wurde keine Kategorie mit der angegebenen ID gefunden.';
}
$stmt->close();
$sql = 'INSERT INTO
News(Titel, Inhalt, Datum, KatID, AutorID)
VALUES
(?, ?, NOW(), ?, ?)';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('ssii', $Titel, $Inhalt, $KatID, $UserID);
if (!$stmt->execute()) {
return $stmt->error;
}
$NewsID = $stmt->insert_id;
$stmt->close();
$sql = 'INSERT INTO
News_Link(NewsID, URL, Beschreibung)
VALUES
(?,?,?)';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
foreach ($Links as $Link) {
$stmt->bind_param('iss', $NewsID, $Link['URL'], $Link['Beschreibung']);
if (!$stmt->execute()) {
return $stmt->error;
}
}
$stmt->close();
unset($_SESSION['News']); // Sessiondaten löschen, brauchen wir nicht mehr.
return showInfo('Die News wurde hinzugefügt.');
}
}
// [...]
?>
7. News bearbeiten
Wenn wir eine News bearbeiten benutzen wir den selben Formularaufbau
wie beim Hinzufügen. Wir müssen nur die Session anders vorinitialisieren.
Statt leere Strings und Arrays füllen wir die Session mit einem
Datensatz aus der News
-Tabelle.
Zuerst schreiben wir den einfachen Fall wo wir eine News über eine
Dropdown-Liste auswählen. Die admin_News_edit_select.tpl
-Datei
sieht wie folgt aus.
<?php /* * Daten: * News - Ein Array mit allen Newsbeiträgen. Bei einem Arrayelement ist der Schlüssel * Die ID der News und der Wert ist ein assoziatives Array mit den folgenden * Werten: * Datum - Das Datum wann die News geschrieben wurde * Titel - Der Titel der News, ungekürzt. */ ?><form action="index.php?section=admin&cat=News&action=edit" method="post"> <fieldset> <legend>News wählen</legend> <label>News: <select name="NewsID"> <option value="0">Bitte wählen</option> <?php foreach ($data['News'] as $key => $value) { ?> <option value="<?php echo $key; ?>">(<?php echo htmlspecialchars($value['Datum']); ?>) <?php echo htmlspecialchars($value['Titel']); ?></option> <?php } ?> </select></label> <input type="submit" name="formaction" value="News wählen" /> </fieldset> </form>
Der erste Teil des PHP-Skripts sieht wie folgt aus.
<?php
$ret = array();
$ret['filename'] = 'admin_News_edit_select.tpl';
$ret['data'] = array();
if ('POST' != $_SERVER['REQUEST_METHOD']) {
$sql = 'SELECT
ID,
Titel,
Datum
FROM
News
ORDER BY
Datum DESC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$News = array();
while ($row = $result->fetch_assoc()) {
$News[$row['ID']] = array('Datum' => $row['Datum'], 'Titel' => $row['Titel']);
}
$ret['data']['News'] = $News;
} else {
// [...]
}
return $ret;
?>
Da wir fast die gleiche Formular fürs Bearbeiten benutzen wie fürs
Hinzufügen einer News können wir die bereits vorhandene Template-Datei
kopieren und in admin_News_edit_details.tpl
speichern.
<?php /* Daten: * Titel - Der Titel der News * Inhalt - Der Text der News * KatID - Die ID der aktuell gewählten Kategorie * Kategorien - Ein Array mit allen Kategorien. Bei einem Arrayelement ist der Schlüssel * die ID und der Wert der Name der Kategorie. * Links - Ein Array mit den Links die zu der News gehört. Bei einem Arrayelement ist der * Schlüssel eine fortlaufende Nummer, der Wert entspricht einem assoziativen * Array mit den Schlüssel 'URL' und 'Beschreibung' mit den entsprechenden Werten. */ ?><form action="index.php?section=admin&cat=News&action=edit" method="post"> <fieldset> <legend>News bearbeiten</legend> <label>Titel <input type="text" name="Titel" value="<?php echo htmlspecialchars($data['Titel']); ?>" /></label> <label>Inhalt <textarea name="Inhalt" cols="40" rows="10"><?php echo htmlspecialchars($data['Inhalt']); ?></textarea></label> <label>Kategorie <select name="KatID"> <option value="0">Bitte wählen</option> <?php foreach ($data['Kategorien'] as $key => $value) { if ($data['KatID'] == $key) { ?> <option value="<?php echo $key; ?>" selected="selected"><?php echo htmlspecialchars($value); ?></option> <?php } else { ?> <option value="<?php echo $key; ?>"><?php echo htmlspecialchars($value); ?></option> <?php } } ?> </select> </label> <fieldset> <legend>Newslinks</legend> <?php if (count($data['Links'])) { ?> <ul> <?php foreach ($data['Links'] as $key => $value) { ?> <li><label> <input type="checkbox" name="LinkID[]" value="<?php echo $key; ?>"/> <?php echo htmlspecialchars($value['Beschreibung']); ?></label> - <a href="<?php echo htmlspecialchars($value['URL']); ?>"><?php echo htmlspecialchars($value['URL']); ?></a> </li> <?php } ?> </ul> <input type="submit" name="formaction" value="Markierte Links löschen" /> <?php } else { ?> <p class="info"> Es sind momentan keine Links hinzugefügt. </p> <?php } ?> <fieldset> <legend>Neuen Link hinzufügen</legend> <label>URL: <input type="text" name="URL" /></label> <label>Beschreibung: <input type="text" name="Beschreibung" /></label> <input type="submit" name="formaction" value="Link hinzufügen" /> </fieldset> </fieldset> <input type="submit" name="formaction" value="News bearbeiten" /> </fieldset> </form>
In der Include-Datei müssen wir nun entsprechend auf die Submit-Buttons reagieren. Zuerst müssen wir jedoch die Session füllen.
<?php
// [...]
} else {
if (!isset($_POST['formaction'])) {
return INVALID_FORM;
}
$ret['filename'] = 'admin_News_edit_details.tpl';
session_start();
if (!isset($_SESSION['News']) OR 'News wählen' == $_POST['formaction']) {
if (!isset($_POST['formaction'], $_POST['NewsID'])) {
return INVALID_FORM;
}
if ('' == $NewsID = trim($_POST['NewsID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID,
Titel,
Inhalt,
KatID
FROM
News
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($NewsID, $Titel, $Inhalt, $KatID);
if (!$stmt->fetch()) {
return 'Es wurde keine News mit dieser ID gefunden.';
}
$stmt->close();
$_SESSION['News'] = array();
$_SESSION['News']['ID'] = $NewsID;
$_SESSION['News']['Titel'] = $Titel;
$_SESSION['News']['Inhalt'] = $Inhalt;
$_SESSION['News']['KatID'] = $KatID;
$sql = 'SELECT
URL,
Beschreibung
FROM
News_Link
WHERE
NewsID = ?';
if (!$stmt = $db->prepare($sql)) {
return $stmt->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$Links = array();
$stmt->bind_result($URL, $Beschreibung);
while ($stmt->fetch()) {
$Links[] = array('URL' => $URL, 'Beschreibung' => $Beschreibung);
}
$stmt->close();
$_SESSION['News']['Links'] = $Links;
}
$sql = 'SELECT
ID,
Name
FROM
News_Kategorie
ORDER BY
Name ASC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$Kategorien = array();
while ($row = $result->fetch_assoc()) {
$Kategorien[$row['ID']] = $row['Name'];
}
$ret['data']['Kategorien'] = $Kategorien;
$ret['data']['Titel'] = $_SESSION['News']['Titel'];
$ret['data']['Inhalt'] = $_SESSION['News']['Inhalt'];
$ret['data']['KatID'] = $_SESSION['News']['KatID'];
$ret['data']['Links'] = $_SESSION['News']['Links'];
}
return $ret;
?>
Dann kommt der gleiche Programmcode zum Speichern der aktuellen Inhalte und zum Verwalten der Links.
<?php
// [...]
$stmt->close();
$_SESSION['News']['Links'] = $Links;
}
if (isset($_POST['Titel'], $_POST['Inhalt'], $_POST['KatID'])) {
// speichern der allgemeinen newsdaten
$_SESSION['News']['Titel'] = $_POST['Titel'];
$_SESSION['News']['Inhalt'] = $_POST['Inhalt'];
$_SESSION['News']['KatID'] = $_POST['KatID'];
}
if ('Markierte Links löschen' == $_POST['formaction']) {
if (!isset($_POST['LinkID'])) {
return EMPTY_FORM;
}
if (!is_array($_POST['LinkID'])) {
return INVALID_FORM;
}
foreach ($_POST['LinkID'] as $IDs) {
unset($_SESSION['News']['Links'][$IDs]);
}
}
if ('Link hinzufügen' == $_POST['formaction']) {
if ('' == $URL = trim($_POST['URL']) OR
'' == $Beschreibung = trim($_POST['Beschreibung'])) {
return EMPTY_FORM;
}
$_SESSION['News']['Links'][] = array('URL' => $URL, 'Beschreibung' => $Beschreibung);
}
$sql = 'SELECT
ID,
Name
FROM
News_Kategorie
ORDER BY
Name ASC';
// [...]
?>
Und zum Schluss der Programmteil zum Speichern der News.
<?php
// [...]
$_SESSION['News']['Links'][] = array('URL' => $URL, 'Beschreibung' => $Beschreibung);
}
if ('News bearbeiten' == $_POST['formaction']) {
if ('' == $Titel = trim($_SESSION['News']['Titel']) OR
'' == $Inhalt = trim($_SESSION['News']['Inhalt']) OR
'' == $KatID = trim($_SESSION['News']['KatID'])) {
return EMPTY_FORM;
}
$Links = $_SESSION['News']['Links'];
// gucken ob es die Kategorie gibt
$sql = 'SELECT
ID
FROM
News_Kategorie
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $KatID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($KatID);
if (!$stmt->fetch()) {
$stmt->close();
return 'Es wurde keine Kategorie mit der angegebenen ID gefunden.';
}
$stmt->close();
// newsdaten speichern
$sql = 'UPDATE
News
SET
Titel = ?,
Inhalt = ?,
KatID = ?
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('ssii', $Titel, $Inhalt, $KatID, $_SESSION['News']['ID']);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
// alte links löschen
$sql = 'DELETE FROM
News_Link
WHERE
NewsID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $_SESSION['News']['ID']);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
// neue links hinzufügen
$sql = 'INSERT INTO
News_Link(NewsID, URL, Beschreibung)
VALUES
(?,?,?)';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
foreach ($Links as $Link) {
$stmt->bind_param('iss', $_SESSION['News']['ID'], $Link['URL'], $Link['Beschreibung']);
if (!$stmt->execute()) {
return $stmt->error;
}
}
$stmt->close();
unset($_SESSION['News']);
return showInfo('Die News wurde bearbietet.');
}
$sql = 'SELECT
ID,
Name
FROM
News_Kategorie
ORDER BY
Name ASC';
?>
8. News löschen
Eine News zu löschen geht bedeutend einfacher da wir nur eine News auswählen brauchen und dann die Kommentare und Links der News sowie die News selbst löschen.
<?php /* * Daten: * News - Ein Array mit allen Newsbeiträgen. Bei einem Arrayelement ist der Schlüssel * Die ID der News und der Wert ist ein assoziatives Array mit den folgenden * Werten: * Datum - Das Datum wann die News geschrieben wurde * Titel - Der Titel der News, ungekürzt. */ ?><form action="index.php?section=admin&cat=News&action=del" method="post"> <fieldset> <legend>News löschen</legend> <label>News: <select name="NewsID"> <option value="0">Bitte wählen</option> <?php foreach ($data['News'] as $key => $value) { ?> <option value="<?php echo $key; ?>">(<?php echo htmlspecialchars($value['Datum']); ?>) <?php echo htmlspecialchars($value['Titel']); ?></option> <?php } ?> </select></label> <input type="submit" name="formaction" value="News löschen" /> </fieldset> </form>
Die Include-Datei beginnt so ähnlich wie die beim Bearbeiten.
<?php
$ret = array();
$ret['filename'] = 'admin_News_del.tpl';
$ret['data'] = array();
if ('POST' != $_SERVER['REQUEST_METHOD']) {
$sql = 'SELECT
ID,
Titel,
Datum
FROM
News
ORDER BY
Datum DESC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$News = array();
while ($row = $result->fetch_assoc()) {
$News[$row['ID']] = array('Datum' => $row['Datum'], 'Titel' => $row['Titel']);
}
$ret['data']['News'] = $News;
return $ret;
}
?>
Nachdem wir eine News ausgewählt haben löschen wir diese.
<?php
// [...]
return $ret;
}
if (!isset($_POST['formaction'], $_POST['NewsID'])) {
return INVALID_FORM;
}
if ('' == $NewsID = trim($_POST['NewsID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($NewsID);
if (!$stmt->fetch()) {
return 'Es wurde keine News mit der ID gefunden.';
}
$stmt->close();
// links löschen
$sql = 'DELETE FROM
News_Link
WHERE
NewsID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
// kommentarie löschen
$sql = 'DELETE FROM
News_Kommentar
WHERE
NewsID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
// news löschen
$sql = 'DELETE FROM
News
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
return showInfo('Die News wurde gelöscht.');
?>
9. News anzeigen
Nachdem wir unsere Newskategorien und News hinzufügen können wollen wir sie auch
auf unserer Seite anzeigen lassen. Dazu schreiben wir unsere Template-Datei
news.tpl
.
<?php /* Daten * News - Ein Array mit Newsbeiträgen mit den folgendem Aufbau: * ID - Die ID der News * Autor - Der Name des Autors der die News geschrieben hat * Datum - Das Datum der Veröffentlichung * Titel - Der Titel der News * Inhalt - Der Text der News * Anzahl - Die Anzahl der Newskommentare * Links - Ein Array mit Links mit den folgendem Aufbau: * URL - Die URL vom Link * Beschreibung - Der Bezeichner vom Link */ ?><h1>News</h1> <?php if (count($data['News'])) { foreach ($data['News'] as $News) { ?> <h2><?php echo htmlspecialchars($News['Titel']); ?></h2> <div class="newsheader">Geschrieben von <?php echo htmlspecialchars($News['Autor']); ?> um <?php echo htmlspecialchars($News['Datum']); ?></div> <p> <?php echo nl2br(htmlspecialchars($News['Inhalt'])); ?> </p> <?php if (count($News['Links'])) { ?> <div class="links"> Links: <ul> <?php foreach ($News['Links'] as $Link) { ?> <li><a href="<?php echo $Link['URL']; ?>"><?php echo htmlspecialchars($Link['Beschreibung']); ?></a></li> <?php } ?> </ul> </div> <?php } else { ?> <p class="info"> Keine Links vorhanden. </p> <?php } ?> <div class="comments"> <a href="index.php?section=newscomments&ID=<?php echo $News['ID']; ?>">Kommentare (<?php echo $News['Anzahl']; ?>)</a> </div> <?php } } else { ?> <p class="info"> Es sind keine News vorhanden. </p> <?php } ?>
Wenn wir uns angucken wie die Template-Daten aussehen müssen stoßen wir an einige kleine und große Probleme wie wir die Daten elegenant aus der Datenbank auslesen.
Um den Benutzernamen des Autors auszulesen verwenden wir ein einfachen
JOIN auf die Tabelle Users
. Für die Anzahl der
Kommentare zu einer News müssen wir uns was einfallen lassen. Eine einfache
Möglichkeit wäre innerhalb einer while-Schleife, die die
Datensätze aus der Newstabelle ausliest, eine MySQL-Anfrage abzuschicken die
die Kommentare der News ausliest und davon dann die Anzahl bestimmen. Mit einem
JOIN und einem GROUP BY geht dies jedoch
viel eleganter.
10. SELECT-Anfragen gruppieren und Spezialfunktionen verwenden
In einer SELECT-Anfrage kann man mehrere Datensätze zu einem Datensatz
zusammenfassen. Dazu wird das Schlüsselwort GROUP BY
verwendet.
Danach gibt man z.B. die Spalte an, nach der die Datensätze zusammengefasst werden.
Foo | Bar |
---|---|
3 | 6 |
3 | 10 |
5 | 9 |
5 | 11 |
5 | 12 |
2 | 3 |
Wenn wir diese Tabelle nach der Spalte Foo
gruppieren enthalten wir
die folgende Ergebnistabelle.
Foo |
---|
3 |
5 |
2 |
Jetzt stellt sich die Frage was passiert wenn wir trotzdem weiterhin den
Wert Bar
auslesen, also weiterhin in der Anfrage SELECT Foo, Bar FROM ...
stehen haben.
Foo | Bar |
---|---|
3 | ? |
5 | ? |
2 | ? |
Die Fragezeichen deuten an dass es ggf. nicht eindeutig ist was ausgelesen
wird. So könnte es z.B. der Bar
-Wert aus dem letzen Datensatz genommen
wird, oder der erste, oder sogar ein zufälliger Wert. In MySQL
gibt es jedoch eine Menge Funktionen
die man mit GROUP BY verwenden kann. Diese Funktionen beziehen sich dabei auf die
Werte die dann du einer Gruppierung gehören. In unserem Beispiel sind das die
Werte (6,10)
für die Gruppierung von Foo=3
,
(9,11,12)
für die Gruppierung von Foo=5
und
(3)
für die Gruppierung von Foo=2
. Anstatt nun
den Wert Bar
abzufragen könnten wir das Maximum aus den
Gruppierungen mit MAX(Bar)
wählen oder den Durchschnitt mit AVG(Bar)
.
Foo | "Zusammengefaste Werte von Bar" | MAX(Bar) | AVG(Bar) |
---|---|---|---|
3 | (6,10) | 10 | 8 |
5 | (9,11,12) | 12 | 10.6667 |
2 | (3) | 3 | 3 |
Für uns ist die MySQL-Funktion COUNT()
interessant die uns sagt wieviele
Datensätze das GROUP BY für einen Wert zusammengefasst hat.
Foo | "Zusammengefaste Werte von Bar" | MAX(Bar) | AVG(Bar) | COUNT(Bar) |
---|---|---|---|---|
3 | (6,10) | 10 | 8 | 2 |
5 | (9,11,12) | 12 | 10.6667 | 3 |
2 | (3) | 3 | 3 | 1 |
11. Entwickeln der SELECT-Anfrage für die News
In unserer SELECT-Anfrage starten wir bei der Tabelle News
und lesen da erstmal die unproblematischen Daten aus.
SELECT News.ID, News.Titel, News.Inhalt, News.Datum FROM News;
Nun machen wir ein JOIN auf die Users
-Tabelle
um den Benutzernamen vom Autor auszulesen.
SELECT News.ID, User.Username, News.Titel, News.Inhalt, News.Datum FROM News JOIN User ON News.AutorID = User.ID;
Nun müssen wir irgendwie aus der News_Kommentar
-Tabelle die
Anzahl der Kommentare zu einer News auslesen. Daher bilden wir
zuerst ein JOIN auf die News_Kommentar
-Tabelle,
dabei verketten wir News.ID
mit News_Kommentar.NewsID
.
SELECT News.ID, User.Username, News.Titel, News.Inhalt, News.Datum FROM News JOIN User ON News.AutorID = User.ID JOIN News_Kommentar ON News.ID = News_Kommentar.NewsID;
Wenn wir diesen Query zum testen in phpMyAdmin eingeben sehen wir folgendes:
nichts. Obwohl wir Newsdatensätze durch die Testerei vom NewsAdd-Bereich aus
dem Adminbereich haben werden nun keine Datensätze mehr angezeigt. Der Grund
dafür ist dass wir keine Kommentare haben, also gibt es auch keine
Verbindungsmöglichkeit. Bei den anderen Tabellen wie Users
ist dies
kein Problem, denn da existieren ja z.B. die Benutzerdatensätze. Aber das
keine News mehr angezeigt werden ist inakzeptabel. Jedoch ist die Lösung für
unser Problem extrem einfach, wir ändern das JOIN
in ein
LEFT JOIN
um.
SELECT News.ID, User.Username, News.Titel, News.Inhalt, News.Datum FROM News JOIN User ON News.AutorID = User.ID LEFT JOIN News_Kommentar ON News.ID = News_Kommentar.NewsID;
Dieses LEFT sorgt dafür das auf jedenfall der Datensatz
aus der linken Tabelle (hier der Verbund von News
und
Users
) angezeigt wird, auch wenn es keine passende Newskommentare gibt.
Wenn dies für ein Datensatz der Fall ist wird für die zweite Tabelle angenommen
dass der Datensatz existiert jedoch alle Werte NULL
sind. In phpMyAdmin
testen wir dies einmal indem wir alle Spalten aus der News_Kommentar
-Tabelle
auslesen.
SELECT News.ID, User.Username, News.Titel, News.Inhalt, News.Datum, News_Kommentar.ID, News_Kommentar.Datum, News_Kommentar.Inhalt, News_Kommentar.NewsID, News_Kommentar.AutorID FROM News JOIN User ON News.AutorID = User.ID LEFT JOIN News_Kommentar ON News.ID = News_Kommentar.NewsID;
Wie man in der Ausgabe in phpMyAdmin sieht hat MySQL für jeden Datensatz einen
Pseudo-Datensatz generiert wo alle Werte NULL
sind. Diese
NULL
-Werte sind später von Bedeutung. Jedoch nehmen wir wieder
die ganzen News_Kommentar.*
-Werte aus der Anfrage raus.
SELECT News.ID, User.Username, News.Titel, News.Inhalt, News.Datum FROM News JOIN User ON News.AutorID = User.ID LEFT JOIN News_Kommentar ON News.ID = News_Kommentar.NewsID;
Wenn wir diese Anfrage ausführen kann es passieren das eine News mehrfach
angezeigt wird, je nach dem wieviele Kommentare zu einer News vorhanden sind.
Wenn es z.B. 5 Kommentare zu einer News gibt wird diese News entsprechend 5
mal angezeigt weil MySQL durch die Bedingung News.ID = News_Kommentar.NewsID
5 Verkettungen gefunden hat. Dies ist jedoch nicht das was wir wollen, wir
haben die News nur einmal veröffentlicht, nicht 5 mal. Hier kommt unser
GROUP BY ins Spiel. Wir gruppieren dabei die Spalte
News.ID
.
SELECT News.ID, User.Username, News.Titel, News.Inhalt, News.Datum FROM News JOIN User ON News.AutorID = User.ID LEFT JOIN News_Kommentar ON News.ID = News_Kommentar.NewsID GROUP BY News.ID;
Obwohl wir nach News.ID
gruppieren lesen wir weiterhin die
gleichen Werte wie User.Username
oder News.Inhalt
aus.
Dies ist auch kein Problem da die Werte eh für eine bestimmte News-ID die gleichen
sind. Im Hinterkopf behalten wir jedoch das für gleiche News-IDs unterschiedliche
Newskommentare zusammengefasst wurden. Mit der COUNT
-Funktion von MySQL
fragen wir nun ab wieviele Datensätze zusammengefasst wurden.
SELECT News.ID, User.Username, News.Titel, News.Inhalt, News.Datum, COUNT(News_Kommentar.ID) AS Anzahl FROM News JOIN User ON News.AutorID = User.ID LEFT JOIN News_Kommentar ON News.ID = News_Kommentar.NewsID GROUP BY News.ID;
Nun liefert diese Anfrage die Anzahl der Kommentare zurück. Es sieht jedoch
komisch aus dass er für die Newsbeiträge die keine Kommentare enthalten
die Anzahl auf 0 errechnet. Dies klingt zwar logisch, aber grade
wurde gesagt MySQL hat einen Pseudo-Datensatz mit NULL
-Werten
erzeugt. Somit müsste die COUNT()
-Funktion diesen Datensatz
finden und zählen. Und das genau macht sie, denn in der Funktionsbeschreibung
zu COUNT()
steht folgendes.
COUNT(expr) - Returns a count of the number of non-NULL values of expr in the rows retrieved by a SELECT statement.
Das wichtige ist der non-NULL-Teil denn er gibt an das NULL-Werte beim
Zählen ignoriert werden. Und da News_Kommentar.ID
bei nicht
vorhandenen Datensätze NULL
ist liefert der Aufruf von
COUNT(News_Kommentar.ID)
entsprechend 0
.
Zum Schluss geben wir der Spalte User.Username
ein Alias auf
Autor
und sortieren die News nach der Datum spalte.
SELECT News.ID, User.Username AS Autor, News.Titel, News.Inhalt, News.Datum, COUNT(News_Kommentar.ID) AS Anzahl FROM News JOIN User ON News.AutorID = User.ID LEFT JOIN News_Kommentar ON News.ID = News_Kommentar.NewsID GROUP BY News.ID ORDER BY News.Datum ASC;
Somit haben wir die Anfrage die wir an die Datenbank senden können und sie liefert uns die News mit dem Name des Autors und der Anzahl der Kommentare.
12. News laden und anzeigen
Diese Anfrage senden wir nun mit der query()
-Methode an die Datenbank
und lesen die News aus. Die Include-Datei heißt dabei news.php
und muss
entsprechend im $dateien
-Array definiert werden.
<?php
$ret = array();
$ret['filename'] = 'news.tpl';
$ret['data'] = array();
$sql = 'SELECT
News.ID,
User.Username AS Autor,
News.Titel,
News.Inhalt,
News.Datum,
COUNT(News_Kommentar.ID) AS Anzahl
FROM
News
JOIN
User
ON
News.AutorID = User.ID
LEFT JOIN
News_Kommentar
ON
News.ID = News_Kommentar.NewsID
GROUP BY
News.ID
ORDER BY
News.Datum DESC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$News = array();
?>
Während wir die News auslesen fragen wir innerhalb der noch zu schreibenen While-Schleife die Links der News ab und speichern sie gleich mit ab.
<?php
// [...]
$News = array();
$sql = 'SELECT
URL,
Beschreibung
FROM
News_Link
WHERE
NewsID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
while ($row = $result->fetch_assoc()) {
$stmt->bind_param('i', $row['ID']);
if (!$stmt->execute()) {
return $stmt->error;
}
$Links = array();
$stmt->bind_result($URL, $Beschreibung);
while ($stmt->fetch()) {
$Links[] = array('URL' => $URL,
'Beschreibung' => $Beschreibung);
}
$row['Links'] = $Links;
$News[] = $row;
}
$stmt->close();
$result->close();
$ret['data']['News'] = $News;
return $ret;
?>
Somit ist schonmal das Anzeigen der News fertig, fehlt noch der Bereich für die Newskommentare.
13. Anzeigen und schreiben von Newskommentaren
In der Template-Datei für die News-Anzeige sehen wir dass die URL
index.php?section=newscomments&ID=...
aufgerufen wird. Für die
Kommentarfunktion schreiben wir zuerst die Template-Datei newscomments.tpl
.
<?php /* Daten * ID - Die ID der News * Autor - Der Name des Autors der die News geschrieben hat * Datum - Das Datum der Veröffentlichung * Titel - Der Titel der News * Inhalt - Der Text der News * Links - Ein Array mit Links mit den folgendem Aufbau: * URL - Die URL vom Link * Beschreibung - Der Bezeichner vom Link * Kommentare - Ein Array mit Newskommentare * ID - Die ID vom Kommentar * Autor - Der Name des Autors der den Kommentar geschrieben hat * Inhalt - Der Text vom Kommentar * Datum - Das Datum wann der Kommentar abgegeben wurde */ ?><h1>News</h1> <h2><?php echo htmlspecialchars($data['Titel']); ?></h2> <div class="newsheader">Geschrieben von <?php echo htmlspecialchars($data['Autor']); ?> um <?php echo htmlspecialchars($data['Datum']); ?></div> <p> <?php echo nl2br(htmlspecialchars($data['Inhalt'])); ?> </p> <?php if (count($data['Links'])) { ?> <div class="links"> Links: <ul> <?php foreach ($data['Links'] as $Link) { ?> <li><a href="<?php echo $Link['URL']; ?>"><?php echo htmlspecialchars($Link['Beschreibung']); ?></a></li> <?php } ?> </ul> </div> <?php } else { ?> <p class="info"> Keine Links vorhanden. </p> <?php } ?> <h3>Kommentare</h3> <?php if (count($data['Kommentare'])) { ?> <?php foreach ($data['Kommentare'] as $Kommentar) { ?> <div class="comment"> <?php echo htmlspecialchars($Kommentar['Autor']); ?> schrieb um <?php echo htmlspecialchars($Kommentar['Datum']); ?>: <p> <?php echo nl2br(htmlspecialchars(preg_replace('~\S{30}~', '\0 ', $Kommentar['Inhalt']))); ?> </p> </div> <?php } ?> <?php } else { ?> <p class="info"> Keine Kommentare vorhanden </p> <?php } ?> <?php if (getUserID($db)) { ?> <form action="index.php?section=newscomments&ID=<?php echo $data['ID']; ?>" method="post"> <fieldset> <legend>Kommentar schreiben</legend> <label>Kommentar: <textarea name="Kommentar" cols="40" rows="10"></textarea></label> <input type="submit" name="formaction" value="Kommentar schreiben" /> </fieldset> </form> <?php } else { ?> <p class="info"> Um Kommentare schreiben zu können müssen sie sich anmelden. </p> <?php } ?>
In der Include-Datei newscomments.php
gucken wir zuerst ob es eine
News gibt und lesen dann alle Daten aus. Vergisst nicht das
$dateien
-Array zu bearbeiten.
<?php
$ret = array();
$ret['filename'] = 'newscomments.tpl';
$ret['data'] = array();
if (!isset($_GET['ID'])) {
return 'Benutzen sie nur Links von der Homepage.';
}
if ('' == $ID = trim($_GET['ID'])) {
return 'Benutzen sie nur Links von der Homepage.';
}
$sql = 'SELECT
News.ID,
User.Username AS Autor,
News.Titel,
News.Inhalt,
News.Datum
FROM
News
JOIN
User ON
News.AutorID = User.ID
WHERE
News.ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $ID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($NewsID, $Autor, $Titel, $Inhalt, $Datum);
if (!$stmt->fetch()) {
return 'Es wurde keine News mit der angegebenen ID gefunden.';
}
$stmt->close();
if ('POST' == $_SERVER['REQUEST_METHOD']) {
// [...]
// todo
}
$sql = 'SELECT
URL,
Beschreibung
FROM
News_Link
WHERE
NewsID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$Links = array();
$stmt->bind_result($URL, $Beschreibung);
while ($stmt->fetch()) {
$Links[] = array('URL' => $URL,
'Beschreibung' => $Beschreibung);
}
$stmt->close();
$sql = 'SELECT
News_Kommentar.ID,
User.Username AS Autor,
News_Kommentar.Inhalt,
News_Kommentar.Datum
FROM
News_Kommentar
JOIN
User ON
News_Kommentar.AutorID = User.ID
WHERE
News_Kommentar.NewsID = ?
ORDER BY
News_Kommentar.Datum ASC';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$Kommentare = array();
$stmt->bind_result($KomID, $KomAutor, $KomInhalt, $KomDatum);
while ($stmt->fetch()) {
$Kommentare[] = array('ID' => $KomID,
'Autor' => $KomAutor,
'Inhalt' => $KomInhalt,
'Datum' => $KomDatum);
}
$stmt->close();
$ret['data']['ID'] = $NewsID;
$ret['data']['Autor'] = $Autor;
$ret['data']['Titel'] = $Titel;
$ret['data']['Inhalt'] = $Inhalt;
$ret['data']['Datum'] = $Datum;
$ret['data']['Links'] = $Links;
$ret['data']['Kommentare'] = $Kommentare;
return $ret;
?>
Wenn wir das Formular abschicken speichern wir einfach die Eingaben ab.
<?php
// [...]
$stmt->close();
if ('POST' == $_SERVER['REQUEST_METHOD']) {
if (!isset($_POST['formaction'], $_POST['Kommentar'])) {
return INVALID_FORM;
}
if (!$UserID = getUserID($db)) {
return NOT_LOGGED_IN;
}
if ('' == $Kommentar = trim($_POST['Kommentar'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News_Kommentar
WHERE
NewsID = ? AND
AutorID = ? AND
Inhalt = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('iis', $NewsID, $UserID, $Kommentar);
if (!$stmt->execute()) {
return $stmt->error;
}
if ($stmt->fetch()) {
return 'Sie haben bereits den Kommentar hinzugefügt.';
}
$stmt->close();
$sql = 'INSERT INTO
News_Kommentar(NewsID, AutorID, Inhalt, Datum)
VALUES
(?, ?, ?, NOW())';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('iis', $NewsID, $UserID, $Kommentar);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
return showInfo('Ihr Kommentar wurde hinzugefügt.');
}
$sql = 'SELECT
URL,
Beschreibung
FROM
News_Link
WHERE
NewsID = ?';
// [...]
?>
Mit der SELECT-Abfrage überprüfen wir ob der Kommentar bereits hinzugefügt wurde, also eine Art von Mini-Spamschutz.
14. Newskommentar bearbeiten
Admins sollten weiterhin die Möglichkeit haben einen Newskommentar
zu bearbeiten oder sogar zu löschen. Daher fügen wir für unserem
Adminbereich das Recht NewsKom
hinzu. Da es Sinnfrei
ist neue Kommentare über das Adminmenu hinzuzufügen legen wir
die Datei admin_NewsKom_add.php
an die nur eine
Fehlermeldung zurückliefert.
<?php
return 'Sie können keine Newskommentare über das Adminmenu hinzufügen.';
?>
Wir werden mehrere Template-Dateien anlegen um eine News auszuwählen, um ein Kommentar auszuwählen und dann um den Kommentar zu bearbeiten.
<?php /* Daten: * News - Ein Array mit allen News im System. * ID - Die ID der News * Titel - Der Titel der News * Datum - Das Datum wann die News geschrieben wurde */ ?><form action="index.php?section=admin&cat=NewsKom&action=edit" method="post"> <fieldset> <legend>News wählen</legend> <label>News: <select name="NewsID"> <option value="0">Bitte wählen</option> <?php foreach ($data['News'] as $value) { ?> <option value="<?php echo $value['ID']; ?>">(<?php echo htmlspecialchars($value['Datum']); ?>) <?php echo htmlspecialchars($value['Titel']); ?></option> <?php } ?> </select></label> <input type="submit" name="formaction" value="News wählen" /> </fieldset> </form>
Dieses Template (admin_NewsKom_edit_select_news.tpl
) zeigen wir
dann an wenn die URL mit der GET-Methode aufgerufen wurde.
<?php
$ret = array();
$ret['filename'] = 'admin_NewsKom_edit_select_news.tpl';
$ret['data'] = array();
if ('POST' != $_SERVER['REQUEST_METHOD']) {
$sql = 'SELECT
ID,
Titel,
Datum
FROM
News
ORDER BY
Datum DESC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$News = array();
while ($row = $result->fetch_assoc()) {
$News[] = $row;
}
$result->close();
$ret['data']['News'] = $News;
return $ret;
}
// [...]
?>
Wenn eine News ausgewählt wurde zeigen wir die Kommentare an und wählen dann
einen aus. Die Template-Datei admin_NewsKom_edit_select_comment.tpl
sieht wie folgt aus.
<?php /* Daten: * Kommentare - Ein Array mit allen Kommentare der zuvor gewählten News. * ID - Die ID vom Kommentar * Datum - Das Datum der Veröffentlichung * Autor - Der Autor der den Kommentar geschrieben hat * Inhalt - Der Kommentar selbst */ ?><form action="index.php?section=admin&cat=NewsKom&action=edit" method="post"> <fieldset> <legend>Kommentar wählen</legend> <label>Kommentar: <select name="KommentarID"> <option value="0">Bitte wählen</option> <?php foreach ($data['Kommentare'] as $Kommentar) { ?> <option value="<?php echo $Kommentar['ID']; ?>">(<?php echo htmlspecialchars($Kommentar['Datum']); ?>) [<?php echo htmlspecialchars($Kommentar['Autor']); ?>] <?php echo htmlspecialchars(substr($Kommentar['Inhalt'], 0, 60).'...'); ?> <?php } ?> </select></label> <input type="submit" name="formaction" value="Kommentar wählen" /> </fieldset> </form>
Und mit folgendem PHP-Code zeigen wir es an.
<?php
// [...]
$ret['data']['News'] = $News;
return $ret;
}
if (!isset($_POST['formaction'])) {
return INVALID_FORM;
}
if ('News wählen' == $_POST['formaction']) {
if (!isset($_POST['NewsID'])) {
return INVALID_FORM;
}
if ('' == $NewsID = trim($_POST['NewsID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($NewsID);
if (!$stmt->fetch()) {
return 'Es wurde keine News mit der angegebenen ID gefunden.';
}
$stmt->close();
$sql = 'SELECT
News_Kommentar.ID,
News_Kommentar.Datum,
User.Username AS Autor,
News_Kommentar.Inhalt
FROM
News_Kommentar
JOIN
User ON
News_Kommentar.AutorID = User.ID
WHERE
News_Kommentar.NewsID = ?
ORDER BY
Datum DESC';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$Kommentare = array();
$stmt->bind_result($ID, $Datum, $Autor, $Inhalt);
while ($stmt->fetch()) {
$Kommentare[] = array('ID' => $ID,
'Datum' => $Datum,
'Autor' => $Autor,
'Inhalt' => $Inhalt);
}
$stmt->close();
$ret['filename'] = 'admin_NewsKom_edit_select_comment.tpl';
$ret['data']['Kommentare'] = $Kommentare;
return $ret;
}
// [...]
?>
Danach zeigen wir nun endlich das Formular an um ein Kommentar bearbeiten zu können.
<?php /* Daten * ID - Die ID vom Kommentar * Inhalt - Der Inhalt vom Kommentar */ ?><form action="index.php?section=admin&cat=NewsKom&action=edit" method="post"> <fieldset> <legend>Kommentar bearbeiten</legend> <label>Kommentar: <textarea name="Inhalt" cols="40" rows="8"><?php echo htmlspecialchars($data['Inhalt']); ?></textarea></label> <input type="submit" name="formaction" value="Kommentar speichern" /> <input type="hidden" name="ID" value="<?php echo $data['ID']; ?>" /> </fieldset> </form>
Der PHP-Code sieht wie folgt aus.
<?php
// [...]
$ret['data']['Kommentare'] = $Kommentare;
return $ret;
}
if ('Kommentar wählen' == $_POST['formaction']) {
if (!isset($_POST['KommentarID'])) {
return INVALID_FORM;
}
if ('' == $KommentarID = trim($_POST['KommentarID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID,
Inhalt
FROM
News_Kommentar
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $KommentarID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ID, $Inhalt);
if (!$stmt->fetch()) {
return 'Es wurde kein Kommentar mit der angegebenen ID gefunden.';
}
$stmt->close();
$ret['data']['ID'] = $ID;
$ret['data']['Inhalt'] = $Inhalt;
$ret['filename'] = 'admin_NewsKom_edit_change.tpl';
return $ret;
}
// [...]
?>
Nachdem wir den Inhalt geändert haben speichern wir diesen in der Datenbank ab.
<?php
// [...]
$ret['filename'] = 'admin_NewsKom_edit_change.tpl';
return $ret;
}
if (!isset($_POST['ID'], $_POST['Inhalt'])) {
return INVALID_FORM;
}
if ('' == $Inhalt = trim($_POST['Inhalt']) OR
'' == $ID = trim($_POST['ID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News_Kommentar
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $ID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ID);
if (!$stmt->fetch()) {
return 'Es wurde kein Kommentar mit der angegebenen ID gefunden.';
}
$stmt->close();
$sql = 'UPDATE
News_Kommentar
SET
Inhalt = ?
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('si', $Inhalt, $ID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
return showInfo('Der Kommentar wurde bearbeitet.');
?>
Jetzt können wir Kommentare bearbeiten.
15. Kommentare löschen
Fürs löschen können wir die selben Auswahlformulare verwenden wie fürs Bearbeiten. Wir müssen nur die URLS und Submitbuttons verändern.
<?php /* Daten: * News - Ein Array mit allen News im System. * ID - Die ID der News * Titel - Der Titel der News * Datum - Das Datum wann die News geschrieben wurde */ ?><form action="index.php?section=admin&cat=NewsKom&action=del" method="post"> <fieldset> <legend>News wählen</legend> <label>News: <select name="NewsID"> <option value="0">Bitte wählen</option> <?php foreach ($data['News'] as $value) { ?> <option value="<?php echo $value['ID']; ?>">(<?php echo htmlspecialchars($value['Datum']); ?>) <?php echo htmlspecialchars($value['Titel']); ?></option> <?php } ?> </select></label> <input type="submit" name="formaction" value="News wählen" /> </fieldset> </form>
<?php /* Daten: * Kommentare - Ein Array mit allen Kommentare der zuvor gewählten News. * ID - Die ID vom Kommentar * Datum - Das Datum der Veröffentlichung * Autor - Der Autor der den Kommentar geschrieben hat * Inhalt - Der Kommentar selbst */ ?><form action="index.php?section=admin&cat=NewsKom&action=del" method="post"> <fieldset> <legend>Kommentar wählen</legend> <label>Kommentar: <select name="ID"> <option value="0">Bitte wählen</option> <?php foreach ($data['Kommentare'] as $Kommentar) { ?> <option value="<?php echo $Kommentar['ID']; ?>">(<?php echo htmlspecialchars($Kommentar['Datum']); ?>) [<?php echo htmlspecialchars($Kommentar['Autor']); ?>] <?php echo htmlspecialchars(substr($Kommentar['Inhalt'], 0, 60).'...'); ?> <?php } ?> </select></label> <input type="submit" name="formaction" value="Kommentar löschen" /> </fieldset> </form>
<?php
$ret = array();
$ret['filename'] = 'admin_NewsKom_del_select_news.tpl';
$ret['data'] = array();
if ('POST' != $_SERVER['REQUEST_METHOD']) {
$sql = 'SELECT
ID,
Titel,
Datum
FROM
News
ORDER BY
Datum DESC';
if (!$result = $db->query($sql)) {
return $db->error;
}
$News = array();
while ($row = $result->fetch_assoc()) {
$News[] = $row;
}
$result->close();
$ret['data']['News'] = $News;
return $ret;
}
if (!isset($_POST['formaction'])) {
return INVALID_FORM;
}
if ('News wählen' == $_POST['formaction']) {
if (!isset($_POST['NewsID'])) {
return INVALID_FORM;
}
if ('' == $NewsID = trim($_POST['NewsID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($NewsID);
if (!$stmt->fetch()) {
return 'Es wurde keine News mit der angegebenen ID gefunden.';
}
$stmt->close();
$sql = 'SELECT
News_Kommentar.ID,
News_Kommentar.Datum,
User.Username AS Autor,
News_Kommentar.Inhalt
FROM
News_Kommentar
JOIN
User ON
News_Kommentar.AutorID = User.ID
WHERE
News_Kommentar.NewsID = ?
ORDER BY
Datum DESC';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $NewsID);
if (!$stmt->execute()) {
return $stmt->error;
}
$Kommentare = array();
$stmt->bind_result($ID, $Datum, $Autor, $Inhalt);
while ($stmt->fetch()) {
$Kommentare[] = array('ID' => $ID,
'Datum' => $Datum,
'Autor' => $Autor,
'Inhalt' => $Inhalt);
}
$stmt->close();
$ret['filename'] = 'admin_NewsKom_del_select_comment.tpl';
$ret['data']['Kommentare'] = $Kommentare;
return $ret;
}
if (!isset($_POST['ID'])) {
return INVALID_FORM;
}
if ('' == $ID = trim($_POST['ID'])) {
return EMPTY_FORM;
}
$sql = 'SELECT
ID
FROM
News_Kommentar
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $ID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->bind_result($ID);
if (!$stmt->fetch()) {
return 'Es wurde kein Kommentar mit der angegebenen ID gefunden.';
}
$stmt->close();
$sql = 'DELETE FROM
News_Kommentar
WHERE
ID = ?';
if (!$stmt = $db->prepare($sql)) {
return $db->error;
}
$stmt->bind_param('i', $ID);
if (!$stmt->execute()) {
return $stmt->error;
}
$stmt->close();
return showInfo('Der Kommentar wurde gelöscht.');
?>
Nun ist unser neues Newsskript fertig.