Mittels API ein Artikel zufügen

Hallo,

ich beschäftige mich nun schon einige Wochen mit Shopware 6. Interessant war es für mich nur wegen der zur Verfügung stehenden API´s. Und genau daran hänge ich jetzt seit Wochen. Ich schreibe einen eigenen Connector zu unserem System. Die bereits zur Verfügung stehenden sind mir zu kompliziert und überladen, ich habe einige probiert und bekomme diese schlicht nicht zum laufen, daher die Entscheidung meinen eigenen zu Schreiben.

Ich will ein Artikel über die Api der DB hinzufügen. Trotz intensiven Lesens der Doku will mir dies nicht gelingen. Ich rufe mir zuerst einen Token ab. Das klappt. Mit dem Token will ich dann gemäß nach Doku einen Artikel hinzufügen. Den Payload habe ich als Array als auch mit einem Artikel probiert.

string(453) "{
    "action": "upsert",
    "entity": "product",
    "payload": {
        "stock": 100,
        "manufacturerId": "dsfhsdhh",
        "taxId": "0x59d974b8a85842549f794acf47656f70",
        "price": {
            "net": 699,
            "gross": 572,
            "linked": false
        },
        "productNumber": "TESTAPI12345",
        "name": "TestApiProduct",
        "createdAt": null,
        "readOnly": false,
        "updatedAt": null
    }
}"

Ich bekomme folgendes von der API zurück…obwohl ich die taxId, price, stock etc übermittle, schreit er mich immer noch an das diese leer seien…Kann mir jemand einen Hinweis geben?

array(1) {
  ["errors"]=>
  array(5) {
    [0]=>
    array(6) {
      ["code"]=>
      string(36) "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
      ["status"]=>
      string(3) "400"
      ["detail"]=>
      string(35) "Dieser Wert sollte nicht leer sein."
      ["template"]=>
      string(31) "This value should not be blank."
      ["meta"]=>
      array(1) {
        ["parameters"]=>
        array(1) {
          ["{{ value }}"]=>
          string(4) "null"
        }
      }
      ["source"]=>
      array(1) {
        ["pointer"]=>
        string(8) "/0/taxId"
      }
    }
    [1]=>
    array(6) {
      ["code"]=>
      string(36) "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
      ["status"]=>
      string(3) "400"
      ["detail"]=>
      string(35) "Dieser Wert sollte nicht leer sein."
      ["template"]=>
      string(31) "This value should not be blank."
      ["meta"]=>
      array(1) {
        ["parameters"]=>
        array(1) {
          ["{{ value }}"]=>
          string(4) "null"
        }
      }
      ["source"]=>
      array(1) {
        ["pointer"]=>
        string(8) "/0/price"
      }
    }
    [2]=>
    array(6) {
      ["code"]=>
      string(36) "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
      ["status"]=>
      string(3) "400"
      ["detail"]=>
      string(35) "Dieser Wert sollte nicht leer sein."
      ["template"]=>
      string(31) "This value should not be blank."
      ["meta"]=>
      array(1) {
        ["parameters"]=>
        array(1) {
          ["{{ value }}"]=>
          string(4) "null"
        }
      }
      ["source"]=>
      array(1) {
        ["pointer"]=>
        string(16) "/0/productNumber"
      }
    }
    [3]=>
    array(6) {
      ["code"]=>
      string(36) "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
      ["status"]=>
      string(3) "400"
      ["detail"]=>
      string(35) "Dieser Wert sollte nicht leer sein."
      ["template"]=>
      string(31) "This value should not be blank."
      ["meta"]=>
      array(1) {
        ["parameters"]=>
        array(1) {
          ["{{ value }}"]=>
          string(4) "null"
        }
      }
      ["source"]=>
      array(1) {
        ["pointer"]=>
        string(8) "/0/stock"
      }
    }
    [4]=>
    array(6) {
      ["code"]=>
      string(36) "c1051bb4-d103-4f74-8988-acbcafc7fdc3"
      ["status"]=>
      string(3) "400"
      ["detail"]=>
      string(35) "Dieser Wert sollte nicht leer sein."
      ["template"]=>
      string(31) "This value should not be blank."
      ["meta"]=>
      array(1) {
        ["parameters"]=>
        array(1) {
          ["{{ value }}"]=>
          string(4) "null"
        }
      }
      ["source"]=>
      array(1) {
        ["pointer"]=>
        string(53) "/0/translations/2fbb5fe2e29a4d70aa5854ce7ce3e20b/name"
      }
    }
  }
}

payload ist ein Array

Vielen Dank für die Antwort. Das habe ich ja auch schon wie oben geschrieben ausprobiert. Bevor ich im Forum jemanden nerve, probiere ich selbst viel aus. Aktuelle habe ich es gleich noch einmal probiert, JSON schaut jetzt so aus…ABER mit dem gleichen Ergebnis.

Um auch Schusselfehler auszuschließen, gebe ich mir per var_dump immer alles aus. Payload sieht also vor dem anhängen an das JSON Objekt so aus…

