Migrationen und Seeders sind leistungsfähige Datenbank-Dienstprogramme, die vom Laravel-PHP-Framework bereitgestellt werden, um Entwicklern einen schnellen Bootstrap, eine schnelle Löschung und neue Erstellung der Datenbank einer Anwendung zu ermöglichen. Diese Dienstprogramme minimieren Probleme der Datenbankinkonsistenz, die auftreten können, wenn mehrere Entwickler an derselben Anwendung arbeiten: Neue Mitwirkende müssen nur ein paar artisan
-Befehle ausführen, um die Datenbank auf einer neuen Installation einzurichten.
In diesem Leitfaden erstellen Sie Migrationen und Seeders, um eine Datenbank einer Laravel-Demo-Anwendung mit Beispieldaten zu füllen. Am Ende können Sie Ihre Datenbank-Tabellen nur mit artisan
-Befehlen so oft Sie möchten löschen und neu erstellen.
Um diesen Leitfaden auszuführen, benötigen Sie:
Anmerkung: In diesem Leitfaden verwenden wir eine containerisierte Entwicklungsumgebung, die von Docker Compose verwaltet wird, um die Anwendung auszuführen. Sie können die Anwendung jedoch auch auf einem LEMP-Server ausführen. Um dies einzurichten, können Sie unseren Leitfaden So installieren und konfigurieren Sie Laravel mit LEMP unter Ubuntu 18.04 ausführen.
Zu Beginn rufen wir die Demoversion der Laravel-Anwendung aus dem Github Repository ab. Wir beschäftigen uns mit dem Zweig tutorial-02
, der eine Docker-Compose-Einrichtung beinhaltet, um die Anwendung auf Containern auszuführen. In diesem Beispiel laden wir die Anwendung in unseren Home-Ordner herunter, aber Sie können jedes Verzeichnis Ihrer Wahl verwenden:
- cd ~
- curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-2.0.1.zip -o travellist.zip
Da wir den Anwendungscode als .zip
-Datei heruntergeladen haben, benötigen wir den Befehl unzip
, um ihn zu entpacken. Wenn Sie es nicht vor Kurzem getan haben, aktualisieren den lokalen Paketindex Ihres Rechners:
- sudo apt update
Installieren Sie dann das unzip
-Paket:
- sudo apt install unzip
Entzippen Sie danach den Inhalt der Anwendung:
- unzip travellist.zip
Benennen Sie dann das entpackte Verzeichnis für einen leichteren Zugriff in travellist-demo um:
- mv travellist-laravel-demo-tutorial-2.0.1 travellist-demo
Im nächsten Schritt erstellen wir eine .env
Konfigurationsdatei, um die Anwendung einzurichten.
.env
Datei der AnwendungIn Laravel wird eine .env
-Datei verwendet, um umgebungsabhängige Konfigurationen einzurichten, wie Anmeldeangaben und alle Informationen, die zwischen Bereitstellungen variieren können. Diese Datei ist nicht Teil der Revisionskontrolle.
Warnung: Die Umgebungs-Konfigurationsdatei enthält sensible Informationen über Ihren Server, einschließlich Anmeldedaten zur Datenbank und Sicherheitsschlüssel. Aus diesem Grund sollten Sie diese Datei nie öffentlich teilen.
Die Werte in der .env
-Datei haben Vorrang vor den Werten, die in regelmäßigen Konfigurationsdateien festgelegt sind, die sich im Verzeichnis config
befinden. Jede Installation in einer neuen Umgebung erfordert eine maßgeschneiderte Umgebungsdatei, um Dinge wie Datenbank-Verbindungseinstellungen, Debug-Optionen, Anwendungs-URL und andere Objekte festzulegen, die je nach der Umgebung variieren können.
Navigieren Sie zum Verzeichnis travellist-demo
:
- cd travellist-demo
Jetzt erstellen wir eine neue .env
Datei, um die Konfigurationsoptionen für die Entwicklungsumgebung anzupassen, die wir einrichten. Laravel wird mit einer .env
-Musterdatei geliefert, die wir zur Erstellung unserer eigenen kopieren können:
- cp .env.example .env
Öffnen Sie diese Datei mit nano
oder dem Texteditor Ihrer Wahl:
- nano .env
So sieht Ihre .env
-Datei jetzt aus:
APP_NAME=Travellist
APP_ENV=dev
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=travellist
DB_USERNAME=travellist_user
DB_PASSWORD=password
…
Die aktuelle .env
-Datei aus der Demo-Anwendung travellist
enthält Einstellungen, um die containerisierte Umgebung zu verwenden, die wir im letzten Teil dieser Serie mit Docker Compose erstellt haben. Sie müssen keine dieser Werte ändern, aber Sie können gerne DB_DATABASE
, DB_USERNAME
und DB_PASSWORD
ändern, da diese von unserer Datei docker-compose.yml
automatisch abgerufen werden, um die Entwicklungsdatenbank einzurichten. Stellen Sie jedoch sicher, dass die Variable DB_HOST
unverändert bleibt, da sie auf den Namen unseres Datenbank-Dienstes innerhalb der Docker-Compose-Umgebung verweist.
Wenn Sie Änderungen an der Datei vornehmen, stellen Sie sicher, dass Sie sie durch Drücken von STRG + X
, Y
, dann der EINGABETASTE
gespeichert und geschlossen haben.
Anmerkung: Wenn Sie sich dafür entschieden haben, die Anwendung auf einem LEMP-Server auszuführen, müssen Sie die hervorgehobenen Werte ändern, um Ihre eigenen Datenbankeinstellungen, einschließlich der Variablen DB_HOST
, zu berücksichtigen.
Jetzt verwenden wir Composer, das Abhängigkeitsmanagement-Tool von PHP, um die Abhängigkeiten der Anwendung zu installieren und sicherzustellen, dass wir artisan
-Befehle ausführen können.
Rufen Sie Ihre Docker-Compose-Umgebung mit dem folgenden Befehl auf. Dadurch werden das Image travellist
für den app
-Dienst erstellt und die zusätzlichen Docker-Images eingefügt, die von den Diensten nginx
und db
benötigt werden, um die Anwendungsumgebung zu erstellen:
- docker-compose up -d
OutputCreating network "travellist-demo_travellist" with driver "bridge"
Building app
Step 1/11 : FROM php:7.4-fpm
---> fa37bd6db22a
Step 2/11 : ARG user
---> Running in 9259bb2ac034
…
Creating travellist-app ... done
Creating travellist-nginx ... done
Creating travellist-db ... done
Dies kann einige Minuten dauern. Sobald der Vorgang abgeschlossen ist, können wir Composer ausführen, um die Abhängigkeiten der Anwendung zu installieren.
Um composer
und andere Befehle im app
-Dienstcontainer auszuführen, verwenden wir docker-compose exec
. Der Befehl exec
ermöglicht es uns, jeden Befehl unserer Wahl auf Containern auszuführen, die von Docker Compose verwaltet werden. Es verwendet die folgende Syntax: docker-compose exec service_name command
.
Anmerkung: Falls Sie sich entschieden haben, einen LEMP-Server zu verwenden, um die Demo-Anwendung auszuführen, sollten Sie den Teil docker-compose exec app
der Befehle in diesem Leitfaden ignorieren. Statt beispielsweise den folgenden Befehl auszuführen, wie er geschrieben ist, würden Sie nur Folgendes ausführen:
- composer install
Um composer install
im app
-Container auszuführen, führen Sie Folgendes aus:
- docker-compose exec app composer install
OutputLoading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 85 installs, 0 updates, 0 removals
- Installing doctrine/inflector (1.3.1): Downloading (100%)
- Installing doctrine/lexer (1.2.0): Downloading (100%)
- Installing dragonmantank/cron-expression (v2.3.0): Downloading (100%)
…
Wenn Composer die Installation der Anwendungsabhängigkeiten abgeschlossen hat, können Sie artisan
-Befehle ausführen. Um zu testen, ob die Anwendung eine Verbindung mit der Datenbank herstellen kann, führen Sie folgenden Befehl aus, der alle bereits vorhandenen Tabellen bereinigen wird:
- docker-compose exec app php artisan db:wipe
Dieser Befehl verwirft alle bereits vorhandenen Tabellen auf der konfigurierten Datenbank. Wenn er erfolgreich ausgeführt wird und die Anwendung eine Verbindung mit der Datenbank herstellen kann, sehen Sie eine Ausgabe wie diese:
OutputDropped all tables successfully.
Nachdem Sie nun die Anwendungsabhängigkeiten mit Composer installiert haben, können Sie das artisan
-Tool verwenden, um Migrationen und Seeders zu erstellen.
Das artisan
-Befehlszeilentool, das mit Laravel geliefert wird, enthält eine Reihe von Hilfsbefehlen, die zur Verwaltung der Anwendung und dem Bootstrap neuer Klassen verwendet werden können. Um eine neue Migrationsklasse zu generieren, können wir den Befehl make:migration
wie folgt verwenden:
- docker-compose exec app php artisan make:migration create_places_table
Laravel leitet die auszuführende Operation (create
), den Namen der Tabelle (places
) und ob diese Migration eine neue Tabelle erstellt oder nicht basierend auf dem beschreibenden Namen ab, der dem Befehl make:migration
bereitgestellt wird.
Sie werden eine Ausgabe sehen, die dieser ähnelt:
OutputCreated Migration: 2020_02_03_143622_create_places_table
Dadurch wird eine neue Datei im Verzeichnis database/migrations
der Anwendung generiert. Der in der automatisch generierten Datei enthaltene Zeitstempel wird von Laravel verwendet, um zu bestimmen, in welcher Reihenfolge Migrationen ausgeführt werden sollen.
Verwenden Sie den Texteditor Ihrer Wahl, um die generierte Migrationsdatei zu öffnen. Vergessen Sie nicht, den hervorgehobenen Wert durch Ihren eigenen Migrationsdatei-Namen zu ersetzen:
- nano database/migrations/2020_02_03_143622_create_places_table.php
Die generierte Migrationsdatei enthält eine Klasse namens CreatePlacesTable
:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePlacesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('places', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('places');
}
}
Diese Klasse verfügt über zwei Methoden: up
und down
. Beide Methoden enthalten Bootstrap-Code, den Sie erweitern können, um das anzupassen, was geschieht, wenn die Migration ausgeführt wird und wenn sie zurückgesetzt wird.
Wir ändern die Methode up
, damit die Tabelle places
die Struktur wiedergibt, die wir bereits in der aktuellen Version der Anwendung verwenden:
id
: primäres Schlüsselfeldname
: Name des Ortesvisited
: ob dieser Ort bereits besucht wurde oder nichtDer Laravel Schema Builder macht Methoden zum Erstellen, Aktualisieren und Löschen von Tabellen in einer Datenbank verfügbar. Die Klasse Blueprint
definiert die Struktur der Tabelle und bietet verschiedene Methoden, um die Definition jedes Tabellenfeldes zusammenzufassen.
Der automatisch generierte Code richtet ein primäres ID-Feld namens id
ein. Die Methode timestamps
erstellt zwei datetime
-Felder, die automatisch von den zugrunde liegenden Datenbank-Klassen aktualisiert werden, wenn Daten in diese Tabelle eingefügt oder aktualisiert werden. Zusätzlich müssen wir einen Namen
und ein visited
-Feld aufnehmen.
Unser Namens
-Feld ist vom Typ Zeichenfolge
und unser visited
-Feld vom Typ Boolean
. Außerdem geben wir einen Standardwert von 0
für das visited
-Feld ein. Wenn kein Wert übergeben wird, bedeutet es dann, dass der Ort noch nicht besucht wurde. So sieht die Methode up
jetzt aus:
…
public function up()
{
Schema::create('places', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name', 100);
$table->boolean('visited')->default(0);
$table->timestamps();
});
}
…
Anmerkung: Die vollständige Liste der verfügbaren Spaltentypen finden Sie in der Laravel-Dokumentation.
Nachdem Sie die beiden hervorgehobenen Zeilen auf Ihrem eigenen Migrations-Skript inkludiert haben, speichern und schließen Sie die Datei.
Ihre Migration kann jetzt über artisan migrate
ausgeführt werden. Dies würde jedoch nur eine leere Tabelle erstellen; Sie müssen auch Beispieldaten für Entwicklung und Test einfügen können. Im nächsten Schritt sehen Sie, wie Sie das mithilfe von Datenbank-Seeders tun können.
Ein Seeder ist eine spezielle Klasse, die zum Generieren und Einfügen von Beispieldaten (Seeds) in einer Datenbank verwendet wird. Dies ist ein wichtiges Merkmal in Entwicklungsumgebungen, denn damit können Sie die Anwendung mit einer frischen Datenbank neu erstellen – mit Beispielwerten, die Sie sonst jedes Mal manuell einfügen müssen, wenn die Datenbank neu erstellt wird.
Wir verwenden jetzt den Befehl artisan
, um eine neue Seeder-Klasse für unsere Tabelle places
namens PlacesTableSeeder
zu generieren:
- docker-compose exec app php artisan make:seeder PlacesTableSeeder
Der Befehl erstellt eine neue Datei namens PlacesTableSeeder.php
im Verzeichnis database/seeds
. Öffnen Sie diese Datei mit dem Texteditor Ihrer Wahl:
- nano database/seeds/PlacesTableSeeder.php
So sieht die automatisch generierte Datei PlacesTableSeeder.php
aus:
<?php
use Illuminate\Database\Seeder;
class PlacesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
//
}
}
Unsere neue Seeder-Klasse enthält eine leere Methode namens run
. Diese Methode wird aufgerufen, wenn der Artisan-Befehl db:seed
ausgeführt wird.
Sie müssen die Methode run
bearbeiten, um Anweisungen zum Einfügen von Beispieldaten in die Datenbank aufzunehmen. Wir verwenden den Laravel query builder, um diesen Vorgang zu optimieren.
Der Laravel query builder bietet eine fließende Schnittstelle für Datenbankoperationen wie Einfügen, Aktualisieren, Löschen und Abrufen von Daten. Er schützt zudem vor SQL-Injection-Angriffen. Der query builder wird von der DB
facade verfügbar gemacht – ein statischer Proxy für zugrunde liegende Datenbank-Klassen im Dienstcontainer.
Jetzt erstellen wir eine statische Klassen-Variable, um alle Beispielorte aufzunehmen, die wir als Array in die Datenbank einfügen möchten. Dadurch können wir eine foreach
-Schleife verwenden, um alle Werte zu durchlaufen und jeden einzelnen mit dem query builder in die Datenbank einzufügen.
Wir nennen diese Variable $places
:
<?php
use Illuminate\Database\Seeder;
class PlacesTableSeeder extends Seeder
{
static $places = [
'Berlin',
'Budapest',
'Cincinnati',
'Denver',
'Helsinki',
'Lisbon',
'Moscow',
'Nairobi',
'Oslo',
'Rio',
'Tokyo'
];
…
Als Nächstes müssen wir eine use
-Anweisung am Anfang unserer PlacesTableSeeder
-Klasse aufnehmen, um die Referenzierung der DB
facade im gesamten Code zu erleichtern:
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class PlacesTableSeeder extends Seeder
…
Wir können jetzt die Array-Werte $places
mit einer foreach
-Schleife durchlaufen und jeden einzelnen mit dem query builder in unsere Tabelle places
einfügen:
…
public function run()
{
foreach (self::$places as $place) {
DB::table('places')->insert([
'name' => $place,
'visited' => rand(0,1) == 1
]);
}
}
Die foreach
-Schleife durchläuft jeden Wert des statischen Arrays $places
. Bei jedem Durchlauf verwenden wir die DB
facade, um eine neue Zeile in der Tabelle places
einzufügen. In das Namens
-Feld geben wir den Namen des Ortes ein, den wir gerade aus dem Array $places
erhalten haben, und in das visited
-Feld einen beliebigen Wert von entweder 0
oder 1
.
So sieht die volle Klasse PlacesTableSeeder
nach allen Aktualisierungen aus:
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class PlacesTableSeeder extends Seeder
{
static $places = [
'Berlin',
'Budapest',
'Cincinnati',
'Denver',
'Helsinki',
'Lisbon',
'Moscow',
'Nairobi',
'Oslo',
'Rio',
'Tokyo'
];
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
foreach (self::$places as $place) {
DB::table('places')->insert([
'name' => $place,
'visited' => rand(0,1) == 1
]);
}
}
}
Speichern und schließen Sie die Datei, wenn Sie diese Änderungen vorgenommen haben.
Seeder-Klassen werden nicht automatisch im der Anwendung geladen. Bearbeiten Sie die Hauptklasse von DatabaseSeeder
, um einen Aufruf zu dem Seeder aufzunehmen, den wir gerade erstellt haben.
Öffnen Sie die Datei database/seeds/DatabaseSeeder.php
mit nano
oder Ihrem bevorzugten Editor:
- nano database/seeds/DatabaseSeeder.php
Die Klasse DatabaseSeeder
sieht wie jeder andere Seeder aus: sie erweitert die Seeder
-Klasse und hat eine run
-Methode. Diese Methode aktualisieren wir, um einen Aufruf von PlacesTableSeeder
aufzunehmen.
Aktualisieren Sie die aktuelle run
-Methode in Ihrer Klasse DatabaseSeeder
, indem Sie die auskommentierte Zeile löschen und sie durch den folgenden hervorgehobenen Code ersetzen:
…
public function run()
{
$this->call(PlacesTableSeeder::class);
}
...
So sieht die volle Klasse DatabaseSeeder
nach der Aktualisierung aus:
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(PlacesTableSeeder::class);
}
}
Speichern und schließen Sie die Datei, wenn Sie mit der Aktualisierung des Inhalts fertig sind.
Jetzt sind wir mit der Einrichtung einer Migration und eines Seeders für unsere Tabelle places
fertig. Im nächsten Schritt sehen wir uns ihre Ausführung an.
Vergewissern Sie sich zuerst, dass Ihre Anwendung ausgeführt wird. Wir richten den Verschlüsselungsschlüssel der Anwendung ein und greifen dann von einem Browser auf die Anwendung zu, um den Webserver zu testen.
Um den für Laravel erforderlichen Verschlüsselungsschlüssel zu generieren, können Sie den Befehl artisan key:generate
verwenden:
- docker-compose exec app php artisan key:generate
Sobald der Schlüssel generiert wurde, können Sie auf die Anwendung zugreifen, indem Sie Ihren Browser auf Ihren Server-Hostname oder die IP-Adresse auf Port 8000
verweisen:
http://server_host_or_ip:8000
Sie sehen in etwa folgende Seite:
Das bedeutet, dass die Anwendung eine Verbindung mit der Datenbank herstellen kann, jedoch keine Tabelle namens places
finden konnte. Wir erstellen jetzt die Tabelle places
mit dem folgenden artisan-Befehl migrate
:
- docker-compose exec app php artisan migrate
Sie sehen eine Ausgabe, die dieser ähnelt:
OutputMigration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.06 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (0.06 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (0.03 seconds)
Migrating: 2020_02_10_144134_create_places_table
Migrated: 2020_02_10_144134_create_places_table (0.03 seconds)
Sie werden feststellen, dass einige weitere Migrationen zusammen mit der Migration create_places_table
ausgeführt wurden, die wir eingerichtet haben. Diese Migrationen werden automatisch generiert, wenn Laravel installiert ist. Obwohl wir diese zusätzlichen Tabellen jetzt nicht verwenden, werden sie in Zukunft benötigt, wenn wir die Anwendung um registrierte Benutzer und geplante Aufträge erweitern. Jetzt können Sie sie vorerst so belassen.
Zu diesem Zeitpunkt ist unsere Tabelle noch leer. Wir müssen den Befehl db:seed
ausführen, um Seeding für die Datenbank mit unseren Beispielorten durchzuführen.
- docker-compose exec app php artisan db:seed
Dadurch werden unser Seeder ausgeführt und die Beispielwerte eingefügt, die wir in unserer Klasse PlacesTableSeeder
definiert haben. Sie sehen eine Ausgabe, die dieser ähnelt:
OutputSeeding: PlacesTableSeeder
Seeded: PlacesTableSeeder (0.06 seconds)
Database seeding completed successfully.
Laden Sie jetzt die Anwendungsseite in Ihrem Browser neu. Sie sehen eine Seite, die so ähnlich wie die folgende aussieht:
Wann immer Sie wieder von vorne beginnen müssen, können Sie alle Datenbank-Tabellen folgendermaßen verwerfen:
- docker-compose exec app php artisan db:wipe
OutputDropped all tables successfully.
Um die Anwendungs-Migrationen auszuführen und Seeding für die Tabellen mit einem einzelnen Befehl durchzuführen, können Sie Folgendes verwenden:
- docker-compose exec app php artisan migrate --seed
OutputMigration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.06 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (0.07 seconds)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (0.03 seconds)
Migrating: 2020_02_10_144134_create_places_table
Migrated: 2020_02_10_144134_create_places_table (0.03 seconds)
Seeding: PlacesTableSeeder
Seeded: PlacesTableSeeder (0.06 seconds)
Database seeding completed successfully.
Wenn Sie eine Migration zurücksetzen möchten, können Sie Folgendes ausführen:
- docker-compose exec app php artisan migrate:rollback
Dadurch wird die Methode down
für jede Migrationsklasse im Ordner migrations
ausgelöst. Üblicherweise werden alle Tabellen entfernt, die durch Migrations-Klassen erstellt wurden, und es bleiben nur jene Tabellen übrig, die von Hand erstellt wurden. Die Ausgabe sieht dann so aus:
OutputRolling back: 2020_02_10_144134_create_places_table
Rolled back: 2020_02_10_144134_create_places_table (0.02 seconds)
Rolling back: 2019_08_19_000000_create_failed_jobs_table
Rolled back: 2019_08_19_000000_create_failed_jobs_table (0.02 seconds)
Rolling back: 2014_10_12_100000_create_password_resets_table
Rolled back: 2014_10_12_100000_create_password_resets_table (0.02 seconds)
Rolling back: 2014_10_12_000000_create_users_table
Rolled back: 2014_10_12_000000_create_users_table (0.02 seconds)
Der Befehl Zurücksetzen ist besonders nützlich, wenn Sie Änderungen an Anwendungsmodellen vornehmen und ein Befehl db:wipe
nicht verwendet werden kann – beispielsweise wenn mehrere Systeme von der gleichen Datenbank abhängen.
In diesem Leitfaden haben Sie gelernt, wie Sie das Einrichten von Entwicklungs- und Test-Datenbänken für eine Laravel-6-Anwendung mithilfe von Datenbank-Migrationen und Seeders erleichtern können.
In der Laravel-Dokumentation finden Sie weitere Informationen zur Verwendung des query builder und von Eloquent models, um das Datenbank-Schema Ihrer Anwendung noch weiter zusammenzufassen.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!