Skip to content

Tutorial

Daniel Hoover edited this page Apr 25, 2018 · 17 revisions

Im nun folgenden Tutorial möchte ich Ihnen nahebringen wie Sie diesen Quelltext für Ihre Projektarbeit einsetzen können.

  • Im ersten Schritt wollen wir die Adressverwaltung um ein weiteres Feld erweitern - Email. Allerdings wollen wir, dass das Feld Email kein Pflichtfeld ist.
  • Im zweiten Schritt wollen wir, dass es für einen Adressdatensatz eine Detailansicht gibt. (aktuell kann man die Werte nur sehen wenn man auf bearbeiten klickt)
  • Im dritten Schritt erweitern wir die Detailansicht um einen Button "VCard herunterladen".
  • Im vierten Schritt sehen wir uns an wie Werte von JavaScript grundsätzlich an den Controller übergeben und gespeichert werden können (etwa bei einem Spiel)

Schritt 1 - Adressverwaltung um ein weiteres Feld erweitern (Email)

3 Dinge müssen wir grundsätzlich erledigen:

  1. Muss das Feld abgespeichert werden - d.h. wir müssen unsere Tabelle um ein Feld erweitern
  2. Muss das Feld auch im Bearbeiten und Neu View mit ausgegeben werden.
  3. Müssen Werte die abgeschickt werden auch mit an das Model übergeben und das Model muss die Daten speichern

Tabelle address um Feld email erweitern

phpMyAdmin aufrufen, die richtige Datenbank auswählen und zur Tabelle address navigieren. Dort auf Struktur klicken. Unterhalb von Struktur findet sich die Möglichkeit x Spalten (an den Anfang, das Ende, nach einem bestimmten Feld einzufügen). Natürlich könnten Sie die Tabelle auch mit einem

ALTER TABLE `address` ADD `email` VARCHAR(255) NOT NULL;

selbst erweitern - aber wir sind faul und klicken uns über die Oberfläche von phpMyAdmin dieses SQL Statement zusammen. Wir entscheiden uns dafür 1 Spalte an das Ende der Tabelle einzufügen und klicken auf "ok". Als Namen für die Spalte vergeben wir "email" und als Datentyp wählen wir varchar aus. Die Länge des Feldes beträgt 255. Mit einem Klick auf speichern ist dieser Teilschritt abgeschlossen.

Das Feld im Bearbeiten und Neu View mit ausgeben

Damit wir das Feld im View ausgeben können benötigen wir diese Information auch vom Model. Wir gehen aber erstmal davon aus, dass wir diese Information hätten und fügen einfach das Feld unserem Formular im View unter includes/views/address.php nach dem Feld von Ort hinzu.

[...]
<div class="form-group">
	<label for="city">Ort:</label>
	<input type="text" name="city" class="form-control" id="city" value="<?php echo $this->city; ?>">
</div>
<!-- das neue Feld: -->
<div class="form-group">
	<label for="email">Email:</label>
	<input type="email" name="email" class="form-control" id="email" value="<?php echo $this->email; ?>">
</div>
[...]

In der gleichen Datei könnten wir im JavaScript unser Feld bei requiredFields mit aufnehmen sofern wir aus dem Feld ein Pflichtfeld machen wollen würden. Wollen wir aber nicht. Es soll optional bleiben. Zufälligerweise ist der Bearbeiten und Neu View genau der gleiche, dass heißt wir haben mit dem einen Feld das wir hinzugefügt haben gleich zwei Dinge auf einmal erledigt. Fehlt nur noch, dass im letzten Schritt der Wert in der Datenbank gespeichert und aus der Datenbank geholt wird wenn man auf speichern klickt.

Das Model und den Controller mit der neuen Information füttern

Bisschen komplizierter - weil ungewohnt ist, dass ausgerechnet dieser View über AJAX und die REST Schnittstelle an die Anwendung geschickt wird. Das heißt wir müssen nicht nach einem Controller suchen, sondern nach dem REST Service. Dieser findet sich im Ordner includes/restservices/Address.php