array(2) {
  [0]=>
  array(9) {
    ["stock"]=>
    int(100)
    ["manufacturerId"]=>
    string(8) "dsfhsdhh"
    ["taxId"]=>
    string(34) "0x59d974b8a85842549f794acf47656f70"
    ["price"]=>
    object(stdClass)#13 (3) {
      ["net"]=>
      float(699)
      ["gross"]=>
      float(572)
      ["linked"]=>
      bool(false)
    }
    ["productNumber"]=>
    string(12) "TESTAPI12345"
    ["name"]=>
    string(14) "TestApiProduct"
    ["createdAt"]=>
    NULL
    ["readOnly"]=>
    bool(false)
    ["updatedAt"]=>
    NULL
  }
  [1]=>
  array(9) {
    ["stock"]=>
    int(100)
    ["manufacturerId"]=>
    string(8) "dsfhsdhh"
    ["taxId"]=>
    string(34) "0x59d974b8a85842549f794acf47656f70"
    ["price"]=>
    object(stdClass)#13 (3) {
      ["net"]=>
      float(699)
      ["gross"]=>
      float(572)
      ["linked"]=>
      bool(false)
    }
    ["productNumber"]=>
    string(12) "TESTAPI12345"
    ["name"]=>
    string(14) "TestApiProduct"
    ["createdAt"]=>
    NULL
    ["readOnly"]=>
    bool(false)
    ["updatedAt"]=>
    NULL
  }
}

Nach dem anhängen sieht das JSON dann so aus…

string(976) "{
    "action": "upsert",
    "entity": "product",
    "payload": [
        {
            "stock": 100,
            "manufacturerId": "dsfhsdhh",
            "taxId": "0x59d974b8a85842549f794acf47656f70",
            "price": {
                "net": 699,
                "gross": 572,
                "linked": false
            },
            "productNumber": "TESTAPI12345",
            "name": "TestApiProduct",
            "createdAt": null,
            "readOnly": false,
            "updatedAt": null
        },
        {
            "stock": 100,
            "manufacturerId": "dsfhsdhh",
            "taxId": "0x59d974b8a85842549f794acf47656f70",
            "price": {
                "net": 699,
                "gross": 572,
                "linked": false
            },
            "productNumber": "TESTAPI12345",
            "name": "TestApiProduct",
            "createdAt": null,
            "readOnly": false,
            "updatedAt": null
        }
    ]
}"

Das ist doch korrekt oder nicht?
So sieht es im Code konkret aus…

$postFields = new stdClass();
$postFields->action = "upsert";
$postFields->entity = "product";
$postFields->payload = $payload;

var_dump(json_encode($postFields, JSON_PRETTY_PRINT));

Übersehe ich etwas???

price ist glaube ich auch ein Array. Bei taxId weiß ich nicht, ob das 0x richtig interpretiert wird

Vielen Dank für die Hinweise. Habe das jetzt auch noch einmal in der Doku nachgelesen, price muss tatsächlich ein Array sein, da ja mehrere Preisangaben in unterschiedlichen Währungen hinzugefügt werden können. Also habe ich das jetzt geändert und ausprobiert.

Mittels adminer habe ich mir jetzt auch dei Tax_id ausgelesen, phpMyAdmin stellt das auch vollkommen idiotisch dar. Also die Tax_id habe ich jetzt auch gegen eine echte aus meiner Testinstallation getauscht.

Also sieht das JSON jetzt so aus

string(595) "{
    "action": "upsert",
    "entity": "product",
    "payload": [
        {
            "stock": 100,
            "manufacturerId": "7F24E96676E944B0A0ADDC20D56728CB",
            "taxId": "59D974B8A85842549F794ACF47656F70",
            "price": [
                {
                    "net": 699,
                    "gross": 572,
                    "linked": false
                }
            ],
            "productNumber": "TESTAPI12345",
            "name": "TestApiProduct",
            "createdAt": null,
            "readOnly": false,
            "updatedAt": null
        }
    ]
}"

Das Ergebnis ist aber immer noch das selbe. Leider.

Der Doku nach ist die kleinste erforderliche Nutzlast folgende…

{
    "name": "test",
    "productNumber": "random",
    "stock": 10,
    "taxId": "db6f3ed762d14b0395a3fd2dc460db42",
    "price": [
        {
            "currencyId" : "b7d2554b0ce847cd82f3ac9bd1c0dfca", 
            "gross": 15, 
            "net": 10, 
            "linked" : false
        }
    ]
}

Also hab ich die currencyId jetzt auch noch mit angegeben. Aber auch das ändert noch immer nichts an dem Ergebnis. Um die Übersichtlichkeit zu erhöhen, habe ich jetzt das was laut Doku nicht erforderlich ist erst einmal entfernt. Damit sieht das JSON nun so aus …

string(506) "{
    "action": "upsert",
    "entity": "product",
    "payload": [
        {
            "name": "TestApiProduct",
            "productNumber": "TESTAPI12345",
            "stock": 100,
            "taxId": "59D974B8A85842549F794ACF47656F70",
            "price": [
                {
                    "net": 699,
                    "gross": 572,
                    "linked": false,
                    "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca"
                }
            ]
        }
    ]
}"

Jedoch ist das Ergebnis noch immer das selbe… :frowning:

Jetzt noch die alle ids in Kleinbuchstaben umwandeln, dann klappt es

Voller Freude auf die Lösung…hab ich das natürlich auch noch umgesetzt. Obwohl ich die ID´s direkt aus der DB über Adminer genommen habe… Konnte in der Doku auch nichts dazu lesen, das diese klein sein müssen.

Nichts desto trotz habe ich die jetzt noch mit strtolower klein gemacht…

