23. Juli 2013 | Leave a comment Hallo und herzlich willkommen zu einem neuen Blog-Eintrag. Heute möchte ich euch ein paar verschiedene Möglichkeiten vorstellen wie man komplexe Daten in seine Datenbank speichern kann. Mit „komplexen Daten“ meine ich an erster Stelle alles was über einfache Zahlen und Strings hinausgeht. Also: Arrays, Objekte, Dateien. Beginnen wir doch am besten direkt mit den Dateien. Komplexe Daten in der Datenbank speichern Bevor ihr weiter lest: Wenn möglich ist es besser Dateien nicht direkt in der Datenbank sondern im Dateisystem abzulegen – Dateien werden eure Datenbank schnell aufblähen und verlangsamen. Datenbanken können, neben Zahlen und Text, jeden beliebigen Datentyp speichern, dazu haben wir zwei Optionen: einen base64 enkodierten String oder ein BLOB (Binary Large OBject). In gewisser Weise sind sich diese beiden Optionen relativ ähnlich… Doch zuerst möchte ich euch zeigen, wie das ganze im Code aussehen sollte: Wir gehen davon aus, wir wollen ein Bild „test-img.jpg“ in der Datenbank speichern. Im ersten Beispiel handelt es sich beim Feld „image_data“ um den Feld-Typen „BLOB“. // "BLOB" example. $img = file_get_contents('test-img.jpg'); // And simply save the file-content inside the database. mysql_query('INSERT INTO tablename SET image_data = ' . mysql_real_escape_string($img) . ';'); So weit, so gut… Kommen wir nun zur base64 Methode. In diesem Beispiel ist der Feldtyp von „image_data“ lediglich „TEXT“. // "base64" example. $img = file_get_contents('test-img.jpg'); // And simply save the file-content inside the database. mysql_query('INSERT INTO tablename SET image_data = ' . mysql_real_escape_string(base64_encode($img)) . ';'); Und wie wir sehen… Der Code ist beinahe identisch. Bitte beachtet: Sowohl für die base64 als auch die BLOB Variante solltet ihr euch UNBEDINGT den MIME-Type als auch die Endung der Datei abspeichern um diese später wieder korrekt verarbeiten (bzw. an den Browser schicken) zu können. In den nächsten beiden Beispielen gehen wir davon aus, das „$row“ die aktuelle Datenbankzeile beinhaltet – inklusive MIME-Type. // "BLOB" display example. header('Content-type: ' . $row['mime_type']); echo $row['image_data']; die; Wir benötigen also entweder einen expliziten Request für das angeforderte BLOB Element, oder wir arbeiten ebenfalls mit base64 (für die Darstellung des Bildes im Frontend): echo '<img src="data:' . $row['mime_type'] . ';base64,' . base64_encode($row['image_data']) . '" />'; … ich glaube ich muss nicht weiter ausführen wie wir das base64 enkodierte Bild darstellen, oder 😉 ? Weiter geht es mit PHP Arrays… Arrays in der Datenbank speichern Um Arrays in der Datenbank zu speichern gibt es zwei Möglichkeiten: serialize oder json_encode. Mir persönlich gefällt die JSON Methode ganz gut, weil dieses Format relativ leserlich ist und natürlich 1:1 an Javascript weitergegeben werden kann. Serialize ist sicherlich auch okay, aber nicht so leserlich … Es eignet sich eher für Objekte (doch darauf komme ich gleich noch). Eine Sache vorweg: JSON kann mit Umlauten (und ggf. Sonderzeichen) nur sauber umgehen, wenn diese UTF8 kodiert sind. Sollte ein String mit Umlauten also mal als NULL ankommen, probiert es mit utf8_encode(). So könnte das ganze etwa aussehen: // Example of "json_encode" and "serialize" - PHP 5.4 Syntax. $array = [1, 2, 3, 'four' => [4]]; echo 'JSON: ' . json_encode($array); echo 'SERIALIZE: ' . serialize($array); Das Ergebnis würde folgendermaßen aussehen JSON: {"0":1,"1":2,"2":3,"four":[4]} SERIALIZE: a:4:{i:0;i:1;i:1;i:2;i:2;i:3;s:4:"four";a:1:{i:0;i:4;}} Die JSON-Syntax würde sogar noch schlanker aussehen, wenn man kein assoziatives Array benutzt. Wir könnten beide Ergebnisse als einfache Strings (bzw. VARCHAR) in der Datenbank speichern – Je nach Größe bzw. Komplexität müssten wir ggf. den TEXT Feldtyp verwenden. Die Daten können dann sehr einfach wieder zurück kodiert werden: // Example of "json_decode". $array = json_decode($row['data'], true); // And "unserialize". $array unserialize($row['data']); Und damit wären wir auch schon beim letzten Punkt für Heute… Objekte in der Datenbank speichern Wie grade beschrieben bietet sich hier ebenfalls ein einfaches „serialize“ an, doch in einigen Fällen wird das leider nicht ganz ausreichen – Wir müssen unsere Klasse unter Umständen nämlich dahingehend vorbereiten. Am einfachsten lässt sich das anhand einer Klasse zur Datenbank-Abstraktion oder eines Datei-handlers erklären: Wenn eine Resource (egal ob Datei oder Datenbank) geöffnet wurde und wir das Objekt serialisieren, geht diese verloren – Beim de-serialisieren kann es anschließend zu Fehlern kommen! Damit wir solche Fälle abdecken können bietet uns PHP zwei Möglichkeiten: Magische Methoden (__sleep() und __wakeup()) Das „Serializable“ Interface Folgendermaßen werden __sleep() und __wakeup() benutzt: // Demo class. class Demo { private $a, $b; public function __construct ($paramA, $paramB) { $this->a = $paramA; $this->b = $paramB; $this->do_stuff(); } public function __sleep () { return array('a', 'b'); } public function __wakeup () { $this->do_stuff(); } public function do_stuff () { echo 'A: ' . $this->a . ', B: ' . $this->b . "\n\n"; } } $x = new Demo('aa', 'bb'); var_dump($x); $x = serialize($x); var_dump($x); $x = unserialize($x); var_dump($x); Das Ergebnis schaut folgendermaßen aus: A: aa, B: bb object(Demo)#1 (2) { ["a":"Demo":private]=> string(2) "aa" ["b":"Demo":private]=> string(2) "bb" } string(61) "O:4:"Demo":2:{s:7:"Demoa";s:2:"aa";s:7:"Demob";s:2:"bb";}" A: aa, B: bb object(Demo)#1 (2) { ["a":"Demo":private]=> string(2) "aa" ["b":"Demo":private]=> string(2) "bb" } Ich möchte kurz erklären, was hier passiert: Die __sleep() Methode liefert ein Array mit Variablen-Namen zurück, die beim aufruf von serialize() serialisiert werden sollen. __wakeup() dagegen wird bei unserialize() aufgerufen und speichert die übergebenen Member-Variablen wieder korrekt ab… Innerhalb der __wakeup() könnten wir z.B. die Datenbank- oder Datei-Resource wiederherstellen. Das serializable interface Dieses Interface verlangt von uns einfach nur zwei Methoden: serialize() und unserialize()… Was wir innerhalb dieser Methoden tun ist wiederum uns überlassen. Hier werden keine Member-Variablen automatisch (bzw. „magisch“) gesetzt – wir haben die volle Kontrolle über den Code 🙂 Sicherlich die, für uns, angenehmere Lösung. Ich werde auf diese beiden Methoden nicht weiter eingehen… Die Beispiele auf php.net sollten dicke ausreichen. Soo… Ich hoffe ich konnte euch diese Woche etwas neues beibringen und freue mich -wie immer- über Kommentare 😉 Damit wünsche ich euch eine angenehme Rest-Woche und natürlich „Happy Coding“!