Hier sind 4 Methoden definiert die je nachdem was für eine Methode an die API geschickt wird ausgeführt (Create, Read, Update oder Delete) wird. Klickt der Nutzer auf bearbeiten oder neu, so erhält er das Formular wegen des GET Requests. In der Zeile 25 innerhalb der Methode getRequest der Datei includes/restservices/Address.php werden mit dem Aufruf der statischen Methode

$dataForView = AddressModel::getAddressById($data['id']);

bereits die Werte aus der Datenbank geholt und mittels

$view->setData((array) $dataForView);

an den View übergeben. D.h. wir müssen nur prüfen ob das Model auch den Wert von email holt. Dafür gehen wir mit dem Cursor auf den Methodenaufruf "getAddressById" und navigieren über den PHPStorm zur Implementierung dieser Methode. In Windows mit der Tastenkombination STRG + B beim Mac mit CMD + B. Glücklicherweise steht im SQL Statement SELECT * FROM address - d.h. das automatisch sämtliche Spalten selektiert werden. Würden hier die Felder einzeln aufgezählt werden die zurückgegeben werden sollen, z.B. mittels SELECT id,userId,firstname,lastname,street,zip,city FROM address dann müssten wir an die Liste noch ,email mit anfügen. Das können wir uns aber sparen. Wie schön. :-)

Dann wäre das ausgeben von Werten aus der Datenbank schon abgehakt ohne, dass wir was getan hätten. Wie sieht es denn mit dem speichern der Werte aus wenn eine Adresse angelegt oder eine bestehende Adresse bearbeitet wird?

Dafür müssen wir einen Blick in die Methoden createRequest (für erzeugen POST Methode) und saveRequest (fürs Speichern PUT Methode) werfen. Scheinbar wird in beiden Fällen nur geprüft ob die Pflichtfelder Werte haben und anschließend wird $data einfach komplett an die jeweiligen statischen Methoden

AddressModel::createNewAddress($data);
AddressModel::saveAddress($data);

übergeben. D.h. auch hier müssen wir nur sicherstellen, dass diese Methoden unser "email" berücksichtigen. Wir prüfen beide Methoden nacheinander. Navigieren zur Implementierung der Methode AddressModel::createNewAddress indem wir mit dem Cursor auf die Methode "createNewAddress" gehen und STRG + B (Mac CMD + B) drücken.

Hier stellen wir fest, dass email leider fehlt und wir somit gezwungen sind einerseits email anzugeben und als letzten Parameter der VALUES auch den Wert von email mit übergeben.

$sql = "INSERT INTO address(userId,firstname,lastname,street,zip,city,email) VALUES('".$db->escapeString($data['userId'])."','".$db->escapeString($data['firstname'])."','".$db->escapeString($data['lastname'])."','".$db->escapeString($data['street'])."','".$db->escapeString($data['zip'])."','".$db->escapeString($data['city'])."','".$db->escapeString($data['email'])."')";

Damit hätten wir erledigt, dass das Feld beim erzeugen von neuen Adressen berücksichtigt wird erledigt.

Jetzt wollen wir noch prüfen ob es beim speichern berücksichtigt wird. In der gleichen Datei includes/models/AddressModel.php in der Zeile 54 ist die Implementierung vom speichern von Werten. Ein Blick auf das SQL Statement zeigt, dass auch leider hier noch nicht berücksichtigt wurde. Das heißt wir müssen email hinzufügen:

$sql = "UPDATE address SET firstname='".$db->escapeString($data['firstname'])."',lastname='".$db->escapeString($data['lastname'])."',street='".$db->escapeString($data['street'])."',zip='".$db->escapeString($data['zip'])."',city='".$db->escapeString($data['city'])."',email='".$db->escapeString($data['email'])."' WHERE id=".intval($data['id']);

Glückwunsch! Jetzt müssten wir nur noch Testen, ob unsere Änderungen auch funktioniert haben. Glückwunsch - Sie haben Schritt 1 geschafft!

Schritt 2 - Detailansicht für einen Adressdatensatz