string(506) "{
    "action": "upsert",
    "entity": "product",
    "payload": [
        {
            "name": "TestApiProduct",
            "productNumber": "TESTAPI12345",
            "stock": 100,
            "taxId": "59d974b8a85842549f794acf47656f70",
            "price": [
                {
                    "net": 699,
                    "gross": 572,
                    "linked": false,
                    "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca"
                }
            ]
        }
    ]
}"

Ohne Erfolg. :frowning: :sleepy:

Ids müssen tatsächlich immer klein übergeben werden.
Wir legen Produkte in Shopware ausschließlich über die API an, das funktioniert schon.
Bei Dir ist übrigens auch ein Fehler bei der Zuweisung zwischen net und gross.

Trotz vielen probierens, komme ich nicht weiter mit diesem Problem. HIer nun mal der vollständige Code. In $json->token->access_token steckt ein String, also der Token, das habe ich auch geprüft.

Ich habe das ganze auch an die URL
http://localhost/api/_action/sync
geschickt, da ich in der Doku gelesen habe, das dies wohl möglich sei, da man darüber ja auch automatisierte Synchronisierungen vornehmen kann. Hier bekam ich jedoch gleich einen Server Error 500. Daher konzentriere ich mich erst mal auf die URL
http://localhost/api/product

	$curl = curl_init();
	curl_setopt_array($curl, [
  CURLOPT_URL => "http://localhost/api/product",
  CURLOPT_RETURNTRANSFER	=> true,
  CURLOPT_ENCODING				=> "",
  CURLOPT_MAXREDIRS				=> 10,
  CURLOPT_TIMEOUT					=> 30,
  CURLOPT_HTTP_VERSION		=> CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST 	=> "POST",
  CURLOPT_HTTPHEADER 			=> [
			    "Authorization:			".$json->token->access_token,
			    "Content-Type:			application/json",
			    "fail-on-error:			",
			    "indexing-behavior:	",
			    "single-operation:	TRUE"
			  											],
  CURLOPT_POSTFIELDS 			=> '
														{
														    "action": "upsert",
														    "entity": "product",
														    "payload": [
														        {
														            "name": "TestApiProduct",
														            "productNumber": "TESTAPI12345",
														            "stock": 100,
														            "taxId": "59d974b8a85842549f794acf47656f70",
														            "price": [
														                {
														                    "net": 572,
														                    "gross": 680.68,
														                    "linked": false,
														                    "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca"
														                }
														            ]
														        }
														    ]
														}',
	]);
	$response = curl_exec($curl);
	$err = curl_error($curl);
	curl_close($curl);
	if ($err) {
	  echo "cURL Error #:" . $err;
	} else {
		var_dump(json_decode($response, JSON_PRETTY_PRINT));
	}

Führe ich den o.g. Code aus, bekomme ich eben immer wieder nur die Fehlermeldung wie im ersten Post geschildert. Wo ist der verdammte Fehler?

Ich bin für jeden noch so kleinen Hinweis dankbar!

Die Authorization muss im Format

Authorization: [TOKEN_TYPE] [ACCESS_TOKEN]

erfolgen. Bei dir fehlt der Token Type (Bearer im Normalfall). Warum setzt du fail-on-error und indexing-behavior ohne werte? Lass das doch einfach weg.

Danke für den Hinweis, auch das habe ich soeben umgesetzt, habe es bereits gelesen und auch ausprobiert, also mit und ohne [TOKEN_TYPE].

Den Header habe ich jetzt noch mal ausgelagert um mir mit var_dump den inhalt ausgeben zu können. (um Schusselfehler auszuschließen)

$header = array(
	"Authorization: ".$json->token->token_type." ".$json->token->access_token,
	"Content-Type: application/json",
	"single-operation: TRUE",
);
var_dump($header);

array(3) {
  [0]=>
  string(740) "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1Ni........"
  [1]=>
  string(30) "Content-Type: application/json"
  [2]=>
  string(22) "single-operation: TRUE"
}

Den Code habe ich daher geringfügig geändert, zum besseren lesen hier noch einmal den aktuellen Stand.

$curl = curl_init();
	curl_setopt_array($curl, [
  CURLOPT_URL => "http://localhost/api/product",
  CURLOPT_RETURNTRANSFER	=> true,
  CURLOPT_ENCODING				=> "",
  CURLOPT_MAXREDIRS				=> 10,
  CURLOPT_TIMEOUT					=> 30,
  CURLOPT_HTTP_VERSION		=> CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST 	=> "POST",
  CURLOPT_HTTPHEADER 			=> $header,
  CURLOPT_POSTFIELDS 			=> '
														{
														    "action": "upsert",
														    "entity": "product",
														    "payload": [
														        {
														            "name": "TestApiProduct",
														            "productNumber": "TESTAPI12345",
														            "stock": 100,
														            "taxId": "59d974b8a85842549f794acf47656f70",
														            "price": [
														                {
														                    "net": 572,
														                    "gross": 680.68,
														                    "linked": false,
														                    "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca"
														                }
														            ]
														        }
														    ]
														}',
	]);
	$response = curl_exec($curl);
	$err = curl_error($curl);
	curl_close($curl);
	if ($err) {
	  echo "cURL Error #:" . $err;
	} else {
		var_dump(json_decode($response, JSON_PRETTY_PRINT));
	}

