Codeigniter Tutorial
Vorbereitung
Dateien herunterladen
Wir laden uns die aktuelle Version von Codeigniter herunter.
Projektordner einrichten
Wir können den Codeigniter-Ordner als Projektordner verwenden.
Wir benötigen allerdings nur die Ordner application, system und die Datei index.php.
In application lebt später unsere Website. Nur hier programmieren wir.
Der Ordner system enthält die Kenfunktionen von Codeigniter.
Datenbank auf Server einrichten
Die für unser Projekt benötigte Datenbank finden Sie in der Datei forum.sql, der Downloaddateien (Moodle). Richten Sie sich einen Server mit Datenbank ein und importieren Sie die SQL-Datei.
alte Forumsdateien als “Steinbruch-Ordner” in Projektordner
Als Projekt setzen wir einen Teil des Forum mit Hilfe von Codeigniter um, was Sie bereits aus dem vorangegangenen Semester kennen sollten. Laden Sie sich den Ordner mit den alten Projektdateien herunter und speichern ihn unter dem Namen _steinbruch im Projektordner.
Kopieren Sie den css-, fonts- und js-Ordner aus dem _steinbruch in das Hauptverzeichnis und stellen Sie einen Unterstrich vor jeden Ordnernamen, damit die Ordner am Anfang der Liste angezeigt werden.
Projektordner herunterladen
Sie können ebenso den vorbereiteten Ordner komprimiert als forum_ci_start.zip herunterladen und als ihren Projektordner übernehmen.
Das Projekt, so wie es am Ende dieses Tutorials aussehen soll, können Sie ebenso mit dem Archiv forum_ci_ready.zip herunterladen.
Natürlich müssen Sie die Konfiguration an ihren Server anpassen.
Konfiguration
base_url
Bevor wir starten, müssen wir einige Konfigurationen vornehmen. In application/config/config.php müssen wir die base_url
angeben. Das ist die URL, unter der die Startseite im Netz zu finden ist. In unserem Beispiel: http://539197-1.web1.fh-htwchur.ch/
$config['base_url'] = 'http://539197-1.web1.fh-htwchur.ch/';
Datenbank
In der Datei application/config/database.php richten wir die Datenbankverbindung ein. Wenn Sie einen HTW-Ausbildungsserver eingerichtet haben, finden Sie die Daten auf dem Datenblatt.
$db['default'] = array(
'dsn' => '',
'hostname' => 'localhost',
'username' => '', // * Pflicht
'password' => '', // * Pflicht
'database' => '', // * Pflicht
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
'char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
);
erste Datei
View
Wir erstellen eine neue Datei application/views/login.php und kopieren den HTML-Teil aus _steinbruch/login.php und fügen ihn in diese neue, leere Datei ein.
PHP-Sicherheits-Code voranstellen
Wir stellen den folgenden PHP-Sicherheits-Code vor den HTML-Teil.
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?>
Er sorgt dafür, dass der View nur von einem Codeigniter-Controller aufgerufen werden kann. Wenn jemand versucht die Datei per Adresseingabe zu laden, erhält er eine Fehlermeldung.
CSS und JS
Damit Bootstrap einwandfrei funktioniert, müssen wir im View die Verweise auf CSS- und JavaScript-Dateien aktualisieren.
<link href=“/_css/…
und <script src=”/_js/…
Controller
Um den View aufzurufen brauchen wir in Codeigniter immer einen Controller. Am einfachsten ist es, wir duplizieren die Datei application/controller/Welcome.php und benennen sie in Login.php um.
Der Klassenname in der Datei muss dem Dateinamen entsprechen. Daher ändern wir in Zeile 4 class Welcome …
in class Login …
.
Wenn wir jetzt in Zeile 23 $this->load->view(‘welcome_message’);
in $this->load->view(‘login’);
ändern, können wir die Seite testen
Aufruf und Routing
Durch das automatische Routing müsste jetzt die URL http://539197-1.web1.fh-htwchur.ch/login
automatisch den Controller Login.php laden, welcher wiederum den View login.php aufruft.
Codeigniter sucht automatisch nach einem Controller mit dem Namen login.php oder Login.php. Auf diese Weise können Sie sehr einfach aussagekräftige und gut zu merkende Adressen generieren.
HTW-Server
Auf dem HTW-Ausbildungsserver funktioniert dieses automatische Routing aufgrund der Serverkonfiguration nicht. Sie können den Login aber über Eingabe der Adresse http://539197-1.web1.fh-htwchur.ch/index.php/login
laden.
Um diese globale Konfiguration für Ihren persönlichen Server zu überschreiben können Sie folgende Änderungen vornehmen.
Erstellen Sie eine .htaccess-Datei mit folgendem Code:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^index.php/(.*)$ /$1 [R=302,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond $1 !^(index\.php|images|robots\.txt)
RewriteRule ^(.*)$ index.php?/$1 [L]
</IfModule>
Ändern Sie in der Datei application/config/config.php den Code
$config['index_page'] = 'index.php';
in
$config['index_page'] = '';
Jetzt sollte das automatische Routing auch auf einem HTW-Ausbildungsserver funktionieren.
Daten vom Controller an den View schicken (Test)
Ergänzen Sie den Controller vor dem Aufruf des Views um die Zeile
$data['msg'] = "Nachricht aus dem Controller";
und erweitern Sie den Aufruf des Views, indem Sie ihm das Array $data
mitgeben.
$this->load->view('login', $data);
Wenn Sie die Login-Seite erneut laden, erscheint die Nachricht am Ende der Seite. Codeigniter wandelt das data-Array im View automatisch in Variablen um, indem es den Bezeichner (in unserem Beispiel msg
) als Variablennamen ($msg
) verwendet und in die neue Variable den im Controller vergebenen Wert schreibt.
Aus
$data['msg'] = "Inhalt der Nachricht";
im Controller wird
$msg = "Inhalt der Nachricht";
im View.
View mit einer eigenen Methode aufrufen
Stellen Sie im Controller die ursprüngliche Methode Index wieder her:
public function index()
{
$this->load->view('login');
}
Erstellen Sie eine neue Methode mit dem Namen log
.
public function log()
{
$data['msg'] = "Nachricht: log";
$this->load->view('login', $data);
}
Rufen Sie die Login-Seite mit der URL http://539197-1.web1.fh-htwchur.ch/login/log
auf.
Sowie der Abschnitt nach dem Domainnamen auf den Controller hinweist, so ruft der nächste Abschnitt eine Methode innerhalb dieses Controllers auf.
Übergabe von Parametern in der URL
Erweitern Sie die Methode log
public function log($firstname, $lastname)
{
$data['msg'] = "Nachricht: Hallo $firstname $lastname";
$this->load->view('login', $data);
}
Erweitern Sie die URL nach folgendem Schema um ihren Vor- und Nachnamen. http://539197-1.web1.fh-htwchur.ch/login/log/Vorname/Nachname
, z. B. http://539197-1.web1.fh-htwchur.ch/login/log/Wolfgang/Bock
.
Sie haben so die Möglichkeit Parameter direkt über die URL an die Methode zu übergeben.
Formulare im View
Vorbereitung im View: <form>
Formulare können wir mit Codeigniter sehr effizient validieren und auswerten. Dazu bedarf es jedoch einiger Vorbereitungen. Um die Formular-Funktionen von Codeigniter zu nutzen, ersetzen wir den öffnenden <form&gr;
-Tag
<form action="<?php echo $_SERVER['PHP_SELF']?>" method="post">
durch die Codeigniter-Schreibweise
<?php echo form_open('login/log'); ?>
In den HTML-Quellcode schreibt Codeigniter
<form action="http://539197-1.web1.fh-htwchur.ch/login/log" method="post" accept-charset="utf-8">
Als method
wird standardmässig post
eingesetzt.
Selbstverständlich können wir auch die HTML-Schreibweise beibehalten. Codeigniter erleichtert uns hier das Codieren.
Vorbereitung im View: Validierung
Mit Codeigniter ist die Validierung von Formularen sehr einfach. Die eigentliche Validierung erfolgt im Controller, die Anzeige der Fehlermeldungen jedoch im View. Vor die Eröffnung des Formulars setzten wir dazu die Zeile
<?php echo validation_errors(); ?>
Sie sorgt dafür, dass alle Fehlermeldungen angezeigt werden.
Verarbeitung der Formulardaten im Controller
Vorbereitung im Controller: Helper und Library einbinden
Im Controller laden wir zunächst den die Codeigniter-Helper “form” und “url”, die uns bei der Formularverarbeitung, sowie bei der Verwendung von URLs unterstützen.
$this->load->helper(array('form', 'url'));
$this->load->library('form_validation');
Validierungsregeln festlegen
Anschliessend validieren wir das Textfeld username
$this->form_validation->set_rules('username', 'Username', 'required',
array('required' => 'Bitte geben Sie einen %s ein.'));
Die Validierungs-Funktion $this->form_validation->set_rules()
benötigt mindestens drei Parameter:
- den exakten Feldnamen (
name=“username”
), - eine verständliche Bezeichnung, die später ggfs. in der Fehlermeldung auftaucht
- und eine die Angabe, auf was Codeigniter das Feld validieren soll.
-
Als vierten (optionalen) Parameter übergeben wir ein Array mit der Eigenschaft, auf die Validiert werden soll als Bezeichner und eine individuellen Nachricht als Wert.
%s
steht als Platzhalter für die verständliche Bezeichnung.Ebenso gehen wir bei der Validierung des Passwort-Feldes vor.
Validierung durchführen
Die Funktion
$this->form_validation->run()
führt die Validierung aus und gibt als Wert TRUE
oder False
zurück. Entsprechend lässt sich die Funktion als Bedingung einer if()
Abfrage verwenden.
if ($this->form_validation->run() == FALSE)
{
$this->load->view('login');
}
else
{
$username = $this->input->post('username');
$password = $this->input->post('password');
$data['msg'] = "Username: $username<br>Passwort: $password";
$this->load->view('login', $data);
}
fehlerhafte Eingabe
Wenn die Validierung negativ ausfällt, die Funktion also FALSE
zurückgibt, rufen wir erneut den View login.php auf. Es ist nicht nötig dem View eine Nachricht mitzugeben. Die Meldungen über fehlerhafte Eingabe erstellt Codeigniter im View automatisch.
korrekte Eingabe
Wenn der User alles korrekt eingegeben hat rufen wir ebenso den login-View auf und geben ihm zum Test eine entsprechende Nachricht mit. Dass der eingegebene Username und das Passwort in der Meldung erscheint ist zu Testzwecken sinnvoll, darf aber in einer Produktionsumgebung nicht passieren.
Fehleranzeige im View
Wenn die Eingaben korrekt waren sollte die entsprechende Testnachricht erscheinen. Sollte der User jedoch vergessen haben einen oder mehrere Werte einzugeben, erscheinen die entsprechenden Meldungen jetzt vor dem Formular.
Fehler an den zugehörigen Feldern anzeigen
Die gruppierte Anzeige wird durch die Zeile
<?php echo validation_errors(); ?>
ausgelöst.
Wenn wir die Fehler an den entsprechenden Eingabefeldern anzeigen wollen, müssen wir z. B. des Username-Feldes ergänzen:
<input type="text" name="username" class="form-control" id="id_username">
<?php echo form_error('username'); ?>
Es wird lediglich die Validierungs-Fehlermeldung für das Feld
‘username’
angezeigt.
Für das Passwortfeld können wir entsprechend verfahren.
Zwar ist die gemeinsame Anzeige der Gruppierten und der einzelnen Fehlermeldung technisch kein Problem, ist aber aus Gründen der Usability nicht zu empfehlen. Daher löschen wir die Zeile
<?php echo validation_errors(); ?>
wieder.
Verfeinerung der Validierung
Codeigniter bietet eine Vielzahl von Validierungsoptionen, die Sie in der Dokumentation unter der Library Form Validation nachschlagen können.
Verfeinerung des Formulars
Ebenso hilft Codeigniter bei der Erstellung von Formularen mit dem Form Helper.
Beispielsweise können wir den Tag des Username-Eingabefeld durch
value="<?php echo set_value('username'); ?>"
erweitern. So geht die Eingabe des Usernames nicht verloren, wenn der User vergessen hat sein Passwort einzutippen.
Model
Wir haben einen View und einen Controller bereits kennengelernt und wissen, dass Daten mittels eines Models aus der Datenbank geholt und in sie hineingeschrieben werden.
Die zentrale Datenbankverbindung haben wir schon zu Anfang in der Datei application/config/database.php vorgenommen.
User Login
Jetzt werden wir ein User-Model mit einer login()-Methode schreiben, mit Hilfe derer sich der User einloggen kann.
Erstellen Sie die Datei application/models/User_model.php und schreiben Sie folgenden Code:
<?php
class User_model extends CI_Model {
public function __construct()
{
$this->load->database();
}
public function login($username, $password)
{
}
}
Wir haben jetzt den Constructor, in dem wir zunächst die Datenbankverbindung laden. Jetzt können wir mit $this->db
auf die Datenbank zugreifen
Danach bereiten wir die Methode login()
mit den Parametern $username
und $password
vor, die wir beide zwingend für einen login benötigen.
die erste Abfrage
Eine Datenbankabfrage ist jetzt ganz einfach. Mit
$query = $this->db->query(//Abfrage z. B. als SQL);
können wir eine Abfrage an die Datenbank schicken und das Ergebnis in der Variable $query
speichern.
Unsere Abfrage lautet also:
$query = $this->db->query("SELECT * FROM user WHERE username='$username' AND password='$password';");
Mit $query->num_rows()
bekommen wir heraus, wieviele Ergebnisse die Abfrage liefert. Wenn unsere Abfrage genau ein Ergebnis liefert, werden wir die Userangaben aus der Datenbank als Array zurückgeben.
Ansonsten geben wir FALSE
zurück.
Der vollständige Code des Models lautet jetzt:
<?php
class User_model extends CI_Model {
public function __construct()
{
$this->load->database();
}
public function login($username, $password)
{
$query = $this->db->query("SELECT * FROM user WHERE username='$username' AND password='$password';");
if($query->num_rows() == 1){
return $query->row_array();
}
return FALSE;
}
}
Ein User-Objekt
Momentan geben wir ein Array mit allen Benutzerdaten an die aufrufende Funktion zurück. (Momentan gibt es eine solche Funktion zwar noch nicht, aber es dauert nicht mehr lange.)
Wir können aber auch das Objekt zuerst um Eigenschaften erweitern und dann das ganze Objekt zurückgeben. So können wir später die Vorteile der OOP nutzen. Im Falle des User-Objekts ist diese Vorgehensweise sinnvoll, da wir es über die Session von einer Seite zur nächsten weitergeben können.
Eigenschaften
Die Eigenschaften des User-Objekts entsprechen den Spalten der Datenbank. Aus Sicherheitsgründen erstellen wir für das Passwort keine eigene Eigenschaft. So ist die Wahrscheinlichkeit, dass es versehentlich ausgelesen wird deutlich geringer.
Ausserdem geben wir dem Model noch die Eigenschaft $fullname
mit, in der wir gleich den vollen Namen, also Vor- und Nachnamen abspeichern. Diese Eigenschaft erstellen wir aus zwei Gründen:
- Weil es uns später das Leben erleichtert und …
- … weil wir es können.
Die Eigenschaften erstellen wir am Anfang der Model-Datei:
<?php
class User_model extends CI_Model {
private $id;
private $username;
private $email;
private $firstname;
private $lastname;
private $fullname;
…
Alle Eigenschaften sind private
, können also nur innerhalb der Klasse verwendet werden. Das ist sinnvoll, da eine Änderung, z. B. des Vornamens nicht nur in der Klasse stattfinden darf, sondern auch in die Datenbank übertragen werden muss.
Um die einzelnen Eigenschaften zu verwenden, werden wir gleich sogenannte Getter und gegebenenfalls Setter programmieren.
Zuerst erweitern wir aber die die login
-Methode.
public function login($username, $password)
{
$query = $this->db->query("SELECT * FROM user WHERE username='$username' AND password='$password';");
if($query->num_rows() == 1){
$user = $query->row_array();
$this->id = $user['user_id'];
$this->username = $user['username'];
$this->email = $user['email'];
$this->firstname = $user['firstname'];
$this->lastname = $user['lastname'];
$this->fullname = $this->firstname." ".$this->lastname;
return $this;
}
return FALSE;
}
Nur der Teil innerhalb der if()
-Bedingung hat sich geändert, der jedoch gewaltig.
Zuerst speichern wir das Abfrageergebnis als Array in einer lokalen Variablen, die wir sinnvollerweise $user
nennen.
Aus diesem Array befüllen wir nun die jeweiligen Eigenschaften.
Mit return $this;
geben wir zuletzt die Instanz des Users an die aufrufende Funktion zurück.
die Getter
Da wir aus guten Gründen die Eigenschaften als private
deklariert haben, sind sie von ausserhalb der Klasse nicht zugänglich. Um die Werte trotzdem ausgeben zu können, müssen wir sog. Getter programmieren. Deren Aufbau ist simpel:
...
public function get_id(){
return $this->id;
}
public function get_username(){
return $this->username;
}
public function get_email(){
return $this->email;
}
public function get_firstname(){
return $this->firstname;
}
public function get_lastname(){
return $this->lastname;
}
public function get_fullname(){
return $this->fullname;
}
Jetzt können wir alle Eigenschaften mittels der entsprechenden Getter-Funktion auslesen, wir können jedoch keine der Eigenschaften ändern, was zumindest im Falle der ID auch sehr sinnvoll ist. Wenn wir später beispielsweise die email
-Eigenschaft ändern wollen, müssen wir eine Setter-Funktion schreiben, die nicht nur die Eigenschaft in der Klasse, sondern auch den dazugehörigen Datenbankeintrag aktualisiert.
Das Model mit dem Controller verbinden
Um das User-Model im Controller zu nutzen, müssen wir es mit ihm verbinden. Dazu erstellen wir im Login-Controller zuerst einen Constructor und laden dort das Model. Da wir den Constructor der Elternklasse erweitern und nicht überschreiben wollen, laden wir jedoch zuerst ebendiesen Parent-Constructor.
public function __construct()
{
parent::__construct();
$this->load->model('user_model');
$this->load->helper('url_helper');
}
Die URL-Helper-Klasse benötigen wir momentan noch nicht zwingend.
Login-Abfrage durchführen
Logischerweise führen wir die Login-Abfrage erst nach erfolgreicher Validierung aus. Wenn wir also die Eingaben validiert haben und sie in die entsprechenden Variablen geschrieben haben, führen wir die Abfrage durch.
$user = $this->user_model->login($username, $password);
Dabei erstellen wir eine Variable $user
und rufen über $this->user_model->login(…)
sie gerade programmierte Login-Methode im User-Model auf. Dabei generiert Codeigniter automatisch über den Aufruf des User_models im Constructor des Controller die Eigenschaft user_model.
Die übergebenen Parameter sind selbsterklärend.
In der Variablen $user
ist nun entweder eine Instanz der User-Models mit allen Eigenschaften, oder der boolsche Wert FALSE
gespeichert.
...
if($user){
$data['msg'] = "Hallo ".$user['firstname'];
$this->load->view('login', $data);
}
else
{
$data['msg']="Leider konnten wir keinen User mit diesem Namen finden.";
$this->load->view('login', $data);
}
...
Eine if()
-Bedingung erkennt nur FALSE
, bzw. NULL
, oder 0
als Nicht-Erfolg. Alle anderen Werte sind gleichbedeutend mit TRUE
.
Wenn die Abfrage also einen User zurückgibt, erkennt PHP das als Erfolg und wir können zu Testzwecken einen User-Wert in die Nachricht schreiben und dann den Login-View mit dem $data
-Array laden.
Anderenfalls erstellen wir eine Warnmeldung und laden ebenfalls den Login-View mit dieser Meldung.
Auf eine andere Seite verweisen
Sobald sich der User eingeloggt hat, soll er auf die Seite “Fragenübersicht”, unsere Index-Seite gelangen, auf der alle bisherigen Fragen aufgelistet sind. Wir werden uns zunächst um den Aufbau dieser Seite kümmern, und uns danach um das Session-Handling und die Weiterleitung kümmern.
die Index-Seite
Wir brauchen wieder einen View, der von einem entsprechenden Controller aufgerufen wird. Ausserdem holen wir die Fragen mit einem neuen Model aus der Datenbank und übergeben sie an den Controller.
Kopie aus dem Steinbruch in den View
Den HTML-Code für den View kopieren wir aus der Datei _steinbruch/index.php in die neue, leere Datei application/views/forum.php.
Links und Verweise anpassen
Damit die Seite tadellos funktioniert müssen wir zunächst die Verweise auf die CSS- und JavaScript-Dateien anpassen. Bei dieser Gelegenheit können wir auch direkt die Links auf die Loginseite von login.php
auf /login
ändern.
Controller erstellen
Um den View aufzurufen brauchen wir einen Controller. Erstellen Sie die Datei application/controller/Forum.php mit folgendem Code:
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
class Forum extends CI_Controller {
public function __construct()
{
parent::__construct();
$this->load->helper('url_helper');
}
public function index()
{
$this->load->view('forum');
}
}
Wir laden dabei im Constructor die URL-Helper-Classe, eine Klasse, die wir bei jeder Seite nutzen werden.
In der index()
-Methode rufen wir unseren neuen View forum.php auf.
PHP-Variablen für View im Controller erzeugen
Wenn wir den Controller jetzt testen, wirft und der Browser einige Fehlermeldungen aus, denn im Code des Views befinden sich einige PHP-Variablen, die noch nicht definiert sind. Um die dynamische Initialisierung dieser Variablen im Controller werden wir uns später kümmern. Jetzt vergeben wir zunächst hardcodiert sinnvolle Werte und übergeben diese an den View.
…
public function index()
{
$data['log_in_out_text'] = "Login";
$data['logged_in'] = FALSE;
$data['user_id'] = 0;
$this->load->view('forum', $data);
}
Thread_model mit get_all()
-Methode
Nun fehlen noch die Fragen-Threads aus der Datenbank. Für das Handling der Threads programmieren wir eine neues Model. Wir erstellen eine neue Datei application/models/Thread_model.php mit folgendem Code:
<?php
class Thread_model extends CI_Model {
public function __construct()
{
$this->load->database();
}
public function get_all()
{
$query = $this->db->query("SELECT * FROM threads WHERE parent_thread_id = 0;");
return $query->result_array();
}
}
Im Constructor laden wir die Datenbankverbindung.
In der öffentlichen Methode get_all()
schicken wir eine Abfrage an die Datenbank und speichern das Ergebnis in der Variablen $query
.
Die Codeigniter-Funktion result_array()
gibt ein Array zurück. Dieses Array, welches wir aus unserem Abfrageergebnis $query
erhalten, geben wir als Ergebnis unserer Funktion get_all()
zurück.
get_all()
-Methode im Controller
Im Controller können wir die get_all()
-Methode jetzt verwenden und das Ergebnis direkt in unserem $data
-Array speichern, was wir im nächsten Schritt an den View weiterreichen.
public function index()
{
$data['log_in_out_text'] = "Login";
$data['logged_in'] = FALSE;
$data['user_id'] = 0;
$data['question_list'] = $this->thread_model->get_all();
$this->load->view('forum', $data);
}
while()
-Schleife im View anpassen
Im View steht uns nun die Variable $question_list
zur Verfügung, ein Array, in dem alle Fragen gespeichert sind. Dieses Array durchlaufen wir mit einer for()
-Schleife, welche die bisherige while()
-Schleife ersetzt.
Aus
<?php while ($question = mysqli_fetch_assoc($questions)) { ?>
wird
<?php foreach ($question_list as $question) { ?>
Wie bisher können wir jetzt jede einzelne Frage im Schleifendurchlauf über die Array-Variable $question
auslesen. Zur Darstellung der einzelnen Fragen müssen wir daher nichts ändern.
Weiterleitung nach erfolgreichem Login
Kehren wir wieder zurück zu unserem Login. Nach erfolgreichem Login soll direkt die Fragelisten-Seite geladen werden. Da Codeigniter nichts anderes ist als PHP funktioniert natürlich auch die PHP-Funktion header()
. Codeigniter bietet aber auch eine eigene Weiterleitungsfunktion.
Ändern sie im Login-Controller die Zeilen
…
if($user){
$data['msg'] = "Hallo ".$user['firstname'];
$this->load->view('login', $data);
}
…
zu
…
if($user){
redirect('/forum');
}
…
Momentan gibt es bei der Anzeige der Fragenübersicht keinen Unterschied, ob der User eingeloggt ist, oder nicht. Um das zu ändern werden wir nach einem erfolgreichen Einloggen die User-Daten mit einer Session von der Login-Seite zur Fragenübersicht übertragen.
Arbeiten mit Sessions
Das Ergebnis ist schon gut, allerdings gibt es noch keinen Unterschied zwischen eingeloggtem und nicht eingeloggtem User.
Hier arbeiten wir wieder mit Sessions, nutzen aber die Session-Funktionen von Codeigniter, die uns das Programmiererdasein erheblich erleichtern.
Session initialisieren
Um die Session zu initialisieren reicht es die entsprechende Codeigniter-Library zu laden, was wir direkt im Constructor des Login-Controllers tun.
$this->load->library('session');
Jetzt können wir über $this->session
sehr einfach auf die Session zugreifen. Mit
$this->session->user = $user;
können wir die komplette User-Instanz in der Session speichern und an die nächste Seite übertragen. Der Redirect incl. der if()
-Bedingung sieht nun wie folgt aus:
…
if($user){
$this->session->user = $user;
redirect('/forum');
}
else
{
$data['msg']="Leider konnten wir keinen User mit diesem Namen finden.";
$this->load->view('login', $data);
}
…
Session zerstören
Zum Ausloggen geschieht durch Löschen der Session-User-Variabklen und der zerstörung der Session. Dazu muss der User lediglich wieder auf die Login-Seite kommen.
Wir fassen diese beiden Schritte in einer privaten Methode kill_session
zusammen.
private function kill_session()
{
if(isset($this->session->user)) unset($_SESSION['user']);
session_destroy();
}
Diese Methode rufen wir innerhalb des Controllers jedesmal (3x) mit
$this->kill_session();
auf, bevor wir den Login-View laden.
Session-Variablen auslesen
Den Forum-Controller können wir jetzt radikal vereinfachen, müssen jedoch danach den View entsprechend anpassen. Denn wir können jetzt unterscheiden, ob der User eingeloggt ist, oder nicht.
Die komplette index()
-Methode sieht jetzt folgendermassen aus:
public function index()
{
if(isset($this->session->user)){
$data['log_in_out_text'] = "Logout";
$data['user'] = $this->session->user;
}else{
$data['log_in_out_text'] = "Login";
}
$data['question_list'] = $this->thread_model->get_all();
$this->load->view('forum', $data);
}
Wir fragen zuerst ab, ob es eine Session-Variable mit dem Namen “user” gibt. Wenn dass der Fall ist, setzen wir den den log_in_out_text (der Text im Menü, der auf die Login-Seite verweist. Ist der User eingeloggt, kann er sich dort ausloggen.) im $data
-Array auf “Logout”.
Danach übergeben wir die gesammte User-Instanz aus der Session an das $data
-Array, sodass wir es im View nutzen können.
Wenn es keine Session-Variable “user” gibt, setzen wir den den log_in_out_text (Ist der User nicht eingeloggt, kann er es unter diesm Link tun.) im $data
-Array auf “Login”.
In den nächsten beiden Zeilen laden wir die Question-List aus dem Thread-Model und laden anschliessend den View.
Forums-View anpassen
Da wir einige Änderungen an dem Controller vorgenommen haben, funktioniert der Forums-View nun nicht mehr. Wir müssen ihn an zwei Stellen anpassen.
Abfrage, ob die Frage dem User gehört
Die Variablen $logged_in
und $user_id
gibt es nicht mehr. Die Zeile
<?php if($logged_in && $question['owner'] == $user_id){ ?>
wird jetzt zu
<?php if(isset($user) && $question['owner'] == $user->get_id()){ ?>
$logged_in
ersetzen wir durch die Abfrage isset($user)
, in der wir prüfen, ob es die Variable $user
überhaupt gibt.
Die Variable $user_id
können wir über den direkten Zugriff auf die Objektmethode get_id()
der User-Instanz ersetzen.
Nachricht im Footer
Im Footer haben wir bisher auf das Uswr-Array zugegriffen, das wir durch die Instanz des User-Objekts ersetzt haben.
Dementsprechend müssen wir die Zeilen
<?php if($logged_in){ ?>
Hallo <?php echo $user['username'] ?>, du kannst Fragen und Antworten bearbeiten. |
Eingeloggt als <?php echo $user['firstname'] . " " . $user['lastname'] ?>.
<?php }else{ ?>
Um Fragen oder Antworten zu bearbeiten <a href='/login'>loggen Sie sich bitte ein</a>.
<?php }?>
in
<?php if(isset($user)){ ?>
Hallo <?php echo $user->get_username() ?>, du kannst Fragen und Antworten bearbeiten. |
Eingeloggt als <?php echo $user->get_fullname() ?>.
<?php }else{ ?>
Um Fragen oder Antworten zu bearbeiten <a href='/login'>loggen Sie sich bitte ein</a>.
<?php }?>
ändern.
Da wir anstatt des user-Arrays jetzt eine Instanz der User-Klasse verwenden, müssen wir die anstatt der Bezeichner des Arrays nun die Getter-Funktionen der Klasse aufrufen, um die gespeicherten Eigenschaften des aktuell eingeloggten Users auszugeben.
Fragenübersicht als Startseite festlegen
Die Fragenübersicht soll die Startseite unseres Projektes werden. Wenn wir momentan die Basis-URL unserer Seite aufrufen (in unserem Beispiel: http://539197-1.web1.fh-htwchur.ch/) läd Codeigniter die Standard-Willkommensseite.
Um eine neue Startseite festzulegen öffnen wir die Datei application/config/routes.php. Am Ende der Datei stehen drei Zeilen Code. Ersetzen Sie den Default-Controller welcome
durch forum
.
$route['default_controller'] = 'forum';
Menü anpassen
Im Menü ist die Fragenübersichts-Seite, unsere neue Startseite noch nicht verlinkt.
Ändern Sie dazu den Wert de href
-Attributs auf die Codeigniter-Funktion base_url()
.
<a href="<?php echo base_url(); ?>">Fragenübersicht</a>
base_url()
gibt die Grund-Adresse zurück, die wir in application/config/config.php anfangs definiert haben, also in unserem Beispiel http://539197-1.web1.fh-htwchur.ch/.
Frage-Detail
Als nächstes wollen wir die Fragedetails anzeigen, Also den kompletten Fragetext und die dazugehörigen Antworten.
Wieder brauchen wir einen neuen View und einen neuen Controller.
neuer View question.php
Als Basis für den View dient uns die Datei _steinbruch/question.php.
Kopieren Sie den HTML-Teil in eine neue, leere Datei application/views/question.php.
View anpassen
Im View passen wir direkt das Menü und den Footer analog zum Login-, bzw. Forums-View an.
Menü
<!-- Menü -->
<nav>
<ul class="nav nav-tabs">
<li role="presentation"><a href="<?php echo base_url(); ?>">Fragenübersicht</a></li>
<li role="presentation"><a href="/login"><?php echo $log_in_out_text ?></a></li>
</ul>
</nav>
<!-- Menü ENDE -->
Footer
<!-- Fusszeile (mit angepasster CSS) -->
<footer class="footer">
<div class="container">
<?php if(isset($user)){ ?>
Hallo <?php echo $user->get_username() ?>, du kannst Fragen und Antworten bearbeiten. |
Eingeloggt als <?php echo $user->get_fullname() ?>.
<?php }else{ ?>
Um Fragen oder Antworten zu bearbeiten <a href='/login'>loggen Sie sich bitte ein</a>.
<?php }?>
</div>
</footer>
<!-- Fusszeile ENDE -->
Ausserdem dürfen wir nicht vergessen die Verweise auf die CSS- und Javascript-Dateien zu aktualisieren. (jeweils /_
voranstellen)
neuer Controller Question.php
Als Grundlage für unseren neuen Question-Controller können wir den Forums-Controller duplizieren, in application/controller/Question.php umbenennen und den Klassennamen im Code anpassen.
…
class Question extends CI_Controller {
…
Wir werden aus diesem Controller je nach Situation verschiedene Views aufrufen. Eine Standardansicht gibt es dagegen nicht. Daher werden wir gleich die index()
-Methode ersetzen.
Planung
Zunächst wollen wir ermöglichen, dass der User die ganze Frage und alle dazugehörigen Antworten ansehen kann. Dazu müssen wir die entsprechende Frage-ID aus der Fragenübersicht zum Question-Controller übermitteln. Dieser kann dann auf das Thread-Model zugreifen, in dem wir die entsprechenden Methoden programmieren werden.
Controller vorbereiten
Zunächst benennen wir die index()
-Methode in view()
um und geben ihr den Parameter $id
mit.
…
public function view($id)
{
…
Als View rufen wir jetzt nicht mehr ‘forum’
, sondern ‘question’
auf.
Den sonstigen Inhalt der Methode lassen wir zunächst unverändert.
User-Handling in den Constructor verlegen
Die Abfrage, ob der User eingeloggt ist oder nicht, werden wir in diesem Controller bei jeder Methode benötigen. Daher ist es sinnvoll eine private Klasseneigenschaft $data
einzurichten und das Userhandling bereits in den Constructor zu verlegen.
private $data;
public function __construct()
{
parent::__construct();
$this->load->model('user_model');
$this->load->model('thread_model');
$this->load->library('session');
$this->load->helper('url_helper');
if(isset($this->session->user)){
$this->data['log_in_out_text'] = "Logout";
$this->data['user'] = $this->session->user;
}else{
$this->data['log_in_out_text'] = "Login";
}
}
Natürlich müssen wir nun die view
-Methode anpassen.
public function view($id)
{
$this->load->view('question', $this->data);
}
Das Userhandling wird nun automatisch beim laden des Controllers durchgeführt und ist in jeder Methode verfügbar.
Thread-Model erweitern
Im Model brauchen wir zwei neue Methoden.
Model: eine Frage aus der Datenbank
Die ausgewählte Frage können wir über die ID aus der Datenbank abrufen. Per Definition darf es nur eine Antwort geben, die wir direkt mit der Codeigniter-Funktion $query->row_array()
an den aufrufende Code zurückgeben.
public function get_question_by_id($id)
{
$query = $this->db->query("SELECT * FROM threads WHERE thread_id=$id;");
return $query->row_array();
}
Model: alle Antworten zu einer Frage
in der zweiten Funktion verfahren wir ganz ähnlich. Allerdings kann es 0-n Ergebnisse geben. Daher geben wir nicht eine einzelne Zeile, sondern das komplette Ergebnis als assoziatives Array mit Hilfe der Codeigniter-Funktion $query->result_array()
an den aufrufende Code zurück.
public function get_answers($id)
{
$query = $this->db->query("SELECT * FROM threads WHERE parent_thread_id=$id;");
return $query->result_array();
}
Question-Controller anpassen
Die soeben Programmierten Methoden im Thread-Model rufen wir jetzt im Question-Controller direkt auf und speichern die Ergebnisse direkt im $data
-Array mit der Bezeichnung ‘question’
, bzw ‘answer_list’
.
Die ‘question_list’
benötigen wir in diesem Controller nicht.
Die view()
-Methode sieht jetzt so aus:
public function view($id)
{
$data['question'] = $this->thread_model->get_question_by_id($id);
$data['answer_list'] = $this->thread_model->get_answers($id);
$this->load->view('question', $this->data);
}
Question-View anpassen
Auch im View müssen wir einige Zeilen anpassen.
Ist der User eingeloggt und gehört ihm die Frage?
Analog zur Frageübersicht müssen wir die Zeile anpassen, in der PHP entscheidet, ob er Editierungsfunktionen anzeigt, oder nicht.
Aus
<?php if($logged_in && $question['owner'] == $user_id){ ?>
wird
<?php if(isset($user) && $question['owner'] == $user->get_id()){ ?>
Gibt es Antworten?
Die Variable $row_count
gibt es nicht. Wir haben jedoch ein $answer_list
-Array mit allen Antworten. Die Anzahl der Elemente dieses Arrays können wir mit der PHP-Funktion count()
herausfinden
Aus der Zeile
if($row_count > 0){
wird
if(count($answer_list) > 0){
und aus <?php echo $row_count ?>
wird <?php echo count($answer_list) ?>
.
Schleife anpassen
In $answer_list
sind keine mySQLi-Results gespeichert, sondern Arrays. Daher müssen wir die Schleife, mit der die Antworten durchlaufen von
while ($answer = mysqli_fetch_assoc($answers)) {
in
foreach ($answer_list as $answer) {
ändern. In jedem Schleifendurchlauf wird die aktuelle Antwort im Array $answer
gespeichert. So können wir den Inhalt der Schleife weitgehend beibehalten.
Ist der User eingeloggt und gehört ihm die Antwort?
Wie bei der Frage auch schon soll der Benutzer die Möglichkeit haben seine eigenen Antworten zu bearbeiten. Die if89
-Bedingung, die darüber bei jedem Schleifendurchlauf entscheidet müssen wir ebenso anpassen.
Aus
<?php if($logged_in && $answer['owner'] == $user_id){ ?>
wird
<?php if(isset($user) && $answer['owner'] == $user->get_id()){ ?>
Aufruf in der Frageübersicht anpassen
Dem Aufruf des Question-Controllers müssen wir eine ID mitgeben. Um die view()
-Methode im Question-Controller aufzurufen, können wir den Methodennamen direkt mit einem /
getrennt an die URL anhängen. Den ID-Parameter hängen wir wiederum an den Methodennamen mit einem /
getrennt an. Um in unserem Beispiel die URL fürdie Frage mit der ID 2: http://539197-1.web1.fh-htwchur.ch/question/view/2
.
In der Frageübersich, also in der Datei application/views/forum.php müssen wir dementsprechend die Code ändern. Der Code lautet jetzt
<a href="/question/view/<?php echo $question['thread_id'] ?>">Details</a>
Neue Frage erfassen
Um eine neue Frage zu erstellen müssen wir das bisher gelernte kombinieren
Neuer View new_thread.php
Wir benötigen wieder einen neuen View. Dabei gehen wir analog zum Question-View vor.
- Wir erstellen eine neue, leere Datei application/views/new_thread.php …
- … und kopieren den HTML-Teil der Datei _steinbruch/write_question.php in diese neue Datei.
- Wir passen die Verweise auf CSS und JavaScript,
- das Menü
- und den Footer an (s. Question-View)
Question-Controller um new()
-Methode erweitern
Wir bleiben im Question-Controller. Dort benötigen wir eine neue Methode. Diese Methode macht nichts anderes, als den neuen View zu laden.
public function new()
{
$this->load->view('new_thread', $this->data);
}
Mit http://539197-1.web1.fh-htwchur.ch/question/new
können wir den View testen.
Wir können jetz auch den Link für eine neue Frage in application/views/forum.php anpassen.
Frage speichern
Thread-Model erweitern
Um die Daten später in die Datenbank zu übertragen brauchen wir eine neue Methode im Thread-Model.
Wir nennen diese Methode save_thread()
und geben ihr alle nötigen Parameter mit:
public function save_thread($title, $answer_content, $owner_id = 0, $parent_question_id=0 )
{
$sql = "INSERT INTO threads (title, content, parent_thread_id, owner)
VALUES ('$title', '$answer_content', $parent_question_id, $owner_id);";
$query = $this->db->query($sql);
return $query;
}
Die beiden letzten Werte setzen wir per Default auf 0.
Das SQL-Statement schicken wir wie bisher mit der Codeigniter-Funktion $this->db->query($sql)
an die Datenbank und speichern das Ergebnis in der Variable $query
und geben es an die aufrufende Funktion zurück. Als Ergebnis erhalten wir lediglich TRUE
oder FALSE
.
Helper in den Constructor des Question-Controllers
Wir werden gleich das Formular validieren. Um auf die Codeigniter-Funktionen zuzugreifen laden wir den zugehörigen Helper und die Library bereits im Constructor.
$this->load->helper('form_helper');
$this->load->library('form_validation');
neue save()
-Methode im Controller
Um die Frage zu speichern brauchen wir wieder eine neue Methode im Question-Controller. Wir können einfach die new()
-Methode kopieren und sie in save()
umbenennen. Einen Parameter benötigen wir nicht.
Validierung im Controller
Die Validierung läuft analog zum Login ab.
public function save()
{
$this->form_validation->set_rules('question_title', 'Titel', 'required',
array('required' => 'Bitte geben Sie einen %s ein.'));
$this->form_validation->set_rules('question_content', 'Frage', 'required',
array('required' => 'Bitte formulieren Sie eine %s.'));
if ($this->form_validation->run() == FALSE)
{
$this->data['msg'] = "Bitte schauen Sie nochmal nach";
$this->load->view('new_thread', $this->data);
}
else
{
$this->data['msg'] = "Danke für die neue Frage";
$this->load->view('new_thread', $this->data);
}
}
Formularwerte im Controller vorbereiten
Wenn die Validierung erfolgreich durchlaufen wurde, sollen die entsprechenden Werte in der Datenbank gespeichert werden.
Titel und Inhalt der Frage können wir mit
$title = $this->input->post('question_title');
$answer_content = $this->input->post('question_content');
aus den eingegebenen Formularwerten auslesen.
Wir brauchen aber auch noch angaben über den User. Ist er eingeloggt, brauchen wir deine ID, ist er nicht eingeloggt, übertragen wir als ID die 0
.
Der entsprechende Teil der save()
-Methode (bei erfolgreicher Validierung) sieht folgendermassen aus
…
}
else
{
if(isset($this->data['user'])){
$owner_id = $this->data['user']->get_id();
}else{
$owner_id = 0;
}
$title = $this->input->post('question_title');
$answer_content = $this->input->post('question_content');
$this->thread_model->save_thread($title, $answer_content, $owner_id, 0 );
$this->data['msg'] = "Danke für die neue Frage";
$this->load->view('new_thread', $this->data);
}
Formular im View anpassen
Um diese Methode als action
im Formular des New-Thread-Views aufzurufen ersetzen wir dort den <form>
-Tag durch
<?php echo form_open('question/save'); ?>
Fehlermeldungen und Value-Werte im View hinzufügen
Damit der User bei einer falschen Eingabe nicht alles neu eintippen muss, können wir auch in diesem Formular entsprechend dem Login-Views die Fehlermeldungen und die bereits übertragenen Werte in den Formularfeldern anzeigen lassen.
<input type="text" name="question_title" value="<?php echo set_value('question_title'); ?>" class="form-control" id="form_title">
<?php echo form_error('question_title'); ?>
und
<textarea type="text" name="question_content" class="form-control" id="form_content"><?php echo set_value('question_content'); ?></textarea>
<?php echo form_error('question_content'); ?>