Development

Changeset 30080

You must first sign up to be able to contribute.

Changeset 30080

Show
Ignore:
Timestamp:
07/01/10 02:33:55 (3 years ago)
Author:
xavier
Message:

[sfDoctrineRestGeneratorPlugin]: major evolutions:

  • Added some events (thanks to Matthew Penrice)
  • Added a default_format parameter (thanks to Matthew Penrice)
  • Added PUT support
  • Switched to json as default serializer (faster and less verbose)
  • Improved the documentation
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/README

    r29536 r30080  
    99  * REST module generation "à la admin-generator" 
    1010  * easy-to-customize generator.yml configuration file 
     11  * validation of the parameters passed to the service using symfony validators 
     12  * serialization as XML or JSON feeds 
    1113  * possibility to embed related models 
    1214  * possibility to embed extra fields 
    13   * validation of the GET and POST constraints using Symfony validators 
    14   * ability to limit the number of results, with/out pagination 
     15  * ability to limit the number of results, with ou without pagination 
    1516  * support for constraints unions (ie.,  http://api.example.org/city?city_id=12,13,14) 
     17  * hookable through events and filters 
    1618  * abstract and replaceable objects serialization 
    17   * serialization as XML or JSON feeds 
     19  * full HTTP support (GET, POST, PUT, DELETE) 
    1820 
    1921 
     
    8789           options: 
    8890             model:   Post 
    89              actions: [ create, list, delete ] 
     91             actions: [ create, delete, list, update ] 
    9092             module:  post 
    9193             column:  id 
     
    101103 
    102104 
    103 You should be able to see your posts as a XML feed at http://api.example.com/post/ 
    104  
    105  
    106 ## Service configuration 
    107  
    108 As for Symfony's admin-generator, the REST generator generates code on-the-fly, 
     105You should be able to see your posts as a JSON feed at http://api.example.com/post.json 
     106 
     107 
     108## Main configuration 
     109 
     110Before configuring the content of the response of the webservice, the first 
     111most important configuration steps must be undergone. They address some 
     112general concerns which impact security, stability and define what the 
     113webservice is supposed to do: 
     114 
     115 * which types of operations does the webservice support? Only fetching objects, or also creating/updating/deleting it? 
     116 * how does one access to the webservice? Which are the main security guidelines? 
     117 
     118### Allowed operations 
     119 
     120The REST web service supports four different operations: 
     121 
     122 * getting a list of items ("list"): http://api.example.com/post.json (`GET` method), 
     123 * creating a new item ("create"): http://api.example.com/post.json (`POST` method), 
     124 * updating an existing item ("update"): http://api.example.com/post/123.json (`PUT` method, the value "123" must be replaced with the primary key of the object to update), 
     125 * deleting an existing item ("delete"): http://api.example.com/post/123.json (`DELETE` method, the value "123" must be replaced with the primary key of the object to delete), 
     126 
     127Each of these operations can be allowed or forbidden in the routing file, 
     128with the `actions` key: only enable the ones that you want to use. 
     129 
     130### Security guidelines 
     131 
     132You should never forget that exposing a webservice with write access may harm 
     133your data. Because of a miss of attention in the way your webservice is 
     134secured, you could lose some important data, or have it be altered while it 
     135shouldn't. 
     136 
     137First, be sure to only allow the strictly required operations. If the client 
     138do not have to delete items with the webservice, then disable this action in 
     139the `routing.yml` file. 
     140 
     141Second, consider a way to make your webservice more secure: 
     142 
     143 * use SSL whenever possible, so that the posted data do not get intercepted and altered by a third party (man in the middle), 
     144 * use HTTP authentication, 
     145 * use a stronger / more extensible authentication system (OAuth for example), 
     146 * deliver unique API keys to your clients, and check the usage that they do of the API. 
     147 
     148 
     149## Detailed service configuration 
     150 
     151As for symfony's admin-generator, the REST generator generates code on-the-fly, 
    109152depending on the configuration done in the `generator.yml` file. 
    110153 
     
    123166        #        separator:                     ','     # separator used for multiple filters 
    124167              get: 
     168        #        additional_params:             []      # list here additional params names, which are not object properties 
     169        #        default_format:                json    # the default format of the response. If not set, will default to json. Accepted values are "xml" or "json" 
    125170        #        display:                       []      # list here fields to render in the response 
    126171        #        embed_relations:               []      # list here relations to embed in the response 
    127         #        global_additional_fields:      []      # list here additionnal calculated global fields 
     172        #        global_additional_fields:      []      # list here additional calculated global fields 
    128173        #        max_items:                     0       # uncomment to fix an absolute limit to the number of items in the response 
    129         #        object_additional_fields:      []      # list here additionnal calculated fields 
     174        #        object_additional_fields:      []      # list here additional calculated fields 
    130175        #        pagination_enabled:            false   # set to true to activate the pagination 
    131176        #        pagination_custom_page_size:   false   # set to true to allow the client to pass a page_size parameter 
     
    161206 
    162207This contains the list of the formats allowed in the communication with the 
    163 API. The default allowed formats are json and XML, XML being the default 
     208API. The default allowed formats are JSON and XML, JSON being the default 
    164209format. 
    165210 
    166211This means that you can call a resource at the following URIs: 
    167212 
    168   * http://api.example.com/post will return a XML formatted list of the posts, 
     213  * http://api.example.com/post will return a JSON formatted list of the posts, 
    169214  * http://api.example.com/post.xml will return a XML formatted list of the posts, 
    170215  * http://api.example.com/post.json will return a JSON formatted list of the posts. 
    171216 
    172217Would you want to add a new serialization format, you should add this format 
    173 in the generator.yml, and create a serializer. See examples in the 
     218in the `generator.yml`, and create a serializer. See examples in the 
    174219`lib/serializer` directory of the plugin. 
    175220 
     
    178223 
    179224The separator to use in url when passing objects primary keys. The generated 
    180 module allows to require several ressources identified by their ids: 
     225module allows to require several resources identified by their ids: 
    181226http://api.example.com/post/?id=12,17,19 
    182227 
     
    185230 
    186231The `get` option lists several options specific to the "get" operation: 
     232 
     233 
     234#### additional_params 
     235 
     236The `default_format` option allows to define an array of parameter names, 
     237which the webservice will accept. 
     238 
     239The validation of the parameters  in the generator is rather strict, and for 
     240every unrecognised parameter passed to the service, the generator will launch 
     241an exception. The option allows not to launch this exception for certain 
     242parameter types, even if these parameters do not actually get used by the 
     243generator. 
     244 
     245The purpose of this parameter is to allow third-party params to be passed to 
     246the service. For instance, you might want to pass a "`token`" or "`api_key`" 
     247parameter, which could then be used to check if the client is allowed to use 
     248the service. 
     249 
     250#### default_format 
     251 
     252The `default_format` option allows to define the default serialization format 
     253when no format is asked for in the request. The accepted values are "json" or 
     254"xml", or any other serialization format that you could develop (see the 
     255"Serialization" paragraph). If this parameter is not set, the generator will 
     256default to a "json" serializer. 
    187257 
    188258 
     
    198268                display:                       [ title, author_id ] 
    199269 
    200 If this option is left empty, all the fieds of the model will be rendered. 
     270If this option is left empty, all the fields of the model will be rendered. 
    201271 
    202272#### embed_relations 
    203273 
    204274The `embed_relations` options contains the list of the Doctrine relations to 
    205 be embedded. It might be 1-n or n-n relations, which content will be embeded 
     275be embedded. It might be 1-n or n-n relations, which content will be embedded 
    206276in each object. Here is a valid configuration for our "Post" model: 
    207277 
     
    237307#### global_additional_fields 
    238308 
    239 In some case, you might want to embed some additionnal fields in the XML or 
     309In some case, you might want to embed some additional fields in the XML or 
    240310JSON response. For instance, you might want to include the total number of 
    241311posts, an average price, etc. 
     
    251321 
    252322This will create an empty method, which has to be manually overridden in the 
    253 generated module, in order to include the additionnal field of your choice: 
    254  
    255     public function embedAdditionalTotalPosts($params) 
     323generated module, in order to include the additional field of your choice: 
     324 
     325    public function embedGlobalAdditionalTotalPosts($params) 
    256326    { 
    257327      $totalObjects = count($this->objects); 
     
    271341#### object_additional_fields 
    272342 
    273 The `object_additional_fields` contains the list of the additionnal fields 
     343The `object_additional_fields` contains the list of the additional fields 
    274344that have to be embedded in each item of the response. For instance, if you 
    275345want to add a field `NbWords`, which would give the number of words in the 
     
    281351 
    282352This will create an empty method, which has to be manually overridden in the 
    283 generated module, in order to include the additionnal field of your choice: 
     353generated module, in order to include the additional field of your choice: 
    284354 
    285355    public function embedAdditionalNbWords($item, $params) 
     
    296366 * use the `object_additional_fields` option to unset the non-desired fields. 
    297367 
    298 The embedAdditionalXXX methods will always look like: 
     368The `embedAdditionalXXX()` methods should always have the following form (the 
     369generator generates this code as comments): 
    299370 
    300371    public function embedAdditionalXXXX($item, $params) 
     
    351422For instance, you might want to get only the posts of a certain category 
    352423using a category_id parameter in the request. If you want to allow the client 
    353 to request the posts of several categories, you have to explicitely allow it, 
    354 as it may create more complex (ie. ressource-consuming and slow) requests. In 
     424to request the posts of several categories, you have to explicitly allow it, 
     425as it may create more complex (ie. resource-consuming and slow) requests. In 
    355426that goal, the key `multiple` has to be set to `true` for this fieldname: 
    356427 
     
    360431                  category_id:                  { multiple: true } 
    361432 
     433This will allow to call the webservice with several category_ids at once, with 
     434a request of the form http://api.example.org/post/?category_id=1,4,5 
     435 
    362436For the dates fields, you might want to tell the plugin which date format is 
    363437accepted. For example: 
     
    368442                  created_at:                  { date_format: 'd-m-Y' } 
    369443 
     444### Other configuration variables 
     445 
     446Some other configuration variables are not present in the default configuration file: 
     447 
     448 * the `actions_base_class` parameter allows to change the name of the base action class which is extended by the module's action class. This permits to use your own action class, which may package several methods which you will want to use in several REST modules. 
     449 
     450 
    370451 
    371452## Serialization 
    372453 
    373454The response to a get request is formatted as a XML feed or a JSON array. The 
    374 XML serializer generates a valid feed, enclosing the content of a field in 
     455XML serializer generates a valid feed, enclosing the content of a field in 
    375456CDATA sections if necessary. 
    376457 
     
    379460 
    380461 
     462## Events 
     463 
     464As of version 0.9.1, the plugin uses events at several places, in order to 
     465improve to overload and extend the default behavior. 
     466 
     467Here is a list of the supported event names : 
     468 
     469 * `sfDoctrineRestGenerator.filter_error_output`: This event is launched in order to filter the error message and enable its customisation 
     470 * `sfDoctrineRestGenerator.filter_results`: This event is launched in order to filter the query result array (add, remove or changes some keys of it). Note that you can achieve the same thing by using the `object_additional_fields` parameter in the `generator.yml` 
     471 * `sfDoctrineRestGenerator.get.pre`: This event gets fired at the very beginning of a request (at the beginning of `executeIndex()`) 
     472 
     473 
    381474## Whishlist 
    382  * more serializers (BSON for example). Currently, the plugin only allows to serialize the resultsets as a XML or JSON feeds (see the chapter "Serialization"). Mobile clients, which require the most compact possible streams, would take benefit from a JSON or even a BSON serialization. 
     475 
     476If you use the plugin and want to help me improve it, you could consider 
     477picking one of the following topics: 
     478 
     479 * possibility to nest the embed_relations parameter (and not limit it at one level only) 
     480 * possibility to disable events notification / filtering (performance) 
     481 * more serializers ([BSON](http://bsonspec.org/) or RDF for example). Currently, the plugin only allows to serialize the resultsets as a XML or JSON feeds (see the chapter "Serialization"). Mobile clients, which require the most compact possible streams, would take benefit from a JSON or even a BSON serialization. 
     482 * possibility to generate client libraries (sfDoctrineRestClientGenerator ?) 
     483 * possibility to generate unit tests 
     484 * possibility to generate API documentation 
     485 * document authentication solutions 
    383486 * all the possible feedback! 
    384487 
     
    386489## Contribute to the plugin, ask for help 
    387490 
    388 Please ask for help on how to use the plugin on Symfony's users mailing list. 
     491Please ask for help on how to use the plugin on symfony's users mailing list. 
    389492You can also send me a mail directly : xavier@lacot.org. 
    390493 
     
    398501## Changelog 
    399502 
     503### version 0.9.2 - 2010-07-01 
     504 
     505 * Added events (thanks to Matthew Penrice) 
     506 * Added a default_format parameter (thanks to Matthew Penrice) 
     507 * Added PUT support 
     508 * Switched to json as default serializer (faster and less verbose) 
     509 * Improved the documentation 
     510 
     511 
     512### version 0.9.1 - 2010-05-14 
     513 
     514Fixed a typo in the previous release 
     515 
     516 
    400517### version 0.9 - 2010-05-14 
    401518 
    402 Added a json serializer. 
     519Added a JSON serializer. 
    403520 
    404521 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/configuration.php

    r26226 r30080  
    1111abstract class Base<?php echo ucfirst($this->getModuleName()) ?>GeneratorConfiguration extends sfDoctrineRestGeneratorConfiguration 
    1212{ 
     13  public function getAdditionalParams() 
     14  { 
     15    return <?php echo $this->asPhp(isset($this->config['get']['additional_params']) ? $this->config['get']['additional_params'] : array()) ?>; 
     16<?php unset($this->config['get']['additional_params']) ?> 
     17  } 
     18 
     19  public function getDefaultFormat() 
     20  { 
     21    return <?php echo $this->asPhp(isset($this->config['get']['default_format']) ? $this->config['get']['default_format'] : 'json') ?>; 
     22<?php unset($this->config['get']['default_format']) ?> 
     23  } 
     24 
    1325  public function getDisplay() 
    1426  { 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/createAction.php

    r29536 r30080  
    2020      if (!in_array($format, <?php var_export($this->configuration->getValue('default.formats_enabled', array('json', 'xml'))) ?>)) 
    2121      { 
    22         $format = 'xml'; 
     22        $format = '<?php echo $this->configuration->getValue('get.default_format') ?>'; 
    2323      } 
    2424 
    2525      $this->getResponse()->setStatusCode(406); 
    26       $error = array(array('message' => $e->getMessage())); 
    2726      $serializer = sfResourceSerializer::getInstance($format); 
    2827      $this->getResponse()->setContentType($serializer->getContentType()); 
    29       $this->output = $serializer->serialize($error, 'error'); 
     28      $error = $e->getMessage(); 
     29 
     30      // event filter to enable customisation of the error message. 
     31      $result = $this->dispatcher->filter( 
     32        new sfEvent($this, 'sfDoctrineRestGenerator.filter_error_output'), 
     33        $error 
     34      )->getReturnValue(); 
     35 
     36      if ($error === $result) 
     37      { 
     38        $error = array(array('message' => $error)); 
     39        $this->output = $serializer->serialize($error, 'error'); 
     40      } 
     41      else 
     42      { 
     43        $this->output = $serializer->serialize($result); 
     44      } 
     45 
    3046      $this->setTemplate('index'); 
    3147      return sfView::SUCCESS; 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/getCreateValidators.php

    r26209 r30080  
    11  /** 
    22   * Returns the list of validators for a create request. 
    3    * @return  array 
     3   * @return  array  an array of validators 
    44   */ 
    55  public function getCreateValidators() 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/getIndexValidators.php

    r26338 r30080  
    11  /** 
    22   * Returns the list of validators for a get request. 
    3    * @return  array 
     3   * @return  array  an array of validators 
    44   */ 
    55  public function getIndexValidators() 
     
    2929    $validators['sort_order'] = new sfValidatorChoice(array('choices' => array('asc', 'desc'), 'required' => false)); 
    3030<?php endif; ?> 
     31<?php $additional_params = $this->configuration->getValue('get.additional_params'); ?> 
     32<?php if ($additional_params): ?> 
     33<?php foreach ($additional_params as $param): ?> 
     34    $validators['<?php echo $param; ?>'] = new sfValidatorPass(array('required' => false)); 
     35<?php endforeach; ?> 
     36<?php endif; ?> 
    3137 
    3238    return $validators; 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/indexAction.php

    r29559 r30080  
    88    $this->forward404Unless($request->isMethod(sfRequest::GET)); 
    99    $params = $request->getParameterHolder()->getAll(); 
    10     $format = isset($params['sf_format']) ? $params['sf_format'] : 'xml'; 
     10 
     11    // notify an event before the action's body starts 
     12    $this->dispatcher->notify(new sfEvent($this, 'sfDoctrineRestGenerator.get.pre', array('params' => $params))); 
     13 
     14    $format = isset($params['sf_format']) ? $params['sf_format'] : '<?php echo $this->configuration->getValue('get.default_format') ?>'; 
    1115    $request->setRequestFormat('html'); 
    1216 
    1317    if (!in_array($format, <?php var_export($this->configuration->getValue('default.formats_enabled', array('json', 'xml'))) ?>)) 
    1418    { 
    15       $format = 'xml'; 
     19      $format = '<?php echo $this->configuration->getValue('get.default_format') ?>'; 
    1620    } 
    1721 
     
    2024    unset($params['action']); 
    2125 
     26    $additional_params = <?php var_export($this->configuration->getValue('get.additional_params', array())); ?>; 
     27 
    2228    foreach ($params as $name => $value) 
    2329    { 
    24       if (!$value
     30      if (!$value || in_array($name, $additional_params)
    2531      { 
    2632        unset($params[$name]); 
     
    3541    { 
    3642      $this->getResponse()->setStatusCode(406); 
    37       $error = array(array('message' => $e->getMessage())); 
    3843      $serializer = sfResourceSerializer::getInstance($format); 
    3944      $this->getResponse()->setContentType($serializer->getContentType()); 
    40       $this->output = $serializer->serialize($error, 'error'); 
     45      $error = $e->getMessage(); 
     46 
     47      // event filter to enable customisation of the error message. 
     48      $result = $this->dispatcher->filter( 
     49        new sfEvent($this, 'sfDoctrineRestGenerator.filter_error_output'), 
     50        $error 
     51      )->getReturnValue(); 
     52 
     53      if ($error === $result) 
     54      { 
     55        $error = array(array('message' => $error)); 
     56        $this->output = $serializer->serialize($error, 'error'); 
     57      } 
     58      else 
     59      { 
     60        $this->output = $serializer->serialize($result); 
     61      } 
     62 
    4163      return sfView::SUCCESS; 
    4264    } 
     
    6688<?php foreach ($global_additional_fields as $field): ?> 
    6789 
    68     $this->embedAdditional<?php echo $field ?>($params); 
     90    $this->embedGlobalAdditional<?php echo $field ?>($params); 
    6991<?php endforeach; ?> 
    7092 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/parsePayload.php

    r29472 r30080  
    33    if ($force || !isset($this->_payload_array)) 
    44    { 
    5       $format = $this->getRequest()->getParameter('sf_format'); 
     5      $format = $this->getRequest()->getParameter('sf_format', '<?php echo $this->configuration->getValue('get.default_format') ?>'); 
    66 
    77      if (!in_array($format, <?php var_export($this->configuration->getValue('default.formats_enabled', array('json', 'xml'))) ?>)) 
    88      { 
    9         $format = 'xml'; 
     9        $format = '<?php echo $this->configuration->getValue('get.default_format') ?>'; 
    1010      } 
    1111 
     
    2121      if (!isset($payload_array) || !$payload_array) 
    2222      { 
    23         throw new sfException('Could not load payload, obviously not a valid XML!'); 
     23        throw new sfException(sprintf('Could not parse payload, obviously not a valid %s data!', $format)); 
    2424      } 
    2525 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/query.php

    r26226 r30080  
    22   * Executes the query for selecting a collection of objects, eventually 
    33   * along with related objects 
    4    * @param   array   $parsm  an array of criterions for the selection 
     4   * 
     5   * @param   array   $params  an array of criterions for the selection 
    56   */ 
    67  public function query($params) 
     
    127128    } 
    128129 
    129     $this->objects = $q->execute(array(), Doctrine::HYDRATE_ARRAY); 
     130    $this->objects = $this->dispatcher->filter( 
     131      new sfEvent( 
     132        $this, 
     133        'sfDoctrineRestGenerator.filter_results', 
     134        array() 
     135      ), 
     136      $q->execute(array(), Doctrine::HYDRATE_ARRAY) 
     137    )->getReturnValue(); 
    130138  } 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/queryAdditionnal.php

    r26209 r30080  
    44 
    55  /** 
    6    * Loads related obect to the currently selected objects 
     6   * Loads related object to the currently selected objects 
    77   */ 
    88  public function embedManyToMany<?php echo $embed_relation ?>() 
     
    1414<?php foreach ($object_additional_fields as $field): ?> 
    1515 
    16   public function embedAdditional<?php echo $field ?>($item, $params) 
    17   { 
    18   } 
     16  /** 
     17   * Allows to embed an additional field "<?php echo $field ?>" in each item 
     18   * of the resultset 
     19   * 
     20   * @param   $item    the index of an item in the resultset 
     21   * @param   $params  the filtered params of the request 
     22   */ 
     23//  public function embedAdditional<?php echo $field ?>($item, $params) 
     24//  { 
     25//    $array = $this->objects[$item]; 
     26//    put your code here, in order to change the content of $array 
     27//    ... 
     28//    $this->objects[$item] = $array; 
     29//  } 
    1930<?php endforeach; ?><?php $global_additional_fields = $this->configuration->getValue('get.global_additional_fields'); ?> 
    2031<?php foreach ($global_additional_fields as $field): ?> 
    2132 
    22   public function embedAdditional<?php echo $field ?>($items, $params) 
    23   { 
    24   } 
     33  /** 
     34   * Allows to embed an additional field "<?php echo $field ?>" at the root 
     35   * level of the resultset 
     36   * 
     37   * @param   $items   the whole resultset 
     38   * @param   $params  the filtered params of the request 
     39   */ 
     40//  public function embedGlobalAdditional<?php echo $field ?>($params) 
     41//  { 
     42//    $array = $this->objects; 
     43//    put your code here, in order to change the content of $array 
     44//    ... 
     45//    $this->objects = $array; 
     46//  } 
    2547<?php endforeach; ?> 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/validateCreate.php

    r28995 r30080  
    11  /** 
    2    * Applies the creation validators to the XML posted to the service 
    3    * @param   string   $xml  A XML string 
     2   * Applies the creation validators to the payload posted to the service 
     3   * 
     4   * @param   string   $payload  A payload string 
    45   */ 
    5   public function validateCreate($xml
     6  public function validateCreate($payload
    67  { 
    78    $validators = $this->getCreateValidators(); 
    8     $params = $this->parsePayload($xml); 
    9     $unused = array_keys($validators); 
    10  
    11     foreach ($params as $name => $value) 
    12     { 
    13       if (!isset($validators[$name])) 
    14       { 
    15         throw new sfException(sprintf('Could not validate extra field "%s"', $name)); 
    16       } 
    17       else 
    18       { 
    19         $validators[$name]->clean($value); 
    20         unset($unused[array_search($name, $unused, true)]); 
    21       } 
    22     } 
    23  
    24     // are non given values required? 
    25     foreach ($unused as $name) 
    26     { 
    27       try 
    28       { 
    29         $validators[$name]->clean(null); 
    30       } 
    31       catch (Exception $e) 
    32       { 
    33         throw new sfException(sprintf('Could not validate field "%s": %s', $name, $e->getMessage())); 
    34       } 
    35     } 
     9    $params = $this->parsePayload($payload); 
     10    $this->validate($params, $validators); 
    3611  } 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/parts/validateIndex.php

    r28995 r30080  
    11  /** 
    2    * Applies the get validators to the XML posted to the service 
     2   * Applies the get validators to the constraint parameters passed to the webservice 
     3   * 
    34   * @param   array   $params  An array of criterions used for the selection 
    45   */ 
     
    67  { 
    78    $validators = $this->getIndexValidators(); 
    8     $unused = array_keys($validators); 
    9  
    10     foreach ($params as $name => $value) 
    11     { 
    12       if (!isset($validators[$name])) 
    13       { 
    14         throw new sfException(sprintf('Could not validate extra field "%s"', $name)); 
    15       } 
    16       else 
    17       { 
    18         $validators[$name]->clean($value); 
    19         unset($unused[array_search($name, $unused, true)]); 
    20       } 
    21     } 
    22  
    23     // are non given values required? 
    24     foreach ($unused as $name) 
    25     { 
    26       try 
    27       { 
    28         $validators[$name]->clean(null); 
    29       } 
    30       catch (Exception $e) 
    31       { 
    32         throw new sfException(sprintf('Could not validate field "%s": %s', $name, $e->getMessage())); 
    33       } 
    34     } 
     9    $this->validate($params, $validators); 
    3510  } 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/skeleton/config/generator.yml

    r29469 r30080  
    66    config: 
    77      default: 
    8 #        fields:                                # list here the fields. 
     8#        fields:                                # list here the fields. 
    99#          created_at:                  { date_format: 'Y-m-d\TH:i:s', tag_name: 'created' }      # for instance 
    1010#        formats_enabled:               [ xml, json ]    # enabled formats 
    1111#        separator:                     ','     # separator used for multiple filters 
    1212      get: 
     13#        additional_params:             []      # list here additionnal params names, which are not object properties 
     14#        default_format:                json    # the default format of the response. If not set, will default to json. accepted values are "xml" or "json" 
    1315#        display:                       []      # list here fields to render in the response 
    1416#        embed_relations:               []      # list here relations to embed in the response 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/data/generator/sfDoctrineRestGenerator/default/template/actions/actions.class.php

    r26209 r30080  
    1313 
    1414{ 
    15   protected $model = '<?php echo $this->getModelClass() ?>'; 
     15  public $model = '<?php echo $this->getModelClass() ?>'; 
    1616 
    1717<?php include dirname(__FILE__).'/../../parts/createAction.php' ?> 
     
    2727<?php include dirname(__FILE__).'/../../parts/getIndexValidators.php' ?> 
    2828 
     29<?php include dirname(__FILE__).'/../../parts/getUpdateValidators.php' ?> 
     30 
    2931<?php include dirname(__FILE__).'/../../parts/indexAction.php' ?> 
    3032 
     
    3436<?php include dirname(__FILE__).'/../../parts/queryAdditionnal.php' ?> 
    3537 
     38<?php include dirname(__FILE__).'/../../parts/updateAction.php' ?> 
     39 
    3640<?php include dirname(__FILE__).'/../../parts/updateObjectFromRequest.php' ?> 
     41 
     42<?php include dirname(__FILE__).'/../../parts/validate.php' ?> 
    3743 
    3844<?php include dirname(__FILE__).'/../../parts/validateCreate.php' ?> 
    3945 
    4046<?php include dirname(__FILE__).'/../../parts/validateIndex.php' ?> 
     47 
     48<?php include dirname(__FILE__).'/../../parts/validateUpdate.php' ?> 
    4149} 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/lib/generator/sfDoctrineRestGeneratorConfiguration.class.php

    r26226 r30080  
    2222      ), 
    2323      'get'     => array( 
     24        'additional_params'           => $this->getAdditionalParams(), 
     25        'default_format'              => $this->getDefaultFormat(), 
    2426        'display'                     => $this->getDisplay(), 
    2527        'embed_relations'             => $this->getEmbedRelations(), 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/lib/task/sfDoctrineRestGenerateModuleTask.class.php

    r26336 r30080  
    5959  options: 
    6060    model:   %s 
    61     actions: [ create, list, delete ] 
     61    actions: [ create, delete, list, update ] 
    6262    module:  %s 
    63     column:  id 
     63    column:  %s 
    6464    format:  xml 
    6565 
    6666 
    6767EOF 
    68       , $model, $model, $module).$content; 
     68      , $model, $model, $module, $primaryKey).$content; 
    6969 
    7070      $this->logSection('file+', $routing); 
  • plugins/sfDoctrineRestGeneratorPlugin/trunk/package.xml

    r29473 r30080  
    1 <?xml version="1.0" encoding="utf-8"?> 
    2 <package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.4.1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> 
    3   <name>sfDoctrineRestGeneratorPlugin</name> 
    4   <channel>plugins.symfony-project.org</channel> 
    5   <summary>This plugin permits to generate REST modules bound to Doctrine models.</summary> 
    6   <description>This plugin permits to generate REST modules bound to Doctrine models. It 
     1<?xml version="1.0" encoding="UTF-8"?> 
     2<package packagerversion="1.9.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd"> 
     3 <name>sfDoctrineRestGeneratorPlugin</name> 
     4 <channel>plugins.symfony-project.org</channel> 
     5 <summary>This plugin permits to generate REST modules bound to Doctrine models.</summary> 
     6 <description>This plugin permits to generate REST modules bound to Doctrine models. It 
    77allows to easily create REST webservices, and provides an extensible framework 
    88for data exchange. Here are some key features : 
    99 
    10   * REST module generation "à la admin-generator" 
     10  * REST module generation &quot;à la admin-generator&quot; 
    1111  * easy-to-customize generator.yml configuration file 
     12  * validation of the parameters passed to the service using symfony validators 
     13  * serialization as XML or JSON feeds 
    1214  * possibility to embed related models 
    1315  * possibility to embed extra fields 
    14   * validation of the GET and POST constraints using Symfony validators 
    15   * ability to limit the number of results, with/out pagination 
     16  * ability to limit the number of results, with ou without pagination 
    1617  * support for constraints unions (ie.,  http://api.example.org/city?city_id=12,13,14) 
    17   * abstract and replaceable objects serialization</description> 
    18   <lead> 
    19     <name>Xavier Lacot</name> 
    20     <user>xavier</user> 
    21     <email>xavier@lacot.org</email> 
    22     <active>yes</active> 
    23   </lead> 
    24   <date>2010-05-14</date> 
    25   <time>18:23:12</time> 
    26   <version> 
    27     <release>0.9.1</release> 
    28     <api>0.9.1</api> 
    29   </version> 
    30   <stability> 
    31     <release>beta</release> 
    32     <api>beta</api> 
    33   </stability> 
    34   <license uri="http://www.opensource.org/licenses/mit-license.html">MIT</license> 
    35   <notes>-</notes> 
    36   <contents> 
    37     <dir baseinstalldir="." name="/"> 
    38       <dir name="data"> 
    39         <dir name="generator"> 
    40           <dir name="sfDoctrineRestGenerator"> 
    41             <dir name="default"> 
    42               <dir name="parts"> 
    43                 <file name="configuration.php" role="data"/> 
    44                 <file name="createAction.php" role="data"/> 
    45                 <file name="createObject.php" role="data"/> 
    46                 <file name="deleteAction.php" role="data"/> 
    47                 <file name="doSave.php" role="data"/> 
    48                 <file name="fieldsConfiguration.php" role="data"/> 
    49                 <file name="getCreateValidators.php" role="data"/> 
    50                 <file name="getIndexValidators.php" role="data"/> 
    51                 <file name="indexAction.php" role="data"/> 
    52                 <file name="paginationConfiguration.php" role="data"/> 
    53                 <file name="parsePayload.php" role="data"/> 
    54                 <file name="query.php" role="data"/> 
    55                 <file name="queryAdditionnal.php" role="data"/> 
    56                 <file name="sortConfiguration.php" role="data"/> 
    57                 <file name="updateObjectFromRequest.php" role="data"/> 
    58                 <file name="validateCreate.php" role="data"/> 
    59                 <file name="validateIndex.php" role="data"/> 
    60               </dir> 
    61               <dir name="skeleton"> 
    62                 <dir name="actions"> 
    63                   <file name="actions.class.php" role="data"/> 
    64                 </dir> 
    65                 <dir name="config"> 
    66                   <file name="generator.yml" role="data"/> 
    67                   <file name="view.yml" role="data"/> 
    68                 </dir> 
    69                 <dir name="lib"> 
    70                   <file name="configuration.php" role="data"/> 
    71                 </dir> 
    72                 <dir name="templates"></dir> 
    73               </dir> 
    74               <dir name="template"> 
    75                 <dir name="actions"> 
    76                   <file name="actions.class.php" role="data"/> 
    77                 </dir> 
    78                 <dir name="config"></dir> 
    79                 <dir name="templates"> 
    80                   <file name="indexSuccess.php" role="data"/> 
    81                 </dir> 
    82               </dir> 
    83             </dir> 
    84           </dir> 
    85         </dir> 
    86       </dir> 
    87       <dir name="lib"> 
    88         <dir name="generator"> 
    89           <file name="sfDoctrineRestGenerator.class.php" role="data"/> 
    90           <file name="sfDoctrineRestGeneratorConfiguration.class.php" role="data"/> 
    91         </dir> 
    92         <dir name="serializer"> 
    93           <file name="sfResourceSerializer.class.php" role="data"/> 
    94           <file name="sfResourceSerializerJson.class.php" role="data"/> 
    95           <file name="sfResourceSerializerXml.class.php" role="data"/> 
    96         </dir> 
    97         <dir name="task"> 
    98           <file name="sfDoctrineRestGenerateModuleTask.class.php" role="data"/> 
    99         </dir> 
    100       </dir> 
    101       <file name="LICENSE" role="data"/> 
    102       <file name="README" role="data"/> 
    103     </dir> 
    104   </contents> 
    105   <dependencies> 
    106     <required> 
    107       <php> 
    108         <min>5.2.4</min> 
    109       </php> 
    110       <pearinstaller> 
    111         <min>1.4.1</min> 
    112       </pearinstaller> 
    113       <package> 
    114         <name>symfony</name> 
    115         <channel>pear.symfony-project.com</channel> 
    116         <min>1.3.0</min> 
    117         <max>2.0.0</max> 
    118         <exclude>2.0.0</exclude> 
    119       </package> 
    120     </required> 
    121   </dependencies> 
    122   <phprelease></phprelease> 
    123   <changelog> 
     18  * hookable through events and filters 
     19  * abstract and replaceable objects serialization 
     20  * full HTTP support (GET, POST, PUT, DELETE)</description> 
     21 <lead> 
     22  <name>Xavier Lacot</name> 
     23  <user>xavier</user> 
     24  <email>xavier@lacot.org</email> 
     25  <active>yes</active> 
     26 </lead> 
     27 <date>2010-07-01</date> 
     28 <time>02:29:15</time> 
     29 <version> 
     30  <release>0.9.2</release> 
     31  <api>0.9.2</api> 
     32 </version> 
     33 <stability> 
     34  <release>beta</release> 
     35  <api>beta</api> 
     36 </stability> 
     37 <license uri="http://www.opensource.org/licenses/mit-license.html">MIT</license> 
     38 <notes> 
     39* Added events (thanks to Matthew Penrice) 
     40* Added a default_format parameter (thanks to Matthew Penrice) 
     41* Added PUT support 
     42* Switched to json as default serializer (faster and less verbose) 
     43* Improved the documentation 
     44 </notes> 
     45 <contents> 
     46  <dir baseinstalldir="." name="/"> 
     47   <file baseinstalldir="." md5sum="f72ebe8f0d391716256e00bf9306abeb" name="data/generator/sfDoctrineRestGenerator/default/parts/configuration.php" role="data" /> 
     48   <file baseinstalldir="." md5sum="12286407c2708190ce5891f274b8ed4d" name="data/generator/sfDoctrineRestGenerator/default/parts/createAction.php" role="data" /> 
     49   <file baseinstalldir="." md5sum="fc2f9a8e6dccedff99431815e16f4535" name="data/generator/sfDoctrineRestGenerator/default/parts/createObject.php" role="data" /> 
     50   <file baseinstalldir="." md5sum="e80ea704c1b5fed090b98e8adff45a6d" name="data/generator/sfDoctrineRestGenerator/default/parts/deleteAction.php" role="data" /> 
     51   <file baseinstalldir="." md5sum="8e10d6281c78d41b25aed781b7b9a0f4" name="data/generator/sfDoctrineRestGenerator/default/parts/doSave.php" role="data" /> 
     52   <file baseinstalldir="." md5sum="212381b5b690aa04888a3b0592dc98fe" name="data/generator/sfDoctrineRestGenerator/default/parts/fieldsConfiguration.php" role="data" /> 
     53   <file baseinstalldir="." md5sum="18538d45efe61438cb09b5b875b9ef0f" name="data/generator/sfDoctrineRestGenerator/default/parts/getCreateValidators.php" role="data" /> 
     54   <file baseinstalldir="." md5sum="e238fd6343fd47ef621da9e044304b23" name="data/generator/sfDoctrineRestGenerator/default/parts/getIndexValidators.php" role="data" /> 
     55   <file baseinstalldir="." md5sum="5f15869bb09c4f77cdb1bde10cab3f07" name="data/generator/sfDoctrineRestGenerator/default/parts/getUpdateValidators.php" role="php" /> 
     56   <file baseinstalldir="." md5sum="b17e0a85e3d1e3d28961e611c44bc34f" name="data/generator/sfDoctrineRestGenerator/default/parts/indexAction.php" role="data" /> 
     57   <file baseinstalldir="." md5sum="68b6c6bf924ee6e437067e41c8b0db3d" name="data/generator/sfDoctrineRestGenerator/default/parts/paginationConfiguration.php" role="data" /> 
     58   <file baseinstalldir="." md5sum="dbb027d32b3271ef43dcb21bcef84fd5" name="data/generator/sfDoctrineRestGenerator/default/parts/parsePayload.php" role="data" /> 
     59   <file baseinstalldir="." md5sum="959326712c513c268f45f846f791c41d" name="data/generator/sfDoctrineRestGenerator/default/parts/query.php" role="data" /> 
     60   <file baseinstalldir="." md5sum="b65e16ffe94186c5d62640f526283fdb" name="data/generator/sfDoctrineRestGenerator/default/parts/queryAdditionnal.php" role="data" /> 
     61   <file baseinstalldir="." md5sum="8831eef0b65854b08de5a84c6b7887d2" name="data/generator/sfDoctrineRestGenerator/default/parts/sortConfiguration.php" role="data" /> 
     62   <file baseinstalldir="." md5sum="e01abb2ca9197d3b4b0746d9571786bc" name="data/generator/sfDoctrineRestGenerator/default/parts/updateAction.php" role="php" /> 
     63   <file baseinstalldir="." md5sum="89f8383000964ee620afa2e8226ac900" name="data/generator/sfDoctrineRestGenerator/default/parts/updateObjectFromRequest.php" role="data" /> 
     64   <file baseinstalldir="." md5sum="c14129f5f3006d8b1f72a03ad2fe7f9e" name="data/generator/sfDoctrineRestGenerator/default/parts/validate.php" role="php" /> 
     65   <file baseinstalldir="." md5sum="63c0c245f2ff815b317cf6d39d6fac6e" name="data/generator/sfDoctrineRestGenerator/default/parts/validateCreate.php" role="data" /> 
     66   <file baseinstalldir="." md5sum="09c9158f962ca6af45b1c5fdd0929f4c" name="data/generator/sfDoctrineRestGenerator/default/parts/validateIndex.php" role="data" /> 
     67   <file baseinstalldir="." md5sum="dc1e971d03413449994512a52a7e8221" name="data/generator/sfDoctrineRestGenerator/default/parts/validateUpdate.php" role="php" /> 
     68   <file baseinstalldir="." md5sum="fa0dc415bd38a12d41b4b29bd672b9ba" name="data/generator/sfDoctrineRestGenerator/default/skeleton/actions/actions.class.php" role="data" /> 
     69   <file baseinstalldir="." md5sum="ab54a4734964fbd38bca5aa6460e3285" name="data/generator/sfDoctrineRestGenerator/default/skeleton/config/generator.yml" role="data" /> 
     70   <file baseinstalldir="." md5sum="bf5f38d67568d73b8de7db565df839fd" name="data/generator/sfDoctrineRestGenerator/default/skeleton/config/view.yml" role="data" /> 
     71   <file baseinstalldir="." md5sum="5557288884e6a517bfb9b3176aa08567" name="data/generator/sfDoctrineRestGenerator/default/skeleton/lib/configuration.php" role="data" /> 
     72   <file baseinstalldir="." md5sum="571067e0d001cd630d9db7e6ae000fc2" name="data/generator/sfDoctrineRestGenerator/default/template/actions/actions.class.php" role="data" /> 
     73   <file baseinstalldir="." md5sum="9ea0d70a8d800ef4c3ebaf8027dcfdc3" name="data/generator/sfDoctrineRestGenerator/default/template/templates/indexSuccess.php" role="data" /> 
     74   <file baseinstalldir="." md5sum="70104ec7ef554b2e465b0161e3d6423e" name="lib/generator/sfDoctrineRestGenerator.class.php" role="data" /> 
     75   <file baseinstalldir="." md5sum="0bcc4d9f5ece25d0924d6483ec83112a" name="lib/generator/sfDoctrineRestGeneratorConfiguration.class.php" role="data" /> 
     76   <file baseinstalldir="." md5sum="d838617c91f6707a74a161f0cc5fc4fe" name="lib/serializer/sfResourceSerializer.class.php" role="data" /> 
     77   <file baseinstalldir="." md5sum="40a5a9dc54af40db49806e79d7314571" name="lib/serializer/sfResourceSerializerJson.class.php" role="data" /> 
     78   <file baseinstalldir="." md5sum="e793c3af427c1aa22b7ee33459fc9284" name="lib/serializer/sfResourceSerializerXml.class.php" role="data" /> 
     79   <file baseinstalldir="." md5sum="cab84c1da056bd2b0713271f69afd9de" name="lib/task/sfDoctrineRestGenerateModuleTask.class.php" role="data" /> 
     80   <file baseinstalldir="." md5sum="42e5bbfed0992e1aa954a1b91282d5d7" name="LICENSE" role="data" /> 
     81   <file baseinstalldir="." md5sum="5c51849f8551c90bb2d25029bd6c176a" name="README" role="data" /> 
     82  </dir> 
     83 </contents> 
     84 <dependencies> 
     85  <required> 
     86   <php> 
     87    <min>5.2.4</min> 
     88   </php> 
     89   <pearinstaller> 
     90    <min>1.4.1</min> 
     91   </pearinstaller> 
     92   <package> 
     93    <name>symfony</name> 
     94    <channel>pear.symfony-project.com</channel> 
     95    <min>1.3.0</min> 
     96    <max>2.0.0</max> 
     97    <exclude>2.0.0</exclude> 
     98   </package> 
     99  </required> 
     100 </dependencies> 
     101 <phprelease /> 
     102 <changelog> 
    124103  <release> 
    125104   <version> 
    126     <release>0.9.1</release> 
    127     <api>0.9.1</api> 
     105    <release>0.8.0</release> 
     106    <api>0.8.0</api> 
    128107   </version> 
    129108   <stability> 
     
    131110    <api>beta</api> 
    132111   </stability> 
    133    <date>2010-05-14</date> 
     112   <date>2010-05-09</date> 
    134113   <license uri="http://www.opensource.org/licenses/mit-license.html">MIT</license> 
    135114   <notes> 
    136   * fixed a typo in the previous release 
     115* REST module generation &quot;à la admin-generator&quot; 
     116* easy-to-customize generator.yml configuration file 
     117* possibility to embed related models 
     118* possibility to embed extra fields 
     119* validation of the GET and POST constraints using Symfony validators 
     120* ability to limit the number of results, with/out pagination 
     121* support for constraints unions (ie.,  http://api.example.org/city?city_id=12,13,14) 
     122* abstract and replaceable objects serialization 
    137123   </notes> 
    138124  </release> 
     
    149135   <license uri="http://www.opensource.org/licenses/mit-license.html">MIT</license> 
    150136   <notes> 
    151   * added a JSON serializer 
     137* added a JSON serializer 
    152138   </notes> 
    153139  </release> 
    154140  <release> 
    155141   <version> 
    156     <release>0.8.0</release> 
    157     <api>0.8.0</api> 
     142    <release>0.9.1</release> 
     143    <api>0.9.1</api> 
    158144   </version> 
    159145   <stability> 
     
    161147    <api>beta</api> 
    162148   </stability> 
    163    <date>2010-05-09</date> 
     149   <date>2010-05-14</date> 
    164150   <license uri="http://www.opensource.org/licenses/mit-license.html">MIT</license> 
    165151   <notes> 
    166   * REST module generation "à la admin-generator" 
    167   * easy-to-customize generator.yml configuration file 
    168   * possibility to embed related models 
    169   * possibility to embed extra fields 
    170   * validation of the GET and POST constraints using Symfony validators 
    171   * ability to limit the number of results, with/out pagination 
    172   * support for constraints unions (ie.,  http://api.example.org/city?city_id=12,13,14) 
    173   * abstract and replaceable objects serialization 
     152* fixed a typo in the previous release 
     153   </notes> 
     154  </release> 
     155  <release> 
     156   <version> 
     157    <release>0.9.2</release> 
     158    <api>0.9.2</api> 
     159   </version> 
     160   <stability> 
     161    <release>beta</release> 
     162    <api>beta</api> 
     163   </stability> 
     164   <date>2010-07-01</date> 
     165   <license uri="http://www.opensource.org/licenses/mit-license.html">MIT</license> 
     166   <notes> 
     167* Added events (thanks to Matthew Penrice) 
     168* Added a default_format parameter (thanks to Matthew Penrice) 
     169* Added PUT support 
     170* Switched to json as default serializer (faster and less verbose) 
     171* Improved the documentation 
    174172   </notes> 
    175173  </release>