I recently started working with SOAP but and finding it hard to understand the use of abstract complex types.
I am trying to place a request to a wsdl yet I keep getting the error. 'The specified type is abstract: name="HomeAffairsIDVRequestedBase".
My php is as follows :
$result = $this->soap->__soapCall('PlaceRequest',array('PlaceRequest'=>
array('request' =>
array(
'type'=> 'tns:HomeAffairsIDVStandardRequest',
"IsBatchSearch"=>false,
"ParentRequestId"=>0,
"Reference"=>'test',
"Requester"=>'axxess',
"SessionId"=>'true',
"IdNumber"=>$this->idNumber
))));
and the targeted wsdl section is as follows
<s:element name="PlaceRequest">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="request" type="tns:HomeAffairsIDVRequestBase"/>
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="HomeAffairsIDVRequestBase" abstract="true">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="IsBatchSearch" type="s:boolean"/>
<s:element minOccurs="1" maxOccurs="1" name="ParentRequestId" nillable="true" type="s:int"/>
<s:element minOccurs="0" maxOccurs="1" name="Reference" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="Requester" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="SessionId" nillable="true" type="s1:guid"/>
</s:sequence>
</s:complexType>
<s:complexType name="HomeAffairsIDVRequest" abstract="true">
<s:complexContent mixed="false">
<s:extension base="tns:HomeAffairsIDVRequestBase">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="IdNumber" type="s:string"/>
</s:sequence>
</s:extension>
</s:complexContent>
</s:complexType>
<s:complexType name="HomeAffairsIDVStandardRequest">
<s:complexContent mixed="false">
<s:extension base="tns:HomeAffairsIDVRequest"/>
</s:complexContent>
</s:complexType>
<s:complexType name="HomeAffairsIDVAdvancedRequest">
<s:complexContent mixed="false">
<s:extension base="tns:HomeAffairsIDVRequest"/>
</s:complexContent>
</s:complexType>
Note: I can not edit the wsdl.
You can read complex type definitions from the wsdl as php class definitions. Each complex type is a php value object. Abstract complex types are abstract value object, which can be inherited from.
As we have the complex types defined, we can build our php classes.
abstract class HomeAffairsIDVRequestBase
{
protected bool $IsBatchSearch;
protected int $ParentRequestId;
protected ?string $Reference;
protected ?string $Requester;
protected string $SessionId;
public function getIsBatchSearch(): bool
{
return $this->IsBatchSearch;
}
public function setIsBatchSearch(bool $isBatchSearch): self
{
$this->isBatchSearch = $isBatchSearch;
return $this;
}
public function getParentRequestId(): int
{
return $this->ParentRequestId;
}
public function setParentRequestId(int $parentRequestId): self
{
$this->ParentRequestId = $parentRequestId;
return $this;
}
public function getReference(): ?string
{
return $this->Reference;
}
public function setReference(string $reference): self
{
$this->Reference = $reference;
return $this;
}
public function getRequester(): ?string
{
return $this->Requester;
}
public function setRequester(string $requester): self
{
$this->Requester = $requester;
return $this;
}
public function getSessionId(): string
{
return $this->SessionId;
}
public function setSessionId(string $sessionId): self
{
$this->SessionId = $sessionId;
return $this;
}
}
This is our first abstract complex type definition HomeAffairsIDVRequestBase
as an abstract php class. As you can see the abstract class has all the properties, which are defined in the wsdl file for this complex type. Beside that we have getter and setter methods for a better handling of the class properties.
When we look at the definition for HomeAffairsIDVStandardRequest
we can see, that it inherits from HomeAffairsIDVRequest
. This in mind we have to write to more php classes.
abstract class HomeAffairsIDVRequest extends HomeAffairsIDVRequestBase
{
protected ?string $IdNumber;
public function getIdNumber(): ?string
{
return $this->IdNumber;
}
public function setIdNumber(?string $idNumber): self
{
$this->IdNumber = $idNumber;
return $this;
}
}
class HomeAffairsIDVStandardRequest extends HomeAffairsIDVRequest
{
}
Since one of the complex types HomeAffairsIDVStandardRequest
and HomeAffairsIDVAdvancedRequest
is not abstract, wie can use this classes for the request. Keep in mind, that abstract php classes can not be initialized directly. Same goes for abstract complex types in the wsdl.
Now we need the request element as a class. This is our parent element in our soap request. The PlaceRequest
element just has one property called request
. Since this element is a inheritation of HomeAffairsIDVRequestBase
and this definition is abstract, wie have to use a type attribute later in the request.
class PlaceRequest
{
protected HomeAffairsIDVRequestBase $request;
public function getRequest(): HomeAffairsIDVRequestBase
{
return $this->request;
}
public function setRequest(HomeAffairsIDVRequestBase $request): self
{
$this->request = $request;
return $this;
}
}
Until now we have our data structure as PHP value objects. That 's all we need so far. Now let us have a look on a possible request.
try {
$wsdl = 'https://path.to.your.wsdl.file.com?wsdl';
$client = new SoapClient($wsdl, [
'exceptions' => true,
'trace' => true,
]);
// initialize our value object
$standard = (new HomeAffairsIDVStandardRequest())
->setIsBatchSearch(false)
->setParentRequestId(0)
->setReference('test')
->setRequester('requester')
->setSessionId('true')
->setIdNumber($idNumber);
// initialize our request element
$request = (new PlaceRequest())
->setRequest($standard);
$response = $client->placeRequest($request);
} catch (SoapFault $fault) {
// error handling like var_dump($fault)
}
Since I don 't see the whole wsdl content, I can not say, how the request XML should look like. It can happen, that the above shown ends up in a soap fault. In this case try the following example.
Change the type hint in the PlaceRequest
class.
class PlaceRequest
{
protected SoapVar $request;
public function getRequest(): SoapVar
{
return $this->request;
}
public function setRequest(SoapVar $request): self
{
$this->request = $request;
return $this;
}
}
After that create a new SoapVar instance with the $standard
instance.
// we 've already defined the $standard object above (sets an type attribute for the request node)
$standard = new SoapVar($standard, SOAP_ENC_OBJECT, 'HomeAffairsIDVStandardRequest', 'http://your.namespace.tld/namespace', 'request', 'http://your.namespace.tld/namespace');
// initialise the request element and set the soap var parameter
$request = (new PlaceRequest())
->setRequest($standard);
// send it via soap client
$client->PlaceRequest($request);
As an additional information you should always look at the send xml and the received xml to have a better overview on error handling. We 've set the trace
option when initializing the soap client. This enables us to see, what the last request and the last response were.
...
} catch (SoapFault $fault) {
if ($client) {
// get last request
var_dump($client->__getLastRequest());
// get last response
var_dump($client->__getLastResponse());
}
}