phpwsdlzend-framework2soapserverzend-soap

Zend Framework 2 SOAP AutoDiscover and complex types


I'm preparing the SOAP server and generating my WSDL using the follow code:

//(... Controller action code ...)
if (key_exists('wsdl', $params)) {
    $autodiscover = new AutoDiscover();
    $autodiscover->setClass('WebServiceClass')
                 ->setUri('http://server/webserver/uri');
    $autodiscover->handle();
} else {
    $server = new Server(null);
    $server->setUri($ws_url);
    $server->setObject($this->getServiceLocator()->get('MyController\Service\WebServiceClass'));
    $server->handle();
}

//(... Controller action code ...)

But in one of my WebService method I have a parameter of type Array in which each element is of type "MyOtherClass", like follows:

    /**
     * Add list of MyOtherClass items
     *
     * @param MyOtherClass[]    $items
     *
     * @return bool
     */
    function add($items) {
        // Function code here
    }

When I try to generate the WSDL I get the follow error:

PHP Warning:  DOMDocument::loadXML(): Empty string supplied as input in /<zend framweork path>/Server/vendor/zendframework/zendframework/library/Zend/Soap/Server.php on line 734

Or this Exception:

Cannot add a complex type MyOtherClass[] that is not an object or where class could not be found in "DefaultComplexType" strategy.

When I added to my code something like this:

//(...)
if (key_exists('wsdl', $params)) {

    $autodiscover = new AutoDiscover();
    $autodiscover->setClass('WebServiceClass');
    $autodiscover->setUri($ws_url);

    $complex_type_strategy = new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeComplex();
    $complex_type_strategy->addComplexType('MyOtherClass');
    $autodiscover->setComplexTypeStrategy($complex_type_strategy);
    $autodiscover->handle();
} else {
//(...)

I get the follow error message:

Fatal error: Call to a member function getTypes() on a non-object in /<project dir>/vendor/zendframework/zendframework/library/Zend/Soap/Wsdl/ComplexTypeStrategy/AbstractComplexTypeStrategy.php on line 54

In resume, the question is: how can I make aware the WSDL of the new Custom Type used as parameter?

Thanks


Solution

  • I did something similar and this is a sample code:

    /* code.... */
    if (array_key_exists('wsdl', $this->request->getQuery()) || array_key_exists('WSDL', $this->request->getQuery())) {
    
                        $auto = new \Zend\Soap\AutoDiscover(new \Zend\Soap\Wsdl\ComplexTypeStrategy\ArrayOfTypeSequence());
    
                        $auto->setClass($controllerClassName);
                        $auto->setUri(sprintf('%s://%s%s', \Application\Bootstrap::getServiceManager()->get('config')[APPLICATION_ENV]['webServer']['protocol'],
                                                         $this->request->getUri()->getHost() , $this->request->getUri()->getPath()));
                        $auto->setServiceName(ucfirst($this->request->getModuleName()) . ucfirst($this->request->getControllerName()));
    
                        header('Content-type: application/xml');
    
                        echo $auto->toXML();
    
    
    
                    } elseif (count($this->request->getQuery()) == 0) {
    
                        $this->preDispatch();
    
                        $wsdl = sprintf('%s://%s%s?wsdl', \Application\Bootstrap::getServiceManager()->get('config')[APPLICATION_ENV]['webServer']['protocol'],
                                                         $this->request->getUri()->getHost() , $this->request->getUri()->getPath());
    
                        $soapServer = new \Zend\Soap\Server($wsdl);
                        $soapServer->setClass($controllerClassName);
                        $soapServer->handle();
                    }
    
    /* code */
    

    This is a fragment of the function signature of one of the classes that the autodiscover will generate the wsdl based on the annotations:

    /**
     * Allows to search for a patient based on the patient id
     *
     * @param int $id
     * @return \ViewModels\PatientViewModel
     * @throws \Application\Exception
     */
    protected function searchPatientById($id) {
     /* .... code */
    

    This is the class \ViewModels\PatientViewModel and \ViewModel\DiagnosisViewModel Notice here how i used the annotations to declare that a field conatins an array of a complextype, and then how that is translated as ArrayOfDiagnosisViewModel on the wsdl

        namespace ViewModels;
    
        class PatientViewModel {
    
            /**
             * @var int
             * */
            public $id;
    
            /**
             * @var string
             * */
            public $firstname;
    
            /**
             * @var string
             * */
            public $lastname;
    
            /**
             *** @var \ViewModels\DiagnosisViewModel[]**
             * */
            public $diagnosis;
    
        }
    
    class DiagnosisViewModel {
    
        /**
         * @var int
         */
        public $id;
    
        /**
         * @var string
         */
        public $name;
    
    }
    

    And this is the WSDL generated

    <?xml version="1.0" encoding="UTF-8"?>
    <definitions xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://soa.local/soap/Sample/Main" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" name="SampleMain" targetNamespace="http://soa.local/soap/Sample/Main">
        <types>
            <xsd:schema targetNamespace="http://soa.local/soap/Sample/Main">
                <xsd:complexType name="DiagnosisViewModel">
                    <xsd:all>
                        <xsd:element name="id" type="xsd:int" nillable="true"/>
                        <xsd:element name="name" type="xsd:string" nillable="true"/>
                    </xsd:all>
                </xsd:complexType>
                **<xsd:complexType name="ArrayOfDiagnosisViewModel">
                    <xsd:sequence>
                        <xsd:element name="item" type="tns:DiagnosisViewModel" minOccurs="0" maxOccurs="unbounded"/>
                    </xsd:sequence>
                </xsd:complexType>**
                <xsd:complexType name="PatientViewModel">
                    <xsd:all>
                        <xsd:element name="id" type="xsd:int" nillable="true"/>
                        <xsd:element name="firstname" type="xsd:string" nillable="true"/>
                        <xsd:element name="lastname" type="xsd:string" nillable="true"/>
                        <xsd:element name="diagnosis" type="tns:ArrayOfDiagnosisViewModel" nillable="true"/>
                    </xsd:all>
                </xsd:complexType>
            </xsd:schema>
        </types>
        <portType name="SampleMainPort">
            <operation name="searchPatientById">
                <documentation>Allows to search for a patient based on the patient id</documentation>
                <input message="tns:searchPatientByIdIn"/>
                <output message="tns:searchPatientByIdOut"/>
            </operation>
        </portType>
        <binding name="SampleMainBinding" type="tns:SampleMainPort">
            <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
            <operation name="searchPatientById">
                <soap:operation soapAction="http://soa.local/soap/Sample/Main#searchPatientById"/>
                <input>
                    <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://soa.local/soap/Sample/Main"/>
                </input>
                <output>
                    <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://soa.local/soap/Sample/Main"/>
                </output>
            </operation>
        </binding>
        <service name="SampleMainService">
            <port name="SampleMainPort" binding="tns:SampleMainBinding">
                <soap:address location="http://soa.local/soap/Sample/Main"/>
            </port>
        </service>
        <message name="searchPatientByIdIn">
            <part name="id" type="xsd:int"/>
        </message>
        <message name="searchPatientByIdOut">
            <part name="return" type="tns:PatientViewModel"/>
        </message>
    </definitions>
    

    NOTICE THAT JUST BY CHANGING THE STRATEGY AND THE RIGHT DOCBLOCKS ANNOTATIONS YOU CAN ACHIEVE THAT.

    HOPE THIS SNIPPETS CAN HELP YOU TO FIND A SOLUTION.