REST API mit benutzerdefinierten Feldern in PHP und MySQL unter Ubuntu 20.04 nutzen
Wenn Sie Daten über eine REST API bereitstellen – beispielsweise aus einer Produktdatenbank mit Feldern wie product_id, product_name, retail_price, discount, category_name, reviews, ratings oder related_products – benötigen Ihre API-Nutzer nicht immer alle verfügbaren Informationen für die Anzeige in Web- oder mobilen Anwendungen.
Hier kommt die Funktion zur Auswahl benutzerdefinierter Felder ins Spiel. Wenn Ihr Endpunkt zum Beispiel http://localhost/products.php lautet und JSON-Daten zurückgibt, können Clients ein Parameter wie ?fields=product_name,category_name,retail_price anhängen. Ihr Backend verarbeitet diese Anfrage, gleicht die Felder mit einer erlaubten Liste ab und gibt nur die geforderten Daten zurück. Das reduziert die Datenmenge und entlastet die Datenbank.
In diesem Schritt-für-Schritt-Tutorial entwickeln Sie genau diese Funktionalität mit PHP und MySQL unter Ubuntu 20.04.
Voraussetzungen
Bevor Sie starten, stellen Sie sicher, dass Folgendes vorhanden ist:
- Ein Ubuntu 20.04 Server
- Ein Benutzer mit Sudo-Rechten
- Ein LAMP-Stack mit MySQL oder MariaDB
Datenbank erstellen
Verbinden Sie sich mit Ihrem Server und melden Sie sich bei MySQL an:
$ sudo mysql -u root -p
Geben Sie Ihr Root-Passwort ein und erstellen Sie anschließend die Datenbank:
mysql> CREATE DATABASE variety_store;
Legen Sie nun einen neuen Benutzer für den Datenbankzugriff an. Ersetzen Sie EXAMPLE_PASSWORD mit einem sicheren Passwort:
mysql> CREATE USER 'variety_store_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
mysql> GRANT ALL PRIVILEGES ON variety_store.* TO 'variety_store_user'@'localhost';
mysql> FLUSH PRIVILEGES;
Verwenden Sie bei einer MariaDB-Installation stattdessen diese Anweisung:
MariaDB> GRANT ALL PRIVILEGES on variety_store.* TO 'variety_store_user'@'localhost' identified by 'EXAMPLE_PASSWORD';
Wechseln Sie anschließend zur neu erstellten Datenbank:
mysql> USE variety_store;
Kategorietabelle erstellen
Erstellen Sie zunächst eine Tabelle zur Verwaltung der Produktkategorien:
mysql> CREATE TABLE products_categories (
category_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
category_name VARCHAR(50)
) ENGINE = InnoDB;
Fügen Sie nun einige Kategorien in die Tabelle ein:
mysql> INSERT INTO products_categories (category_name) VALUES ('ELECTRONICS');
mysql> INSERT INTO products_categories (category_name) VALUES ('CLOTHING');
mysql> INSERT INTO products_categories (category_name) VALUES ('SHOES');
Überprüfen Sie, ob die Daten korrekt gespeichert wurden:
mysql> SELECT
category_id,
category_name
from products_categories;
Produkttabelle erstellen
Nun erstellen Sie eine Tabelle für die Produkte mit Feldern wie Name, Preis und Rabatt:
mysql> CREATE TABLE products (
product_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(50),
category_id INT,
retail_price DOUBLE,
discount DOUBLE
) ENGINE = InnoDB;
Fügen Sie nun einige Produktbeispiele ein. Die Spalte category_id stellt dabei die Verbindung zur Kategorie-Tabelle her:
mysql> INSERT INTO products (product_name, category_id, retail_price, discount) VALUES ('5G PHONE', '1', '420.25', '15.00');
mysql> INSERT INTO products (product_name, category_id, retail_price, discount) VALUES ('LED TORCH', '1', '5.35', '0.00');
mysql> INSERT INTO products (product_name, category_id, retail_price, discount) VALUES ('WOOLEN SOCKS', '2', '9.99', '0.00');
mysql> INSERT INTO products (product_name, category_id, retail_price, discount) VALUES ('LONG SLEEVE POLO', '2', '18.25', '4.25');
mysql> INSERT INTO products (product_name, category_id, retail_price, discount) VALUES ('SUEDE SHOE', '3', '33.40', '3.40');
Daten mit JOIN-Abfrage validieren
Führen Sie eine SQL-Join-Abfrage durch, um zu prüfen, ob die Produktdaten den richtigen Kategorien zugewiesen wurden:
mysql> SELECT
product_id,
product_name,
products.category_id,
category_name,
retail_price,
discount
FROM products
LEFT JOIN products_categories
ON products.category_id = products_categories.category_id;
Das Ergebnis sollte etwa wie folgt aussehen:
+------------+------------------+-------------+---------------+--------------+----------+
| product_id | product_name | category_id | category_name | retail_price | discount |
+------------+------------------+-------------+---------------+--------------+----------+
| 1 | 5G PHONE | 1 | ELECTRONICS | 420.25 | 15 |
| 2 | LED TORCH | 1 | ELECTRONICS | 5.35 | 0 |
| 3 | WOOLEN SOCKS | 2 | CLOTHING | 9.99 | 0 |
| 4 | LONG SLEEVE POLO | 2 | CLOTHING | 18.25 | 4.25 |
| 5 | SUEDE SHOE | 3 | SHOES | 33.4 | 3.4 |
+------------+------------------+-------------+---------------+--------------+----------+
MySQL-Konsole verlassen
Nachdem Sie geprüft haben, ob alle Tabellen und Daten korrekt eingerichtet wurden, können Sie MySQL beenden:
mysql> QUIT;
Nun ist Ihre Datenbank vollständig vorbereitet. Die Tabellen und Beispielinhalte stehen bereit. Im nächsten Schritt erstellen Sie ein PHP-Skript, das eine Verbindung zur Datenbank aufbaut und je nach Anfrage gezielt Daten im JSON-Format zurückgibt.
PHP-Ressourcendatei erstellen
Erstellen Sie im nächsten Schritt die Datei /var/www/html/products.php auf Ihrem Webserver mit dem Editor nano:
$ sudo nano /var/www/html/products.php
Öffnen Sie in dieser Datei zunächst den PHP-Tag und setzen Sie den passenden HTTP-Header für die JSON-Ausgabe. Nutzen Sie dann einen try-Block, um die Verbindung zur Datenbank mithilfe der zuvor definierten Zugangsdaten herzustellen:
<?php
header("Content-Type:application/json");
try {
$db_name = 'variety_store';
$db_user = 'variety_store_user';
$db_password = 'EXAMPLE_PASSWORD';
$db_host = 'localhost';
$pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Als Nächstes definieren Sie eine Liste mit Standardfeldern, die ausgegeben wird, wenn keine individuellen Felder vom Client übergeben werden. Die Liste wird in ein Array umgewandelt und mithilfe von array_map bereinigt:
$default_fields = 'product_id,
product_name,
category_id,
category_name,
retail_price,
discount,
sale_price
';
$default_fields = array_map('trim', explode(',', $default_fields));
Nun wird geprüft, ob der Client ein Feldparameter wie ?fields= übergeben hat. Falls ja, werden diese in ein Array aufgeteilt und gegen die erlaubten Felder geprüft. Falls nicht, greift die Standardliste. Ungültige Felder werden mit array_intersect herausgefiltert:
if (isset($_GET['fields'])) {
$user_fields = array_map('trim', explode(',', $_GET['fields']));
$columns = array_intersect($default_fields, $user_fields);
} else {
$columns = $default_fields;
}
Danach erfolgt die Behandlung spezieller Felder: category_id muss eindeutig zugeordnet werden, um Konflikte beim SQL-Join zu vermeiden, und sale_price ist ein berechnetes Feld, das dynamisch erstellt wird:
for ($i = 0 ; $i < count($columns) ; $i++) {
if ($columns[$i] == 'category_id') {
$columns[$i] = 'products.category_id';
}
if ($columns[$i] == 'sale_price') {
$columns[$i] = '(retail_price - discount) as sale_price';
}
}
$columns = implode(',', $columns);
if (trim($columns) == '') {
$error = [];
$error['message'] = "No valid fields are supplied";
echo json_encode($error, JSON_PRETTY_PRINT) . "\n";
exit();
}
Abschließend wird die SQL-Abfrage mit den ausgewählten Spalten ausgeführt und die Ergebnisse im JSON-Format ausgegeben. Bei Fehlern erfolgt eine entsprechende Rückmeldung:
$sql = 'select '
. $columns
. ' from products
left join products_categories
on products.category_id = products_categories.category_id
';
$stmt = $pdo->prepare($sql);
$stmt->execute();
$products = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$products[] = $row;
}
$response = [];
$response['data'] = $products;
echo json_encode($response, JSON_PRETTY_PRINT) . "\n";
} catch (PDOException $e) {
$error = [];
$error['message'] = $e->getMessage();
echo json_encode($error, JSON_PRETTY_PRINT) . "\n";
}
Speichern und schließen Sie die Datei products.php. Das vollständige Skript ist nun bereit, um auf API-Anfragen zu reagieren und nur die geforderten Datenfelder zurückzugeben.
REST API Funktion testen
Rufen Sie zum Test die URL http://localhost/products.php ohne Parameter auf:
$ curl http://localhost/products.php
Die Rückgabe sollte alle Standardfelder aus dem Array $default_fields enthalten. Das Feld sale_price wird automatisch als Differenz von retail_price und discount berechnet:
{
"data": [
{
"product_id": 1,
"product_name": "5G PHONE",
"category_id": 1,
"category_name": "ELECTRONICS",
"retail_price": 420.25,
"discount": 15,
"sale_price": 405.25
},
{
"product_id": 2,
"product_name": "LED TORCH",
"category_id": 1,
"category_name": "ELECTRONICS",
"retail_price": 5.35,
"discount": 0,
"sale_price": 5.35
},
{
"product_id": 3,
"product_name": "WOOLEN SOCKS",
"category_id": 2,
"category_name": "CLOTHING",
"retail_price": 9.99,
"discount": 0,
"sale_price": 9.99
},
{
"product_id": 4,
"product_name": "LONG SLEEVE POLO",
"category_id": 2,
"category_name": "CLOTHING",
"retail_price": 18.25,
"discount": 4.25,
"sale_price": 14
},
{
"product_id": 5,
"product_name": "SUEDE SHOE",
"category_id": 3,
"category_name": "SHOES",
"retail_price": 33.4,
"discount": 3.4,
"sale_price": 30
}
]
}
Spezifische Felder per URL-Parameter abfragen
Nutzen Sie benutzerdefinierte URL-Parameter, um gezielt bestimmte Felder im Ergebnis zurückzugeben:
$ curl http://localhost/products.php?fields=product_name,category_name,retail_price
Das Ergebnis zeigt ausschließlich die angeforderten Felder:
{
"data": [
{
"product_name": "5G PHONE",
"category_name": "ELECTRONICS",
"retail_price": 420.25
},
{
"product_name": "LED TORCH",
"category_name": "ELECTRONICS",
"retail_price": 5.35
},
{
"product_name": "WOOLEN SOCKS",
"category_name": "CLOTHING",
"retail_price": 9.99
},
{
"product_name": "LONG SLEEVE POLO",
"category_name": "CLOTHING",
"retail_price": 18.25
},
{
"product_name": "SUEDE SHOE",
"category_name": "SHOES",
"retail_price": 33.4
}
]
}
Einzelnes Feld abfragen
Um ausschließlich die Produktnamen zu erhalten, verwenden Sie diesen Aufruf:
$ curl http://localhost/products.php?fields=product_name
Das Ergebnis listet nur die Spalte product_name auf:
{
"data": [
{
"product_name": "5G PHONE"
},
{
"product_name": "LED TORCH"
},
{
"product_name": "WOOLEN SOCKS"
},
{
"product_name": "LONG SLEEVE POLO"
},
{
"product_name": "SUEDE SHOE"
}
]
}
Kombination gültiger und ungültiger Felder
Auch wenn Sie unerlaubte Feldnamen übergeben, ignoriert das Skript diese und gibt nur die gültigen Felder zurück:
$ curl http://localhost/products.php?fields=product_id,zzzz,product_name,yyyyyyy
Die Antwort enthält nur gültige Spaltennamen:
{
"data": [
{
"product_id": 1,
"product_name": "5G PHONE"
},
{
"product_id": 2,
"product_name": "LED TORCH"
},
{
"product_id": 3,
"product_name": "WOOLEN SOCKS"
},
{
"product_id": 4,
"product_name": "LONG SLEEVE POLO"
},
{
"product_id": 5,
"product_name": "SUEDE SHOE"
}
]
}
Ausschließlich ungültige Felder übergeben
Wenn ausschließlich ungültige Felder übermittelt werden, gibt das Skript eine Fehlermeldung zurück:
$ curl http://localhost/products.php?fields=xxx,yyy,zzz
Die Rückmeldung sieht wie folgt aus:
{
"message": "No valid fields are supplied"
}
Damit ist bestätigt, dass Ihre REST API mit Unterstützung für benutzerdefinierte Felder sowohl funktional als auch sicher arbeitet.
Fazit
In diesem Leitfaden haben Sie eine REST API unter Ubuntu 20.04 mit PHP und MySQL entwickelt, die es Clients ermöglicht, gezielt Datenfelder per URL-Parameter abzufragen. Sie können das gezeigte Beispiel ganz einfach auf Ihre individuellen Anwendungsfälle erweitern oder anpassen.


