1. Code
  2. WordPress
  3. Plugin Development

Benutzerdefinierte Datenbanktabellen: Erstellen der Tabelle

Scroll to top
This post is part of a series called Custom Database Tables.
Custom Database Tables: Safety First

German (Deutsch) translation by Wei Zhang (you can also view the original English article)

In dieser Serie werden benutzerdefinierte Datenbanktabellen verwendet. In diesem Artikel erfahren Sie, wie Sie die Tabelle erstellen, pflegen und entfernen sowie wie Sie Daten sicher und effizient hinzufügen, entfernen und abfragen. In diesem ersten Artikel wird beschrieben, wann benutzerdefinierte Tabellen geeignet sind, die Vor- und Nachteile ihrer Verwendung und wie die Tabelle erstellt wird.

Glücklicherweise bietet WordPress eine recht umfangreiche API, die das Erstellen und Interagieren mit benutzerdefinierten Tabellen ein bisschen einfacher macht. Vor allem die $wpdb-Klasse und die dbDelta() - Funktion, von der wir in der Serie mehr sehen werden. Trotzdem bedeutet das Erstellen einer benutzerdefinierten Tabelle, dass Sie etwas Fremdes für WordPress erstellen - und Sie verlieren den Großteil des Frameworks, das die nativen Tabellen umgibt. Aus diesem Grund sind Sie als Autor des Plugins für die sichere und effiziente Interaktion mit dem Plugin verantwortlich. Bevor Sie einsteigen, müssen Sie sorgfältig überlegen, ob die Verwendung einer vorhandenen Kerntabelle besser geeignet ist.


Die Nachteile der Verwendung einer benutzerdefinierten Tabelle

Wie bereits erwähnt, liegen benutzerdefinierte Tabellen außerhalb des normalen WordPress-Frameworks - und dies ist zum größten Teil die Ursache für ihre Nachteile:

  • Es gibt keine systemeigenen Funktionen zum Hinzufügen, Entfernen, Aktualisieren oder Abfragen, mit denen Sie mit der Tabelle interagieren können.
  • Die Benutzeroberfläche muss (fast) von Grund auf neu erstellt werden.
  • Die Desinfektion und das Caching liegt bei Ihnen (obwohl WordPress in dieser Hinsicht viel Hilfe bietet).
  • Andere Plugins und WordPress selbst erwarten nicht, dass Ihre Tabelle dabei ist. Wenn es sich bei Ihren Daten jedoch um einen benutzerdefinierten Post-Typ handelt, funktionieren die meisten Drittanbieter-Plug-Ins mit diesen.
  • WordPress - oder viele andere verwandte Plugins - sichern oder exportieren Ihre Tabelle. (Eigentlich unterstützen einige Backup-Plugins Nicht-Core-Tabellen, aber das Exportieren / Importieren ist nicht so einfach)
  • Sie sind dafür verantwortlich, die Struktur Ihrer benutzerdefinierten Tabelle (n) auf effizienteste Weise festzulegen, einschließlich der Auswahl des geeignetsten Datentyps für die Spalten.
  • Sie sind für das Schreiben fehlerfreier und effizienter SQL-Abfragen verantwortlich.

Wann ist das Erstellen einer benutzerdefinierten Tabelle sinnvoll?

Es gibt keine "richtige" Antwort darauf, und ein vernünftiges Urteil des Für und Wider ist erforderlich. Im vorherigen Abschnitt werden jedoch einige gravierende Nachteile genannt, wenn das vorhandene WordPress-Schema nicht verwendet wird. Wenn Sie sich nicht sicher sind, sollten Sie in der Regel keine Tabellen erstellen. Darüber hinaus erfordert ein benutzerdefinierter Tabellenansatz eine Menge Arbeit und bietet ausreichend Gelegenheit, um Fehler einzuschleichen.

Die Datenstruktur

Eines der wichtigsten Argumente für benutzerdefinierte Tabellen ist, wenn die Daten so strukturiert werden müssen, dass sie für die nativen Tabellen nicht geeignet sind. Die *_posts-Tabelle ist inhärent auf Posts und Seiten ausgerichtet, die für Ihre Daten möglicherweise völlig ungeeignet sind. Tatsächlich können Ihre Daten am besten auf mehrere Tabellen verteilt sein, wobei die Beziehungen zwischen ihnen bestehen. Es ist möglicherweise nicht einmal so kompliziert: Das Posts 2 Posts-Plugin verwendet eine benutzerdefinierte Tabelle, um viele-zu-viele-Beziehungen zwischen Posttypen zu speichern. Dies kann mithilfe der Taxonomie-API (und war ursprünglich) oder der Meta-API durchgeführt werden. Beide Methoden sind jedoch nicht besonders effizient - und obwohl sie für kleinere Websites geeignet sind, lässt sie sich nicht gut skalieren. Scribu hat Beiträge 2 Beiträge in eine benutzerdefinierte Tabellenimplementierung verschoben, um Informationen über eine Beziehung zu speichern.