Immer noch das gleiche unschöne, unerwünschte Ergebnis…

Ahhh so. Dieses Json kannst Du so nicht an http://localhost/api/product abschicken, das ist für „_action/sync“ nur gültig. An http://localhost/api/product gibt es kein Payload und auch action und entity werden nicht angegeben (die sind ja über den Aufruf schon bekannt).

Okay, danke für den Hinweis. Hab das gleich noch einmal geändert, jedoch wie bereits oben mal geschrieben, bekomme ich da gleich ein Server Error. (500)

Dazu hatte ich auch schon mal was googlen können, jedoch hat der gefundene Thread keine Lösung.

Dann wollte ich Shopware aktualisieren, ich habe die 6.4.3.1 und wollte dann auf die 6.4.5.1 updaten, da ich vermutete, das es eventuell auch daran liegen könnte. Das schlug natürlich auch wieder grandios fehl. Über den Browser blieb der Prozess stehen, stundenlang. Okay, gegoogelt und das Update dann über die Shell probiert. Sah erst einmal gut aus. Datenbankmigration hat geklappt, beim Schritt Cleanup old files, clearing caches… kam dann ein Fatal Error. Danach gegoogelt - okay, die Idee habe ich dann auch verworfen. Ich hatte gelesen, das man die Updates der Reihe nach einspielen sollte…hab ich dann auch probiert. Also von der 6.4.3.1 auf die 6.4.4.0 - so war zumindest der Plan. Das Ergebnis war dann auch wieder das selbe. hmpf

Okay, nächster Versuch…neue VM gezaubert, Shopware 6.4.5.1 neu drauf installiert. Klappt. Die ApiTestDatei.php rüber kopiert, getestet…hmpf gleicher Fehler wie bei der Version 6.4.3.1. Also die Versionen kann ich wohl ausschließen. Trotz aller Bemühungen am heutigen Tage, keinen Schritt weiter.

Mein Stand ist also aktuell…sowohl mit 6.4.3.1 als auch mit der 6.4.5.1

	$curl = curl_init();
	curl_setopt_array($curl, [
  CURLOPT_URL => "http://localhost/api/_action/sync",
  CURLOPT_RETURNTRANSFER	=> true,
  CURLOPT_ENCODING				=> "",
  CURLOPT_MAXREDIRS				=> 10,
  CURLOPT_TIMEOUT					=> 30,
  CURLOPT_HTTP_VERSION		=> CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST 	=> "POST",
  CURLOPT_HTTPHEADER 			=> $header,
  CURLOPT_POSTFIELDS 			=> '
														{
														    "action": "upsert",
														    "entity": "product",
														    "payload": [
														        {
														            "name": "TestApiProduct",
														            "productNumber": "TESTAPI12345",
														            "stock": 100,
														            "taxId": "59d974b8a85842549f794acf47656f70",
														            "price": [
														                {
														                    "net": 572,
														                    "gross": 680.68,
														                    "linked": false,
														                    "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca"
														                }
														            ]
														        }
														    ]
														}',
	]);
	$response = curl_exec($curl);
	$err = curl_error($curl);
	curl_close($curl);
	if ($err) {
	  echo "cURL Error #:" . $err;
	} else {
		var_dump(json_decode($response, JSON_PRETTY_PRINT));
	}

wirft mir das Ergebnis…

