Controllers

Note: these docs are for the Standard Router implementation, provided with FW Api. If you are using a custom router implementation, this will likely not apply.

Before explaining how to map URLs to Controllers, it is first helpful to explain what exactly a Controller is and how it works within both MVC structure and the FW Api architecture.

Controllers in FW accomplish essentially the same function as the controllers in a typical MVC architecture: they get data from the model, and present it to the view. The view can also submit data/actions to the controller, and the controller decides what to do with it.

Controller classes themselves are normally groups of related functionality. For example, if you had a blog app, you might have a "Posts" controller that handles all actions related to posts.

If using the standard implementation of the Router module, all controller classes must extend the FW\Router\Standard\AbstractController class. It provides essential setup functions that are extremely helpful when programming your controllers.

A basic controller might look something like this:

// your-app/server/src/Controller/SomeController.php

// controllers should go in this namespace unless you define
// a custom namespace.
namespace YourApp\Controller;

class SomeController extends \FW\Router\Standard\AbstractController {

}

Actions

"Actions" in a controller are specific methods in the class that handle different functionality. In most controllers you will have basic ones that handle retrieval and updating of data. Many of the applications built by the Benner Library team use the "BREAD" method (browse, read, edit, add, delete) for defining controller actions.

To use the blog app example, you might have a controller called "Posts", and within that controller you would have several methods to get and edit data for posts. A browse action could return all of the posts, or a subset of all of the posts, whereas an add action would save a new post to the database. Depending on how you map your routes (see the mapping section next), the "posts" controller would be roughly converted into several api urls:

  • GET '/api/posts/' - Would call the browse action
  • POST '/api/posts/' - Would call the add action
  • GET '/api/posts/tag/some-tag/' - Could call a method on the controller called "tag" which could return all the posts for a particular tag.

For those routes, you'd have a controller that looks something like this:

// your-app/server/src/Controller/Posts.php
namespace YourApp\Controller;

class Posts extends \FW\Router\Standard\AbstractController {

    public function browse() {
        // handles the browse action
    }

    public function add() {
        // handles the add action
    }

    public function tag($tagName) {
        // handles the tag action
    }
}

Controller Properties

  • config

The controller has access to the global FW config property. See the config documentation for more information.

  • request and response

The controller also has access to both the request and the response properties of the Slim router. See the request and the response docs for more information.

Note: you shouldn't need to use these a lot, but they are there in case you need them.

  • adapter

The controller has access to the adapter that is part of the data module. See the data module documentation for more information.

  • view

The controller has access to the view module through the view variable. See the view module documentation for more information.

Controller Methods

There are a few predefined methods for you which can be used or overridden. When calling these methods you can call them within Controller class which extends AbstractController by using $this->methodName($args);.

  • checkObject This method takes two arguments, and is designed to take an array that has been sent from Ember and turn it into one that can be added to the database through the Adapter. This function is used in practically every single add or edit function. Ember sends the data for a model over as an associative array beginning with the model name, for example:
[
    "user" => [
        "nameFirst" => "John",
        "nameLast" => "Smith",
        "email" => "jrsmith@domain.com"
        // ...
    ]
];

So then, on the serverside, we want to do two things: make sure that we are receiving a user object in the first place. Then also, get the $object['user'] to be set to $object because that is what we care about anyway. This can be done by calling $object = $this->checkObject($object, "user");. This will throw an error if there is not a key for user (thus not passing a user object), and if there is will just return it like so:

[
    "nameFirst" => "John",
    "nameLast" => "Smith",
    "email" => "jrsmith@domain.com"
    // ...
];
  • afterLoad This function is only ever called internally, but is designed so that you can override it if you need something additional to be set up whenever a controller loads (by default the above properties are setup in load). I have never seen a use case for this function, but it is there to make it easy for you to load something else for your specific controller.

More information

You can find more information about how to setup a generic controller when you use RoutingUtils::http function to setup the Router.php file, in the example described here. This will help you when formatting your BREAD requests within the controller, or setting up default parameters (such as allowedOptions or includes array).

You can also find some more information about Controllers within the Group Control Dependent Apps (which are currently all of our apps), in the documentation about the AuthController class. This is because all of our Controllers extend AuthController from Group Control, which in turn extends AbstractController from the FW API.