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);
        }
    }
Hiernach:
PHP cURL sync and async
arbeitet curl synchron, d. h. in dem Fall steht die betr. Variable $order_id noch zur Verfügung und Du kannst damit arbeiten:
Code:
    if($decoded['success'] == 0) {
      echo "$order_id ist nicht mehr im Endpunkt";
      continue;
    } else {
        //get desired fields
Das continue müsstest Du weg lassen können, denn Du hast ja einen else-Zweig, so dass die Aktionen darin nicht ausgeführt werden.
 
Funzt! Danke dir.
Wenn ich das Continue rausnehme, bekomme ich NULL ausgegeben, also quasi den Inhalt, des leeren Objekts. Lasse es daher drin.
 
Ich bekomme jetzt immer diese Meldung:
Code:
Incorrect date value: '' for column `db`.`orders`.`created_date`
Ich dachte eigtl, dass die zweite Zeile hier genau das abfängt und den Wert durch NULL ersetzt:
PHP:
$created_date = $decoded['data']['res']['created_date'];
if($created_date == '' OR !isset($created_date)){$created_date = null;};
Was mache ich falsch?
 
Ich hatte auch nicht genau genug gelesen: Die Fehlermeldung sagt, dass der Wert '' ist obwohl Du versuchst, das mit dem genannten Code anzufangen. Wenn ich den Code teste, funktioniert er auch, ich bekomme in beiden Fällen null. Habe erst Mal keine Idee, warum sich der Code bei dir anders verhält. Poste doch vielleicht etwas mehr von dem Umfeld, von dem Lesen aus dem JSON bis zum Schreiben in die Datenbank
Mein Editor korrigiert OR zu or und in der Doku bei php.net finde ich es auch nur klein geschrieben. Ändere es sicherheitshalber mal auf Kleinschreibung.
 
OR oder or scheint kein unterschied zu machen.

Auszug aus var_dump($decoded);
PHP:
["created_date"]=>NULL

DB-Insert
PHP:
if($decoded['success'] == 0) {
      echo "order_id: $order_id nicht mehr im Endpunkt vorhanden \n";
      continue;
    } else {
        //get desired fields
        $created_date = $decoded['data']['res']['created_date'];

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

        //insert into mysql table
        $insert_data = <<<SQL
        INSERT INTO $destination_table_name(
                    created_date
                  )
                    VALUES(
                      '$created_date'
                    )
        SQL;
        $result = mysqli_query($db, $insert_data);
      }
}
 
Mir scheint, hier liegt das Problem:
Code:
        INSERT INTO $destination_table_name(
                    created_date
                  )
                    VALUES(
                      '$created_date'
                    )
        SQL;
Wenn der Wert null ist, ist es kein String mehr und müsste ohne Hochkommas übergeben werden.
 
Wenn der Wert null ist, ist es kein String mehr und müsste ohne Hochkommas übergeben werden.
Hattest recht, ich habe diesen Teil:
PHP:
$insert_data = <<<SQL
        INSERT INTO $destination_table_name(
                    created_date
                  )
                    VALUES(
                      '$created_date'
                    )
        SQL;
Durch folgenden ersetzt:
PHP:
$insert_data = "INSERT INTO $destination_table_name(
                    created_date
                  )
                    VALUES(
                      " . ($created_date == '' ? 'NULL' : "'$created_date'") . "
                    )";

Sollte eh mit Prepared Statement gelöst werden. Dann muss man sich keine Gedanken über Datumsformatierung und NULL machen.
Wäre für Snippets sehr dankbar. Wenn ich mir dann keine Gedanken mehr darum machen muss und mir das gefriemel mit den ganzen zeichen hier " . ($created_date == '' ? 'NULL' : "'$created_date'") . " sparen kann umso besser.
 
Zurück