![]() |
|
Snippets |
|
This has been tested with the version ( 1.0.0beta4 ), I had a bit different structure for older version.
Ok ... here we go... how to create a soap server and take advatage of symfony's models and actions.
First we need the soap environment file, this will be the file that will initiate the soap API environment and the soap server:
name: /web/soapapi.php (soap server)
<?php define('SF_ROOT_DIR', realpath(dirname(__FILE__).'/..')); define('SF_APP', 'fo'); define('SF_ENVIRONMENT', 'soap'); define('SF_DEBUG', true); require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php'); ini_set("soap.wsdl_cache_enabled", "0"); $server = new SoapServer('http://www.example.com/soapapi.wsdl'); //this file is defined in the next step $server->setClass("mySoapController"); // more to come on this one $server->handle(); ?>
Now we need a wsdl file that will take advantage of the environment above.
name: /web/soapapi.wsdl (soap server definition)
<?xml version='1.0' encoding='UTF-8'?> <definitions name="soapapi" targetNamespace="urn:soapapi" xmlns:typens="urn:soapapi" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/"> <message name="getFactorial"> <part name="user" type="xsd:string">user name</part> <part name="password" type="xsd:string">user password</part> <part name="number" type="xsd:float">user password</part> </message> <message name="getFactorialResponse"> <part name="return" type="xsd:float"/> </message> <portType name="soapapiPortType"> <operation name="getFactorial"> <documentation> user required password required Generates the factorial of a passed number. </documentation> <input message="typens:getFactorial"/> <output message="typens:getFactorialResponse"/> </operation> </portType> <binding name="soapapiBinding" type="typens:soapapiPortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="getFactorial"> <soap:operation soapAction="urn:soapapiAction"/> <input> <soap:body namespace="urn:soapapi" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body namespace="urn:soapapi" use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="soapapiService"> <port name="soapapiPort" binding="typens:soapapiBinding"> <soap:address location="http://www.example.com/soapapi.php"/> </port> </service> </definitions>
Now we need to write our soap cotroller layer and that is the class that is asssigned by the soap server above.
name: /apps/fo/lib/mySoapController.class.php
<?php class mySoapController extends sfController { public $request; public function __construct(){ /** * Since we're bypassing Symfony's action dispatcher, we have to initialize manually. */ $this->context = sfContext::getInstance(); $this->request = $this->context->getRequest(); } protected function soapAuth($domain,$password){ try { $c = new Criteria(); $c->add(UserPeer::USERNAME ,$domain); $c->add(UserPeer::PASSWORD ,$password); $check = UserPeer::doSelectOne($c); if($check){ $user = $this->context->getUser(); $user->addCredential($check->getCredential()); $user->setAuthenticated(true); }else{ throw new Exception('Soap Authentication failed'); } }catch (Exception $e){ throw new SoapFault("1",$e->getMessage()); } } function getFactorial($user, $password, $number){ $this->soapAuth($user, $password); //I call this at the start of each function call in the soap controller (You can choose not to do it) $this->request->setParameter('number',$number); $action = $this->getAction('soapapi','getFactorial'); $result = $action->executeGetFactorial(); return $result; } } ?>
We are almost there ... Next we need action and view
name: /apps/fo/modules/soapapi/actions.class.php
<?php class soapapiActions extends sfActions { public function __construct(){ if(SF_ENVIRONMENT == 'soap') $this->initialize(sfContext::getInstance()); } public function executeIndex() { $this->forward('default', 'module'); } public function executeGetFactorial(){ $num = $this->getRequestParameter('number'); $result = 1; for($i=1;$i<$num;$i++) $result = $result + ($result * $i); if(SF_ENVIRONMENT == 'soap') return $result; //you can return the view or just a number... usually for soap you want to return simple stuff ... so you need to do the check $this->result = $result; } } ?>
finally the view...
name: /apps/fo/modules/soapapi/getFactorialSuccess.php
<?php echo "The factorial for ".$sf_params->get('number')." is {$result} "; ?>
Comments on this snippet
Please note: I have also tested this with the stable 1.0 and works like a charm.
Fuad, I strongly recommend you to package this as a plugin.
Hi Francois,
That is a swell idea... if you can send me some ideas/pointers I would really appreciate it. And if you like to undertake the plugin project yourself I have no issues or objections ;-)...
Fuad: I can only recommend the book chapter about plugins (Chapter 17 - http://www.symfony-project.com/book/trunk/17-Extending-Symfony) and having a look at existing plugin code (http://trac.symfony-project.com/trac/browser/plugins) for inspiration. If you have a problem, send a mail to the mailing-list and we'll give you a hand.
Hello, this isn't working, I copied verbatim from above and get: PHP Fatal error: Call to a member function get() on a non-object in /usr/share/php/symfony/action/sfComponent.class.php on line 146
This is version 1.0.2 of symfony I'm using. Please can someone update snippet with fix?
Sorry, please ignore above post, I forgot to copy something to action class. Please delete if you can.. Thanks.
Hi Fuad,
thank your for this code! But do you think, there is a way to get the view as a SOAP result? I have just finished a module, that should be available via HTTP as well as via SOAP, but as far as I can see, there's now way to this :-(
Thanx for any hint! Eddie
Hi Excessive demon,
I don't see why not! it will all depend on your wsdl definition e.g. xsd:string response can easily be accomodated. However When you have multiple consumers to the same request, you can only go so far in re-using your code. evidently when you have multiple clients to your application ( e.g. Browser and soap client) and each has a different response needs, you'll need to take care of it :-)
I hope this helps.
Hello,
Is there a way to use the action/template system for SOAP results ?
I was thinking about something like that : $result = $this->getPresentationFor('soapApi', 'getFactorial')
instead of : $action = $this->getAction('soapapi','getFactorial'); $result = $action->executeGetFactorial();
Because the current code bypass the template engine.
Unfortunatly I can't find a way to get that work.
Any ideas ?
Hello everybody, I'm a newbie in the Symfony world, I tried to adapt this snippet to my project but i don't find any config.php file in the path: SF_ROOT_DIR./.'apps'./.SF_APP./.'config'./
I'm using Symfony v1.1.4 do you think this snippet is compatible with my version? Has someone a SoapServer included in a Symfony project?
Thanks to Fuad for this snippet and thanks in advance to the community.
Hello. I tested this snippet on Symfony 1.1 but I have a problem. The idea is that, in the soapController / constructor method, I have a problem with the context. Symfony tell me that the "default" context does not exists. Well, I believe is true because of the fact that is not really a http request. However, someone have an idea !?
Thank you
Actually, to avoid this issue, you have to create the instance, using sfContext object in the file that will initiate the soap API environment, before the sequence where you initiate the SOAP Server
For this use sfContext::createInstance($your_project_configuration);
Hi, I implemented the code in symfony 1.2 but I have an issue. The issue is with the line
$action = $this->getAction('soapapi','getFactorial');
I get the following result
<faultcode>SOAP-ENV:Server</faultcode> <faultstring>Call to a member function getConfiguration() on a non-object</faultstring>
It works without this line, but then I cant forward the request to the action method
Any ideas?