Dieses Tutorial baut auf jQuery auf, zum Grundverständnis sollte eventuell sich das jQuery Tutorial durchgelesen werden.
Es gibt vier Dateien, functions.js, index.php, mysql.php und functions.php. In der functions.js sind alle unsere JavaScript Funktionen abgelegt. Die index.php beinhaltet zum einen das HTML Template und beantwortet die Ajax-Anfragen. In der mysql.php befinden sich die Verbindungs/Login Informationen zur MySQL Datenbank. Und zu guter letzt in der functions.php befinden sich alle zusätzlichen PHP Funktionen die wir brauchen.

Die mysql.php

In der mysql.php werden die Login Daten zur MySQL eingeben. In diesem Beispiel ist der Benutzername username und das Passwort auf Passwort gesetzt. Der Host steht auf localhost und die Datenbank ist im Beispiel auf database gestellt. Diese Werte müssen entsprechend euerer Datenbank angepasst und gespeichert werden.

// Verbindung aufbauen, auswählen einer Datenbank
$link = mysql_connect("localhost", "username", "passwort")
    or die("Keine Verbindung möglich: " . mysql_error());

// Datenbank auswählen
mysql_select_db("database") or die("Auswahl der Datenbank fehlgeschlagen\n");

Unsere functions.php

Das Herzstück, denn dort befinden sich unsere PHP-Funktionen. Zu aller erst wird eine Konstante definiert Namens IS_AJAX. Über diesen Boolean Wert können wir prüfen ob es sich um einen Ajax oder einen normalen Webseiten Anfrage handelt.
Als nächstes kommt eine MakeRow Funktion, welche uns den HTML Quelltext einer Row inklusive tr zurückliefert, wenn wir ihr ein Array mit Spalten geben, die wir erstellen wollen.

define('IS_AJAX', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); // TRUE bei Ajax Request


// Erstellt eine H T M L Tabellen Zeile
function MakeRow($line) {
    $return = "\t<tr>\n";
    foreach ($line as $col_value) {
        $return .= "\t\t<td>$col_value</td>\n";
    }
    $return .= "\t</tr>\n";
	
	return $return;
}

// Führt MySQL Queries aus und zeigt ggf. Fehler an
function sql_query($query)
{
	$result = mysql_query($query);
	if(!$result) {
		die("Anfrage fehlgeschlagen: " . mysql_error());
	}	
	return $result;
}

function saveColumn($value) {
	$value = mysql_real_escape_string($value);
	$value = htmlspecialchars($value);
	return $value;
}

Dann gibt es noch eine Funktion sql_query, sie führt einen mysql_query aus, ich habe nur einen Wrapper herumgebaut, sodass man sofort sieht, wenn ein SQL-Query fehlschlägt.
Zum guten Schluss gibt es noch eine saveColumn Funktion. Ihr werden Werte gegeben, bevor sie in der Datenbank gespeichert werden, sodass keine SQL-Injection möglich ist und mögliche HTML Values korrekt gespeichert werden.

Die index.php regelt es

Die index.php ist dazu da, um Anfragen vom Browser anzunehmen und die ihm die Korrekten Antworten zu geben. Es gibt zwei Mögliche Anfrage Typen, entweder es soll die HTML Seite ausgegeben werden, oder es sollen JSON Anfragen bearbeitet werden.
Der Grobaufbau der index.php ist deshalb, das zu Anfang die entsprechenden Libraries geladen werden (mysql.php und functions.php). Und danach ein if else kommt, welches erstmal Grundsätzlich unterscheided zwischen Ajax Request und normalen HTTP Request. Falls es ein normaler Request ist, soll der HTML Code ausgeben werden und bei einem Ajax Request per JSON geantwortet werden. Am Ende wird noch die MySQL Verbindung geschlossen.

<?php
//mysql_real_escape_string
require('mysql.php');
require('functions.php');


// Wenn kein AJAX, dann HTML Page ausgeben, sonst ajax actions ausführen
if(!IS_AJAX) {
	// KEIN AJAX Request
	// Erstellt die "normale" HTML Page

} else {
	// JSON Anfragen beantworten
}

// Schließen der Verbinung
mysql_close($link);
?>

