Tutorial: Teil 4 - Wir implementieren einen Layouter

BasicAvid

Member
Hallo,

nachdem im Teil 3 dieser Tutorialserie die Frage aufkam, wie man nach dem MVC-Prinzip dynamische Tables usw. ausgibt, hab ich mir gedacht, "Da muss ich mal ein weiteres Tutorial drüber schreiben".

Wer nicht weiß was MVC ist oder was es bedeutet, der kann sich das ganze mal auf Wikipedia anschauen.

Bei unserem kleinen Framework wäre dann das Template = View, die Action = Controller und z.B. die DBMapper (zu denen kommen wir in einem weiteren Tutorial) = Model. Um jetzt z.B. die Daten aus einer Datenbank als Tabelle ins Template zu bekommen, greifen wir auf Layouter zurück.

Aber was genau macht der Layouter?
Der Layouter nimmt die Daten, die z.B. aus einer DB kommen, entgegen und rendert nun z.B. eine Tabelle daraus und übergibt diese ans Template.

Was brauchen wir also alles?
Wir brauchen eine neue Klasse, diese nennen wir TestLayouter.class.php und legen Sie ins Core Verzeichnis.

Hier mal der Source der Klasse, ich hab die Klasse so einfach wie möglich gehalten. Es werden also keine Requestdaten oder so verarbeitet.

TestLayouter.class.php
PHP:
class TestLayouter {
    
    // Hier werden die Daten (Dataset, Array, ...) gespeichert
    private $data;
    
    // Der Tabelle geben wir natürlich eine ID, so hat man es später
    // beim einbau einer Paginierung einfacher
    private $id;
    
    // Hier werden die Outputdaten gespeichert
    private $buffer;
    
    /**
     * Der Konstruktor, wurde keine Id übergeben, so wird eine Exception
     * ausgelöst. Da es vorkommen kann, dass eine Tabelle noch keine Daten
     * hat, wird hier keine Exception ausgelöst.
     */
    public function __construct($data, $id) {	
	$this->data = $data;
	if (empty($id)) {
	    throw new Exception('Keine Id übergeben!');
	}
	$this->id = $id;
    }
    
    /**
     * Die toString Methode wird aufgerufen, wenn man das Objekt mit
     * z.B. echo new TestLayouter($data, 'test'); aufruft.
     * In dieser Methode wird die Tabelle erzeugt.
     */
    public function __toString() {
	if (empty($this->data)) {
	    return "<p>Keine Daten zum Anzeigen gefunden!</p>";
	}
	$this->openTable();
	$rowCnt = sizeof($this->data);
	for ($row = 0; $row < $rowCnt; $row++) {
	    $this->openRow();
	    $columnCnt = sizeof($this->data[$row]);
	    for ($col = 0; $col < $columnCnt; $col++) {
		$this->writeColumn($this->data[$row][$col]);
	    }
	    $this->closeRow();
	}
	$this->closeTable();
	
	return $this->buffer;	
    }    
    
    /**
     * Wir öffnen die Table und setzen die ID
     */
    private function openTable() {
	$this->buffer = "<table id='" . $this->id . "'>\n";
    }
    
    /**
     * Zum öffnen einer Zeile(Row)
     */
    private function openRow() {
	$this->buffer .= "\t<tr>\n";
    }
    
    /**
     * Zum schreiben einer Spalte(Column)
     */
    private function writeColumn($value) {
	$this->buffer .= "\t\t<td>" . $value . "</td>\n";
    }
    
    /**
     * Schließt die Zeile
     */
    private function closeRow() {
	$this->buffer .= "\t</tr>\n";
    }
    
    /**
     * Schließt die Table
     */
    private function closeTable() {
	$this->buffer .= "</table>\n";
    }
    
}

Ok, das war die TestLayouter-Klasse, kommen wir nun zur benutzung der Klasse.
Dazu brauchen wir folgende Dateien aus dem letzten Tutorial:
Main.class.php
Main.tpl.php

Wir öffnen die Main.class.php, und erweitern die Klasse so, dass Sie danach folgendermaßen ausschaut.

PHP:
class Main extends Action {

	public function __toString() {
		return 'Die Main Action';
	}

	public function run() {
		AutoLoader::loadPackage('core\TestLayouter.class.php');
		$tpl = new Template();
		
		$text = 'Dies ist ein Text der dem Template übergeben wurde!';
		
		$tpl->assign('text', $text);
		
		$data = null;
                // Hier wird nur schnell zu Testzwecken das Array mit 10x10 Spalten befüllt
		for ($i = 0; $i < 10; $i++) {
			for ($j = 0; $j < 10; $j++) {
				$data[$i][] = ($i+1) * ($j+1);
			}
		}
                // Hier übergeben wir eine Instanz des Layouters an das Template
		$tpl->assign('table', new TestLayouter($data, 'test'));
		$tpl->display('main.Main');
	}	
}

Gut gut, jetzt haben wir zwar den Layouter in die Action eingebaut, aber ausgegeben wird deshalb auch noch nichts.
Deshalb öffnen wir nun das Template und erweitern es um ein einfaches echo $this->get('table');

Das Template sieht dann folgendermaßen aus:
PHP:
<?php
$this->includeAction('main.IncMainHeader');
?>

<h1>Dies ist unser erstes Template!</h1>

<p>
Hier wird dann ein Text angezeigt!
<br />
<?php echo $this->get('text'); ?>
</p>
<br />

<?php
// Hier wird die Tabelle ausgegeben.
echo $this->get('table');
?>

<?php
$this->includeAction('main.IncMainFooter');
?>

Und das wars dann auch schon wieder, nun können wir dynamisch generierte Daten im Template ausgegeben.

Ich hoffe Ihr seit auch beim nächsten Mal wieder dabei, wenn es heißt "Wir bauen uns ein Framework, und PHP ist geil!".
 

easteregg

Member of Honour
auch wenns etwas spät kommt, da ich das hier irgendwie aus den augen verloren hatte.
aber ist es nicht an sich sinvoller, das in die template klasse mit einzubauen?

ich hab mir dort ne methode reingebaut, die ähnlich wie dein layouter arbeitet und sich im endeffekt nur $tpl->assigntable($name,$id,$data); schimpft und per $this->getTable($name) abgerufen wird.

ich versteh bei deiner sache nicht so ganz die notwendigkeit einer neuen klasse! grade da sich das ganze nur auf ne tabelle bezieht, was machst du wenn du zb listen noch häufig brauchst, oder dynamisch generierte links etc... ?

ansonsten find ich den trick mit der __toString methode klasse! :)
 

BasicAvid

Member
aber ist es nicht an sich sinvoller, das in die template klasse mit einzubauen?

Nein, den was machst Du wenn Du noch vier weitere Layouter hast? Alle in die Template-Klasse mit aufnehmen? Man sollte immer darauf achten, so wenig Klassen-Kopplungen wie möglich zu haben, und zu dem ist die Template-Klasse für sowas nicht zuständig.

ich versteh bei deiner sache nicht so ganz die notwendigkeit einer neuen klasse! grade da sich das ganze nur auf ne tabelle bezieht, was machst du wenn du zb listen noch häufig brauchst, oder dynamisch generierte links etc... ?

Tja, und was machst Du, wenn die Tabelle z.B. nur eine Zelle, Navigation und Header haben sollte, oder Sortierbar usw. sein soll, und das andere mal nicht? Willst Du dann jedes mal die Template-Klasse ändern oder erweitern? Häufig gebrauchte Listen, oder dynamisch generierte Links stellen ja so auch kein Problem dar.

So einen Layouter kann man sehr abstrakt halten, und Ihn dann einfach weiter spezifizieren. Somit kann man sehr einfach jeden Tabellen-Typ darstellen, und auf jedes Problem individuell reagieren.

Ich benutze zu den TableLayouter noch eine Table Klasse, diese Klasse bereitet mir die Daten aus einer DB auf und der Layouter verarbeitet diese dann.
 

easteregg

Member of Honour
das bedeutet im "schlimmsten" fall baust du dir für jedes anwendungsszenario von vielgebruachen sachen (listen, tabllen, strange wiederkehrende verschalteltung von div elementen... etc. ne layout klasse und erweiterst die dann bis ins detail?

das is beeindruckend, aber lohnt sich das wirklich?
ich selbst hab bis jetzt nur ein projekt was man wirklich als solches betiteln kann, aber ich seh bei deinen sachen teils nicht so richtig den sinn, das so extrem aufzusplitten. kannst du mir die hindergründe da mal kurz darlegen, was nun wirklich der vorteil von vielen kleinen sachen gegenüber einer "dicken" ultimativen template klasse ist?
 

BasicAvid

Member
das bedeutet im "schlimmsten" fall baust du dir für jedes anwendungsszenario von vielgebruachen sachen (listen, tabllen, strange wiederkehrende verschalteltung von div elementen... etc. ne layout klasse und erweiterst die dann bis ins detail?

Nö nö, Du hast mich falsch verstanden, ich klone die Layouter nicht, das mach ich mit Vererbung/Ableitung.

Ich habe z.B. einen HTMLTableLayouter dieser Layouter beinhaltet alles was ich für eine Tabelle mit Header und Navigation brauche. Jetzt brauch ich aber z.B. eine Tabelle die nur eine Zeile ausgibt, aber alle Daten der aktuellen Row darin anzeigt, in Deinem Fall müsste ich jetzt hergehen und eine neue Funktion in der Template-Klasse implementieren die genau sowas macht. Ich gehe jetzt aber her und erweitere die Klasse durch Vererbung/Ableitung und überschreiben der betroffenen Methoden. Somit bekomme ich alle Features der Parent-Klasse, und kann meine neue Klasse konkretisieren, und muss mich nicht nochmal um z.B. die Navigation kümmern.

das is beeindruckend, aber lohnt sich das wirklich?

Danke, und ja es lohnt sich, ich kann so sachen viel schneller entwickeln.

kannst du mir die hindergründe da mal kurz darlegen, was nun wirklich der vorteil von vielen kleinen sachen gegenüber einer "dicken" ultimativen template klasse ist?

Gleich mal vorweg, es gibt keine "Ultimative Template Klasse", und wie schon geschrieben, die Template Klasse ist nicht dafür zuständig Tabellen zu bauen. Somit wird das MVC Prinzip gebrochen. Wenn Du alles in die Template Klasse steckst, wirst Du irgendwann den Überblick verlieren. Vorallem wirst Du nicht immer alles gebrauchen was in Deiner Klasse steckt, aber Du wirst immer alle Methoden in Deiner Instanz haben , welche irgendwann sehr viel speicher belegt.

Ich könnte Dir jetzt noch einige Gründe nennen warum man nicht alles in eine Klasse packen soll, aber das würde den Rahmen sprengen.
 

doc.D

New member
Hehe

der thread is zwar schon einbisschen alt aber ich muss sagen SUPER !!!!! Tutorial, durch die Kommetare auch super erklärt ;)

lg
 

rolf1000

New member
Hallo,

das Tutorial ist super, bringt mich doll weiter.

Kannst Du noch kurz erklären, wie man jetzt eine zweite Seite erstellt?

Danke!
 

BasicAvid

Member
Also eine zweite Seite machste genau so, wie die erste.

Hier ist mal ein Beispiel:

AboutUs.tpl.php
PHP:
<?php
$this->includeAction('main.IncMainHeader');
?>

<h1>Über uns!</h1>

<p>
Hier wird dann ein Text angezeigt!
<br />
<?php
//Hier holen wir uns eine Variable, die wir in der Action registriert haben.
echo $this->get('text'); 

?>
</p>


<?php
$this->includeAction('main.IncMainFooter');
?>
Das wars Template und jetzt kommt noch die Action, die vom Grundgerüst genau gleich ist wie die Main.class.php.

PHP:
<?php

class AboutUs extends Action {

    public function __toString() {
        return 'Die About us Action';
    }

    public function run() {
        $tpl = new Template();
        $tpl->assign('text', 'Dies ist ein Text der dem Template übergeben wurde!');
        $tpl->display('main.AboutUs');
    }
}
?>
Actions (Controller) haben immer das gleiche Grundgerüst. Sowie oben kannste weitere Actions implementieren.
 

rolf1000

New member
Super, danke!
Mich hatte anfangs die Order-Struktur etwas irritiert.

Wenn ich zum Beispiel eine Gebrauchtwagenseite habe, dann könnte ich die Struktur der URLs so aufbauen (habe den Punkt zwischen Modul und Action per htaccess durch / ersetzt):

auto/Suchen
auto/Kaufen
auto/Gebot-einstellen
auto/Finanzieren

ueber/ueber_uns
ueber/Team
ueber/AGB

Dann wären 'auto' und 'ueber' Module und was nach dem Slash kommt die Actions. D.h. ich hätte im Ordner modules die Unterorder auto und ueber, mit den jeweiligen Dateien in den Unterordnern actions und templates.

Wenn ich das so mache, kann ich dann den Header und Footer und den Classes Ordner nicht aus den Modul-Verzeichnissen auslagern? Sonst muesste ich die ja bei jedem Modul neu includieren?
 

BasicAvid

Member
Dann wären 'auto' und 'ueber' Module und was nach dem Slash kommt die Actions. D.h. ich hätte im Ordner modules die Unterorder auto und ueber, mit den jeweiligen Dateien in den Unterordnern actions und templates.

Genau!

Wenn ich das so mache, kann ich dann den Header und Footer und den Classes Ordner nicht aus den Modul-Verzeichnissen auslagern? Sonst muesste ich die ja bei jedem Modul neu includieren?

Also der Classes Ordner ist ja für Modul-Spezifische Klassen/Layouter/DBMapper/Models usw. gedacht. Deshalb würde ich diesen nicht auslagern. Für allgemeingültige Sachen kannst Du ja ein neues Modul erstellen, z.B. application welches die Header und Footer-Actions enthält. Dieses Modul könntest Du dann im FrontController per Autoloader laden lassen. Somit wären der Header und Footer in jedem Modul bekannt.
 

lasramblas

New member
Genau so ein Tutorial habe ich gesucht, spitze! Ich beschäftige mich derweil auch mit dem Thema:"wie erstelle ich eine Homepage", aber die ganzen Seiten wie self.html usw. helfen mir leider auch nicht immer weiter, da ich ein ziemlicher Newbie auf dem Gebiet bin. Das Video werde ich mir erstmal heute Abend zu Gemüte fügen, vielen Dank!
 
Oben