Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save manuelmitasch/5955312 to your computer and use it in GitHub Desktop.
Save manuelmitasch/5955312 to your computer and use it in GitHub Desktop.

Context

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.

Feedback wanted: Automagical controller vs. generated controllers

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:

  1. Generated controllers: One controller for each model that handles the requests for the specific model.
  2. Automagical controller: One controller that handles all requests for all models.

Generated Controllers

PRO

  • 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)

CONS

  • 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.

Automagical controller

PRO

  • 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.

CONS

  • 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.

Generated controllers example

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

Automagical controller example

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

Feedback Wanted!

We tend to find the automagical controller solution more suited for a conventional API and much more concise. What do you think?

@cognifloyd
Copy link

+1 automagical, but find a cleaner way to extend the controller on a per-model basis. So, let me generate controllers when I need to override something for a particular domain model object, but for the most part, use one automagical controller.

@frans-beech-it
Copy link

+1 for the combo @cognifloyd describes.

So if you allow Ember to access you domain model and there is no generated controller the Automagical controller will be used. If there is a generated controller that one is used.

@svparijs
Copy link

+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.

@manuelmitasch
Copy link
Author

Thanks for the great input!
It turned out to be quite easy to implement the best from both solutions.

I implemented an automagical controller that can be easily reused as a controller for a specific model. There you can implement any different behavior. After that you just create a new route in Routes.yaml with your new controller + action set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment