PHP/MySQL: Verarbeitung / Durchiterieren von json response aus API-Endpunkt

canju

Erfahrenes Mitglied
Hallo ihr Lieben, ich bins mal wieder :)
Ich habe zwei Anliegen, bei denen ich wieder eure Hilfe gebrauchen kann.

Es geht um das Herunterladen von .json files über einen API-Endpunkt via PHP (7.4.3) und das anschließende Importieren in eine MariaDB (10.3).
Hierzu verwende ich zwei PHP Scripte, das eine Lädt die json Datei herunter und speichert diese auf dem Server ab, das andere kümmert sich dann um den Import in die MariaDB.

Das Herunterladen und speichern der json response in eine .json datei klappt schon einwandfrei, hierzu bräuchte ich eure Unterstützung bei einer Erweitertung unter Punkt 2.

1. Beim Import in die MariaDB erhalte ich immer die PHP Notice:

Bash:
PHP Notice:  Trying to access array offset on value of type int in /path/to/import/script.php on line 25
PHP Notice:  Trying to access array offset on value of type null in /path/to/import/script.php on line 25

Die order_number wird zwar in die MariaDB importiert, aber die beiden Notices stören mich.

Die order_number ($order_number = $row['res']['no'];) scheint aber als int vorzuliegen, weil mir var_dump($order_number); die folgende Augabe gibt: int(123456).

Die json Datei enthält folgendes:
JSON:
{
    "success": 1,
    "data":
    {
        "res":
        {
            "no": 123456,
            "completed_date": "2020-10-03",
            "external_incoming_order_id": "659874g23643",
            "customstatus_id": 4,
            "customstatus": null,
            "unifiedstatus_code": "TRANSMITTED",
            "country": "DE"
        }
    }
}

Mein Code für den Import:

PHP:
<?php
// Load the database configuration file
include_once 'db_config.php';
$table_name = 'tabellen_name';

$script_start_time = microtime(true);
$current_datetime = date("Y-m-d H:i:s");
$script_name = $_SERVER['SCRIPT_NAME'];

$qryTruncateTable = <<<SQL
TRUNCATE TABLE $table_name
SQL;
$db->query($qryTruncateTable) or die($current_datetime . " - FAILED - " . $script_name . " - Failed to truncate table an_cads_transactions");
echo ($current_datetime . " - SUCCESS - " . $script_name . " - Truncate table  $table_name  successfull \n");

//read the json file contents
$jsondata = file_get_contents('/path/to/response_example.json');


//convert json object to php associative array
$data = json_decode($jsondata, true);

foreach ($data as $row) {
  //get data details
  $order_number = $row['res']['no'];

  // replace empty fields with null
  if($order_number == '' OR !isset($order_number) ){$order_number = null;};

  //insert into mysql table
    $insert_data = <<<SQL
    INSERT INTO $table_name(
                  order_number
                )
                VALUES(
                  '$order_number'
                  )
    SQL;
    $result = mysqli_query($db, $insert_data);
}

if ($result == false) {
    echo "Error description: " . mysqli_error($db) . "\n";
} else {echo ($current_datetime . " - SUCCESS - " . $script_name . " - Data import successfull");}

mysqli_close($db);
$script_runtime = microtime(true) - $script_start_time;
$total_script_duration = "'(' . sprintf('%.5f', $script_runtime) . ' sec)'";

echo '(' . sprintf('%.5f', $script_runtime) . ' sec)' . "\n";
var_dump($data);
var_dump($order_number);
?>

