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
andresponse
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 singleadd
oredit
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 inload
). 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.