Im Prinzip benötigen wir einen neuen View und einen neuen Controller für das vorhaben. Anschließend müssen wir nur auf diesen View verlinken. Wir denken uns einfach einen neue URL für unsere Detailansicht aus. Sagen wir, dass eine Detailansicht erreichbar ist unter der URL /adresse?id=XY wobei XY für eine ID der Tabelle Adresse steht.

Was ist also zu tun?

  • Eine Route für die URL /adresse definieren und hierfür einen eigenen Controller vorsehen.
  • einen neuen Controller erzeugen mit Namen AddressDetailController und implementieren
  • einen View erstellen für die Detailansicht mit Namen addressdetail.php

Route /adresse hinzufügen

Wir öffnen die Datei includes/routes.php und fügen eine neue Route an das Ende hinzu:

[...]
$route['/logout.html'] = array('controller' => 'LogoutController', 'uniqueName' => 'logout');

//neue Route:
$route['/adresse'] = array('controller' => 'AddressDetailController', 'uniqueName' => 'addressdetail'); 

neuen Controller erzeugen mit Namen AddressDetailController

Am besten kopieren wir uns einen bestehenden Controller und benennen diesen um. Dafür nehmen wir den IndexController als Kopiervorlage.

Benennen Sie die kopierte Datei IndexController.php bitte AddressDetailController.php. Die Klasse in AddressDetailController.php muss auch noch von class IndexController extends Controller in class AddressDetailController extends Controller umbenannt werden.

Als $viewFileName nehmen wir nicht "index" sondern "addressdetail" wie wir uns das schon für Schritt 3 überlegt haben. $loginRequired = true lassen wir so - immerhin wollen wir nicht, dass jemand der nicht angemeldet ist einen Adressdatensatz zu sehen bekommt.

In der Methode run passen wir noch den title an zu Adressdetail. username lassen wir so. Die letzte Zeile in der Methode run ändern wir noch ab sodass wir dort $this->view->address = AddressModel::getAddressById($id); stehen haben.

<?php

/**
 * @author Daniel Hoover <https://github.com/danielhoover>
 */
class AddressDetailController extends Controller
{
	protected $viewFileName = "addressdetail"; //this will be the View that gets the data...
	protected $loginRequired = true;


	public function run()
	{
		$this->view->title = "Adressdetails";
		$this->view->username = $this->user->username;

		$this->view->address = AddressModel::getAddressById($id);
	}

}

Wir nutzen aktuell eine Variable $id aus dem "nichts". Die hat aktuell noch keinen Wert. Wenn eine Id gesetzt ist, dann soll überhaupt erst ein Wert für $this->view->address gesetzt werden. Parameter über GET bekommen wir über die vordefinierte Variable $_GET. Es gibt eine Funktion isset() von PHP die für uns überprüft ob eine Variable gesetzt ist. Die nehmen wir her.

<?php

/**
 * @author Daniel Hoover <https://github.com/danielhoover>
 */
class AddressDetailController extends Controller
{
	protected $viewFileName = "addressdetail"; //this will be the View that gets the data...
	protected $loginRequired = true;


	public function run()
	{
		$this->view->title = "Adressdetails";
		$this->view->username = $this->user->username;

		if(isset($_GET['id']))
		{
			$id = $_GET['id'];
			$this->view->address = AddressModel::getAddressById($id);
		}
	}

}

Jetzt sollten wir vielleicht noch verhindern, dass ein Nutzer einfach den GET Parameter verändert und so durch Zufall zugriff auf eine Adresse erhalten würde, die nicht seine ist. Wir wissen ja welche Id der aktuell angemeldete Nutzer hat und bei der Adresse ist eine userId gespeichert. Wenn die nicht zusammenpassen, dann setzen wir $this->view->address wieder auf null - und somit glaubt der view, dass entweder keine Id oder eine ungültige Id übergeben worden ist.

<?php

/**
 * @author Daniel Hoover <https://github.com/danielhoover>
 */
class AddressDetailController extends Controller
{
	protected $viewFileName = "addressdetail"; //this will be the View that gets the data...
	protected $loginRequired = true;