array(1) {
  ["errors"]=>
  array(1) {
    [0]=>
    array(5) {
      ["code"]=>
      string(1) "0"
      ["status"]=>
      string(3) "500"
      ["title"]=>
      string(21) "Internal Server Error"
      ["detail"]=>
      string(45) "Cannot access offset of type string on string"
      ["meta"]=>
      array(3) {
        ["trace"]=>
        array(11) {
          [0]=>
          array(5) {
            ["file"]=>
            string(55) "/var/www/html/vendor/symfony/http-kernel/HttpKernel.php"
            ["line"]=>
            int(157)
            ["function"]=>
            string(4) "sync"
            ["class"]=>
            string(53) "Shopware\Core\Framework\Api\Controller\SyncController"
            ["type"]=>
            string(2) "->"
          }
          [1]=>
          array(5) {
            ["file"]=>
            string(55) "/var/www/html/vendor/symfony/http-kernel/HttpKernel.php"
            ["line"]=>
            int(79)
            ["function"]=>
            string(9) "handleRaw"
            ["class"]=>
            string(39) "Symfony\Component\HttpKernel\HttpKernel"
            ["type"]=>
            string(2) "->"
          }
          [2]=>
          array(5) {
            ["file"]=>
            string(51) "/var/www/html/vendor/symfony/http-kernel/Kernel.php"
            ["line"]=>
            int(199)
            ["function"]=>
            string(6) "handle"
            ["class"]=>
            string(39) "Symfony\Component\HttpKernel\HttpKernel"
            ["type"]=>
            string(2) "->"
          }
          [3]=>
          array(5) {
            ["file"]=>
            string(72) "/var/www/html/vendor/symfony/http-kernel/HttpCache/SubRequestHandler.php"
            ["line"]=>
            int(85)
            ["function"]=>
            string(6) "handle"
            ["class"]=>
            string(35) "Symfony\Component\HttpKernel\Kernel"
            ["type"]=>
            string(2) "->"
          }
          [4]=>
          array(5) {
            ["file"]=>
            string(64) "/var/www/html/vendor/symfony/http-kernel/HttpCache/HttpCache.php"
            ["line"]=>
            int(477)
            ["function"]=>
            string(6) "handle"
            ["class"]=>
            string(56) "Symfony\Component\HttpKernel\HttpCache\SubRequestHandler"
            ["type"]=>
            string(2) "::"
          }
          [5]=>
          array(5) {
            ["file"]=>
            string(64) "/var/www/html/vendor/symfony/http-kernel/HttpCache/HttpCache.php"
            ["line"]=>
            int(267)
            ["function"]=>
            string(7) "forward"
            ["class"]=>
            string(48) "Symfony\Component\HttpKernel\HttpCache\HttpCache"
            ["type"]=>
            string(2) "->"
          }
          [6]=>
          array(5) {
            ["file"]=>
            string(64) "/var/www/html/vendor/symfony/http-kernel/HttpCache/HttpCache.php"
            ["line"]=>
            int(283)
            ["function"]=>
            string(4) "pass"
            ["class"]=>
            string(48) "Symfony\Component\HttpKernel\HttpCache\HttpCache"
            ["type"]=>
            string(2) "->"
          }
          [7]=>
          array(5) {
            ["file"]=>
            string(64) "/var/www/html/vendor/symfony/http-kernel/HttpCache/HttpCache.php"
            ["line"]=>
            int(211)
            ["function"]=>
            string(10) "invalidate"
            ["class"]=>
            string(48) "Symfony\Component\HttpKernel\HttpCache\HttpCache"
            ["type"]=>
            string(2) "->"
          }
          [8]=>
          array(5) {
            ["file"]=>
            string(49) "/var/www/html/vendor/shopware/core/HttpKernel.php"
            ["line"]=>
            int(178)
            ["function"]=>
            string(6) "handle"
            ["class"]=>
            string(48) "Symfony\Component\HttpKernel\HttpCache\HttpCache"
            ["type"]=>
            string(2) "->"
          }
          [9]=>
          array(5) {
            ["file"]=>
            string(49) "/var/www/html/vendor/shopware/core/HttpKernel.php"
            ["line"]=>
            int(81)
            ["function"]=>
            string(8) "doHandle"
            ["class"]=>
            string(24) "Shopware\Core\HttpKernel"
            ["type"]=>
            string(2) "->"
          }
          [10]=>
          array(5) {
            ["file"]=>
            string(30) "/var/www/html/public/index.php"
            ["line"]=>
            int(77)
            ["function"]=>
            string(6) "handle"
            ["class"]=>
            string(24) "Shopware\Core\HttpKernel"
            ["type"]=>
            string(2) "->"
          }
        }
        ["file"]=>
        string(78) "/var/www/html/vendor/shopware/core/Framework/Api/Controller/SyncController.php"
        ["line"]=>
        int(151)
      }
    }
  }
}

Ich werd hier noch bekloppt…ich möchte das unbedingt zum laufen bekommen!

Ich glaube Du musst noch die Product ID mitgeben, also

"id": "UUID,
"type": "product"
"name": "TestApiProduct",
"productNumber": "TESTAPI12345",
"stock": 100,

Auch solltest Du die visibilities mitgeben.

Ich glaube das Deine Postfields falsch sind. Du überträgst hier einen „quasi“ JSON String.
Pack das in ein Array und übergib es als http_build_query($array) an POSTFIELDS.

Hallo,

das konnte ich so aus der Dokumentation nicht heraus lesen…

Ein Produkt hat nur eine Handvoll Pflichtfelder:

  • name [Zeichenfolge]
  • productNumber [Zeichenfolge]
  • taxId [Zeichenfolge]
  • price [Objekt]
  • stock [int]

Genau das habe ich probiert. Der Hinweis mit dem JSON String war auch aber eine sehr gute Information. Die Fehlermeldung die mir der Server zurück gibt ist zumindest schon mal eine andere. Man starrt auf den Code und sieht es einfach nicht mehr.

Trotz der Handvoll Pflichtfelder bekomme ich die Fehlermeldung The JSON payload is malformed.

array(5) {
  ["name"]=>
  string(14) "TestApiProduct"
  ["productNumber"]=>
  string(12) "TESTAPI12345"
  ["stock"]=>
  int(100)
  ["taxId"]=>
  string(32) "59d974b8a85842549f794acf47656f70"
  ["price"]=>
  array(1) {
    [0]=>
    array(4) {
      ["net"]=>
      int(572)
      ["gross"]=>
      float(680.68)
      ["linked"]=>
      bool(false)
      ["currencyId"]=>
      string(32) "b7d2554b0ce847cd82f3ac9bd1c0dfca"
    }
  }
}

Alle Hinweise aus den vorherigen Antworten wurden berücksichtigt. Price ist ein Array, die ID´s sind alle klein, und das Array übergebe ich so an cUrl.

CURLOPT_POSTFIELDS => http_build_query($postFields),

Der Fehlermeldung nach zu urteilen liegt es an dem JSON Aufbau. Also habe ich Deine beiden Hinweise auch berücksichtigt und sowohl die type als auch id testweise mit in das Array übernommen. Das sah dann so aus…wobei ich mich bei der id schon nach dem Sinn frage. Ich möchte den Artikel ja erst einmal anlegen. Wenn er angelegt ist, kann ich die id sicherlich abfragen.

