Implementing SOAP Server on CakePHP

23 01 2008

My key concept is: create the SOAP server in the Controller and the methods in the Model. Why? Because controller is … (read this) and model is this.

As an example here we gonna make web service for Post of a Blog (see Blog Tutorial) using native SOAP in PHP version 5.

We’ll put the SOAP handler at URL: http://yourhost/posts/service and the WSDL (which is will be used by the SOAP consumer) at URL: http://yourhost/posts/wsdl.

  1. Create table Post (see the Blog Tutorial for details).
  2. Create Post model (models/post.php) as usual.
  3. Create Post controller (controllers/posts_controller.php), see bellow. You need RequestHandler component for create XML-type response on wsdl action.
  4. Create view for action “wsdl” (views/posts/wsdl.ctp) and define your service method in it. You can publish all of methods in Post model including from its parent (AppModel), eg. findById(), find(), findAll(), etc. Note: you should turning off “short_open_tag” in php.ini to avoid error on rendering the xml of wsdl view. And don’t forget to define your service address in the WSDL to http://yourhost/posts/service.
  5. Now your service consumer could consume your service by pointing to your WSDL URL (http://yourhost/posts/wsdl).

Here are the sample codes:

models/post.php

<?php
class Post extends AppModel {

	/**
	 * Example method
	 * @param string
	 * @return string
	 */
	function yourMethod($inputParam) {
		return "Your input is {$inputParam}";
	}
}
?>

controllers/posts_controller.php

<?php
class PostsController extends AppController {
	var $components = array('RequestHandler');

	function service() {
		$this->layout = false;
		$this->autoRender = false;
		Configure::write('debug', 0);
		ini_set("soap.wsdl_cache_enabled", "0"); // disabling WSDL cache
		$server = new SoapServer('http://yourhost/posts/wsdl');
		$server->setClass("Post");
		$server->handle();
	}

	function wsdl() {
		$this->layout = false;
		Configure::write('debug', 0);
		$this->RequestHandler->respondAs('xml');
	}
}
?>

views/posts/wsdl.ctp

<?xml version='1.0' encoding='UTF-8'?>
<definitions name='YourTNS'
  targetNamespace='YourTNS'
  xmlns:tns='YourTNS'
  xmlns:soap='http://schemas.xmlsoap.org/wsdl/soap/'
  xmlns:xsd='http://www.w3.org/2001/XMLSchema'
  xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/'
  xmlns:wsdl='http://schemas.xmlsoap.org/wsdl/'
  xmlns='http://schemas.xmlsoap.org/wsdl/'>
<types>
	<schema xmlns="http://www.w3.org/2001/XMLSchema"
				targetNamespace="http://www.ecerami.com/schema"
				xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
				xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
		<complexType name="ArrayOfString">
			<complexContent>
				<restriction base="soapenc:Array">
					<attribute ref="soapenc:arrayType"
									wsdl:arrayType="string[]"/>
				</restriction>
			</complexContent>
		</complexType>
	</schema>
</types>
<message name='findByIdRequest'>
	<part name='PostId' type='xsd:string'/>
</message>
<message name='findByIdResponse'>
  <part name='Result' type='xsd:ArrayOfString'/>
</message>
<message name='yourMethodRequest'>
	<part name='inputParam' type='xsd:string'/>
</message>
<message name='yourMethodResponse'>
  <part name='Result' type='xsd:string'/>
</message>
<portType name='PostPortType'>
  <operation name='findById'>
    <input message='tns:findByIdRequest'/>
    <output message='tns:findByIdResponse'/>
  </operation>
  <operation name='yourMethod'>
    <input message='tns:yourMethodRequest'/>
    <output message='tns:yourMethodResponse'/>
  </operation>
</portType>
<binding name='PostBinding' type='tns:PostPortType'>
  <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/>
  <operation name='findById'>
    <soap:operation soapAction='urn:your-urn'/>
    <input>
      <soap:body use='encoded' namespace='urn:your-urn'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </input>
    <output>
      <soap:body use='encoded' namespace='urn:your-urn'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </output>
  </operation>
  <operation name='yourMethod'>
    <soap:operation soapAction='urn:your-urn'/>
    <input>
      <soap:body use='encoded' namespace='urn:your-urn'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </input>
    <output>
      <soap:body use='encoded' namespace='urn:your-urn'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </output>
  </operation>
</binding>
<service name='PostService'>
  <port name='PostPort' binding='PostBinding'>
    <soap:address location='http://yourhost/posts/service'/>
  </port>
</service>
</definitions>

Update

I was using CakePHP version 1.2.0.6311 beta and PHP version 5.2.3.


Actions

Information

4 responses

24 01 2008
Rui Cruz

Hi, just found your article and I’m trying to follow it. Which cakephp version did you use?

I’m getting a Fatal error: Call to undefined method RequestHandlerComponent::respondAs() in D:\Web\xampp\htdocs\cake\iwsl\controllers\artigos_controller.php on line 16

30 01 2008
Aaron

Hi, this is a great tutorial. I tried it on my windows system and it worked perfectly. The problem is when i tried to move it to a linux server, i keep getting this error output in the browser.

ML parsing failed: syntax error (Line: 1, Character: 0)

Reparse document as HTML
Error:missing root element
Specification:http://www.w3.org/TR/REC-xml/

I appreciate any help…

25 02 2008
Ananda Putra

@Aaron, where (which URL) you got that error? If it about XML of the WSDL, maybe you didn’t set “Off” the value of “short_open_tag” directive in your php.ini. The value must be “Off” because short_open_tag (<?) violate XML’s open tag.

20 01 2009
J0NES

Looks great – I had the same idea and was wondering, if anybody did it like this… I am still testing – btw there are some smilies in the representation of the code – would you mind offering the code in a separate zip file for downloading to avoid the blog text filter to destroy the code? THANKS!!!

Leave a comment