	public function run()
	{
		$this->view->title = "Adressdetails";
		$this->view->username = $this->user->username;

		if(isset($_GET['id']))
		{
			$id = $_GET['id'];
			$this->view->address = AddressModel::getAddressById($id);
			if($this->view->address !== null && $this->view->address->userId != $this->user->id)
			{
				$this->view->address = null;
			}
		}
	}

}

Das müsste reichen um den View mit Daten versorgt und das ganze vor fremden Zugriff geschützt zu haben. Als nächstes muss nur noch der View erstellt und irgendwo ein Link auf diesen neue URL gesetzt werden.

Setzen wir zunächst einmal einen Link auf den View. Hierfür eignet sich zum Beispiel die Listenansicht in der Datei includes/views/index.php

[...]
<?php if($this->addresses): ?>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th>
                    <th>Vorname</th>
                    <th>Ort</th>
                    <th>VCard</th>
                    <th>Bearbeiten</th>
                    <th>Löschen</th>
                </tr>
            </thead>
            <tbody>
            <?php foreach($this->addresses as $address): ?>
                <tr>
                    <td><?php echo $address->id; ?></td>
                    <td><?php echo $address->lastname; ?></td>
                    <td><?php echo $address->firstname; ?></td>
                    <td><?php echo $address->city; ?></td>
                    <td><a href="download?id=<?php echo $address->id; ?>" class="btn btn-primary">Download</a></td>
                    <td><button class="btn btn-default" data-toggle="modal" data-target="#editModal" data-id="<?php echo $address->id; ?>"><i class="glyphicon glyphicon-pencil"></i> Bearbeiten</button></td>
                    <td><a class="btn btn-danger triggerDelete" href="api/address/" data-id="<?php echo $address->id; ?>"><i class="glyphicon glyphicon-trash"></i> Löschen</a></td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
        <?php else: ?>
                <p>&nbsp;</p>
                <div class="alert alert-info">Noch keine Adressen vorhanden - Sie können über den Button <strong>Neue Adresse anlegen</strong> eine neue Adresse Ihrer Adressverwaltung hinzufügen.</div>
        <?php endif; ?>
[...]

Fügen wir der Einfachheit halber einen Link ein (denkbar wäre natürlich auch u.a. ein eigener Button) der um das Feld ID geht:

[...]
<?php if($this->addresses): ?>
        <table class="table table-striped">
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th>
                    <th>Vorname</th>
                    <th>Ort</th>
                    <th>VCard</th>
                    <th>Bearbeiten</th>
                    <th>Löschen</th>
                </tr>
            </thead>
            <tbody>
            <?php foreach($this->addresses as $address): ?>
                <tr>
                    <td><a href="adresse?id=<?php echo $address->id; ?>"><?php echo $address->id; ?></a></td>
                    <td><?php echo $address->lastname; ?></td>
                    <td><?php echo $address->firstname; ?></td>
                    <td><?php echo $address->city; ?></td>
                    <td><a href="download?id=<?php echo $address->id; ?>" class="btn btn-primary">Download</a></td>
                    <td><button class="btn btn-default" data-toggle="modal" data-target="#editModal" data-id="<?php echo $address->id; ?>"><i class="glyphicon glyphicon-pencil"></i> Bearbeiten</button></td>
                    <td><a class="btn btn-danger triggerDelete" href="api/address/" data-id="<?php echo $address->id; ?>"><i class="glyphicon glyphicon-trash"></i> Löschen</a></td>
                </tr>
            <?php endforeach; ?>
            </tbody>
        </table>
        <?php else: ?>
                <p>&nbsp;</p>
                <div class="alert alert-info">Noch keine Adressen vorhanden - Sie können über den Button <strong>Neue Adresse anlegen</strong> eine neue Adresse Ihrer Adressverwaltung hinzufügen.</div>
        <?php endif; ?>
[...]

