Concept Draft for TYPO3 4.3+ MVC

If you have comments to this document, please post them into the newsgroup typo3.projects.typo3v4mvc. We are very curious about your comments and we really need your participation!
Thanks, the project members

Goals

One of the main purposes of supporting the Model-View-Controller (MVC) pattern in the 4.x branch is to ease the transition to TYPO3v5. There are a lot of different flavours of the MVC patterns out there. We decided to stick as close as possible to the paradigms of FLOW3.

Many of the (extension) developers will get in touch with MVC the first time. Thus our main goal is to ease the 'mental' transition. The concepts of the MVC framework should therefore be visible and not hidden behind too much 'magic'. But we also want to provide comfortable support for experienced developers.

In short we plan to
  • set up a clean structure of controller, domain model and views,
  • write a new kickstarter to produce a clear structure of mvc-classes, files and folders (and support domain driven development)
  • support extension developers by documentation and and example code.

Have a look at the two UML diagrams which are linked in the bottom for brief overview.

The new way to code extensions

The MVC framework for extensions provides clear structured code and files. If you follow some conventions it does a lot of work for you. In the end you will be able to code a lot more efficient than the common pi based extensions.

Extension Directory Layout

The directory structure of an extension follows a certain convention which has the advantage that you don't need to care about any extension-related configuration. If you put your files into the right directories, everything will just work. (That's "convention over configuration").