Die meisten Fälle können mithilfe von Post-Meta in die *_posts-Form eingepreßt werden. Dies stellt jedoch möglicherweise nicht die effizienteste Route dar: Die Post-Metatabelle verwendet eine nicht indizierte Wertespalte zum Speichern von Daten. Es ist unglaublich schnell, die Metadaten eines Beitrags abzurufen (WordPress verwendet auch das Zwischenspeichern), aber komplexe Abfragen mit der Metatabelle können ineffizient oder nahezu unmöglich sein.

Komplexe Abfragen

In Verbindung mit dem Vorstehenden stehen komplexe Abfragen, die von den nativen Tabellen möglicherweise nicht effizient ausgeführt werden können. In Event Organizer zum Beispiel ist ein Ereignis ein Beitrag, bei dem die Ereignisdaten in einer separaten Tabelle gespeichert sind. Es wäre zwar möglich, diese Datumsangaben als Post-Meta-Format zu speichern. Wenn dies mehr als ein Datum für Ereignisse hat, werden alle datumsbasierten Abfragen äußerst schwierig und ineffizient, da die Meta-Wert-Spalte nicht indiziert ist.

Rahmen

Wenn Sie wp_posts verwenden und Ihre Daten ausreichend groß sind (100.000 Beiträge), kann dies die Leistung beeinträchtigen, je nachdem, welche Abfragen Sie ausführen. Dieses Argument an sich ist ziemlich schwach, da es viele Unbekannte gibt, die seine Gültigkeit beeinflussen werden. Im Allgemeinen sind Datenbanken jedoch schnell in der Lage, was sie tun - und das umgebende WordPress-Framework dient dazu, Abfragen so gut wie möglich zu optimieren. In Kombination mit den beiden anderen Faktoren können Sie jedoch feststellen, dass eine benutzerdefinierte Tabelle die sinnvollste Option darstellt.


Die Tabelle erstellen

Wenn Sie entschieden haben, dass eine benutzerdefinierte Tabelle erforderlich ist, müssen Sie die Tabelle erstellen. Bevor wir dies tun, speichern wir den Namen unserer benutzerdefinierten Tabelle in $wpdb. Dieses globale Element enthält alle Informationen, die sich auf die Datenbank für das aktuelle Blog beziehen (sie werden von Site zu Site geändert, wenn Sie mehrere Sites verwenden). Wir werden unseren Tabellennamen zu diesem globalen Namen hinzufügen. Dies ist überhaupt nicht notwendig, macht aber den Rest unseres Codes etwas übersichtlicher:

1
add_action( 'init', 'wptuts_register_activity_log_table', 1 );
2
add_action( 'switch_blog', 'wptuts_register_activity_log_table' );
3
4
function wptuts_register_activity_log_table() {
5
  global $wpdb;
6
	$wpdb->wptuts_activity_log = "{$wpdb->prefix}wptuts_activity_log";
7
}

Der obige Code verwendet $wpdb->prefix, um dem Tabellennamen ein Präfix hinzuzufügen. Das Präfix ist standardmäßig wp_, kann aber vom Benutzer in der wp-config.php geändert werden. Dies ist erforderlich, wenn Sie möglicherweise mehr als eine WordPress-Installation mit derselben Datenbank installiert haben. Sie kann jedoch auch aus anderen Gründen geändert werden. Als solches können Sie nicht davon ausgehen, dass das Präfix wp_ ist. Wie bei Funktionen, Klassen und Einstellungen usw. sollten Sie sicherstellen, dass Ihr Tabellenname eindeutig ist.

In dieser Serie werden wir auf das folgende Beispiel zurückkommen. Wir stellen uns vor, dass wir eine Tabelle erstellen, um Benutzeraktivitäten zu protokollieren (Aktualisieren oder Entfernen von Beiträgen, Ändern von Einstellungen, Hochladen eines Bildes usw.).

Spaltenbenennungskonventionen

Es gibt verschiedene Konventionen für die Benennung Ihrer Spalten (und Ihrer Tabellen für diese Angelegenheit) - aber unabhängig von der Benennung der Spalten ist es wichtig, konsistent zu sein. Ich würde empfehlen, nur Kleinbuchstaben zu verwenden, da in manchen Fällen Spaltennamen die Groß- und Kleinschreibung berücksichtigen können. Durch das Erzwingen dieser Regel werden Fehler weniger wahrscheinlich und die Lesbarkeit wird verbessert. Wie wir später in der Serie sehen werden, ist es auch nützlich, wenn Sie Spalten auf die Whitelist setzen müssen. Sie sollten Wörter in Spaltennamen (z. B. post_data, post_content) zur besseren Lesbarkeit trennen. Dies sollte jedoch mit Unterstrichen und niemals mit Leerzeichen erfolgen.

