Codeigniter Tutorial

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:

  1. Weil es uns später das Leben erleichtert und …
  2. … 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'); ?>
Comments are closed.