Somit kann der Nutzer nun durch klick auf ID zur Detailansicht der Adresse gelangen. Aktuell fehlt aber noch der View adressdetail.php - den werden wir jetzt vereinfacht erstellen. Legen Sie dazu bitte eine Datei includes/views/adressdetail.php mit folgendem Inhalt an:

<?php

echo $this->header;

?>
<div id="main" class="addressDetail">
<?php if($this->address): ?>
    <h1><?php echo $this->address->firstname; ?> <?php echo $this->address->lastname; ?></h1><br>
    <span class="street"><?php echo $this->address->street; ?></span><br>
    <span class="zip"><?php echo $this->address->zip; ?></span><span class="city"><?php echo $this->address->city; ?></span><br>
    <?php if($this->address->email != ''): ?>
    <a href="mailto:<?php echo $this->address->email; ?>" class="email"><?php echo $this->address->email; ?></a><br>
    <?php endif; ?>
<?php else: ?>
    <h1>Ungültige Adresse!</h1>
<?php endif; ?>
    
</div>
<?php

echo $this->footer;

?>

Fertig! Das ganze kann jetzt natürlich noch per CSS entsprechend gestyled werden sodass es ansprechend aussieht. Sofern Ihnen das wichtig sein sollte können Sie das gerne machen. An dieser Stelle denke ich sollten Sie alleine zurecht kommen.

Schritt 3 - Detailansicht um einen Button "VCard herunterladen" erweitern

Das ist ein stark backendlastiger Schritt. Nachdem die serverseitige Programmierung (also das Backend) erst im vierten Semester gefordert wird können Sie an dieser Stelle aufhören. Sollten Sie allerdings die Aufgabenstellung sehr interessieren dürfen Sie natürlich auch gerne weiterlesen:

Folgendes müssen wir grundsätzlich machen:

  1. verstehen was eine VCard eigentlich ist
  2. uns eine URL ausdenken die wir für den VCard Download nutzen
  3. diese URL in den Routen hinzufügen und uns einen neuen Controller erstellen der für diesen VCard Download zuständig ist
  4. im neuen VCardDownloadController prüfen ob eine gültige ID übergeben wurde (falls nein -> Fehlermeldung)
  5. prüfen ob der betreffende Nutzer überhaupt die VCard herunterladen darf (falls nein -> Fehlermeldung)
  6. Sofern bisher keine Fehlermeldung erzeugt wurde den VCard String zusammenbauen
  7. Uns einen Dateinamen überlegen
  8. Den String so zurückgeben, dass ein Dateidownload vom Browser angestoßen wird
  9. Button in der Detailansicht hinzufügen und Link auf Download setzen

1. verstehen was eine VCard eigentlich ist

Grundsätzlich ist eine VCard eine digitale Visitenkarte. Viele Mailprogramme (aber auch Smartphones) sind in der Lage solche Visitenkarten direkt zu den Kontakten hinzuzufügen.

Wir haben ja bereits Adressdaten die sich wunderbar als Visitenkartendaten für so eine VCard eignen würden. Zunächst muss man verstehen was das VCard Format eigentlich ist. Es ist eine Art String der auf eine besondere Art und Weise geschrieben wird um darzustellen welches Datum was genau eigentlich ist. Ein solcher String beginnt mit BEGIN:VCARD in der folgenden Zeile steht dann die Versionsnummer des VCard Formats z.B.: VERSION:4.0 anschließend folgen in beliebiger Reihenfolge Datenfelder. Abschießen muss der String mit END:VCARD.

Also strukturell in etwa so:

BEGIN:VCARD
VERSION:4.0
EIGENSCHAFT[;PARAMETER]:Attribut[;Attribut]
END:VCARD

2. uns eine URL ausdenken die wir für den VCard Download nutzen

Gut - da ist Kreativität gefordert. Naheliegend wäre sowas wie

download?id=xy

Wobei xy natürlich für eine beliebige numerische ID steht. Denkbar wäre natürlich auch etwas wie downloadVCard?id=xy oder letsDownloadThisAwesomeVCardNow?id=xy. Letztlich ist es egal was Sie sich da für eine tolle URL ausdenken. Für das aktuelle Beispiel bleiben wir jetzt mal bei download als URL. Sie können natürlich auch eine andere URL nehmen, müssen dann aber bitte an den Stellen wo download als URL vorkommt entsprechend Ihre URL verwenden.