array(7) {
  ["id"]=>
  string(42) "usgvuasgvausvbasuivbasuivbasuifgvsuibasuiv"
  ["type"]=>
  string(7) "product"
  ["name"]=>
  string(14) "TestApiProduct"
  ["productNumber"]=>
  string(12) "TESTAPI12345"
  ["stock"]=>
  int(100)
  ["taxId"]=>
  string(32) "59d974b8a85842549f794acf47656f70"
  ["price"]=>
  array(1) {
    [0]=>
    array(4) {
      ["net"]=>
      int(572)
      ["gross"]=>
      float(680.68)
      ["linked"]=>
      bool(false)
      ["currencyId"]=>
      string(32) "b7d2554b0ce847cd82f3ac9bd1c0dfca"
    }
  }
}

Aber auch damit ist der Fehler jetzt The JSON payload is malformed.

Mach mal um das Array noch ein Array, also einfach

[ "0" => 
  [
    ["name" => "TestApiProduct"],
    [productNumber" => "TESTAPI12345"]
  ]
]

Weil das Payload ist ja ein Array mit mehreren Objekten in JSON.

Okay, das ist nachvollziehbar. Gesagt, getan. An dem Ergebnis ändert das jedoch noch immer nichts. Leider.
The JSON payload is malformed.

object(stdClass)#10 (3) {
  ["action"]=>
  string(6) "upsert"
  ["entity"]=>
  string(7) "product"
  ["payload"]=>
  array(1) {
    [0]=>
    object(stdClass)#9 (5) {
      ["name"]=>
      string(14) "TestApiProduct"
      ["productNumber"]=>
      string(12) "TESTAPI12345"
      ["stock"]=>
      int(100)
      ["taxId"]=>
      string(32) "59d974b8a85842549f794acf47656f70"
      ["price"]=>
      array(1) {
        [0]=>
        array(4) {
          ["net"]=>
          int(572)
          ["gross"]=>
          float(680.68)
          ["linked"]=>
          bool(false)
          ["currencyId"]=>
          string(32) "b7d2554b0ce847cd82f3ac9bd1c0dfca"
        }
      }
    }
  }
}
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "http://localhost/api/_action/sync",
CURLOPT_RETURNTRANSFER	=> true,
CURLOPT_ENCODING		=> "",
CURLOPT_MAXREDIRS		=> 10,
CURLOPT_TIMEOUT			=> 30,
CURLOPT_HTTP_VERSION	=> CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST   => "POST",
CURLOPT_HTTPHEADER 	  => $header,
CURLOPT_POSTFIELDS 		=> http_build_query($postFields),
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
  echo "cURL Error #:" . $err;
} else {
	var_dump(json_decode($response, JSON_PRETTY_PRINT));
}

Als JSON schauts jetzt so aus…

string(585) "{
    "action": "upsert",
    "entity": "product",
    "payload": [
        [
            {
                "name": "TestApiProduct",
                "productNumber": "TESTAPI12345",
                "stock": 100,
                "taxId": "59d974b8a85842549f794acf47656f70",
                "price": [
                    {
                        "net": 572,
                        "gross": 680.68,
                        "linked": false,
                        "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca"
                    }
                ]
            }
        ]
    ]
}"

Habe das auch bei beiden Versionen probiert. Noch eine Idee?

So, jetzt ist meine Geduld auch langsam am Ende. Ich habe jetzt noch folgendes probiert. Über die Backend Administration habe ich einen fiktiven Artikel angelegt. Über den Symfony Request/Response Monitor habe ich mir dann die passende Anfrage die an die Api _action/sync gerichtet war raus gesucht. Von Dort habe ich mir das JSON raus gesucht und in mein Script kopiert.

$string = '
[
    {
        "key": "write",
        "action": "upsert",
        "entity": "product",
        "payload": [
            {
                "id": "0a17af6193db4f348cae49deb5c024b6",
                "manufacturerId": "227555f2519f4156930ad78a09736b84",
                "taxId": "59d974b8a85842549f794acf47656f70",
                "featureSetId": "006499ae68124105a06ee6b24da8b65b",
                "price": [
                    {
                        "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca",
                        "net": 839.49579831933,
                        "linked": true,
                        "gross": 999
                    }
                ],
                "productNumber": "SW10004",
                "stock": 14,
                "active": true,
                "purchasePrices": [
                    {
                        "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca",
                        "net": 144,
                        "linked": true,
                        "gross": 171.36
                    }
                ],
                "name": "Samsung S36",
                "visibilities": [
                    {
                        "id": "ea3f779c452944b3bbf98ffec4ec9ba3",
                        "productId": "0a17af6193db4f348cae49deb5c024b6",
                        "salesChannelId": "50813d40a6914f0484c6e43b01851815",
                        "visibility": 30
                    }
                ]
            }
        ]
    }
]
';

Mit $test = json_decode($string) habe ich mir ein Objekt liefern lassen. Das schaut dann alles so aus…
var_dump($test);