The suggested directory layout of an extension is as follows:

  • [extension key]/
    • Classes/ This directory contains the actual source code for the extension. Extension authors are free to add (only!) class or interface files directly to this directory or add subdirectories to organize the content if necessary.
      • Controller/ The controller invoked by the dispatcher
      • Domain/
        • Model/ The domain model classes
        • Service/ The service oriented classes of the domain (not directly related to the topmost Services folder
      • View/ The views
        • Helper/ The view helper (e.g. a class to generate a gravatar)
    • Configuration/ All kinds of configuration which are delivered with the package reside in this directory. The configuration files are immutable and must not be changed by the user or administrator. The most prominent configuration files are the Settings/setup.txt file which may be used to configure the extension which contains general user-level settings.
    • Documentation/ Holds the extension documentation. The format for manuals is DocBook OpenOffice (sorry ;-)).
    • Modules/ Holds the well known backend modules in subdirectories (as a kind of legacy code that can't be merged into the mvc classes folder)
    • Services/ Holds the services in subdirectories (as a kind of legacy code that can't be merged into the mvc classes folder)
    • [More top level directories with legacy code]
    • Resources/ Contains static resources the package needs, such as library code, template files, graphics, ... .
      • Private/ This folder holds files that should not be delivered directly to the FE
      • Public/ This folder holds templates, images, language files, PDF, Flash, CSS and other files that will be delivered to the client.
    • Tests/ Holds the unit tests for the package. Test cases will be recognized by the Testing package if they follow the required naming convention.

Naming Conventions

  • All classes must be prefixed with Tx_ (UpperCamelCase)
  • All class names and file names are written in UpperCamelCase
  • The filename consists of the last part of a class name (after the last "_") follwed by the ".php". Example: The class "Tx_BlogExample_Controller_PostController" can be found in the file "htdocs/typos/typosconf/ext/blogexample/Classes/Controller/PostController.php"

How to configure the extension to invoke the MVC framework

To add a plugin you have to call the dispatcher of the framework. The dispatcher calls the appropriate controller of your extension and hands over the settings. First you have to include the dispatcher (first line). For each plugin you have to define an extension name, a plugin key, and one or more controller. The configuration of a controller must define a controller name and actions that are allowed to be performed via the plugin. The first action of the first controller will be called as default if no other is available. You can also specify non cachable actions ("nonCachableActions = search"). The following plugin is able to perform the defined actions of the Blog Controller, the Post Controller, and the Comment Controller (normally we don't want to have such an omnipotent plugin; just for testing).

includeLibs.tx_extbase_dispatcher = EXT:extbase/class.tx_extbase_dispatcher.php

tt_content.list.20.blogexample_blog = USER
tt_content.list.20.blogexample_blog {
    userFunc = tx_extbase_dispatcher->dispatch
    pluginKey = blog
    extensionName = BlogExample
    controllers {
        10.controllerName = Blog
        10.actions = index,show,new,create,delete,edit,setup,test
        20.controllerName = Post
        20.actions = index,show
        30.controllerName = Comment
        30.actions = create
    }
}

This configuration will be done by having the following line in ext_localconf.php.

t3lib_extMgm::addExtbasePlugin(...);

The Framework: Technical Background

Dispatcher

  • The Dispatcher is the "bridge" from the TYPO3 v4 framework to the MVC extension and resides in the TYPO3 v4 framework.
  • By default, the dispatcher of the MVC framework is called - but you can override it by changing the plugin configuration.

Control flow:

  1. create a request object
  2. select the controller handling the request
  3. call the controller's injectSettings() method with the TypoScript configuration of the extension
  4. call the controller's processRequest($request, $response) method.
  5. take the response object, check if headers should be set, and finally return the content to the TYPO3 framework.

A sequence diagram of the MVC cycle of a single extension can be found at:
http://www.typoplanet.de/mvc/mvc_sequence_v2.png

For a sequence diagram of FLOW3 see:
http://flow3.typo3.org/fileadmin/manual/MVCFramework_RequestResponseWorkflow.png

Request Object

  • Contains name of controller / action
  • Contains raw arguments (POST/GET)

Response Object

  • DONE: A new response object is created on each Dispatcher invocation, and it is destroyed after the call to the controller.

Controller

  • DONE: We have the same AbstractController, and ActionController class hierarchy as in FLOW3

ActionController

  • DONE: The Dispatcher calls the processRequest(Request, Response) method on the ActionController
  • DONE: Before $this->arguments is filled the arguments are validated. Thus, the data mapping is started here. See section about data mapping for details.

Data Mapping

  • Implement Validation of incoming arguments from $_GET and $_POST
  • We don't implement Data mapping of complex objects yet.
  • Control flow:
    • Find out which arguments are registered (either by reflection, or by RegExps) -> TODO: Make them cachable
    • For each argument:
      • Check if the argument is available in Request -> if yes, trigger validation

Example: How are arguments defined:

/**
* @param integer $id ID of record to save
* @param string $name Name of record to save
* @param string $description Description of blog posting
*/
public function saveAction($id, $name, $description) {
}
  • DONE: Evaluate the variable names of the phpdoc above If you specify.
  • DONE: Validation Rules

Model

  • DONE: The model consists of Domain Classes (all classes belonging to your application domain), and appropriate Repositories through which you can find the domain classes.

Domain classes

  • DONE: The model is based on the "Domain Object" design pattern
  • DONE: Every domain model which handles database data will be derived from an Entity or ValueObject class, which themself implement AbstractDomainModel
  • DONE: The AbstractDomainModel holds methods to monitor if an object is dirty (changed after beeing reconstituted from database) and some other framework related methods
  • TODO: If the property is boolean, the getter will be called isXXXX (or hasXXXX if it tells if a property has a value, eg. "hasCustomerNumber()" for checking if customerNumber !== NULL)

Repository

  • DONE: The repository encapsulates all data access logic and hands it over to a Object Relational Mapper
  • There will be one repository for each domain class
  • DONE: All repositories inherit from an AbstractRepository
  • DONE: The AbstractRepository implements the following magic methods:
    • findByPROPERTY('foo') (returns objects that matches PROPERTY = "foo")
    • findOneByPROPERTY (returns one object that matches PROPERTY = "foo" - the first object found)
  • There should be no SQL calls inside any domain model
  • all SQL should be written in the appropriate Repository.

Foreign Keys

  • DONE: We support 1:n and m:n relations (with relation tables) but we do not support comma separated relations
  • TODO: The AbstractDomainModel provides some methods for Lazy Loading
  • DONE: All Domain Objects are saved and updated recursively, starting from the Aggregate Root Objects

View

  • TODO: Concept will be fully taken from FLOW3.
  • TODO: There is an AbstractView, which implements a render() method.
  • DONE: All other views extend the AbstractView.
  • DONE: Fluid will be integrated here (now already for 4.3 as discussed on the snowboard tour)
  • DONE: To put a variable into the view: $this->view->assign(string variableName, Object objectValue) (Do we want to call it like this?)

Kickstarter

  • TODO: Domain Model should be autogenerated
    • Do not ask for Database columns, but instead ask for Objects with properties AND Behaviors
    • Getters / Setters should be autogenerated (With fluent interface(?))
    • stub methods for each "behavior" should be autogenerated as well.
  • TODO: We generate Repositories for every Domain Model by default.
  • TODO: Scaffolding for:
    • Listing
    • New Entries
    • ...
  • TODO: Connection to the from project
  • TODO:Exceptions: The kickstarter generates one exception per extension, which extends the most generic exception inside TYPO3 v4.

Frontend Plugin

  • DONE: The extension author will define entry points consisting of a Controller and an Action name.
  • DONE: Each controller / action combination which is defined will be visible as one plugin inside the "Insert Plugin" Content element (see above)
  • DONE: Maybe we should enable multiple "views" (controller/action) inside a plugin (like those provided by tt_news or cal)
    • DONE: This may lead to a caching issue: if one of the actions is not cachable, the whole plugin will be not cached

Caching

  • DONE: The dispatcher FE plugin is defined as USER, so by default, it is cachable.
  • DONE: By default, all actions are cached.
  • TODO: The actions not to be cached can be specified with the method t3lib_extMgm::addExtbasePlugin().

Example

Inside ext_localconf:php:

t3lib_extMgm::addExtbasePlugin('theblogplugin', $_EXTKEY, 'Blog', 'list,edit', 'edit');

Inside the controller:

class Tx_BlogExample_Controller_BlogController extends Tx_Extbase_Controller_ActionController {

public function listAction(...) {
... // is cached
}

public function editAction(...) {
... // is NOT cached
}
}

Documentation

  • TODO: We'll write a Blog Tutorial as well
  • Documentation TYPO3 v4 and FLOW3 MVC uses the same examples

MVC_Kickoff_Karlsruhe_Notes_1.jpg (1.1 MB) Sebastian Kurfuerst, 2008-12-15 14:05

MVC_Kickoff_Karlsruhe_Notes_2.jpg (1.2 MB) Sebastian Kurfuerst, 2008-12-15 14:05

MVC_UML_CallFlow.png (45.9 kB) Sebastian Kurfuerst, 2008-12-15 14:05

MVC_UML_ControllerHierarchy.png (189.3 kB) Sebastian Kurfuerst, 2008-12-15 14:05

typo3v4-mvc-uml.zip - Editable BOUML UML Project (24.7 kB) Sebastian Kurfuerst, 2008-12-16 14:51