3. diese URL in den Routen hinzufügen und uns einen neuen Controller erstellen der für diesen VCard Download zuständig ist

Wir öffnen die Datei includes/routes.php und erstellen einen neuen Eintrag damit wir sagen: es gibt eine URL "download" und wenn jemand diese URL aufruft, dann ist der Controller VCardDownloadController zuständig (den wir uns gerade ausgedacht aber noch nicht erstellt haben). Als uniqueName vergeben wir noch einen Namen. Der ist aber ziemlich egal, da wir nicht vorhaben im View besonders auf die Tatsache zu reagieren, dass wir uns auf der Seite des VCardDownloads befinden.

$route['/download'] = array('controller' => 'VCardDownloadController', 'uniqueName' => 'vcarddownload');

Jetzt kopieren wir uns die bestehende Datei includes/controllers/IndexController.php und benennen diese in VCardDownloadController.php um. Anschließend öffnen wir die kopierte Datei VCardDownloadController.php und ändern diese wie folgt ab:

<?php

class VCardDownloadController extends Controller
{
	protected $viewFileName = "vcarddownload"; //this will be the View that gets the data...
	protected $loginRequired = true;


	public function run()
	{
		$this->view->title = "Download";
		$this->view->username = $this->user->username;

		//TODO
	}

}

$loginRequired ist auf true gesetzt, da wir nur den Leuten den Download erlauben wollen die überhaupt angemeldet sind.

Die Schritte 4 bis 8

An der Stelle wo vorher //TODO stand fangen wir jetzt an ein paar Dinge einzufügen. Erstmal prüfen wir ob überhaupt eine id übergeben worden ist, prüfen ob die ID gültig ist, prüfen ob der Nutzer überhaupt Eigentümer der Adresse ist und falls ja die VCard so ausgeben, dass der Browser das als Dateidownload entgegennimmt

<?php

class VCardDownloadController extends Controller
{
	protected $viewFileName = "vcarddownload"; //this will be the View that gets the data...
	protected $loginRequired = true;


	public function run()
	{
		$this->view->title = "Download";
		$this->view->username = $this->user->username;

		$id = (isset($_GET['id'])) ? $_GET['id'] : null;

		if($id === null)
		{
			$this->view->noIdError = true;
		}
		else
		{
			$addressObj = AddressModel::getAddressById($id);

			if($addressObj === null)
			{
				$this->view->invalidIdError = true;
			}
			else
			{
				if($addressObj->userId != $this->user->id)
				{
					$this->view->noRightsError = true;
				}
				else
				{
					//ok, we have an Id - we have a valid Id and the User has the right to access that address

					//lets create the VCard String

					$vCardString = "BEGIN:VCARD\n";
					$vCardString .= "VERSION:4.0\n";

					$vCardString .= "N:".$addressObj->lastname.";".$addressObj->firstname.";;;\n";
					$vCardString .= "FN:".$addressObj->firstname." ".$addressObj->lastname."\n";
					$vCardString .= "FN:".$addressObj->firstname." ".$addressObj->lastname."\n";

					$vCardString .= "ADR;WORK;POSTAL;LABEL=\"".$addressObj->street."\\n".$addressObj->zip." ".$addressObj->city."\"";

					$vCardString .= ":;;".$addressObj->street.";".$addressObj->city.";;".$addressObj->zip.";\n";

					if($addressObj->email)
					{
						$vCardString .= "EMAIL:".$addressObj->email."\n";
					}

					$vCardString .= "REV:".date('c')."\n";
					$vCardString .= "END:VCARD";

					//now lets think of an amazing Filename:

					$vCardFilename = "VCard".$addressObj->id.".vcf";

					//lets send that thing to the browser
					header('Content-Type: text/vcard'); //text/vcard is mimetype of vcard
					header("Content-Transfer-Encoding: Binary");
					header("Content-disposition: attachment; filename=\"" .$vCardFilename . "\"");

					echo $vCardString;
					exit();


				}
			}
		}

	}

}

