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.