Boost Fertility New Invention Ups Success Rates.pdf
A Unified SOAP/JSON API with Symfony2
1. A UNIFIED SOAP / JSON API
using Symfony2
Tuesday, 12 June 12
2. ABOUT ME
• Craig Marvelley
• Developer at Box UK
• @craigmarvelley
• Using Symfony for ~ 1 year
Tuesday, 12 June 12
3. THE PROBLEM
WEBSITE
LEGACY APIS
(SOAP)
MOBILE DEVICES
Tuesday, 12 June 12
4. THE SOLUTION
SOAP
WEBSITE
FACADE API SOAP LEGACY APIS
(SOAP/JSON) (SOAP)
JSON
MOBILE DEVICES
Tuesday, 12 June 12
5. Request
/json /soap
JSONController SOAPController
Processing
Service
JSON Response SOAP Response
Tuesday, 12 June 12
6. KEY COMPONENTS
• Individual request classes to encapsulate data
•A custom ParamConverter creates objects from JSON
requests
• Objects created from SOAP requests according to WSDL
• WebserviceManager class performs processing and creates an
individual response object
• Response is returned to appropriate controller, and output
Tuesday, 12 June 12
7. <?php
namespace BoxUKBundleApiBundleRequest;
use BoxUKBundleApiBundleRequest;
use SymfonyComponentValidatorConstraints as Assert;
use BeSimpleSoapBundleServiceDefinitionAnnotation as Soap;
/**
* @AssertCallback(methods={
* { "BoxUKBundleApiBundleRequestValidator", "isValidDomainName"}
* })
*/
class DomainInfoRequest extends AbstractRequest {
/**
* @AssertNotBlank()
* @SoapComplexType("string")
*/
private $domainName;
public function setDomainName( $domainName ) {
$this->domainName = $domainName;
}
public function getDomainName() {
return $this->domainName;
}
}
Tuesday, 12 June 12
8. <?php
namespace BoxUKBundleApiBundleRequest;
use BoxUKBundleApiBundleRequest;
use SymfonyComponentValidatorConstraints as Assert;
use BeSimpleSoapBundleServiceDefinitionAnnotation as Soap;
/**
* @AssertCallback(methods={
* { "BoxUKBundleApiBundleRequestValidator", "isValidDomainName"}
* })
*/
class DomainInfoRequest extends AbstractRequest {
/**
* @AssertNotBlank()
* @SoapComplexType("string")
*/
private $domainName;
public function setDomainName( $domainName ) {
$this->domainName = $domainName;
}
public function getDomainName() {
return $this->domainName;
}
}
Tuesday, 12 June 12
9. <?php
namespace BoxUKBundleApiBundleRequest;
use BoxUKBundleApiBundleRequest;
use SymfonyComponentValidatorConstraints as Assert;
use BeSimpleSoapBundleServiceDefinitionAnnotation as Soap;
/**
* @AssertCallback(methods={
* { "BoxUKBundleApiBundleRequestValidator", "isValidDomainName"}
* })
*/
class DomainInfoRequest extends AbstractRequest {
/**
* @AssertNotBlank()
* @SoapComplexType("string")
*/
private $domainName;
public function setDomainName( $domainName ) {
$this->domainName = $domainName;
}
public function getDomainName() {
return $this->domainName;
}
}
Tuesday, 12 June 12
10. <?php
namespace BoxUKBundleApiBundleController;
use SymfonyBundleFrameworkBundleControllerController;
use BoxUKBundleApiBundleRequestDomainInfoRequest;
/**
* @Route("/json")
*/
class JsonController extends Controller
{
/**
* @Route("/domainInfo")
* @Method("GET")
*/
public function domainInfoAction(DomainInfoRequest $request) {
return $this->respond( $this->getManager()->domainInfo( $request ) );
}
/**
* @return BoxUKBundleApiBundleManagementWebserviceManager
*/
protected function getManager() {
return $this->container->get( 'box_uk.api.webservice_manager' );
}
....
}
Tuesday, 12 June 12
11. <?php
namespace BoxUKBundleApiBundleController;
use BeSimpleSoapBundleServiceDefinitionAnnotation as Soap;
use SymfonyComponentDependencyInjectionContainerAware;
class SoapController extends ContainerAware
{
/**
* @SoapMethod("domainInfo")
* @SoapParam("request", phpType = "BoxUKBundleApiBundleRequest
DomainInfoRequest")
* @SoapResult(phpType = "BoxUKBundleApiBundleResponseDomainInfoRequest")
*/
public function domainInfoAction(DomainInfoRequest $request)
{
$response = $this->getManager()->domainInfo($request);
return $this->respond($response);
}
....
}
Tuesday, 12 June 12
12. <?php
namespace BoxUKBundleApiBundleController;
use BeSimpleSoapBundleServiceDefinitionAnnotation as Soap;
use SymfonyComponentDependencyInjectionContainerAware;
class SoapController extends ContainerAware
{
/**
* @SoapMethod("domainInfo")
* @SoapParam("request", phpType = "BoxUKBundleApiBundleRequest
DomainInfoRequest")
* @SoapResult(phpType = "BoxUKBundleApiBundleResponseDomainInfoRequest")
*/
public function domainInfoAction(DomainInfoRequest $request)
{
$response = $this->getManager()->domainInfo($request);
return $this->respond($response);
}
....
}
Tuesday, 12 June 12
13. <?php
namespace BoxUKBundleApiBundleController;
use BeSimpleSoapBundleServiceDefinitionAnnotation as Soap;
use SymfonyComponentDependencyInjectionContainerAware;
class SoapController extends ContainerAware
{
/**
* @param BoxUKBundleApiBundleResponse $response
* @return mixed
*/
protected function respond( $response ) {
if ( !$response->getSuccess() ) {
$code = $response->getCode();
throw new SoapFault(
$faultcode,
$response->getErrorMessage(),
null,
$response->getErrorCode()
);
}
return $this->getSoapResponse()->setReturnValue( $response );
}
}
Tuesday, 12 June 12
14. WEBSERVICE MANAGER
• Registered as a service in services.xml
• Injected into both JSON and SOAP controllers
• Validates request content according to annotations
• Handles communication with legacy webservice API
• Uses Monolog for fine-grained logging (error & activity)
• Uses Doctrine2 to access and persist data
• Constructs responses
Tuesday, 12 June 12
15. HANDY SYMFONY2 FEATURES
• Used a custom annotation serializer to transform objects into
JSON
• Used a ParamConverter to transform Symfony Request into
agnostic Request objects (JSON only)
• Used a kernel listener to automatically validate user’s access
key (JSON only)
• Used commands with a crontab to perform periodic updates
to the database
Tuesday, 12 June 12
16. COOL BUNDLES
• BeSimpleSoapBundle - Provides SOAP integration for
Symfony2, automatically serialize/deserialise data to objects.
USES ZENDSOAP!
• LiipFunctionalTestBundle - Enhanced functional tests, database
caching
• DoctrineFixturesBundle - For maintaining test data for
functional tests
• DoctrineMigrationsBundle - For versioning the database
schema
Tuesday, 12 June 12
17. TESTING
• Lots and lots of unit tests
• Functional tests for controller actions
• Used a developer-in-test
• He used SoapUI to create test cases
• Automated SOAP request/responses from WSDL
Tuesday, 12 June 12
18. THANKS FOR LISTENING!
https://joind.in/talk/view/6667
@craigmarvelley
Tuesday, 12 June 12