Sie sollten auch reservierte Wörter vermeiden. Wenn sich die Spalte auf eine Fremdtabelle bezieht, wird empfohlen, den Namen dieser Fremdspalte zu verwenden (z. B. user_id, unser Beispiel).

In unserem Beispiel benennen wir unsere Spalten:

  • log_id - die Log-ID.
  • user_id - die Benutzer-ID, der das Protokoll entspricht.
  • activity - die Aktivität, die aufgetreten ist.
  • object_id - Die ID des Objekts (z. B. Beitrags-ID, Benutzer-ID, Kommentar-ID usw.), das Gegenstand der Aktivität des Benutzers war.
  • object_type - der Objekttyp (z. B. "post", "user", "comment" usw.).
  • activity_date - Datumszeit der Aktivität.

Festlegung der Spaltentypen

Bevor Sie fortfahren, müssen Sie entscheiden, welche Datentypen der Spalten Ihre Tabelle haben soll. Spaltentypen können in drei Kategorien unterteilt werden: Zeichenfolgen, Numerik und Datumsangaben. Für jede davon gibt es viele Varianten. Eine vollständige Referenz finden Sie hier.

Es ist wichtig, den geeigneten Datentyp für Ihre Tabelle zu wählen, da dies die Effizienz Ihrer Abfragen beeinflusst. Bei einigen Datentypen können Sie ein Limit festlegen (z. B. varchar(40), in dem Sie bis zu 40 Zeichen speichern können. Das Limit ist optional, wird jedoch empfohlen, da dies die Leistung verbessern kann. Sie müssen also für jede Spalte entscheiden, wie viele Zeichen maximal für die Spalte erforderlich sind. Beachten Sie bei numerischen Datentypen, dass sich die Länge auf die Anzahl der Ziffern bezieht - nicht das Maximum (z. B. INT(10) erlaubt nicht negative Ganzzahlen mit bis zu 10 Ziffern - also bis zu 4.294.967.295).

Beim Speichern von Datumsangaben sollten Sie fast immer den Datentyp DATETIME verwenden (gespeichert als 2012-11-05 14:55:10) - und sicherlich keine für den Benutzer freundliche Darstellung des Datums (z. B. 5. November 2012, 14:55 Uhr). DATETIME-Werte können mithilfe von Funktionen wie mysql2date() leicht in eine vom Menschen lesbare Form formatiert werden. Sie sollten Datumsangaben in der UTC-Zeitzone speichern und bei der Ausgabe ggf. auf eine andere Zeitzone umstellen.

In unserem Beispiel haben wir:

  • log_id - bigint (20)
  • user_id - bigint (20)
  • activity - Varchar (20)
  • object_id - bigint (20)
  • object_type - varchar (20)
  • date - datetime

Spalten indizieren

Als Nächstes müssen Sie entscheiden, welche Spalten indiziert werden sollen - diese werden als KEYs deklariert, von denen einer der PRIMARY KEY ist. Der Primärschlüssel ist eine Spalte, in der jede Zeile einen eindeutigen Eintrag hat. Normalerweise handelt es sich dabei nur um eine automatisch inkrementierende Ganzzahl, im Wesentlichen die 'Zeilennummer'.

Die Werte der anderen indizierten Spalten müssen nicht eindeutig sein, der Wert sollte jedoch eine relativ kleine Menge von Datensätzen bestimmen. Die Idee der Indizierung besteht darin, Leseabfragen zu verbessern. Ohne einen Index müsste eine Suche die gesamte Tabelle durchlesen, um übereinstimmende Zeilen zu finden. Wenn eine Spalte indiziert ist und Teil der Abfrage ist, kann sie schnell Zeilen finden, die dieser Spalte entsprechen, und dann kann die kleinere Teilmenge der übereinstimmenden Zeilen anhand der Abfrage geprüft werden (Die Analogie ist ein Index für ein Buch).

Wenn Sie also nicht nach dieser Spalte abfragen, wird die Indexierung dieser Spalte nicht helfen (wenn Sie niemals ein Wort im Index des Buches nachschlagen, ist es möglicherweise auch nicht vorhanden). Auch wenn viele Datensätze denselben Wert haben, beispielsweise eine Spalte "Geschlecht", da dies bei einem vollständigen Tabellenscan keine große Verbesserung darstellt (stellen Sie sich einen Buchindex vor, der ein Wort enthält, das auf jeder anderen Seite erscheint).

Die Indizierung ist auch nicht frei: Als KEYs deklarierte Spalten reduzieren die Schreibleistung (um die Analogie fortzusetzen, müssten Sie den Buchindex aktualisieren, wenn ein indiziertes Wort hinzugefügt oder entfernt wird). Daher müssen Sie entscheiden, welche Balance die richtige ist für Ihre Einrichtung Weitere Informationen finden Sie hier.

Da es wahrscheinlich ist, dass wir nach Benutzer fragen möchten (um die jüngsten Aktivitäten anzuzeigen), werden wir diese Spalte indizieren und log_id als Primärschlüssel verwenden.

Die Tabelle erstellen

Wir werden den Code zum Erstellen der benutzerdefinierten Tabelle in die folgende Funktion einfügen:

1
function wptuts_create_tables() {
2
	// Code for creating a table goes here

3
}
4
5
// Create tables on plugin activation

6
register_activation_hook( __FILE__, 'wptuts_create_tables' );

Diese Funktion muss über den Aktivierungshaken des Plugins aufgerufen werden. Außerdem müssen Sie jederzeit Änderungen an der Tabelle vornehmen, z. B. Spalten hinzufügen oder ihren Datentyp ändern.

Die Tatsache, dass wptuts_create_tables() mit Hilfe des Aktivierungshakens aufgerufen werden kann, wenn bereits eine Tabelle vorhanden ist, ist kein Versehen - und wir werden später in der Serie darüber sprechen, warum.

In diese Funktion schließen wir wp-admin / includes / upgrade.php ein, um einige Konstanten einzurichten und die Funktion dbDelta() zu laden. Beachten Sie, dass bei Aktivierung eines Plugins der init-Hook fehlt, sodass wptuts_register_activity_log_table() manuell aufgerufen werden muss.

1
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
2
global $wpdb;
3
global $charset_collate;
4
// Call this manually as we may have missed the init hook

5
wptuts_register_activity_log_table();

Das globale $charset_collate enthält den Zeichensatz und die Sortierung, die von den nativen WordPress-Tabellen verwendet werden. Die Kodierungen der Zeichen und deren Vergleich sind lose definiert. Da WordPress in vielen verschiedenen Sprachen verwendet wird, ist es wichtig, die richtige Sortierung für Ihre Tabelle zu verwenden.

Neben der Sortierung sollte die SQL-Anweisung den Tabellennamen sowie jede Spalte, deren Typ und Standardwert sowie alle KEY-Spalten (einschließlich einer PRIMARY KEY-Spalte) angeben. Normalerweise hat es die Form:

1
CREATE TABLE [table name] (
2
	[primary key column] bigint(20) unsigned NOT NULL auto_increment,
3
	[column name] [data type] [default],
4
	PRIMARY KEY  ([column name]) ,
5
	KEY key_name ([column name])
6
) [collation];

Um diese Tabelle zu erstellen, fügen wir unserer Funktion wptuts_create_tables() Folgendes hinzu:

1
$sql_create_table = "CREATE TABLE {$wpdb->wptuts_activity_log} (

2
          log_id bigint(20) unsigned NOT NULL auto_increment,

3
          user_id bigint(20) unsigned NOT NULL default '0',

4
          activity varchar(20) NOT NULL default 'updated',

5
          object_id bigint(20) unsigned NOT NULL default '0',

6
          object_type varchar(20) NOT NULL default 'post',

7
          activity_date datetime NOT NULL default '0000-00-00 00:00:00',

8
          PRIMARY KEY  (log_id),

9
          KEY user_id (user_id)

10
     ) $charset_collate; ";