Button in der Detailansicht hinzufügen und verlinken

Öffnen der Datei includes/views/index.php

[...]
 <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th>
                    <th>Vorname</th>
                    <th>Ort</th>
                    <th>VCard</th>
                    <th>Bearbeiten</th>
                    <th>Löschen</th>
                </tr>
            </thead>
            <tbody>
            <?php foreach($this->addresses as $address): ?>
                <tr>
                    <td><a href="adresse?id=<?php echo $address->id; ?>"><?php echo $address->id; ?></a></td>
                    <td><?php echo $address->lastname; ?></td>
                    <td><?php echo $address->firstname; ?></td>
                    <td><?php echo $address->city; ?></td>
                    <td><a href="download?id=<?php echo $address->id; ?>" class="btn btn-primary">Download</a></td>
                    <td><button class="btn btn-default" data-toggle="modal" data-target="#editModal" data-id="<?php echo $address->id; ?>"><i class="glyphicon glyphicon-pencil"></i> Bearbeiten</button></td>
                    <td><a class="btn btn-danger triggerDelete" href="api/address/" data-id="<?php echo $address->id; ?>"><i class="glyphicon glyphicon-trash"></i> Löschen</a></td>
                </tr>
            <?php endforeach; ?>
[...]

Fertig... :-)

Schritt 4: Werte von JavaScript grundsätzlich an den Controller übergeben und speichern (etwa bei einem Spiel)

Nehmen wir mal an Sie hätten ein Spiel programmiert. Das Spiel läuft auch soweit. Jetzt wäre es notwendig, dass Sie entsprechend am Ende des Spiels irgendwelche Werte (z.B. NumAttempts und UserScore) an die Datenbank übergeben müssen. Stellen Sie sich dazu vor, dass Sie eine Tabelle game hätten die wie folgt aussieht:

id userid score attempts
1 2 400 7
2 2 600 5
3 4 300 9
4 5 760 4

Folgendes wäre also zu tun:

  1. eine Funktion in JavaScript schreiben die die Werte an das Backend schickt und je nachdem ob es geklappt hat darauf reagiert (zum Beispiel auf eine Seite mit den Highscores wechseln)
  2. Im Backend (Controller) die Daten entgegennehmen und an das Model weitergeben und an das Frontend eine Nachricht schicken, dass alles geklappt hat
  3. Eine Methode im Modell schreiben die das entsprechende SQL Statement für die Datenbank generiert und ausführt

Fangen wir also mal damit an, dass wir eine Funktion brauchen die Daten an das Backend schickt. Wir erleichtern uns die Arbeit und nutzen jQuery für den AJAX Request. Sollten Sie Bootstrap 4 nutzen wird standardmäßig eine jQuery Version verwendet (jQuery slim) bei der u.a. AJAX fehlt. Sofern also noch nicht geschehen müssten Sie an der Stelle wo bei Ihnen jQuery eingebunden wird z.B. folgendes:

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>

durch folgendes ersetzen:

<script src="http://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>

Jetzt haben wir auch (u.a.) AJAX als Funktionen in jQuery zur Verfügung.

Nun nehmen wir mal an Sie haben eine Datei game.js in der Ihr Spiel abläuft. Dort gibt es eine Stelle wo das Spiel vorbei ist. Gedanklich in etwa so:

function gameWon(numAttempts, userScore) {
    //... some awesome stuff happened here before...
    //... now is the time to save ...
    //after save change url to scoreboard
}

Gut. Wir nehmen weiter folgendes an: das Spiel läuft auf der Seite /game und wir haben hierfür einen Controller mit Namen GameController. (denkbar wäre natürlich auch, dass bei Ihnen das Spiel auf der Seite /index mit einem IndexController läuft - oder /wasserhahn mit einem WasserhahnController)

