Logo der TYPO3 Usergruppe Leipzig

Einleitung Model View Controller Konzept

Seit kurzem stehen auf typo3.org die Extensions lib und div im TER zum Download bereit, welche das Model-View-Controller Konzept in TYPO3 für die Extensionentwicklung bereitstellt. Diese beiden Extensions(tx_lib, tx_div) sollen in Zukunft eine Alternative zur pi_base bereitstellen. Die Entwicklung wird von der TYPO3-Association geleitet (Newsgroup:Extension-Coordination-Team), Hauptentwickler ist Elmar Hinz.

Warum MVC?

  • Alle Teile (Model, View, Controller) sind unabhängig und austauschbar
  • "Zwingt" den Programmierer sauber zu coden
  • Jeder Code wird nur einmal geschrieben
  • erhöht Übersicht und Ordung durch Reduzierung der Komplexität
  • Fachkräfte können optimal Ihrer Fähigkeiten eingesetzt werden
  • Hoher Wert durch Wiederverwendbarkeit aller Komponeten

Warum MVC und Typo3?

  • ermöglicht Plugins in TYPO3 5.0 relativ einfach zu migrieren
  • löst Probleme beim Erweitern von Extensions mittels XCLASS
  • soll Entwicklern einfacher die TYPO3-Core-Engine Programmierung ermöglichen, da TYPO3 5.0 selbst auf einem eigenen MVC-Konzept aufbauen wird. FE-MVC Plugins werden über einen Dispatcher mit dem TYPO3 Framework kommunizieren
  • flexibel durch "Addons" erweiterbar
  • für reine TYPO3-Programmierer ein Grund sich mit PHP5 zu befassen
  • mehr Spaß bei der Extensionentwicklung

Allgemeines

MVC Datenfluss

Das Konzept wurde zuerst von Trygve Reenskaug als Seeheim-Modell beschrieben und wurde für Benutzeroberflächen in Smalltalk genutzt. Das war wohl in den 70ern, und seit dem hat es sich als Standard bei komplexen Softwaresystemen etabliert. TYPO3 ist, wie wir alle wissen, auch eine mittlerweile komplexe Software und arbeitet praktisch auch schon mit diesem Konzept. Bei Desktopanwendungen spricht man meist von "Events". Das kann ein Schieberegler, Mausklick etc. sein, in Webanwendungen von "Actions", das kann ein Formular oder URL-Parameter sein. Meist spielen ein View und ein Controller zusammen, wohingegen das Model nix von beiden weiß.

Der View

  • Ein View ist meist eine GUI-Komponete, z.B. eine Webseite mit Linkparametern und Formularen, mit denen der Besucher kommunizieren kann.
  • Der View kennt seinen Controller und auch sein Model
  • Er kann Informationen aus dem Model abrufen und darstellen
  • Der View erhält bei Änderungen, bzw. Ereignisse vom Model, beispielsweise ob ein gesuchter Datensatz, wie z. B. ein Suchbegriff aus einem Suchformular, gefunden wurde oder nicht.
  • Eine Änderung des Views kann auch direkt vom Controller angestoßen werden. z. B. bei einem Loginformular könnte der Controller entscheiden, je nachdem ob man eingeloggt ist, welcher View, also die Anzeige/Ausgabe auf der Seite, dargestellt werden soll.
  • Ein View stellt also meist Aspekte des Zustandes eines Model dar.

Das Model

  • Das Model weiss nichts von View oder Controller.
  • Es stellt aber Operationen bereit, die vom Controller genutzt werden können, z. B. eine Suchfunktion.
  • Er stellt Zustandsinformation für den View bereit, z. B. die aktuellen Datensätze des Suchergebnisses.
  • Das Model repräsentiert also die Zustände und Operationen der Anwendung.

Der Controller

  • Die Anwendungslogik sollte nicht im Controller, sondern im Model implementiert werden.
  • Der Controller kann als dünner Adapter zwischen Ereignissen auf der Webseite und den Operationen des Models und des Views angesehen werden.
  • Er ordnet also nur zu, welche Action welche Operationen auslöst.
  • Die vom View dargestellten GUI-Komponenten, also Webseite mit Links und Formularen, machen Informationen über den Zustand von  Model und View für den Benutzer/Besucher nicht einfach nur sichtbar, sondern erlauben ihm auch durch klicken auf der Webseite Actions mit Parametern zu schicken.
  • Der Controller interpretiert solche Ereignisse (Actions) der Webseite, die der ihm zugehörige View erzeugt hat, durch Operation des Models und des Views, welche ja beiden bekannt sind.

Typo3 Extension mit dem MVC-Modell

MVC Konzept in Typo3
  • Der Besucher/Benutzer fordert aus einer View-Komponente eine Action an.
  • Diese Action kann auch Parameter wie ein Suchwort beinhalten.
  • Wird keine Action übergeben hat jeder Controller eine defaultAction.
  • Die Action ist praktisch die alte main function in Extensions mit der pi_base.
  • Der Controller Action wird instanziert und seine Action aufgerufen.
  • Dabei werden die Parameter (Formulare, URL-Parameter) übergeben.
  • Zusätzlich wird Typoscript für die Formatierung übergeben.
  • Optional kann hier auch eine Pluginkonfiguration übergeben werden.
  • Dann übergibt der Controller, je nachdem, View und Model die Parameter/Config und gibt den nächsten View zurück.
  • Beim Lesen wird dem View ein Model übergeben.
  • Beim Schreiben wird dem Model ein View übergeben.
  • Die Seite/View wird ausgegeben.

Eigene Extensions mit lib/div

  • Zuerst werden im EM natürlich die Extensions tx_lib und tx_div installiert.
  • Dann im Kickstarter eine neue Extension mit den benötigten Tabellen anlegen.
  • Allerdings ohne FE-Plugins!
  • Wir nennen unsere Extension "mvclogin" und brauchen keine Tabellen.
  • Im Extensionordner werden die Ordner controllers, models, views und static erstellt.
  • Danach erstellen wir uns einen Controller folgendermaßen: class.tx_###EXTKEY###_controllers_###NAME###.php
  • Das ist praktisch unsere main Funktion, wie man es von der pi_base kennt.

Beispiel Login Controller

class.tx_mvclogin_controllers_login.php
//hiermit ermöglichen wir das Vererben des "Hauptcontrollers" 
require_once(t3lib_extMgm::extPath('div') . 'class.tx_div.php');
tx_div::load('tx_lib_controller');
 
//unsere Klasse vererbt Funktionen die das MVC-Konzept integrieren
class tx_mvclogin_controllers_login extends tx_lib_controller{
  //der Exension key
  var $extKey = 'mvclogin';
  //die Action wenn keine Action vom View aufgrufen worde
  var $defaultAction = 'login';
  //da lib/div momentan noch recht schmal im Funktionsumfang, hier ein paar           Hilfsfunktionen
  var $functions;
 
//checkt den login state und setzt die defaultAction
function tx_mvclogin_controllers_login() {
  if($GLOBALS['TSFE']->fe_user->user['username']) $this->defaultAction = 'logout';
  //instanziere Hilfsfunktionen
  $this->functions = tx_div::makeInstance('tx_mvclogin_controllers_functions');
  }
 
//zeigt ein loginform an 
function loginAction($out, $conf, $parameters)  {
  //xml von pluginconfig wird hinzugefuegt
  $conf = $this->functions->flexToConf($conf,$this->cObj->data['pi_flexform']);
  //wir instanzieren den view
  $view = tx_div::makeInstance('tx_mvclogin_views_login');
  // uebergeben dem view die configuration
  $view->setConfiguration($conf);
  //setzen die parameters(get/post) 
  $view->setParameters($parameters);
  und setzen den pfad wo der view die templates suchen soll
  $view->setTemplatePath($this->extPath() . 'views/templates/login/');
  //mit render wird dann die ausgabe zurueckgegeben
  return $view->render('login');
  }
 
function logoutAction($out, $conf, $parameters)  {
  $conf = $this->functions->flexToConf($conf,$this->cObj->data['pi_flexform']);
  $view = tx_div::makeInstance('tx_mvclogin_views_login');
  $view->setConfiguration($conf);
  $view->setParameters($parameters);
  $view->setTemplatePath($this->extPath() . 'views/templates/login/');
  return $view->render('logout');
  }
 
  function forgotAction($out, $conf, $parameters)  {
  $conf = $this->functions->flexToConf($conf,$this->cObj->data['pi_flexform']);
  //wir brauchen ein model um auf die fe_user tabelle 
  $model = tx_div::makeInstance('tx_mvclogin_models_feuser');
  //setzten die pid
  $model->setFolder((int)$conf['sDEF']['userFolder']);
  $model->setLanguage(0);
  //wenn eine emal vorhanden aus paramertrn sicht das model nach dem fe_user
  if(t3lib_div::validEmail($this->parameters['email'])) $result = $model->select(null,null,$this->parameters['email']);
  //wir holden uns den view
  $view = tx_div::makeInstance('tx_mvclogin_views_login');
  $view->setConfiguration($conf);
  $view->setParameters($parameters);
  //wir uebergeben dem view das db result
  $view->exchangeArray($result); 
  $view->setTemplatePath($this->extPath() . 'views/templates/login/');
  return $view->render('forgot');
  }
}

Extensions als FE-Plugin konfigurieren

  • Wir denken uns mal, dass wir schon den View und unser Model haben ;-)
  • Im ordner static  erstellen wir uns die Dateien setup.txt und constants.txt
  • In diesen beiden Dateien können wir der Extension sagen wie das Plugin heißt und welchem Controller es zugeordnet ist.
  • Hier können auch TypoScript für die Formatierung, Stylesheet etc. übergeben werden.
  • Jetzt die ext_tables.php öffnen und mittels t3lib_extMgm::addPlugin() unser Plugin registrieren. Ab jetzt können wir es als Plugin auf einer Seite einfügen.
  • Ebenfalls in der  ext_tables.php mit t3lib_extMgm::addStaticFile() können wir unser Typoscript aus dem static Ordner für Root Template übergeben.
  • Im EM die Extension installieren.
  • Im Root Template der Hauptseite unser Typoscript hinzufügen.
  • Extensions als Plugin auf der Seite einfügen, fertig.

Beispiel setup.txt

setup.txt
tt_content.list.20.mvclogin_login       =< plugin.tx_mvclogin_login
plugin.tx_mvclogin_login.userFolder  = {$plugin.tx_mvclogin.userFolder}
plugin.tx_mvclogin_login             = USER_INT
plugin.tx_mvclogin_login.includeLibs = EXT:any_mvclogin/controllers/class.tx_mvclogin_controllers_login.php
plugin.tx_mvclogin_login.userFunc    = tx_mvclogin_controllers_login->main
plugin.tx_mvclogin_login._CSS_DEFAULT_STYLE (
  form.tx_mvclogin_login label{ 
			font-size: 9px;
  }
  form.tx_mvclogin_login input {
			width: 78px;
			margin: 1px 0 1px 0;
			padding: 1px 0 1px 0;
			border: 1px solid #dbdbed;
			font-size: 9px;
			color: #333366;
  }
  form.tx_mvclogin_login submit{
			width: 30px;
			margin: 0;
			padding: 0;
			font-size: 10px;
			color: #333366;
			background: #dbdbed;
			border: 1px solid #dbdbed;
  }
)

BEISPIEL CONTANTS.TXT

constants.txt
//wo soll unser login nach user suchen
plugin.tx_mvclogin.userFolder = 204
//hier könnte man auch wrapelemente definieren...

Auszug ext_tables.php

ext_tables.php
t3lib_div::loadTCA('tt_content');
// list plugins in plugin selection
t3lib_extMgm::addPlugin(Array('LLL:EXT:mvclogin/locallang_db.php:loginPluginLabel','mvclogin_login'),'list_type');
//kosmetik
$TCA['tt_content']['types']['list']['subtypes_excludelist']['mvclogin_login']='layout,select_key';
// list static templates in templates selection
t3lib_extMgm::addStaticFile('mvclogin','static','MVC Login TS');

Wie gehts weiter?

Um nun selbst mit diesen Extensions zu arbeiten sollte man als Erstes die Quellcodes von tx_lib und tx_div studieren. Desweiteren gibt es auch eine Demoextension, mit Namen tx_articles, welche das MVC-Konzept am Beispiel zeigt. Sehr zu empfehlen, da bisher die einzige Dokumentation, ist das Buch "Typo3 4.0 Handbuch für Entwickler". Hier werden detailliert die Klassen erklärt, und am Beispiel von tx_articles eine Extension Schritt für Schritt erstellt. Sehr zu empfehlen ist auch die Newsgroup des ECT. Lib/Div sind zwar im Moment noch sehr schmal im Funktionsumfang, aber es lohnt sich schon jetzt damit zu arbeiten, da man mit dem MVC-Konzept die Power von PHP5 erst richtig nutzen kann.

 

Autor: Frank Thelemann

TUGLE News

TYPO3camp Bremen 2014

Moin Moin ... ... zum ersten TYPO3camp Bremen 2014. Das TYPO3Camp ist ein Themen Camp im Format...

TYPO3 Developer Days 2014

Komm wir fahren nach Amsterdam ...  Die Schedule für die diesjährigen Developer Days findet...