Die index.php bei einer normalen Webanfrage

Wenn es kein Ajax Request ist, holen wir erstmal aus der Datenbank die erforderlichen Daten und sortieren sie nach dem timestamp. Wir nutzen dazu die sql_query Funktion, welche oben weiter beschrieben wurde.

if(!IS_AJAX) {
	// KEIN A J A X Request
	// Erstellt die "normale" HTML Page

	// Daten aus Datenbank besorgen
	$query = "SELECT `dev_jqueryui-table-edit_id`, timestamp, name, comment FROM `dev_jqueryui-table-edit` ORDER BY timestamp DESC"; // Ausführen einer SQL-Anfrage
	$result = sql_query($query);

Als nächstes gehen wir die Daten, welche wir von der MySQL bekommen haben durch und überprüfen die Spalte timestamp und parsen das den timestamp in das deutsche Datumsformat mit $col_value = date(‘d.m.Y H:i’, $col_value);. Danach werden alle Spalten der Zeile in das $data Data Array hinzugefügt und am Ende der Zeile werden noch die Aktions Links hinzugefügt, wie die löschen Funktion.

	$i = 0;
	while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
		$i++;
		
		// Alle Spalten
		foreach ($line as $key=>$col_value) {
			if($key=='timestamp') {
				$col_value = date('d.m.Y H:i', $col_value);
			}
			$data[$i][] = $col_value;
		}
		
		$data[$i][] = '<a class="delete_row" href="#"><img src="images/icon_del_light.png" alt="delete" /></a>'; // Aktions Links
	}

Als nächstes definieren wir die $return Variable, in der wir den HTML Quelltext unserer Tabelle aufbauen, sodass wir ihn nacher einfach benutzen können. Als erstes wird der Tabellen Kopf mit den Überschriften hinzugefügt.

	$return = "<table border=\"1\">\n";
	
	//Tabellen Kopf
	$return .= "\t<tr>\n";
	$return .= "\t\t<th>ID</th>\n";
	$return .= "\t\t<th>Erstell Datum</th>\n";
	$return .= "\t\t<th>Name</th>\n";
	$return .= "\t\t<th>Kommentar</th>\n";
	$return .= "\t\t".'<th style="width:90px">Aktionen <a class="add_row" href="#"><img src="images/icon_add_light.png" alt="add" /></a></th>'."\n";
	$return .= "\t</tr>\n";
	///////
	

Zum Schluss fügen wir unserer $return Variable noch den Inhalt der Tabelle aus der MySQL Datenbank hinzu. Dies machen wir mit der MakeRow Funktion, welche uns den entsprechenden HTML Inhalt für eine Zeile zurückliefert.

	//Tabellen Daten
	if(count($data)>0) {
		foreach($data as $line) 
		{
			$return .= MakeRow($line);
		}
	}
	$return .= "</table>\n";
	///////	

Nachdem wir die $return gefüllt haben, erstellen wir das HTML Template in die Variable $template. Im Head Bereich des Dokumentes, fügen wir jQuery und jQuery UI ein.
Außerdem laden wir noch unsere functions.js, dazu später mehr. Und wir erstelle noch ein div mit der id=loader, welches ein Div ist, welches oben in der Mitte der Webseite mit der CSS Eigenschaft position: fixed angezeigt wird, welches eine Ladeanimation während der Anfragen anzeigen soll.
Und es wird die vorher gefüllte Variable $return in den Body eingefügt, sodass auch unsere Tabelle angezeigt wird.

$template = 
'<html>
	<head>
		<style>
			table {
				width:800px;
			}
			
			input, textarea {
				background-color: #66FF99;
			}
			#loader { position: fixed; left:50%; top:30px; display:none }
		</style>	
		
		<!-- JQuery + Jquery UI einbinden -->
		<link type="text/css" href="./../jqueryui-progressbar/css/ui-lightness/jquery-ui-1.8.13.custom.css" rel="stylesheet" />
		
		<script type="text/javascript" src="./../jqueryui-progressbar/js/jquery-1.5.1.min.js"></script>
		<script type="text/javascript" src="./../jqueryui-progressbar/js/jquery-ui-1.8.13.custom.min.js"></script>
		<script type="text/javascript" src="./functions.js"></script>
	</head>

	<body>
		<div id="loader"><img src="images/ajax-loader.gif"></div> <!-- Ajax Loader -->
		'.$return.'
	</body>