array(1) {
  [0]=>
  object(stdClass)#9 (4) {
    ["key"]=>
    string(5) "write"
    ["action"]=>
    string(6) "upsert"
    ["entity"]=>
    string(7) "product"
    ["payload"]=>
    array(1) {
      [0]=>
      object(stdClass)#10 (11) {
        ["id"]=>
        string(32) "0a17af6193db4f348cae49deb5c024b6"
        ["manufacturerId"]=>
        string(32) "227555f2519f4156930ad78a09736b84"
        ["taxId"]=>
        string(32) "59d974b8a85842549f794acf47656f70"
        ["featureSetId"]=>
        string(32) "006499ae68124105a06ee6b24da8b65b"
        ["price"]=>
        array(1) {
          [0]=>
          object(stdClass)#11 (4) {
            ["currencyId"]=>
            string(32) "b7d2554b0ce847cd82f3ac9bd1c0dfca"
            ["net"]=>
            float(839.49579831933)
            ["linked"]=>
            bool(true)
            ["gross"]=>
            int(999)
          }
        }
        ["productNumber"]=>
        string(7) "SW10004"
        ["stock"]=>
        int(14)
        ["active"]=>
        bool(true)
        ["purchasePrices"]=>
        array(1) {
          [0]=>
          object(stdClass)#12 (4) {
            ["currencyId"]=>
            string(32) "b7d2554b0ce847cd82f3ac9bd1c0dfca"
            ["net"]=>
            int(144)
            ["linked"]=>
            bool(true)
            ["gross"]=>
            float(171.36)
          }
        }
        ["name"]=>
        string(11) "Samsung S36"
        ["visibilities"]=>
        array(1) {
          [0]=>
          object(stdClass)#13 (4) {
            ["id"]=>
            string(32) "ea3f779c452944b3bbf98ffec4ec9ba3"
            ["productId"]=>
            string(32) "0a17af6193db4f348cae49deb5c024b6"
            ["salesChannelId"]=>
            string(32) "50813d40a6914f0484c6e43b01851815"
            ["visibility"]=>
            int(30)
          }
        }
      }
    }
  }
}

Dieses Object habe ich dann per cUrl an die Api gesendet und zwar mit genau diesem Code.

$curl = curl_init();
curl_setopt_array($curl, [
 CURLOPT_URL => "http://localhost/api/_action/sync",
 CURLOPT_RETURNTRANSFER	=> true,
 CURLOPT_ENCODING			=> "",
 CURLOPT_MAXREDIRS			=> 10,
 CURLOPT_TIMEOUT			=> 30,
 CURLOPT_HTTP_VERSION		=> CURL_HTTP_VERSION_1_1,
 CURLOPT_CUSTOMREQUEST 	=> "POST",
 CURLOPT_HTTPHEADER 		=> $header,
 CURLOPT_POSTFIELDS 			=> http_build_query($test),
]);

$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
  echo "cURL Error #:" . $err;
} else {
	var_dump(json_decode($response, JSON_PRETTY_PRINT));
}

Prompt bekomme ich wieder die gleiche Fehlermeldung! Das JSON ist vermutlich absolut fehlerfrei, da ich es aus dem Admin von Symfony kopiert habe, nachdem ich ein Artikel angelegt habe. Genau dieses gleiche JSON schicke ich nun mit meinem eigenen Script an die API und bekomme wieder die Fehlermeldung das dass JSON nicht korrekt sei?

Ich dreh hier gleich ab wie n Hubschrauber. Das kann doch nicht mehr sein.

:frowning:

Hier ist die Lösung!

DAS IST FALSCH!

Ich habe heute nochmal den ganzen Tag damit verbracht das Problem zu lösen. Nachdem ich ja nun versucht habe ein JSON welches direkt von Shopware selbst generiert wurde über meine eigene API an den Endpunkt zu senden, und auch dies fehl schlug, blieb mir jetzt nur noch probieren, probieren, probieren, probieren, probieren.

Nun hab ich durch das viele probieren die Lösung gefunden. In meiner Klasse habe nicht wie mo-dev oben geschrieben hatte http_build_query($array) an POSTFIELD übergeben sondern dies mit json_encode($array/object) getan (so war es ja auch geplant, so hatte ich es gelesen). Den Token hatte ich aus der Klasse gezogen, weshalb ich ja einen Token von der Api auch bekommen hatte. Das hatte funktioniert. Nun habe ich mit Extra Code (Entwicklungszeit) mir cURL ausserhalb der Klasse neu initiiert und dort wie mo-dev geschrieben http_build_query($array) an POSTFIELD übergeben. Dadurch hatte ich einen Token der funktionierte aber bekam immer wieder die Fehlermeldung das dass JSON nicht konform sei.

mo-dev, sowas kannste doch mit mir nicht machen!

Hier nun meine Klasse, die sich natürlich noch in der Entwicklung befindet. Das ist aber für jemand anderen erst einmal ein Anfang! Und das ist mir wichtig, erst einmal einen Anfang zu haben, dann kann man drum herum immer mehr bauen / ändern / verbessern.

class shopwareApi {

	var $curl;					//array zum sammeln der zu sendenen informationen
	var $header;				//array mit headerinformationen
	var $token;					//beinhaltet das objekt mit den access token daten
	var $endPoints;			//object mit endpunkt referenzen
	private $requiredData; //notwendige daten die beim aufruf der klasse übergeben werden müssen

