WsdlToPhp / PackageGenerator

Generates a PHP SDK based on a WSDL, simple and powerful, WSDL to PHP
https://providr.io
MIT License
426 stars 73 forks source link

[Question] choice type not working #164

Closed eduarguz closed 5 years ago

eduarguz commented 5 years ago

Hi, first of all thank you for this package, been using for a while and it is pretty cool.

I have an issue with one of the Messages that I need to generate

There is a type defined like:

<xsd:complexType name="ReservationUpdateItemType">
    <xsd:choice>
        <xsd:element maxOccurs="1" minOccurs="0" name="PassengerNameUpdate" type="PassengerNameUpdate.PNRB"/>
        <xsd:element maxOccurs="1" minOccurs="0" name="GroupNameUpdate" type="GroupNameUpdate.PNRB"/>
        ....
    </xsd:choice>
</xsd:complexType>

Reading the docs they point out that I can use the ReservationUpdateItem tag with any of the options it has.

Then I was trying something like that:

$body = new \Sabre\UpdateReservation\StructType\UpdateReservationRQ();

$body->setReservationUpdateList(
    (new \Sabre\UpdateReservation\StructType\ReservationUpdateListType())
        ->setReservationUpdateItem(
            [
                // WORKS
                (new \Sabre\UpdateReservation\StructType\ReservationUpdateItemType())->setPassengerNameUpdate(
                    new \Sabre\UpdateReservation\StructType\PassengerNameUpdate_PNRB()
                ),

                // DOES NOT WORK
                (new \Sabre\UpdateReservation\StructType\ReservationUpdateItemType())->setGroupNameUpdate(
                    new \Sabre\UpdateReservation\StructType\GroupNameUpdate_PNRB()
                ),

                // Nothing else works, only the first one
            ]
        )
);

But only the first one of them works, using print_r($update->getLastRequest());.

LastRequest

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://webservices.sabre.com/pnrbuilder/v1_19" xmlns:ns2="http://www.ebxml.org/namespaces/messageHeader" xmlns:ns3="http://schemas.xmlsoap.org/ws/2002/12/secext">
  <SOAP-ENV:Header>
    <ns2:MessageHeader>
      <ns2:From/>
      <ns2:To/>
      <ns2:CPAId/>
      <ns2:ConversationId/>
      <ns2:Service/>
      <ns2:Action/>
      <ns2:MessageData/>
    </ns2:MessageHeader>
    <ns3:Security/>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <ns1:UpdateReservationRQ>
      <ns1:ReservationUpdateList>
        <ns1:ReservationUpdateItem>
          <ns1:PassengerNameUpdate/>
        </ns1:ReservationUpdateItem>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem/>
        <ns1:ReservationUpdateItem UpdateId="2121"/>
      </ns1:ReservationUpdateList>
    </ns1:UpdateReservationRQ>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Is there any problem with the generated code, my code or the wsdl?

I have an example-repository with the generated code and the tutorial.php I am using to expose the issue, hope someone can help me with that

Thanks!!

mikaelcom commented 5 years ago

Probably related to https://github.com/WsdlToPhp/PackageGenerator/issues/133, A choice means you normally can send only one element

eduarguz commented 5 years ago

Hi, thanks for your answer

In fact that is what I am doing, only selecting one element.

Each new ReservationUpdateItemType() has only one element, the issue is that only the PassengerNameUpdate_PNRB element can be added.

<ns1:ReservationUpdateItem> //this one accepts the child
    <ns1:PassengerNameUpdate/>
</ns1:ReservationUpdateItem> 

<ns1:ReservationUpdateItem/>// the rest of them dont 
<ns1:ReservationUpdateItem/>
<ns1:ReservationUpdateItem/>
mikaelcom commented 5 years ago

This is a bug from the native PHP SoapClient class. This would mean you have to override the SoapClient.

You have to do as in the https://github.com/WsdlToPhp/PackageEws365 project by using your proper SoapClient implementation and using it by defining it using the soapclient option.

Your SoapClient would then need to update the XML request before it is actually sent in order to add the missing XML elements using the PHP objects from the parameters. The parameters have to be "saved" locally when the __call method is called before the __doRequest method is called.

mikaelcom commented 5 years ago

Did you figure out a solution? Is this indeed a bug from the native PHP SoapClient class?

eduarguz commented 5 years ago

Well, I've had some time to re-review the issues.

TLDR; My solution was to change <xsd:choice> tag to <xsd:sequence> and add minOccurs="0"to every element in the sequence

In case anyone is interested...

  1. In this StackOverflow Question, the user points out that the php SoapClient does not work properly with choice types. Apparently the php SoapClient tries to match the provided "child element" with one of the elements it accepts, giving issues because some of the types are pretty similar, some of them are optional, some others can appear many times, etc... The user in StackOverflow solves the problem by downloading and editing the wsdl and xsd specification, making the elements act as required elements (minOccurs="1"). this solves the problem for him.

  2. In my case it didnt work for all the elements, this, given some of them can appear many times (maxOccurs="unbounded"). Whenever these elements were used the message was generated incorrectly. (Really weird)

  3. For me, the solution was: Change the choice tag to a sequence tag. Make every element in the "sequence" act as optional, and, from my side generating the message properly, meaning only using one element type in the sequence at once.

I am not an expert on wsdl, xsd and how It works. The problem has no valid explanation and the solution lacks arguments to be reliable. But, this works for me.

Thank you for your follow up on the issue and your support.

I'll leave the comments in case anyone is interested

mikaelcom commented 5 years ago

Thanks for this detailed feedback!