Necessary changes in your webservice definition
First of all the definition of the necessary complex type PlaceOrder
does not mention an element of the type tns:OrderLine
. I guess you have to change the definition of the element. Because your webservice is very loosely defined, it works like you 've shown in your question.
Here 's the current definition of the PlaceOrder
request. This says, that the PlaceOrder
element is required as request parameter.
<wsdl:message name="IService_PlaceOrder_InputMessage">
<wsdl:part name="parameters" element="tns:PlaceOrder"/>
</wsdl:message>
The current definition of the PlaceOrder
complex type shows, that there is no OrderLine
element.
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
</xs:sequence>
</xs:complexType>
</xs:element>
This means, that you can send everything in addition. Your webservice does not know an OrderLine
element in the PlaceOrder
context because it is not defined here. You have to change the definition of the PlaceOrder
element into the following notation.
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
<xs:element minOccurs="0" name="OrderLine" nillable="true" type="q1:ArrayOfOrderLine"/>
</xs:sequence>
</xs:complexType>
</xs:element>
The definition of ArrayOfOrderLine
is defined as follows:
<xs:complexType name="ArrayOfOrderLine">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLine" nillable="true" type="tns:OrderLine"/>
</xs:sequence>
</xs:complexType>
This definition says, that you want the OrderLine
complex types with a parent node OrderLine
. So the parent node occurs exactly as defined in your wsdl file. To omit the parent node you have to redefine the PlaceOrder
complex type as follows:
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLine" nillable="true" type="tns:OrderLine"/>
</xs:sequence>
</xs:complexType>
</xs:element>
This new definition shows that the "OrderLine" element cannot be named or can be named more than once. The parent node in this case is PlaceOrder
.
A possible PHP example
Soap follows a strictly object-oriented approach. Based on this understanding, you also have to work with objects in PHP. First you need value objects (sometimes called entities) based on your xsd/wsdl definition. Keep in mind, that this example uses the redefined PlaceOrder
definition.
<?php
namespace Webservice\Entity;
use ArrayObject;
use SoapVar;
class PlaceOrder
{
protected $userToken;
protected $ediOrder;
protected $OrderLine;
public function __construct()
{
$this->OrderLine = new ArrayObject();
}
public function getUserToken(): ?SoapVar
{
return $this->userToken;
}
public function setUserToken(SoapVar $userToken): self
{
$this->userToken = $userToken;
return $this;
}
public function getEdiOrder() : ?SoapVar
{
return $this->ediOrder;
}
public function setEdiOrder(SoapVar $ediOrder): self
{
$this->ediOrder = $ediOrder;
return $this;
}
public function getOrderLine(): ArrayObject
{
return $this->OrderLine;
}
public function attachOrderLine(SoapVar $orderLine): self
{
$this->orderLine->append($orderLine);
return $this;
}
public function setOrderLine(ArrayObject $orderLine): self
{
$this->OrderLine = $orderLine;
return $this;
}
}
The above shown PHP code shows the PlaceOrder
value object. As you can see all elements, which are defined in your webservice definition, occur as properties of this class. This class is an exact php implementation of the PlaceOrder
complex type. You can say that all complex types are always PHP classes. Further the accepted method parameters are mainly SoapVar
instances. This is important for the soap client because this guarantees the right xml structure at the end.
The OrderLine value object ...
<?php
namespace Webservice\Entity;
class OrderLine
{
protected $AdditionalCustomerReferenceNumber;
protected $LineID;
protected $OrderedArticle;
protected $PortalReference;
// getters and setters here
}
With this two classes one can do a full webservice call with PHP. The following example is not testet and shows how to work with the PHP SoapClient
class. The class is sometimes a bit deceptive and it takes a bit of work to get the right result at the end. But mainly this is the way how to work it.
<?php
namespace Wesbervice;
use Webservice\Entity\Order;
use Webservice\Entity\OrderLine;
use Webservice\Entity\PlaceOrder;
use SoapFault;
use SoapVar;
try {
// this url contains the wrong defined PlaceOrder complex type
$wsdl = 'https://uat-salesapi.ndias.com/service.svc?singlewsdl&version=27';
$client = new SoapClient($wsdl, [
'cache_wsdl' => WSDL_CACHE_NONE, // as long as you work on your wsdl
'encoding' => 'utf-8',
'exceptions' => true,
'soap_version' => SOAP_1_1,
'trace' => true, // enables tracing and __getLastRequest() / __getLastResponse()
'classmap' => [
'order' => Order::class,
'OrderLine' => OrderLine::class,
'PlaceOrder' => PlaceOrder::class,
],
]);
// user token
$usertoken = new SoapVar('bla', XSD_STRING, '', '', 'userToken', 'http://tempuri.org/');
// edi order
$order = (new Order())
->setBlanketOrderReference(new SoapVar(...))
->setBuyerParty(new SoapVar(...);
$order = new SoapVar($order, SOAP_ENC_OBJECT, '', '', 'ediOrder', 'http://tempuri.org/');
// compile our request parameter
$parameter = (new PlaceOrder())
->setUserToken($usertoken)
->setEdiOrder($order);
// order list objects
$orderLine1 = (new OrderLine())
->setAdditionalCustomerReferenceNumber(new SoapVar(...))
->setLineID(new SoapVar(...));
$orderLine1 = new SoapVar($orderLine1, SOAP_ENC_OBJECT, '', '', 'OrderLine', 'http://tempuri.org/');
$parameter->attachOrderLine($orderLine1);
$orderLine2 = (new OrderLine())
->setAdditionalCustomerReferenceNumber(new SoapVar(...))
->setLineID(new SoapVar(...));
$orderLine2 = new SoapVar($orderLine2, SOAP_ENC_OBJECT, '', '', 'OrderLine', 'http://tempuri.org/');
$parameter->attachOrderLine($orderLine2);
$parameter = new SoapVar($parameter, SOAP_ENC_OBJECT, '', '', 'PlaceOrder', 'http://tempuri.org/');
// the client knows the PlaceOrder method from the wsdl
$result = $client->PlaceOrder($parameter);
// the result is a stdClass structure, als long as the classmap parameter does not contain definitions of type to php entity classes
echo "<pre>";
var_dump($result);
echo "</pre>";
} catch (SoapFault $fault) {
echo "<pre>";
var_dump($fault);
echo "</pre>";
}
Conclusion
Your web service is very imprecisely defined. For this reason, you should simply rethink the definitions for the parameters and define them more precisely in the WSDL file. Then it works with PHP, too. PHP uses strictly web standards in its soap extension.