MySQL vs. UTF-8 (vs. CodeIgniter)

MySQL kann UTF-8… falsch. Besser gesagt: Ungenau. MySQL kann UTF-8, aber das heißt da anders:

Ein Zeichen in UTF-8 ist (bis zu) vier Bytes lang, “UTF-8” in MySQL kann aber nur bis zu drei. Deswegen fällt der Unterschied oft gar nicht auf, oder erst beim ersten Zeichen, das vier Bytes braucht. Will man echtes UTF-8, muss man einiges umkonfigurieren; in Kurzform:

  1. SQL muss in der Version 5.3.3+ vorhanden sein, siehe Link oben
  2. Jede Datenbank, jede Tabelle (ggf. jede Spalte) müssen auf “CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci” umgestellt werden
    • Dabei zu beachten: InnoDB kann wohl nur 767 Bytes pro Index Schlüssel. Wenn man vier (statt drei) Bytes pro Zeichen rechnet, sind das bsplw. nur 191 (statt 255) Zeichen!
    • Das gilt auch für Typen wie TINYTEXT, die wohl nur 255 Bytes = 63 Zeichen bei 4 Byte/Zeichen speichern können. Ggf. ist hier ein anderer Typ zu wählen
  3. ggf. Server, Client, etc. umstellen (dabei hilft “SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';“)
  4. ggf. Datenbank reparieren und optimieren (war bei mir nicht nötig, ich habe alle Tabellen neu angelegt):

bzw.

Insbesondere bei Punkt 3 hängt es bei CodeIgniter, btw. Wenn man in der Datenbank-Config

 stehen hat, dann ist das ebenfalls nicht das echte UTF-8. Stattdessen korrekt ist:

Update

Das Problem betrifft auch mysqldump: Will man die eine utf8mb4-Datenbank dumpen, muss man das explizit sagen, denn mysqldump nutzt per Default nur utf8 (Quelle):

Das ist so tief verankert, dass auch Dumps über Tools wie Sequel Pro nur utf8 nutzen m(

PHP: PDFs erstellen mit FPDF

FPDF ist ein gutes Tool, um mit PHP recht einfach PDFs zu erstellen. Hier meine gesammelten Learnings nach der ersten Anwendung:

  • Eigene Fonts werden mit MakeFont() in für PHP verständliche Form gebracht (gibt’s auch als Online-Tool)
  • Die Einbindung eigener Fonts geschieht mittels AddFont(), wobei die “Font Family” der String ist, der in der mit MakeFont() erzeugten PHP-Datei $name heißt
  • Text, der mit Write() geschrieben wird, bricht die Seite nicht um
  • Text im Header bricht die Seite nicht um, Text im Footer bricht die Seite nicht um. Mir erschien es zuerst etwas uneinsichtig, wo ich den Content erzeuge – es gibt Header() und Footer(), aber kein Content() oder Body(). Der Grund: Header() und Footer() werden von AddPage() aufgerufen, und damit auch bei automatischem Page-Break. Sprich: Jedesmal; deshalb sind sie ausgelagert. Der Content dagegen wird nur ein mal hinzugefügt. In den Beispielen meistens von außen, aber es geht auch im Konstruktor.
  • Text bricht dann die Seite automatisch um, wenn er mit Cell() (man beachte Parameter “ln”) oder MultiCell() hinzugefügt wird
  • Text in Cell() bricht selbst nicht um, nur in MultiCell()
  • t scheint nicht zu funktionieren, n dagegen schon

Und schließlich, nicht direkt zu FPDF: Wenn es UTF8-Probleme mit Texten aus der Datenbank gibt, kann es daran liegen, dass der Text nicht UTF8-kodiert aus gelesen wird. Ein

hilft dann.

WordPress: Posts sortieren über mehrere Custom Fields

In WordPress kann man eigene Daten zu jedem Post speichern, so genannte Custom Fields. Technisch funktioniert das so, dass pro Post und Field ein Eintrag in der Tabelle wp_postmeta angelegt wird.

Beispiel: Ein Rating-Plugin könnte pro bewertetem Post einen Eintrag <meta_key:rating> mit der Gesamtzahl an “Sternen” (1=schlecht, 5=super), und einen Eintrag <meta_key:raters> mit der Anzahl an abgegebenen Stimmen anlegen. Also zwei Zeilen in wp_postmeta pro Post, für den Stimmen abgegeben wurden. Genau so macht es “Post Rate“. Anmerkung: Man speichert das schon deshalb als zwei diskrete Werte, um bei der nächsten Bewertung wieder den Durchschnitt berechnen zu können – ich musste kurz überlegen, bis mir das klar wurde 🙂

So, nun hat man in dem Beispiel bewertete Posts, und möchte diese Posts vielleicht nach ihrer Bewertung sortiert anzeigen. Kann ja nicht so schwer sein, schließlich gibt es zB $wpdb mit get_results(). Zu einem Problem wird das (für jemanden, dessen SQL-Kenntnisse etwas eingerostet sind mich) dadurch, dass es eben zwei Einträge in wp_postmeta gibt – und beide speichern ihren Wert in wp_postmeta.meta_value! Für eine Sortierung müsste man die Bewertung (stehen unter <meta_key:rating> in meta_value) durch die Anzahl an Stimmen (stehen unter <meta_key:raters> in meta_value) teilen.

Lösung: JOIN. Erst habe ich einen LEFT JOIN versucht geklaut, aber der definiert sich so, dass er alle Posts ausgegeben hätte, selbst wenn für sie gar kein Rating abgegeben wurde. Deshalb also nur JOIN. Basierend auf dem Foreneintrag mit dem LEFT JOIN ist dabei folgendes entstanden:

Etwas wie AS lag mir schon auf der Zunge, allerdings eher als SELECT AS… das in einem (bzw. zwei) JOIN zu verwenden, und zwar sowohl in dem JOIN (“rating.post_id”), wie auch im ORDER BY hätte ich nicht hinbekommen. In diesem Sinne: HTH 🙂

PS: Die formatierte Ausgabe ergibt sich dann aus dem bereits erwähnten Eintrag.

SQL: Custom Order

Normalerweise sortiert man Datensätze alphabetisch oder numerisch oder so. Will man eine bestimmte Gruppe von Datensätzen (z.B. alle mit category 4) ganz oben sehen (statt an vierter Stelle), so nutze man CASE:

Das Ganze macht, dass die Datensätze in zwei Kategorien eingeteilt werden, nämlich “1” und “2”. Nach diesen wird (vor-)sortiert, jede weitere Sortierung erfolgt dann innerhalb dieser Kategorien.

HTH