In the design of FLOW3's architecture we have taken great care to separate concerns and assign each part of the framework with well-defined tasks. The separation of concerns is an important principle of good software design and its most prominent representative probably is the Model-View-Controller pattern. MVC separates the business logic from the presentation by splitting up user interaction into three roles:
The model is an object which contains data and business logic of a certain domain. It doesn't contain any information about the presentation of that data, but rather defines the behaviour. In the FLOW3 project we prefer a special kind of model, the Domain Model.
The view represents the display of the model on the web or another output channel. Views only display data, they don't build or modify it.
The controller reacts on user input, selects and manipulates the model as accordingly, selects a view and passes it the prepared model for rendering.
This diagram outlines the collaboration between model, view and controller:
Design Patterns (and MVC is one of them) are not only great for solving reoccuring design problems in a structured manner - they also help you communicating software designs. The following patterns play an important role in FLOW3's MVC mechanism and might give you a better idea of the overall design:
Incoming requests are handled by a Request Handler which takes the role of a Front Controller.
Template View is the most commonly used pattern for views, but Transform Views and Two-Step Views are equally supported.
The preferred type of model is the Domain Model.
Let's start with an example before we go into greater detail of request handling and the internals of the MVC framework. The minimal approach is to create an Action Controller which just returns “Hello World!”. To begin with, we need to create some directories which contain the code of our FLOW3 package and eventually the controller class:
Packages/
Demo/
Classes/
Controller/
F3_Demo_Controller_Default.php
The Default Controller class looks as simple as this (leaving out the very recommended comments):
Example 1.30. Hello World! controller
class F3_Demo_Controller_Default extends F3_FLOW3_MVC_Controller_ActionController {
public function defaultAction() {
return "Hello World!";
}
}Provided that the web root directory of your local server points
to FLOW3's public/ directory, you will get the
following output when calling the URI
http://localhost/demo/:
Hello World!
Great, that was easy - but didn't we say that it's the view's responsibility to take care of the presentation? Let's create a simple PHP-based view for that purpose:
Packages/
Demo/
Classes/
Controller/
F3_Demo_Controller_Default.php
View/
Default/
F3_Demo_View_Default_Default.php
The view's code is equally trivial:
Example 1.31. Hello World! view
class F3_Demo_View_Default_Default extends F3_FLOW3_MVC_View_Abstract {
public function render() {
return "Hello World!";
}
}Finally our action controller needs a little tweak to return the rendered view instead of shouting “Hello World!” itself:
Example 1.32. Improved Hello World! controller
class F3_Demo_Controller_Default extends F3_FLOW3_MVC_Controller_ActionController {
public function defaultAction() {
return $this->view->render();
}
}As you have seen in the hello world example, conventions for the directory layout simplify your development a lot. There's no need to register controllers, actions or views if you follow our recommended file structure. These are the rules:
Controllers are located in their own
directory Controller just below the
Classes directory of your package. They can
have arbitrary names while the Default
controller has a special meaning: If the package was specified in
the request but no controller, the Default
controller will be used.
View classes are situated below a
View directory. They are grouped by controllers
and named by their actions: Directories below the
View folder are named after the controller and
the view's class name refers to the action.
This sample directory layout demonstrates the above rules:
Example 1.33. Sample file structure
Packages/
Demo/
Classes/
Controller/
F3_Demo_Controller_Default.php
F3_Demo_Controller_Customer.php
F3_Demo_Controller_Order.php
View/
Default/
F3_Demo_View_Default_Default.php
Customer/
F3_Demo_View_Customer_Default.php
F3_Demo_View_Customer_List.php
F3_Demo_View_Customer_Details.php
Order/
F3_Demo_View_Order_List.php
Adhering to these conventions has the advantage that views for example are resolved automatically. However it is possible (and not really difficult) to deviate from this layout and have a completely different structure.
No matter if a FLOW3 application runs in a web context or is launched from the command line, the basic workflow is always the same: The user request is analyzed and forwarded to an appropriate controller which decides on which actions to take and finally returns a response which is handed over to the user. This section highlights the flow and the collaborators in the request-response machinery.
A sequence diagram is worth a thousand words said my grandma, so let's take a look at the standard request-response workflow in FLOW3:
As you see, there are a lot of parts of the framework involved for answering a request - and the diagram doesn't even consider caching or forwarding of requests. But we didn't create this structure just for the fun of it - each component plays an important role as you'll see in the next sections.
The request handler takes the important task to handle and respond to a request. There exists exactly one request handler for each request type. By default web and command line requests are supported, but more specialized request handlers can be registered, too.
Before one of the request handlers comes to play, the framework needs to determine which of them is the most suitable for the current request. The request handler resolver asks all of the registered request handlers to rate on a scale how well they can handle the current raw request. The resolver then chooses the request handler with the most points and passes over the control.
When a request handler receives a raw request, it needs to build a request object which can be passed to the dispatcher and later to the controller. The request building delegated to a request builder which can build the required request type (ie. web, CLI etc.).
The building process mainly consists of
create a new request object
set some request-type specific parameters (like the request URI for a web request)
determine and set the responsible controller, action and action arguments
Especially the last step is important and requires some more or less complex routing in case of web requests.
Requests which were built by the request builder usually fit the most common needs. For special demands it is possible to postprocess the request object before it is sent to the dispatcher. Request processors can be registered through the Request Processor Chain Manager and are - as the name suggests - invoked in a chain.
The final task of the MVC framework consists in dispatching the request to the controller specified in the request object. The request dispatcher will try to call the action specified in the request object and if none was specified fall back on a default action.
There are more features planned for the dispatcher, but at the time of this writing they have not yet been implemented.
FLOW3 supports the most important request types out of the box.
Additional request types can easily be implemented by extending the
F3_FLOW3_MVC_Request class and registering a
request handling which can handle the new request type (and takes care
of building the request object). Here are the request types which come
with the default FLOW3 distribution:
Web requests are the most common request types. Currently only the basic features are implemented, but further options - especially for the web response - are in the pipeline.
Requests from the command line are recognized by the used SAPI (Server Application Programming Interface). This request type is basically the same as the generic request type and mainly exists as a marker.