</html>';

	echo $template; // Alles ausgeben
	///////	

} else {

Die index.php bei einer AJAX Anfrage

Falls es ein AJAX Request ist, landen wir in dem else Teil unserer if Abfrage. Danach machen wir ein switch auf $_POST['action'], denn über diese Variable posted unser Ajax Request, welche Aktion wir eigentlich ausführen wollen.
In unserem kleinen Beispiel, gibt es nur add_new und delete_row.

if(!IS_AJAX) {
	// Template erstellen
	// ...
} else {
	switch ($_POST['action']) {
		case "add_new":
			// ... neue Zeile hinzufügen
		
		case "delete_row":
			// ... Zeile löschen
			break; 
	}
}

Bei der add_new Funktion erwarten wir von der AJAX Anfrage die Post Felder: name und comment. Die wir zusammen mit dem aktuelle Timestamp in die Datenbank speichern. Die Post Values werden über die saveColumn Funktion sicher gespeichert (in der funktions.php erklärt).
Außerdem erwartet unsere JavaScript Funktion die neu erstellte Zeile als HTML String zurück. Deshalb holen wir die neue Zeile erneut aus der Datenbank (hier wäre Optimierungs Bedarf) und geben im JSON über den Index row den HTML Quelltext zurück. Theoretisch, könnte man die success Variable die hier in diesem Beispiel immer auf true steht sinnvoll füllen.

		case "add_new":
			// Speicher neuen Eintrag und Tabelle Zeile zurückgeben
			
			// Daten Speichen
			$query = "INSERT INTO `dev_jqueryui-table-edit` (`name` ,`comment` ,`timestamp`)VALUES ('".saveColumn($_POST['name'])."', '".saveColumn($_POST['comment'])."', ".time().");"; 
			$result = sql_query($query);
			$id = mysql_insert_id();
				
			// Die neue Zeile erstellen
			$query = "SELECT `dev_jqueryui-table-edit_id`, timestamp, name, comment FROM `dev_jqueryui-table-edit` WHERE `dev_jqueryui-table-edit_id`=".$id;
			$result = sql_query($query);

			$i = 0;
			while ($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
				$i++;
				
				// Alle Spalten
				foreach ($line as $key=>$col_value) {
					if($key=='timestamp') {
						$col_value = date('d.m.Y H:i', $col_value);
					}
					$data[$i][] = $col_value;
				}
				
				$data[$i][] = '<a class="delete_row" href="#"><img src="images/icon_del_light.png" alt="delete" /></a>'; // Aktions Links
			}
		
			foreach($data as $line) 
			{
				$json['row'] = MakeRow($line);
			}
			
			$json['success'] = true;
			echo json_encode($json);
			break;    
		

Unsere zweite und letzte Action ist die delete_row. Sie soll einfach nur unsere Zeile löschen aus der MySQL Datenbank. Dazu führen wir nur den DELETE Query aus mit einem Filter auf unsere ID, welche wir per JavaScript mit Posten.

		case "delete_row":
			$query = "DELETE FROM `dev_jqueryui-table-edit` WHERE`dev_jqueryui-table-edit_id`=".saveColumn($_POST['id']); 
			echo $query;
			sql_query($query);
			break; 
	}
}

Die functions.js, der JavaScript Kern