Was wir im Prinzip machen ist Daten mittels JavaScript (unter zuhilfenahme von AJAX) abzuschicken. Stellen Sie sich das einfach so vor, dass Sie über JavaScript in der Lage sind etwas abzuschicken wie Sie das von Formularen her kennen, ohne dass Sie ein Formular dafür brauchen würden.

Schicken wir also die Daten an die Seite game

function gameWon(numAttempts, userScore) {
    //... some awesome stuff happened here before...
    $.ajax({
        'url':    'game',
        'method': 'post',
        'data':    {'action': 'saveScore', 'attempts': numAttempts, 'score': userScore},
        'success': function(receivedData) {
            if(receivedData.result) {
                //after save change url to scoreboard
                location.href = 'scoreboard';
            }
        }
    });
    
}

Auch hier ist es so, dass wir dem Backend helfen über die "action" zu sagen was wir wollen. Sonst müsste in das Backend eine Logik implementiert werden die anhand der verschickten Parameter herausfindet was wir ursprünglich geplant hatten.

Jetzt ist es an uns im Backend beim GameController dafür zu sorgen, dass die Daten angenommen werden. Nehmen wir mal folgenden GameController an:

<?php

class GameController extends Controller
{
	protected $viewFileName = "game"; //this will be the View that gets the data...
	protected $loginRequired = true;


	public function run()
	{
		$this->view->title = "Game";
		$this->view->username = $this->user->username;

		//maybe some other awesome stuff that happens here...
	}

}

Gut, diesen GameController erweitern wir jetzt. Wir wollen, dass die Methode run() schön schlank bleibt. Daher definieren wir eine neue Methode checkForSaveScorePost() die wir mit unserer Erweiterung füttern. Wir tun jetzt auch mal so als könnte unser Model bereits einen Score und Attempts speichern... (implementieren wir im Anschluss)

<?php

class GameController extends Controller
{
	protected $viewFileName = "game"; //this will be the View that gets the data...
	protected $loginRequired = true;


	public function run()
	{
		$this->view->title = "Game";
		$this->view->username = $this->user->username;

		//maybe some other awesome stuff that happens here...

		$this->checkForSaveScorePost();
	}

	private function checkForSaveScorePost()
	{
		if(isset($_POST['action']) && $_POST['action'] == 'saveScore')
		{
			$score = $_POST['score'];
			$attempts = $_POST['attempts'];
			$userid = $this->user->id;

			//now we need our Model to save the values
			GameModel::saveScoreAndAttempts($userid, $score, $attempts); //:: ist only working when we define a Method as static. That means one can use the method without instanciating an object
			//normally we would first make a new object like so:
			//$gameObj = new GameModel();
			//$gameObj->saveScoreAndAttempts($userid, $score, $attempts);
			//but if a method is defined as static - it can be used directly like a function

			//finally send a JSON message that we saved the values...
			$jsonResponse = new JSON();
			$jsonResponse->result = true; //this is important, as the frontend expects result true if everything was ok
			$jsonResponse->setMessage("Saved the values!"); //(optional)
			$jsonResponse->send();
		}
	}

}

Den Controller hätten wir schonmal erledigt. Jetzt muss nur noch das Model um die soeben erfundene statische Methode erweitert werden. (anderfalls gäbe es eine fiese Fehlermeldung, dass es diese Methode gar nicht gibt) Nehmen wir an es gibt eine Model includes/models/GameModel.php

<?php

class GameModel
{
	public static function saveScoreAndAttempts($userid, $score, $attempts)
	{
		$db = new Database();

		//prevent SQL Injection:
		$userid = $db->escapeString($userid);
		$score = $db->escapeString($score);
		$attempts = $db->escapeString($attempts);

		$sql = "INSERT INTO game(`userid`,`attempts`,`score`) VALUES('".$userid."','".$score."','".$attempts."')";
		$db->query($sql);
	}

	//... and other awesome stuff in the GameModel that we are currently not interessted in...
}

Fertig. Wenn Sie das ausprobieren sollten jetzt die Werte in der Datenbank gespeichert werden die wir per JavaScript übergeben haben.