In the Google Summer of Code Project "TYPO3 Flow meets Ember.js" we are trying to make the server-side framework TYPO3 Flow and the client-side framework Ember.js (using Ember Data as the persistence layer) work together easily through conventions and a powerful scaffolding mechanism. You can visit our website to see a more detailed concept.
Please refer to the overview gist that gives a brief overview of how things should work together.
We are facing a decision how we want to logically organize the server-side controllers responsible for handling the REST API requests. There are two direction:
- Generated controllers: One controller for each model that handles the requests for the specific model.
- Automagical controller: One controller that handles all requests for all models.
- Every provided part of REST API is represented in a separate controller file => It is very clear, which controllers/models are available and where they are located.
- Can easily add additional application specific methods to the API (eg. /last_posts)
- Will lead to many files containing the very same content.
- Need to configure routes for every controller/model. This will result in a huge Routes.yaml file that is difficult to manage.
- Seems to play nice with the idea of a conventional API => same API for every model
- Just one controller file for the whole REST API needed.
- Only need to configure the routes once.
- It is more difficult to understand which routes/models are available. (Could be tackled by providing an auto-generated API description)
- Can add additional application specific methods to the API (eg. /last_posts), but methods for different models need to be mixed together in a single controller file.
For each model the following code sample would be needed to provide Embers RESTAdapter methods. The controller inherits the needed standard functionality from a controller provided by our package.
<?php
namespace Mmitasch\Blog\Controller;
use TYPO3\Flow\Annotations as Flow;
/**
* This REST controller is made for handling ember-data DS.RESTAdapter calls for post models.
*
* @Flow\Scope("singleton")
*/
class PostController extends Radmiraal\Emberjs\Controller\RestController {
}
?>
For each model the following needs to be configured in Routes.yaml. The put, post, delete, option configuration are omitted for simplification purposes. Thus, an actual configuration would be 6 instead of the shown 2 configuration blocks.
-
name: 'REST API GET all posts'
uriPattern: 'rest/posts'
defaults:
'@package': 'Radmiraal.Emberjs'
'@controller': 'Post'
'@action': 'list'
'@format': 'json'
httpMethods: ['GET']
-
name: 'REST API GET post by Identifier'
uriPattern: 'rest/posts/{modelid}'
defaults:
'@package': 'Radmiraal.Emberjs'
'@controller': 'Post'
'@action': 'show'
'@format': 'json'
httpMethods: ['GET']
# put, post, delete, option configuration omitted for simplification purposes
The following code sample would be needed once to provide Ember's RESTAdapter methods for all models. The controller inherits the needed standard functionality from a controller provided by our package.
<?php
namespace Mmitasch\Blog\Controller;
use TYPO3\Flow\Annotations as Flow;
/**
* This REST controller is made for handling ember-data DS.RESTAdapter calls for ALL models.
*
* @Flow\Scope("singleton")
*/
class RestController extends Radmiraal\Emberjs\Controller\RestController {
}
?>
The following needs to be configured once in Routes.yaml. The put, post, delete, option configuration are omitted for simplification purposes. Thus, an actual configuration would be 6 instead of the shown 2 configuration blocks.
-
name: 'REST API GET all posts'
uriPattern: 'rest/{modelName}' # notice the dynamic route segment here
defaults:
'@package': 'Radmiraal.Emberjs'
'@controller': 'Rest'
'@action': 'list'
'@format': 'json'
httpMethods: ['GET']
routeParts:
'modelName':
handler: 'Radmiraal\Emberjs\Routing\ModelNameRoutePart'
-
name: 'REST API GET post by Identifier'
uriPattern: 'rest/{modelName}/{modelid}' # notice the dynamic route segments here
defaults:
'@package': 'Radmiraal.Emberjs'
'@controller': 'Rest'
'@action': 'show'
'@format': 'json'
httpMethods: ['GET']
routeParts:
'modelName':
handler: 'Radmiraal\Emberjs\Routing\ModelNameRoutePart'
# put, post, delete, option configuration ommited for simplification purposes
We tend to find the automagical controller solution more suited for a conventional API and much more concise. What do you think?
+1 aswell for the extendable version described by @cognifloyd.
I see multiple additional issues with the generated controller, not only:
Dry code and the verbose changes needed to make 1 global change aren't what any developer wants to do.