11
12
dbDelta( $sql_create_table );

Die Funktion dbDelta() führt unseren Befehl CREATE TABLE aus. Es kann sehr streng sein, was die SQL-Anweisung betrifft. Zum Beispiel müssen zwischen PRIMARY KEY und der Primärschlüsselspalte zwei Leerzeichen stehen. und Schlüssel müssen einen Namen erhalten.

Debugging

Wenn Sie bei der Aktivierung feststellen, dass die Fehlermeldung "Sie haben X-Zeichen der unerwarteten Ausgabe ..." angezeigt werden, liegt möglicherweise ein Fehler in Ihrer SQL-Anweisung vor. Manchmal liegt es an der Strenge von dbDelta(). Wenn Sie hinzufügen wp_die(); Nach dbDelta() beendet dies die Verarbeitung und (mit WP_DEBUG` = true) werden alle Fehlermeldungen angezeigt.

Zusammenfassung

In diesem Artikel haben wir Gründe dafür aufgeführt, warum Sie benutzerdefinierte Tabellen verwenden sollten und sollten, sowie die Details, die Sie berücksichtigen müssen, und schließlich das Erstellen einer Tabelle. Der nächste Teil dieser Serie behandelt die Desinfektion, die SQL-Injektion und wie Sie sich davor schützen können. Der Code in diesem Artikel ist in diesem GitHub-Repository verfügbar und wird im Verlauf der Serie aktualisiert.


Ressourcen