	function __construct($requiredData){

		$this->requiredData = $requiredData;

		$this->curl = curl_init();
		curl_setopt_array($this->curl,[
		  //CURLOPT_URL => $requiredData->endPointBaseUrl,
		  CURLOPT_RETURNTRANSFER => true,
		  CURLOPT_ENCODING => "",
		  CURLOPT_MAXREDIRS => 10,
		  CURLOPT_TIMEOUT => 30,
		  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
		]);



		$this->endPoints = new stdClass();
		$this->endPoints->getApi			= '_info/config';
		$this->endPoints->getVersion	= '_info/version';
		$this->endPoints->getOpenApi	= '_info/openapi3.json';
		$this->endPoints->sync				=	'_action/sync';



//testweise
		self::getToken();
	}

	private function setUrl($data){

		curl_setopt($this->curl, CURLOPT_URL, $this->requiredData->endPointBaseUrl.$data);  

	}

	private function setRequestType($data){

		curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, $data);  

	}

	private function setHttpHeader($data){

		curl_setopt($this->curl, CURLOPT_HTTPHEADER, $data);  

	}

	private function setPostFields($data){

		curl_setopt($this->curl, CURLOPT_POSTFIELDS, json_encode($data));

	}

	private function send($requestType, $header, $postFields=FALSE){
		self::setRequestType($requestType);
		self::setHttpHeader($header);
		self::setPostFields($postFields);
		$response = curl_exec($this->curl);

//var_dump($this->curl);

		$err = curl_error($this->curl);
		curl_close($this->curl);
		if($err){
  		echo "cURL Error #:" . $err;
		}else{
var_dump(json_decode($response, JSON_PRETTY_PRINT));
			return json_decode($response);
		}

	}

	private function getAuthorization($data){
		$this->header = array_merge($this->header, array('Authorization:'.$data));
	}

	private function getToken(){
		self::getHeader();
		self::getAuthorization($this->requiredData->tokenUserData->header->Authorization);
		self::setUrl($this->requiredData->tokenUserData->endpoint);
		$this->token = self::send('POST', $this->header, $this->requiredData->tokenUserData->postFields);
	}

	private function getHeader(){
		$this->header = array(
			"Content-Type:			application/json",
			"fail-on-error:			true",
			"indexing-behavior: use-queue-indexing",
			"single-operation:	true",
		);
	}

	public function setProduct($postFields){
//var_dump($postFields);
		self::getToken();

var_dump($this->token);

		self::getHeader();
		self::getAuthorization($this->token->token_type." ".$this->token->access_token);
		//self::getAuthorization($this->token->access_token);
//var_dump($this->header);
		self::setUrl($this->endPoints->sync);
		self::send('POST', $this->header, $postFields);

	}







	public function getVersion(){
		self::getToken();
		self::getHeader();
		self::getAuthorization($this->token->token_type." ".$this->token->access_token);
		self::setUrl($this->endPoints->getVersion);
		self::send('GET', $this->header);
	}



}

Und damit konnte ich dann den Aufruf starten, der Token wird von der Klasse vorab selbst geholt, darum muss man sich dann nicht mehr kümmern. Die nächste Woche ist gerettet! Ich komme endlich weiter.

$json->setProduct($test);

zum Verständnis was in $test steckt…

$string = '
[
    {
        "key": "write",
        "action": "upsert",
        "entity": "product",
        "payload": [
            {
                "id": "0a17af6193db4f348cae49deb5c024b6",
                "manufacturerId": "227555f2519f4156930ad78a09736b84",
                "taxId": "59d974b8a85842549f794acf47656f70",
                "featureSetId": "006499ae68124105a06ee6b24da8b65b",
                "price": [
                    {
                        "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca",
                        "net": 839.49579831933,
                        "linked": true,
                        "gross": 999
                    }
                ],
                "productNumber": "SW10004",
                "stock": 14,
                "active": true,
                "purchasePrices": [
                    {
                        "currencyId": "b7d2554b0ce847cd82f3ac9bd1c0dfca",
                        "net": 144,
                        "linked": true,
                        "gross": 171.36
                    }
                ],
                "name": "Samsung S36",
                "visibilities": [
                    {
                        "id": "ea3f779c452944b3bbf98ffec4ec9ba3",
                        "productId": "0a17af6193db4f348cae49deb5c024b6",
                        "salesChannelId": "50813d40a6914f0484c6e43b01851815",
                        "visibility": 30
                    }
                ]
            }
        ]
    }
]
';

$test = json_decode($string);

Danke für die zahlreichen Tipps an alle, auch wenn mal ein falscher dabei war :slight_smile:

Als Ergebnis bekam ich damit nun …

array(5) {
  ["success"]=>
  bool(true)
  ["data"]=>
  array(3) {
    ["product"]=>
    array(1) {
      [0]=>
      string(32) "0a17af6193db4f348cae49deb5c024b6"
    }
    ["product_translation"]=>
    array(1) {
      [0]=>
      array(2) {
        ["productId"]=>
        string(32) "0a17af6193db4f348cae49deb5c024b6"
        ["languageId"]=>
        string(32) "2fbb5fe2e29a4d70aa5854ce7ce3e20b"
      }
    }
    ["product_visibility"]=>
    array(1) {
      [0]=>
      string(32) "ea3f779c452944b3bbf98ffec4ec9ba3"
    }
  }
  ["deleted"]=>
  array(0) {
  }
  ["notFound"]=>
  array(0) {
  }
  ["extensions"]=>
  array(0) {
  }
}

Ich freue mich hier gerade wie ein kleines Kind das n Lolli bekommen hat. :slight_smile:

1 „Gefällt mir“