Templatesystem Mit Php
#1
geschrieben 28. August 2009 - 20:22
Habe hier
http://www.developers-guide.net/forums/684...ystem-schreiben
ein super tutorial gefunden welches beschreibt wie man sich ein eigenes templatesystem in php schreiben kann.
Habe das dann sogleich ausprobiert allerdings ohne die "Sprachdateien".
Es klappt auch alles soweit.
Allerdings bräuchte ich ja bspw. bei einem Gästebuch eine Schleife die immer einen bestimmten Teil des Templates wiederholt und mit anderem Inhalt füllt.
Auf der dritten Seite des Tutorials hat einer beschrieben, wie man eine solche Schleife einfügt, allerdings bin ich nicht wirklich hinter die Logik gekommen und kapier das überhaupt nicht. Auch meine anderen Versuche sind alle gescheitert.
Kann mir da vllt jemand mit helfen?
Vielen Dank
Dennis
Anzeige
#2
geschrieben 29. August 2009 - 19:11
Wenn du einen bestimmten Teil in mehreren Templates brauchst, dann lager das am besten in eine Datei aus. Am besten du bastelst dir eine kleine Funktion, die
1. den Namen des "Teils", ich nenne es mal Partitial
2. die Werte, welche als Array übergeben werden.
z.B.
function include_part($part_name, array $values) { $name = $values['name']; $entries = $values['entries']; include($part_name); }
part_schleife.php:
echo 'Hallo '.$name.'!<br />'; <?php foreach($entries as $entry): ?> $entry['name']; <?php endforeach ?>
Jetzt könntest du aus jedem Template aus diese Funktion aufrufen:
include_part('part_schleife.php', array('entries' => $gbook_eintraege, 'name' => $vorname));
Hoffe, es ist verständlich
Zitat
Spritify! Easy CSS-Sprite-Generator | Albanisch Deutsch Wörterbuch
#3
geschrieben 29. August 2009 - 21:13
habe es soweit schon einigermaßen verstanden.
aber dann muss ich ja in der template-html datei wieder eine Funktion aufrufen
irgendwie sagt es mir noch nicht ganz zu
oder ich muss einfach noch mal darüber nachdenken^^
#4
geschrieben 29. August 2009 - 22:00
Diese eine Funktion musst du aufrufen. Diese Funktion hat keinerlei Logik, sie hat nur den Sinn, dass sie den "Part X" einfügt und dafür sorgt, dass das Variablen richtig eingesetzt werden.
z.B.
... include_part('part_schleife.php', array('entries' => $liste_x, 'name' => $nachname)) ....
oder:
... include_part('part_schleife.php', array('entries' => $kundenliste, 'name' => $kundenverwaltung)) ...
Zitat
Spritify! Easy CSS-Sprite-Generator | Albanisch Deutsch Wörterbuch
#5
geschrieben 30. August 2009 - 02:27
Die Fehlerbehandlung habe ich zu gunsten der Übersichtlichkeit entfernt.
Auf unnötige Klammern wurde verzichtet.
Also, als erstes ein kleines Beispiel-Template:
<each=each_1> {V} {N}<br> </each> Blubb<br> <each=each_2> {N}, {V}, {G}<br> </each> <each=each_3> {KN}<br> </each>
each_1, each_2 und each_3 sind die Bezeichner um der Schleife eine Variable des Parsers zuzuordnen.
{V}, {N}, {G}, {KN} sind Bezeichner für Variablen innerhalb der Schleife.
(V = Vorname, N = Nachname, G = Geschlecht, KN = Kompletter Name)
Wie der Name schon vermuten lässt soll das ganze ein Foreach-Konstrukt werden.
Das Ziel des ganzen soll es werden drei Arrays mit den vorgaben entsprechenden Inhalten mit Hilfe des oben gezeigen Templates auszugeben.
Als erstes wird die Instanz des entsprechenden Template-Parsers (Code folgt später) erzeugt:
$p = new template_each();
Dann wird das Template eingelesen:
$p->set('template.tpl');
Nun wird der Array für die Schleife each_1 hinzugefügt. Die Items des Arrays sind jeweils Assoziative Arrays deren Items aus Key - Value Paaren bestehen. Als Key wird der Name des Platzhalters in der Schleife verwendet für den der Wert Eingesetzt werden soll.
Als wenn {V} durch Horst ersetzt werden soll lautet das Item "V" => "Horst".
Jeder Array entspricht also einem Schleifendurchlauf, was insgesammt 3 Durchläufe ergibt.
$p->add('each_1', array(
array("V" => "Horst", "N" => "Merkel"),
array("V" => "Karl", "N" => "Seehofer"),
array("V" => "Hainz", "N" => "Steinbrück")
));
Das selbe Spiel folgt für die Schleifen each_2 (zwei Datensätze, je 3 Paare) und each_3 (nur ein Datensatz mit einem Paar):
$p->add('each_2', array(
array("V" => "Karl", "N" => "Seehofer", "G" => "m"),
array("V" => "Angie", "N" => "Merkel", "G" => "w")
));
$p->add('each_3', array(
array("KN" => "Karl Hainz")
));
Und zum Schluss die geparste Ausgabe:
$p->parse();
$p->out();
Was dann folgendes Ergebnis liefert:
Zitat
Karl Seehofer
Hainz Steinbrück
Blubb
Seehofer, Karl, m
Merkel, Angie, w
Karl Hainz
Nun die eigentliche Klasse für das Template und ersetzen von Platzhaltern der Form {NAME}, sollte soweit eigentlich relativ selbsterklärend sein:
class template { var $tpl; var $vars; function set($tpl){ $this->tpl = file_get_contents($tpl); } function add($name, $value){ $this->vars[$name] = $value; } function get(){ return $this->tpl; } function out(){ echo $this->tpl; } function parse(){ foreach($this->vars as $key => $value) $this->tpl = str_replace("{{$key}}", $value, $this->tpl); } }
Etwas komplizierter wird es nun das ganze Gebilde mit den Schleifen zum Laufen zu bringen.
Also schauen wir uns die Implemenierung an:
das Grundgerüst sieht so aus:
class template_each extends template
{
<Klasse>
}
Es werden also die Methoden der Template Klasse geerbt.
Die Methode parse() wird nun für das parsen der Schleife durch folgende Methode überschrieben:
function parse(){ // Für jede Schleife den Ersetzungsvorgang durchführen. foreach($this->vars as $key => $value){ // GLOBALS dient dazu $value im Callback verwenden zu können $GLOBALS['tevs'] = $value; // Beim RegEx ist zu beachten, dass er zeilenübergreifend sein soll // Der Methode Callback wird der Inhalt der Schleife übergeben, // also der zu wiederholende Teil des Templates $this->tpl = preg_replace_callback('/<each=' . $key . '>(.*?)<\/each>/ms', array($this, 'callback'), $this->tpl); } }
Und als letztes die Methode callback($matches), die die Ersetzungen im Körper der Schleife durchführt:
function callback($matches) { // Jeden Schleifendurchlauf behandeln, // die Items für jeden Durchlauf kommen aus der globalen Hilfsvariable, // da ja nur $matches von preg_replace_callback übergeben wurde. foreach($GLOBALS['tevs'] as $value){ // Den Inhalt des Templates in eine temporäre Varibale kopieren // damit der zu wiederholende Teil nicht überschrieben wird // $matches[0] enthält den Körper der Schleife, // $matches[1] enthält die ganze Schleife also <each> bis </each> $cache = $matches[0]; // Alle $key => $value Paare der eigentlichen Ersetzungen behandeln foreach($value as $key => $subvalue){ $cache = str_replace("{{$key}}", $subvalue, $cache); } // Den fertig geparsten Schleifendurchlauf an der Ergebnis anhängen // und anschließend mit dem nächsten Durchlauf fortfahren $return .= $cache; } // Den kompletten Inhalt der Schleife an preg_replace_callback zurückgeben return $return; }
Und das auch schon wars.
Also im Prinzip ist es ganz einfach.
Dieser Beitrag wurde von [Elite-|-Killer] bearbeitet: 30. August 2009 - 02:33
#6
geschrieben 31. August 2009 - 19:29
danke
also ich würde lügen wenn ich alles verstanden habe^^
des heißt für die schleifen muss ich zwei neue klassen schreiben.
Kann ich nicht die Klasse die ich schon besitze zum normalen ersetzen ein bisschen verändern und dann einfach die Schleifenklasse auch wieder als vererbte Klasse ableiten?
Ich setz hier jetzt mal schnell die 3 Dateien rein, die ich ein bisschen vereinfacht aus dem Tutorial habe (ohne Sprachentemplate)
index.tpl
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de" > <head> <link rel="stylesheet" href="" type="text/css" /> <title>{$website_title}</title> <META HTTP-EQUIV="Content-Type" CONTENT="text/xhtml; charset=ISO-8859-1" /> <META HTTP-EQUIV="Language" CONTENT="{$website_language}"> <META name="description" content="{$website_description}"> <META name="keywords" content="{$website_keywords}"> <META name="robots" content="index, follow"> <META name="author" content="Dennis Bruggner"> <META name="copyright" content="TeaTime - CMS"> <META name="publisher" content="{$website_publisher}"> <META name="page-topic" content="{$website_page-topic}"> <META name="page-type" content="{$website_page-type}"> </head> <body> <h1>{$header}</h1> <div>{$topmenu}</div> <div>{$submenu}</div> Hallo {$name}. Der Aktuelle Timestamp lautet: {$time} {* Ein Template einbinden *} {*include file="index.contentbox.tpl"*} </body> </html>
template.class.php
<?php class Template { /** * Der Ordner in dem sich die Template-Dateien befinden. * * @access public * @var string */ protected $templateDir = "templates/"; /** * Der linke Delimter für einen Standard-Platzhalter * * @access public * @var string */ protected $leftDelimiter = '{$'; /** * Der rechte Delimter für einen Standard-Platzhalter * * @access public * @var string */ protected $rightDelimiter = '}'; /** * Der linke Delimter für eine Funktion * * @access public * @var string */ protected $leftDelimiterF = '{'; /** * Der rechte Delimter für eine Funktion * * @access public * @var string */ protected $rightDelimiterF = '}'; /** * Der linke Delimter für ein Kommentar * Sonderzeichen müssen escaped werden, weil der Delimter in einem RegExp * verwendet wird. * * @access public * @var string */ protected $leftDelimiterC = '\{\*'; /** * Der rechte Delimter für ein Kommentar * Sonderzeichen müssen escaped werden, weil der Delimter in einem RegExp * verwendet wird. * * @access public * @var string */ protected $rightDelimiterC = '\*\}'; /** * Der komplette Pfad der Templatedatei. * * @access protected * @var string */ protected $templateFile = ""; /** * Der Dateiname der Templatedatei * * @access protected * @var string */ protected $templateName = ""; /** * Der Inhalt des Templates. * * @access protected * @var string */ protected $template = ""; /** * Ein paar Eigenschaften ihre Werte zuweisen * * @access public * @return boolean */ public function template($tpl_dir = "") { // Template Ordner ändern if (!empty($tpl_dir)) { $this->templateDir = $tpl_dir; } return true; } /** * Die Templatedatei öffnen * * @access public * @param string $file Dateiname des Templates * @return boolean */ public function load($file) { // Die Eigenschaften zuweisen $this->templateName = $file; $this->templateFile = $this->templateDir.$file; // Wenn ein Dateiname übergeben wurde, versuchen, die Datei zu öffnen if(!empty($this->templateFile)) { if($fp = @fopen($this->templateFile, "r")) { // Den Inhalt des Templates einlesen $this->template = fread($fp, filesize($this->templateFile)); fclose ($fp); } else { return false; } } // Die methode replaceFuntions() aufrufen $this->replaceFunctions(); return true; } /** * Die Standard-Platzhalter ersetzen * * @access public * @param string $replace Name of var which should be replaced * @param string $replacement Text with which to replace the var * @return boolean */ public function assign($replace, $replacement) { $this->template = str_replace($this->leftDelimiter.$replace.$this->rightDelimiter, $replacement, $this->template); return true; } /** * Die Funktionen ersetzen * * @access protected * @return boolean */ protected function replaceFunctions() { // Includes ersetzen ( {include file="..."} ) while(preg_match("/".$this->leftDelimiterF."include file=\"(.*)\.(.*)\"".$this->rightDelimiterF."/isUe", $this->template)) { $this->template = preg_replace("/".$this->leftDelimiterF."include file=\"(.*)\.(.*)\"".$this->rightDelimiterF."/isUe", "file_get_contents(\$this->templateDir.'\\1'.'.'.'\\2')", $this->template); } // Kommentare löschen $this->template = preg_replace("/".$this->leftDelimiterC."(.*)".$this->rightDelimiterC."/isUe", "", $this->template); return true; } /** * Das fertige Template ausgeben * * @access public * @return boolean */ public function out() { echo $this->template; return true; } } ?>
index.php
<?php // Das Templatesystem einbinden include("template.class.php"); // Eine neue Instanz der Template Klasse erzeugen $tpl = new Template(); // Das Template laden $tpl->load("index.tpl"); // Den Platzhaltern einen Inhalt zuweisen $tpl->assign("website_title", "Willkommen"); $tpl->assign("website_language", "de"); $tpl->assign("website_description", "Beschreibung"); $tpl->assign("website_keywords", "Schlüsselwörter"); $tpl->assign("website_publisher", "Irgendwer"); $tpl->assign("website_page-topic", "Irgendwas"); $tpl->assign("website_page-type", "Irgendwas"); $tpl->assign("name", "Benutzer"); $tpl->assign("time", time()); $tpl->assign("header", "Startseite"); // Und das fertige Template ausgeben $tpl->out(); ?>
Also das ist mein jetziger Stand.
Ich will ja nicht dass ihr alles für mich macht, da lerne ich ja gar nichts dabei, nur ein bisschen (viel) unter die Arme greifen.
Den Code aus dem Tutorial versteh ich so ziemlich ganz.
Nur wirds dann halt schwierig mit der anderen Klasse.
Kann mir vllt. einer zeigen/sagen, wie ich die Klasse von Elite-Killer in meine integrier (falls das ohne größere Umstände geht) und dann die abgeleitete Klasse noch anpasse.
Danke:-)
#7
geschrieben 01. September 2009 - 01:23
<?php class Template_Each extends Template { private $array; public function assignEach($name, $array) { $this->array = $array; $this->template = preg_replace_callback('/<each="' . $name . '">(.*?)<\/each>/ms', array($this, 'eachCallback'), $this->template); return $this; } private function eachCallback($matches) { foreach($this->array as $value) { $cache = $matches[0]; foreach($value as $key => $subValue) $cache = str_replace($this->leftDelimiter . $key . $this->rightDelimiter, $subValue, $cache); $return .= $cache; } return $return; } } ?>
#8
geschrieben 01. September 2009 - 15:33
ok
in der templatedatei habe ich ja dann wieder folgenden code
<each=each_1> {V} {N}<br> </each> Blubb<br> <each=each_2> {N}, {V}, {G}<br> </each> <each=each_3> {KN}<br> </each>
für die schleifen
und wie ersetze ich das jetzt in der index.php bzw. welche funktion aus der template_each klasse muss ich da aufrufen?
Ich muss zugeben, dass ich das erste mal in PHP OOP programmiere^^
Vielen Dank
#9
geschrieben 03. September 2009 - 19:03
Muss ich dann auch nochmals die index.tpl per load in die neue Variable laden?
$tplloop = new Template_Each; $tplloop->load("index.tpl");
Danach lade ich per assignEach das Array in die Schleife
$tplloop->assignEach('each_1', array( array("V" => "Horst", "N" => "Merkel"), array("V" => "Karl", "N" => "Seehofer"), array("V" => "Hainz", "N" => "Steinbrück") ));
Nur funktioniert das ganze noch nicht so ganz.
Woran kann das liegen?
#10
geschrieben 03. September 2009 - 20:57
Aber da ich gerade etwas Zeit habe werde ich es mal testen:
Edit:
So ich habs jetzt mal getestet, die Template-Klasse, sowie die erweiterte Template-Klasse sind in Ordnung.
Ich hab nun mal die Template-Datei und die index.php mit einem entsprechenden funktionierenden Beispiel versehen:
Template Datei:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="de" xml:lang="de" > <head> <link rel="stylesheet" href="" type="text/css" /> <title>{$website_title}</title> <META HTTP-EQUIV="Content-Type" CONTENT="text/xhtml; charset=ISO-8859-1" /> <META HTTP-EQUIV="Language" CONTENT="{$website_language}"> <META name="description" content="{$website_description}"> <META name="keywords" content="{$website_keywords}"> <META name="robots" content="index, follow"> <META name="author" content="Dennis Bruggner"> <META name="copyright" content="TeaTime - CMS"> <META name="publisher" content="{$website_publisher}"> <META name="page-topic" content="{$website_page-topic}"> <META name="page-type" content="{$website_page-type}"> </head> <body> <h1>{$header}</h1> <div>{$topmenu}</div> <div>{$submenu}</div> Hallo {$name}. Der Aktuelle Timestamp lautet: {$time} {* Ein Template einbinden *} {*include file="index.contentbox.tpl"*} <each="Meine_Liste"> <p><span style="color: red;">{$Vorname}</span>, <span style="color: blue;">{$Nachname}</span></p> </each> <ul> <each="lol"> <li>{$0}, {$1}, {$2}</li> </each> </ul> </body> </html>
index.php:
<?php // Das Templatesystem einbinden include("Template_Each.php"); // Eine neue Instanz der Template Klasse erzeugen $tpl = new Template_Each(); // Das Template laden $tpl->load("index.tpl"); // Den Platzhaltern einen Inhalt zuweisen $tpl->assign("website_title", "Willkommen"); $tpl->assign("website_language", "de"); $tpl->assign("website_description", "Beschreibung"); $tpl->assign("website_keywords", "Schlüsselwörter"); $tpl->assign("website_publisher", "Irgendwer"); $tpl->assign("website_page-topic", "Irgendwas"); $tpl->assign("website_page-type", "Irgendwas"); $tpl->assign("name", "Benutzer"); $tpl->assign("time", time()); $tpl->assign("header", "Startseite"); $tpl->assignEach("Meine_Liste", array( array("Vorname" => "Angela", "Nachname" => "Merkel"), array("Vorname" => "Horst", "Nachname" => "Koehler"), array("Vorname" => "Edmund", "Nachname" => "Stoiber") ) ); $tpl->assignEach("lol", array( array("Angela", "Merkel", "w"), array("Horst", "Koehler", "m"), array("Edmund", "Stoiber", "m") ) ); // Und das fertige Template ausgeben $tpl->out(); ?>
(Zu beachten ist, dass ich die Syntax der each-Schleife im Template gegenüber der ersten Version leicht modifiziert habe, ggf. ist es ja bei die daran gescheitert:
statt <each=NAME></each> wird <each="NAME"></each> geparst)
Dieser Beitrag wurde von [Elite-|-Killer] bearbeitet: 04. September 2009 - 01:02
#11
geschrieben 14. September 2009 - 16:02
nochmals vielen Dank.
klappt alles super.
Aber ich habe jetzt im HTML Quelltext danach immer die <each> Anweisungen stehn. Macht das etwas aus, oder wird das von den Browsern einfach ignoriert?
Und was mir gerade noch eingefallen ist, was ich fragen wollte.
Also das mit den Schleifen klappt ja jetzt echt perfekt.
Wie löse ich es jetzt aber, wenn ich zum Beispiel ein Menü mit Menüpunkte und darunter den jeweiligen Untermenüpunkten anzeigen lassen will?
Muss ich da an der Klasse was ändern, und wenn ja was oder kann ich das auch (was ich weniger glaube) durch geschickten Aufbau einen Arrays bewerkstelligen?
Danke und Grüße
Dennis
Dieser Beitrag wurde von Dyon bearbeitet: 14. September 2009 - 16:11
#12
geschrieben 14. September 2009 - 17:33
Zitat (Dyon: 14.09.2009, 17:02)
nochmals vielen Dank.
klappt alles super.
Kein Problem
Zitat
Werden zwar ignoriert, war aber dennoch keine Absicht.
Die Zeile
$cache = $matches[0];
in der Klasse Template_Each muss
$cache = $matches[1];
heissen, dann fliegt das <each> beim parsen raus.
Zitat
Also das mit den Schleifen klappt ja jetzt echt perfekt.
Wie löse ich es jetzt aber, wenn ich zum Beispiel ein Menü mit Menüpunkte und darunter den jeweiligen Untermenüpunkten anzeigen lassen will?
Muss ich da an der Klasse was ändern, und wenn ja was oder kann ich das auch (was ich weniger glaube) durch geschickten Aufbau einen Arrays bewerkstelligen?
Rekursives Vorgehen wird bei der Klasse leider nicht direkt unterstützt.
Es ließe sich allerdings folgendes Menü problemlos einbetten:
im Template:
<ul><each="Menu">{$0}</each></ul>
Parser folgendes ergänzen:
$tpl->load("index.tpl")->assignEach("Menu", array( array('<li>Item1 <ul><each="Submenu1">{$0}</each></ul></li>'), array('<li>Item2 <ul><each="Submenu2">{$0}</each></ul></li>'), array('<li>Item3 <ul><each="Submenu3">{$0}</each></ul></li>') ) )->assignEach("Submenu1", array( array("<li>Subitem 1.1</li>"), array("<li>Subitem 1.2</li>"), array("<li>Subitem 1.3</li>") ) )->assignEach("Submenu2", array( array("<li>Subitem 2.1</li>"), array("<li>Subitem 2.2</li>") ) )->assignEach("Submenu3", array( array("<li>Subitem 3.1</li>"), array("<li>Subitem 3.2</li>"), array("<li>Subitem 3.3</li>"), array("<li>Subitem 3.4</li>") ) )->out();
Alternativ kann man statt <each="Submenu1">{$0}</each> auch gleich ein fertig geparstes Template einer anderen Instanz der Klasse entgegen nehmen.
Dieser Beitrag wurde von [Elite-|-Killer] bearbeitet: 14. September 2009 - 17:37
#13
geschrieben 15. September 2009 - 09:45
Zitat ([Elite-|-Killer]: 14.09.2009, 18:33)
Das ist wohl der "richtigere" Weg
Dieser Beitrag wurde von K050V4 bearbeitet: 15. September 2009 - 09:46
Zitat
Spritify! Easy CSS-Sprite-Generator | Albanisch Deutsch Wörterbuch
#14
geschrieben 15. September 2009 - 10:25
<li>Item1 <ul><each="Submenu1">{$0}</each></ul></li>
Wobei ich das nicht wirklich schön finde. HTML-Code hat im eigentlichen Quellcode nichts verloren, sondern gehört in die Templates.
Da kriegt man später viel Spaß, wenn man bspw auf die Idee kommt, die Aufzählungsliste durch eine nummerierte Liste (ol -> li) oder Definitionsliste (dl -> dt) zu ersetzen. Durch eine direkte Auslagerung in Templates könnte man direkt alle drei Varianten anbieten.
#15
geschrieben 15. September 2009 - 13:54
Zitat (Witi: 15.09.2009, 11:25)
<li>Item1 <ul><each="Submenu1">{$0}</each></ul></li>
Wobei ich das nicht wirklich schön finde. HTML-Code hat im eigentlichen Quellcode nichts verloren, sondern gehört in die Templates.
Da kriegt man später viel Spaß, wenn man bspw auf die Idee kommt, die Aufzählungsliste durch eine nummerierte Liste (ol -> li) oder Definitionsliste (dl -> dt) zu ersetzen. Durch eine direkte Auslagerung in Templates könnte man direkt alle drei Varianten anbieten.
Naja, deshalb auch noch der Nachsatz, dass man es in die Templates auslagern soll, ich wollte es beim Beispiel nicht gleich übertreiben.
Am schönsten würde ich es finden wenn man es im Template einfach verschachteln würde, so könnte man auch noch Templates einsparen, also nach dem Schema:
<foreach loop="$Loop"> <foreach loop="$InnerLoop"> Something usefull... </foreach> </foreach>
Aber da würde man sich das Leben mit einem XML-Parser statt regulären Ausdrücken bedeutend erleichtern, aber ich denke das ist für den Anfang sowieso etwas zu viel .