# sfRestWebServicePlugins The `sfRestWebServicePlugin` offers an easy interface for REST API based on your domain model. ## Installation and configuration ### Installation Use the default plugin installer procedure php symfony plugin:install sfRestWebServicePlugin then enable the plugin in your projectConfiguration class ( remember it needs the `sfDoctrinePlugin` enabled too, cause the services are based on your Doctrine schema ): public function setup() { $this->enablePlugins('sfDoctrinePlugin'); $this->enablePlugins('sfRestWebServicePlugin'); } Last step, enable the module in the settings.yml of the application you want the webservices to be exposed into: all: ... enabled_modules: [sfRestWebService] ... ### Configuration You can - obviously - override and extends plugin's classes by creating them in your application's module directory. The `sfRestWebServicePlugin` is based on the `sfRestWebService` module bundled with the plugin, so you only need to replicate the module on your application: $ mkdir apps/myApp/modules/sfRestWebService For example, to override a template you will only need to create it on your application at the path: `apps/myApp/modules/sfRestWebService/templates/errorSuccess.json.php` The **core** configuration on the module lies in the config.yml that you have to locally override: $ touch apps/myApp/modules/sfRestWebService/config/config.yml all: protected: true allowed: [127.0.0.1] protectedRoute: secure services: name: model: user methodForQuery: findActives states: [GET, PUT] Here's a brief explanation for every configuration parameter: all: The environment protected: boolean, the webservices are protected or not? allowed: if the webservices are protected, specify a YAML array of IP addresses that can access the services protectedRoute: sets the route that non-allowed IP addresses will be redirected to services: an array of single services configurations name: the service name ( used in the service URL ) model: the model of the service methodForQuery: a method for GET requests. If not specified, doctrine will do a `->createQuery()->execute()` states: allowed request states ( PUT, POST, GET, DELETE ). If not specified, all state are allowed If you turn on authentication, **remember to specify a secure route**. If you have module `default` enabled, the route can be `secure` ( which is the name of the `default/secure` route ). ## Requirements This plugins requires PHP's `short open tags` parameter set to `OFF`. It would not be such a difficult matter to make the plugin work also with `short open tags` enabled, the point is that you shouldn't work this way. ## A specification Since **PHP** sucks in so many ways handling PUT requests this plugin handles them with symfony's native REST architecture ( so, not not real PUT requests, but requests with the additional parametere `sf_method` set to PUT ). ## URLs Suppose a configuration like: all: protected: true allowed: [127.0.0.1] protectedRoute: secure services: users: model: User methodForQuery: ~ states: ~ The URLs that the `sfRestWebService` module will match are: * http://domain.tld/app.php/api/user ( known as **entry** ) * http://domain.tld/app.php/api/user/1 ( known as **resource** ) * http://domain.tld/app.php/api/user/search/name/fabien ( known as **search** ) From now on, we will refer to _ask an entry_, or _ask a search_ and so on. ## Asking the services Here are just a few examples on how to query an imaginary service with CURL. ### Ask an entry GET $ curl -X GET http://domain.tld/index.php/api/user POST $ curl -X GET http://domain.tld/index.php/api/user -F name='John Doe' -F email='john@sf.com' ### Ask a resource GET $ curl -X GET http://domain.tld/index.php/api/user/1 DELETE $ curl -X DELETE http://domain.tld/index.php/api/user/1 PUT $ curl -X POST http://domain.tld/index.php/api/user/1 -F sf_method=PUT -F name='John C.Hanged' ### Ask a search GET $ curl -X GET http://domain.tld/index.php/api/user/search/email/gmail ## Responses ### Entry http://domain.tld/app.php/api/user #### GET Returns a collection of objects: <?xml version="1.0" encoding="utf-8"?> <objects> <object id="1"> <id>1</id> <name>John Doe</name> </object> <object id="2"> <id>2</id> <name>Mark Madsen</name> </object> </objects> an error if the service is available but the configuration is malformed: <error> Internal server error: unsupported service </error> or a 404 status code if the service doesn't exists. #### POST Returns the just created object: <object id="7"> <id>7</id> <name>Alessandro Nadalin</name> </object> an error if the data passed via POST doesn't pass validation: <error> Validation failed in class User 1 field had validation error: * 1 validator failed on name (notnull) </error> an error if the service is available but the configuration is malformed: <error> Internal server error: unsupported service </error> or a 404 status code if the service doesn't exists. #### DELETE Not supported. #### PUT Not supported. ### Resource http://domain.tld/app.php/api/user/:id #### GET Returns the requested resource by ID: <object id="7"> <id>7</id> <name>Alessandro Nadalin</name> </object> an error if the resource doesn't exist: <error> Unable to load the specified resource </error> an error if the service is available but the configuration is malformed: <error> Internal server error: unsupported service </error> or a 404 status code if the service doesn't exists. #### POST Not supported. #### DELETE Returns a simple feedback: <object> Object has been deleted </object> an error if the resource you are trying to delete doesn't exist: <error> Unable to load the specified resource </error> an error if the service is available but the configuration is malformed: <error> Internal server error: unsupported service </error> or a 404 status code if the service doesn't exists. #### PUT Returns the just updated object: <object id="7"> <id>7</id> <name>Alessandro Nadalin has been updated</name> </object> an error if the resource doesn't exist: <error> Unable to load the specified resource </error> an error if the service is available but the configuration is malformed: <error> Internal server error: unsupported service </error> or a 404 status code if the service doesn't exists. ### Search #### GET http://domain.tld/app.php/api/user/search/:column/:value Returns a collection of objects matching a `where(":column LIKE ?", "%:value%")` statement: <objects> <object id="2"> <id>2</id> <name>Mark Madsen</name> </object> <object id="7"> <id>7</id> <name>Alessandro Nadalin</name> </object> </objects> an error if the column you are trying to search by doesn't exist: <error> Invalid search column </error> an error if the service is available but the configuration is malformed: <error> Internal server error: unsupported service </error> or a 404 status code if the service doesn't exists. #### POST Not supported. #### DELETE Not supported. #### PUT Not supported. ## Formats The services send responses in: * XML ( default format: http://domain.tld/app.php/api/user ) * JSON ( http://domain.tld/app.php/api/user.json ) * YAML ( http://domain.tld/app.php/api/user.yaml ) ## The `methodForQuery` parameter If specified, it's used in a case: processing **a GET request on an entry**. Supposing your service's `methodForQuery` is `findItalian` and the `model` parameter is `user` you will need to create a new method in the `UserTable` class: public function findItalian(Doctrine_Query $query) { $query = // ...do stuff with the query... return $query; } The `$query` that the method receives is always: Doctrine::getTable('model')->createQuery('wsmodel'); **NOTE:** do not execute the query.