Dunsin Olubobokun
Dunsin Olubobokun

Reputation: 902

PHP - How to send a SoapClient Request having multiple elements under a node with same name in the XML request body?

There are two SOAP endpoints I am trying to consume using WSDL. I have been able to successfully implement the first, I'm currently having issues with the second because of the way the XML request body format is - it contains multiple elements under a node having the same name. I'm using PHP array/nested arrays to construct the payload. It worked fine for the first because there was no occurrence of a parent element having multiple child elements with same name.

This is the body of my second XML request:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ext="http://xxx.xxx.xxx.xxx.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ext:requestPurchase>
         <context>
            <channel>xxx</channel>
            <prepareOnly>xxxx</prepareOnly>
            <clientReference>12q31a1a456677881</clientReference>
            <clientRequestTimeout>500</clientRequestTimeout>
            <initiatorPrincipalId>
               <id>xxxx</id>
               <type>xxxx</type>
            <userId>xxxx</userId>
            </initiatorPrincipalId>
            <password>xxxxx</password>
            <transactionProperties>
               <entry>
                  <key>preferredLanguage</key>
                  <value>en</value>
               </entry>
               <entry>
                  <key>productSKU</key>
                  <value>xxx</value>
               </entry>
               <entry>
                  <key>currency</key>
                  <value>NGN</value>
               </entry>
               <entry>
                  <key>purchaseAmount</key>
                  <value>3</value>
               </entry>
            </transactionProperties>
         </context>
         <!--- other elements --->
         </ext:requestPurchase>
   </soapenv:Body>
</soapenv:Envelope>

I have entry under transactionProperties occurring 4 times. The method I used for the first can't work for the second request because PHP works with unique array keys (or is there a way to by-pass this). I also tried using raw XML, but it's still not working. I'd be really glad to get a solution. Thanks!

This is how my code looks like (Using Laravel/PHP Framework):

<?php

namespace App\Engine;

use Illuminate\Http\Request;
use SoapClient;
use SoapVar;
use Log;
use Exception;

class Transaction
{

  public function handle($data)
  {
    // Setup Host,WSDL location, Soap Client options
    $uri = "http://xxxx.xxxx.xx.xxx.com/";
    $wsdl = "http://host IP/xxxx/service?wsdl"; 
    $soapclientOptions = array();
    $soapclientOptions['trace'] = TRUE;
    $soapclientOptions['exceptions'] = 1;
    $soapclientOptions['use'] = 'SOAP_LITERAL';
    $soapclientOptions['uri'] = $uri;
    // $soapclientOptions['connection_timeout']  = 15;
    // $soapclient_options['location'] = $host;
    $soapclientOptions['cache_wsdl'] = WSDL_CACHE_NONE;

    $e = '';
    try 
    {
       $client = new SoapClient($wsdl, $soapclientOptions);
    }
    catch(Exception $e)  
    {
      Log::error("Error occurred on attempt to open Soap Connection. Error details: ".json_encode($e));
      return response()->failure(null, 'Error occurred on attempt to open Soap Connection. Check log for error details', 400);
    }

    try
    {
      $handleRequest = $this->formPayloadAndProcessRequest($client, $data);
      if(!$payload[0]) return response()->failure(null, $payload[1], 400);

      dd($handleRequest);   
    }
    catch (Exception $e)
    {

      Log::error("Error occurred on attempt to process Soap Method. Error details: ".json_encode($e));
       return response()->failure(null, 'Error occurred on attempt to process Soap Connection. Check log for error details', 400);
    }

  }

  private function formPayloadAndProcessRequest($client,$data)
  {

    switch($data->product_type)
    {

      case('product1'): // this is for my first request
        $payload = array("key1" => array("keyA"=>"value","keyB"=>$data->amount),
                        "key2" => array("keyX"=>$data->msisdn,"keyY"=>"xxxx","keyZ"=>"xxxx"),
                        'key3' => "xxxxx"
                );
                $result = $client->functionName1($payload); // call function 1
            return [true, $result];
        break;

        case('product2'): // my second request
          $payload = $this->getVodRawXml($data->request_id,'xxx',$data->product_count);                                
          //$xmlVar = new SoapVar($payload,XSD_ANYXML);
          //dd($xmlVar);
          try
          {
            $result = $client->requestPurchase($payload); // call function 2
            dd($result);
          }
          catch(Exception $e)
          { 
              dd($e);
          }
        break;

        // other product cases

        default: 
            return [false, "Invalid product or service type supplied"];
        break;       
    }
  }

  private function getVodRawXml($requestId,$productId,$productCount)
  {
          $rawXml = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ext="http://xxx.xxx.xxx.xxx.com/">
               <soapenv:Header/>
               <soapenv:Body>
                  <ext:requestPurchase>
                     <context>
                        <channel>xxx</channel>
                        <prepareOnly>xxxx</prepareOnly>
                        <clientReference>12q31a1a456677881</clientReference>
                        <clientRequestTimeout>500</clientRequestTimeout>
                        <initiatorPrincipalId>
                           <id>xxxx</id>
                           <type>xxxx</type>
                        <userId>xxxx</userId>
                        </initiatorPrincipalId>
                        <password>xxxxx</password>
                        <transactionProperties>
                           <entry>
                              <key>preferredLanguage</key>
                              <value>en</value>
                           </entry>
                           <entry>
                              <key>productSKU</key>
                              <value>xxx</value>
                           </entry>
                           <entry>
                              <key>currency</key>
                              <value>NGN</value>
                           </entry>
                           <entry>
                              <key>purchaseAmount</key>
                              <value>3</value>
                           </entry>
                        </transactionProperties>
                     </context>
                     // other elements
                     </ext:requestPurchase>
               </soapenv:Body>
            </soapenv:Envelope>';

          return $rawXml; 
  }

}

Upvotes: 0

Views: 1612

Answers (1)

geldek
geldek

Reputation: 554

The idea behind SoapClient and WSDL is not to deal with raw XML. Instead of using an associative array, use an object (class).

class requestPurchase
{
    public context;
}

class entry
{
    public key;
    public value;
}

class context
{
    public channel;
    public prepareOnly;
    public clientReference;
    public transactionProperties = array();
}

$entry1 = new entry;
$entry2 = new entry;
$entry3 = new entry;

$context = new context;
$context->transactionProperties[] = $entry1;
$context->transactionProperties[] = $entry2;
$context->transactionProperties[] = $entry3;

$requestPurchase = new requestPurchase;
$requestPurchase->context = $context;

$client = new SoapClient($wsdl, array('classmap' => 
    array('requestPurchase' => requestPurchase::class)));
$response = $client->requestPurchase($requestPurchase);

Upvotes: 2

Related Questions