Sie fängt an mit $(function(){, der Document Ready Funktion von jQuery, sodass der JavaScript Code erst ausgeführt wird, wenn der HTML Quelltext bereits geladen ist.
Außerdem definieren wir die Variable editTemplate. Sie ist das HTML Template, welches hinzugefügt wird, wenn man einen neuen Eintrag machen möchte, diese brauchen wir erst später.

$(function(){
	var editTemplate = '<tr><form id="t"><td>&nbsp;</td><td>&nbsp;</td><td><input name="name" style="width:100%" type="text" /></td><td><textarea name="comment" style="height:22px;width:100%"></textarea></td><td><a class="save" href="#"><img src="images/save.png" alt="save" /></a><a class="delete_new" href="#"><img src="images/icon_del_light.png" alt="delete" /></a></td></form></tr>';

});

Als nächste bauen wir die Funktion, die auf einen Klick auf den Hinzufügen Button reagiert. Sie fügt einfach immer als erste Zeile das oben definierte editTemplate hinzu. Dies machen wir, in dem wir uns das erste tr mit $(“table tr”).first() und mit .after() den HTML Quelltext hinzufügen. Wir nutzen after, da unsere Tabelle eine Zeile mit Überschriften hat, deshalb ist es in diesem Fall die zweite Zeile.
Außerdem sorgen wir dafür das alle Events erneut aktiviert werden, da der HTML Quelltext verändert wurde und neue hinzugefügter HTML Quelltext sonst auf die Events nicht reagiert. Dazu rufen wir addEvents(); die unten weiter erklärt wird.

	// Neue Zeile an erste Stelle der Tabelle hinzufügen zum Hinzufügen von Daten
	$(".add_row").click(function() {
		$("table tr").first().after(editTemplate);
		addEvents();
	});


Die Funktion addEvents sorgt dafür, das alle Funktionen mit Events ihre Events neu Abonnieren an den HTML Tags.

Als erstes ein Event welches auf den Klick vom Speichern Button lauscht, welches wenn wir auf den speichern Button klicken ein Ajax Request ausgeführt wird an unsere index.php, die den Eintrag speichern soll und uns die Zeile als HTML String zurückgeben soll.

$(".save").click(function() {

Zu Anfang zeigen wir deshalb erst den Ajax Loader an, in dem wir einfach das Div welches wir in der index.php definiert haben, auf sichtbar schalten.

			$('#loader').show(); // Zeige Ajax Loader

Danach holen wir uns über $(this).parent().parent().find(‘input[name="name"]‘).val() den Namen. Wir gehen mit zweimal parent auf die Tabellen Ebene hoch und suchen mit .find() nach einem Input Feld, welches als name Attribut den Inhalt name beinhaltet. Dasselbe machen wir mit dem comment Feld für den Kommentar.

			var name = $(this).parent().parent().find('input[name="name"]').val(); // Holt den Namen aus dem Input Feld der Aktuellen Zeile
			var comment = $(this).parent().parent().find('textarea[name="comment"]').val(); // Holt Kommentar
			var currentItem = this;

Danach überprüfen wir ob der Name und ein Kommentar eingeben wurde. Achtung hier wird nur auf JavaScript Seite geprüft. Aus Sicherheitstechnischen Gründen, sollte man dieses auch auf PHP Seiten nocheinmal prüfen!
Wenn ein Kommentar und der Name da ist, Posten wir an die index.php unsere Daten in dem wir die jQuery Funktion .post benutzen und als ersten Paramter die Zielseite angeben und als zweiten Parameter im JSON Array unser Post Werte.
Der dritte Parameter ist eine Funktion die ausgeführt wird wenn die Anfrage erfolgreich war. Dort erstezten wir die Aktuelle Zeile (unsere Edit Maske) mit dem HTML Content, den uns die PHP Seite als JSON Antwort in dem index row liefert.
Wenn der HTML Quelltext aktualisert wurde, regestrieren wir alle Events wieder neue und verstecken den Ladebalken wieder mit $(‘#loader’).hide();.
Der vierte Parameter gibt an, das es sich um eine json Request handelt.

			if(name!='' && comment!='') {
				// Schicke speicher Anfrage an PHP
				$.post("index.php", { "comment": comment, "name": name, "action": "add_new" },
				function(data){
					$(currentItem).parent().parent().replaceWith(data.row); // Ersetzt die Aktuelle Zeile mit der gespeicherten
					addEvents();
					$('#loader').hide(); // Verstecke Ajax Loader
				}, "json");
			} else {
				$('#loader').hide(); // Verstecke Ajax Loader
				alert('Bitte trage Namen und Kommentar ein');
			}

Im Else Teil Informieren wir den Benutzer, das er Bitte Kommentar und Name eingeben soll und wir verstecken die Ladeanimation.

Das nächste Event ist das delete_new Event. Wenn man einen neuen Eintrag hinzufügt aber diesen noch nicht gespeichert hat und die Zeile löschen möchte, brauch nur aus dem HTML Quelltext diese entfernt werden. Dies machen wir einfach über $(this).parent().parent().remove().

		// Entfernt die neue Row, da sie noch nicht gespeichert wurde wird nur der HTML Code entfernt
		$(".delete_new").click(function() {
			$(this).parent().parent().remove(); // Mit parent wird 2 Objekte höher gegangen um die komplette Zeile zu löschen nicht nur den löschen Button
		});

Als letzte Funktion kommt die delete_row Funktion. Sie löscht eine Zeile die bereits in der MySQL ist. Dazu wird als erstes das Loader Div angezeigt.
Danach holen wir und die ID, in dem wir über $(this).parent().parent() die aktuelle Zeile holen und dann aus dem ersten td mit find(“td:first”).html() uns die ID besorgen, die in der ersten Spalte steht.

		// Entfernt eine Zeile
		$(".delete_row").click(function() {
			$('#loader').show();
			var id = $(this).parent().parent().find("td:first").html();
			$(this).parent().parent().remove(); // Mit parent wird 2 Objekte höher gegangen um die komplette Zeile zu löschen nicht nur den löschen Button
			$.post("index.php", { "action": "delete_row", "id": id }, function(){
				$('#loader').hide();
			});
		});

Danach schicken wir einen Ajax Request los, an die index.php der befiehlt, die row mit der entsprechenden ID aus der Datenbank zu löschen, danach wir der Loader Div wieder versteckt.

			$.post("index.php", { "action": "delete_row", "id": id }, function(){
				$('#loader').hide();
			});

Gesamte addEvents Funktion:

	function addEvents() {
		// Sorgt dafür, dass beim mehrfachen hinzufügen von zeilen, der Eventhandler nicht mehrfach ausgeführt wird.
		$(".save").unbind();

		// Speichern eines neuen Eintrags
		$(".save").click(function() {
			$('#loader').show(); // Zeige Ajax Loader
			var name = $(this).parent().parent().find('input[name="name"]').val(); // Holt den Namen aus dem Input Feld der Aktuellen Zeile
			var comment = $(this).parent().parent().find('textarea[name="comment"]').val(); // Holt Kommentar
			var currentItem = this;
			
			if(name!='' && comment!='') {
				// Schicke speicher Anfrage an PHP
				$.post("index.php", { "comment": comment, "name": name, "action": "add_new" },
				function(data){
					$(currentItem).parent().parent().replaceWith(data.row); // Ersetzt die Aktuelle Zeile mit der gespeicherten
					addEvents();
					$('#loader').hide(); // Verstecke Ajax Loader
				}, "json");
			} else {
				$('#loader').hide(); // Verstecke Ajax Loader
				alert('Bitte trage Namen und Kommentar ein');
			}
		});
		
		// Entfernt die neue Row, da sie noch nicht gespeichert wurde wird nur der HTML Code entfernt
		$(".delete_new").click(function() {
			$(this).parent().parent().remove(); // Mit parent wird 2 Objekte höher gegangen um die komplette Zeile zu löschen nicht nur den löschen Button
		});
		
		// Entfernt eine Zeile
		$(".delete_row").click(function() {
			$('#loader').show();
			var id = $(this).parent().parent().find("td:first").html();
			$(this).parent().parent().remove(); // Mit parent wird 2 Objekte höher gegangen um die komplette Zeile zu löschen nicht nur den löschen Button
			$.post("index.php", { "action": "delete_row", "id": id }, function(){
				$('#loader').hide();
			});
		});
	}
	
	addEvents();

Zu guter schluss wird direkt beim initalisieren der HTML Webseite einmalig die addEvents(); Funktion aufgerufen, das die Events einmalig regestriert sind.
Wenn wir alles richtig gemacht haben, sieht das Ergebnis wie folgt aus: Vorschau im Browser.

Wer noch lusten hat, kann das ganze weiter ausbauen, z.B: eine Funktion zum Bearbeiten fehlt noch und das ganze vielleicht noch etwas Grafisch aufwerten.

Themenrelevanten Artikel: