Um Daten zu speichern, benötigen wir eine Datenbank mit verschiedenen Tabellen. Jede Tabelle ist dabei der Platzhalter für alle Instanzen eines Objektes.
Wir möchten eine App zum Tracken von Haushalts-Ausgaben bauen. Daher benötigen wir zunächst eine Tabelle zum Speichern der einzelnen Ausgaben.
Eine Ausgabe hat dabei folgende Eigenschaften:
id – eine eindeutige Kennzeichnung
name – Name der Ausgabe
project_id – dies ist die eindeutige id des übergeordneten Projektes, zu dem die Ausgabe gehört
Wert – die Höhe der Ausgabe, wobei ein Wert von 100 1,00 Euro entspricht
date_of – Datum, wann die Ausgabe entstand.
Dies ist der SQL-Befehl zum Erstellen der Tabelle. Die Anweisung as Identity
hinter id
stellt sicher, dass die id eindeutig ist.
create table expenses (
id bigint generated by default as identity primary key,
name text check (char_length(name) > 0),
value int,
date_of timestamp not null
);
Um supabase nun dazu zu bringen, die Tabelle im Backend anzulegen, werden sogenannte Migrations oder Migrations-Dateien angelegt. Das hat den Vorteil, dass wenn wir das System auf eine andere Umgebung bringen – zum Beispiel von der lokalen Entwicklung-Umgebung, die auf unserem lokalen Rechner läuft, auf eine Produktiv-Umgebung, werden bei dem Deployment diese Migrationen automatisch ausgeführt und somit die Datenbank-Tabellen auch auf der neuen Umgebung angelegt.
Bei professioneller Software-Entwicklung gibt es natürlich noch mehr Umgebungen, wie zum Beispiel eine im Intranet verfügbare Entwicklungs- und eine Stageing- oder Testing-Umgebung, über die kollaborativ an der Software gearbeitet werden kann.
Für unser Projekt verwenden wir aber nur eine lokale Entwicklungs-Umgebung. Später werde ich dann noch zeigen, wie diese lokale Umgebung dann in Produktion gehen kann, damit wir sie global verfügbar machen können.
Um ein Migrations-File anzulegen, benötigen wir folgenden Befehl:
supabase migration new create_expenses_table
Wir finden die neu erstellte Datei unter supabase/migrations/<timestamp>_create_expenses_table.sql
.
Hier fügen wir nun unseren SQL-Befehl von oben ein und speichern die Datei.
Der folgende Befehl führt dazu, dass alle Migrationen ausgeführt werden:
supabase db reset
Jetzt können wir im Dashboard die neue Tabelle sehen:
http://localhost:54323/project/default/editor
Tabellen mit Beziehungen
Da wir unsere Ausgaben verschiedenen Projekten zuordnen möchten, brauchen wir noch eine weitere Tabelle mit dem Namen projects.
Hierzu wieder eine migration anlegen und mit folgendem Code füllen:
create table projects (
id bigint generated by default as identity primary key,
title text not null,
description text not null
);
jetzt müssen wir der expenses Tabelle noch mitteilen, dass ihre Einträge eine Beziehung mit den Einträgen der projects Tabelle eingehen. Und zwar ist es eine 0 … ∞ zu 1 Beziehung haben: jede expense hat genau ein zugewiesenes project, aber ein project kann keine bis unendlich viele expenses haben.
Um diese Beziehung darzustellen, benötigen die expenses einen foreign key, der auf das übergeordnete Projekt zeigt. Dieser foreign key muss gesetzt sein (NOT NULL) und entspricht dem Eintrag id von der Tabelle projects (projects(id)). Wenn ein project gelöscht wird, müssen alle verbundenen expenses auch gelöscht werden, ansonsten bekommen wir Probleme mit inkonsistenten Daten.
Wir können also eine neue migration anlegen:
supabase migration new add_projects_relation_to_expenses_table
Diese füllen wir mit folgendem Befehl:
ALTER TABLE expenses
ADD COLUMN project_id bigint NOT NULL,
ADD CONSTRAINT fk_project
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
Alternativ könnte auch die migration create_expenses_table um folgende Zeile ergänzt werden:
project_id bigint references projects on delete cascade not null
wichtig ist, dass diese Ergänzung erst ausgeführt werden kann, wenn die projects Tabelle bereits existiert, ansonsten würde sich SQL direkt beschweren, dass eine nicht erfüllbare Bedingung aufgestellt wird.
Datenbank mit Inhalt füllen
Wir können die Datenbank initial mit Daten befüllen. Dazu können wir folgenden Befehl in der Datei supabase/seed.sql einfügen:
INSERT INTO public.projects (title, description)
VALUES
('Project 1', 'Description for Project 1'),
('Project 2', 'Description for Project 2');
INSERT INTO
public.expenses (name, value, project_id, date_of)
values
('Benzin', 10000, 1, '2022-02-25 15:37:39'),
('Supermarkt', 5000, 1, '2022-02-25 15:37:39'),
('Miete', 20000, 2, '2022-02-25 15:37:39'),
('Essen gehen', 3000, 2, '2022-02-25 15:37:39');
wichtig ist auch hier, dass zuerst die projects vor den expenses angelegt werden, da sich auch hier SQL beschweren würde, dass die angegebenen project_ids nicht existieren.
supabase reset
sorgt dafür, dass diese Änderungen angewendet werden.