Ausgabe von var_dump($data); (Habe nur die ersten paar Zeilen hier eingefügt)
PHP:
array(2) {
  ["success"]=>
  int(1)
  ["data"]=>
  array(1) {
    ["res"]=>
    array(64) {
      ["no"]=>
      int(123456)
      ["completed_date"]=>
      string(10) "2020-10-03"
      ["external_incoming_order_id"]=>
      string(12) "659874g23643"
      ["customstatus_id"]=>
      int(4)

...

Ausgabe von var_dump($row); (Habe nur die ersten paar Zeilen hier eingefügt)
PHP:
array(1) {
  ["res"]=>
  array(64) {
    ["no"]=>
    int(123456)
    ["completed_date"]=>
    string(10) "2020-10-03"
    ["external_incoming_order_id"]=>
    string(12) "659874g23643"
    ["customstatus_id"]=>
    int(4)

...

Ich habe gelesen, dass einige das PHP Error-Reporting umstellen, sodass PHP Notice einfach nur unterdrückt werden. Das scheint mir aber keine Lösung zu sein und möchte das nicht machen.

Was mache ich hier falsch, wie bekomme ich die PHP Notice weg?



2. Erweiterung des Download-Scripts

Bei meinem zweiten Anliegen geht es darum, die Request-URL anhand einer order_number liste durhzuiterieren und dann als ergebnis in eine .json Datei zu speichern. Der Endpunkt lässt leider keinen Call zu, der mir alle orders auf einmal zurückgibt, daher ein Call pro order_number notwendig.

Die order_ids habe ich in einer MariaDB Tabelle vorliegen.

SQL:
SELECT
    order_id
FROM orders

569872
569873
569874
569875
569876
569877
569878
569879

Ich verwende diesen Code zum herunterladen, der für das Abfragen eine order id auch wunderbar funktioniert:
PHP:
<?php
$script_start_time = microtime(true);

// GET ACCESS TOKEN
include_once 'get_accesstoken.php';

/*** DOWNLOAD FILE FROM CURL ****/
//API KEY AND PASSWORD
$headers = array('Content-Type: application/json', 'Accept:application/json', 'Accesskey:' . $accesskey, 'Token:' . $accesstoken);

$current_datetime = date("Y-m-d H:i:s");
$script_name = $_SERVER['SCRIPT_NAME'];


//URL
$url='https://api-endpoint.de/api/v1/orders?order_id=123456';

//FILE NAME
$filename = 'orders.json';

//DOWNLOAD PATH
$path = '/path/to/download/directory/'.$filename;

//FOLDER PATH
$fp = fopen($path, 'w');

//SETTING UP CURL REQUEST
$ch = curl_init($url) or die($current_datetime . " - FAILED - ". $script_name . " - Couldnt reach endpoint \n");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$data = curl_exec($ch);

//CONNECTION CLOSE
curl_close($ch);
fclose($fp);
echo $current_datetime . " - SUCCESS - ". $script_name . " - File download successfull \n";

$script_runtime = microtime(true) - $script_start_time;
$total_script_duration = "'(' . sprintf('%.5f', $script_runtime) . ' sec)'";

echo '(' . sprintf('%.5f', $script_runtime) . ' sec)' . "\n";

?>

Ich weiß leider nicht wie ich das Durchiterieren umsetzen soll, sodass dann am Schluss eine .json Datei mit allen Orders asu der orders tabelle als Ergebnis gespeichert wird.

Hoffe ihr könnt mir hierbei wieder helfen.


Beste Grüße,
canju
 
Zuletzt bearbeitet:
Lösung
In etwa so, ungetestet:
Code:
    if ($decoded['success'] == 0) {
        echo "Keine Tarife zur Kombination vorhanden: $zipcode und $consumption \n";
        continue;
    } else {
        // loop through all results
        foreach ($decoded["data"]["result"] as $record) {
            //get desired fields
            $tariff_id = $record["tariff_id"];
            //Parameter übergeben
            mysqli_stmt_bind_param($stmt, "s",
                $tariff_id
            );
            //Ausführen
            mysqli_stmt_execute($stmt);
        }
    }
Nicht ganz, ich meinte nur das prepare vor der Schleife, aber das bind und das execute innerhalb. Du willst ja die vorbereitete Abfrage mit verschiedenen Parametern ausführen.
Ich hatte gerade begonnen, das selber auszutesten, bin aber durch einen Anruf unterbrochen worden. Daher noch kein Ergebnis.
 
Hatte ich kurz vor deinem Post auch probiert. Bleibt aber beim gleichen ergebnis.
Bekomme weiterhin nur je das erste Ergebnis pro URL-Aufruf.
 
OK, dann weiß ich erst Mal nicht weiter. Ich versuche, es weiter auszutesten. Sicher meldet sich noch jemand, der erweiterte Kenntnisse hat.
 
Ganz dumme Frage:
Du wertest ja das Ergebnis des JSON so aus:
Code:
        //get desired fields
        $tariff_id = $decoded["data"]["result"][0]["tariff_id"];
fields in der Mehrzahl aber Du holst nur die tariff_id aus dem ersten Datensatz heraus. Ist es das was Du meinst wenn Du schreibst:
allerdings wird immer nur das erste Ergebnis jedes Calls in die DB geschrieben.
Das wäre dann genau, was Du programmiert hast aber es entspricht offenbar nicht deinem Wunsch. Also müsstest Du eine zweite Schleife über das Array result im Ergebnis des API-Abrufs programmieren?
 
Es sind sehr viele Felder in der response. Zum Testen hab ich erstmal nur die tariff_id mit drin, damit ich während der Testphase nicht so viele Zeilen im Script habe. Die anderen Felder werde ich dann hinzufügen, sobald alles läuft.

Mit dem:
allerdings wird immer nur das erste Ergebnis jedes Calls in die DB geschrieben.
meinte ich eigtl., dass ich nur den ersten Datensatz eines jeden url aufrufs in die db geschrieben bekomme.
  1. Dieser Call https://api-provider.com/api/tariffs?zipcode=08297&consumption=2500 liefert 114 Ergebnisse (Tarife) - davon wird nur der erste in die DB geschrieben
  2. Dieser Call https://api-provider.com/api/tariffs?zipcode=18556&consumption=4000 liefert nochmal 100 Ergebnisse (Tarife) - hiervon wird wiederum nur der erste in die DB geschrieben.
Ich müsste also insgesamt 214 tariff_ids in der DB gespeichert bekommen. Es werden aber nur 2 gespeichert (jeweils die erste)
Also müsstest Du eine zweite Schleife über das Array result im Ergebnis des API-Abrufs programmieren?
Du meinste eine zweite Schleife im ELSE (=erfolgreich)-Strang? Wenn ja welche Schleife würdest du einsetzen?
 
In etwa so, ungetestet:
Code:
    if ($decoded['success'] == 0) {
        echo "Keine Tarife zur Kombination vorhanden: $zipcode und $consumption \n";
        continue;
    } else {
        // loop through all results
        foreach ($decoded["data"]["result"] as $record) {
            //get desired fields
            $tariff_id = $record["tariff_id"];
            //Parameter übergeben
            mysqli_stmt_bind_param($stmt, "s",
                $tariff_id
            );
            //Ausführen
            mysqli_stmt_execute($stmt);
        }
    }
 
Lösung
Super, freut mich zu lesen. Mit den Datenbanksachen am Anfang waren wir da ja vollkommen auf der falschen Spur und die Lösung war relativ einfach.
 
Zurück