Kürzlich habe ich ein bisschen umrissen, dass man innerhalb seiner xt:Commerce-Templates mit den $this-Daten so Einiges anstellen und sich dadurch das Bearbeiten von System-Dateien sparen kann. Nur was man damit im Einzelnen machen kann, ist etwas zu kurz gekommen.

In diesem Beitrag gibt’s also als Ergänzung zwei Beispiele aus der Praxis - Das zweite davon ist ein bisschen umfangreicher und kann auch - quasi als “Template-PlugIn” (wenn man das so nennen mag) - heruntergeladen werden.

Eine Anleitung und den Download-Link finden Sie hier

 

Beispiel 1 - Ungewünschte Tabellen entfernen

Wer seinen Shop weitgehend tabellenfrei layouten möchte, ist sicherlich schon das ein oder andere Mal über Funktionen “gestolpert”, deren HTML-Ausgabe man nicht verändern kann - und die einem bisweilen das Leben noch ein kleines Stückchen schwerer machen.

Eine davon ist zum Beispiel der “Zähler” in einigen Produkt-Auflistungen - “Zeige 1 bis 20 von 47 Produkten (…)” - in dem für relativ wenig Text eine Tabelle mit 100% Breite ausgegeben wird - was in manche Fällen und für manchen Browsern einige Rettungsmaßnahmen erfordert, damit das Layout nicht “zerschoben” wird.

Diese kurze Info-Zeile mit nicht so kurzem Quellcode soll also anders (und vor allem ohne Tabelle) ausgegeben werden:


<table border="0" width="100%" cellspacing="0" cellpadding="2">
  <tr>
    <td class="smallText">Zeige <b>1</b> bis <b>20</b> (von insgesamt <b>47</b> Artikeln)</td>
    <td class="smallText" align="right">Seiten: &nbsp;<b>1</b>&nbsp;&nbsp;<a href="#" class="pageResults" title=" Seite 2 ">2</a>&nbsp;&nbsp;<a href="#" class="pageResults" title=" Seite 3 ">3</a>&nbsp;&nbsp;<a href="#" class="pageResults" title=" n&auml;chste Seite ">[n&auml;chste&nbsp;&gt;&gt;]</a>&nbsp;</td>
  </tr>
</table>
	

Lösung - Ansatz

Erzeugt wird diese Tabelle in den Vorlagen für Produkt-Listen - Zum Beispiel in der Datei “product_listing_v1.html” - Und anders als in den meisten “Boxen” kann man in den “Modulen” über die Template-Dateien kaum etwas an der HTML-Ausgabe der einzelnen Smarty-Tags verändern.

Hier dreht es sich konkret um {$NAVIGATION} - Die dazugehörige System-Datei ist schnell gefunden, aber die machen wir ebenso schnell wieder zu - Denn es geht auch anders.

Und manchmal muss es das auch: in meinem Fall sollten die Systemdateien angesichts eines “drohenden” Upgrades möglichst unberührt bleiben.

Zunächst müssen wir dafür sorgen, dass an dieser Stelle kein HTML ausgegeben wird - Stattdessen soll der Quelltext in einer Variablen gespeichert werden, die wir dann an eine “eigene” Funktion weiterreichen können. Wir schreiben also statt {$NAVIGATION} Folgendes:

{php}
$NAVI = $this->_tpl_vars['NAVIGATION'];
NaviAnpassen($NAVI);
{/php}

Die Funktion “NaviAnpassen();” fehlt natürlich noch - und muss geschrieben werden. Dazu nehmen wir einfach in die Datei “xtc_show_category.inc.php” aus unserem Template-Ordner. Denn was dort an Funktionen definiert ist, kann aus jedem Template-File heraus benutzt werden.

Lösung - Funktion

“NaviAnpassen();” soll den übermittelten HTML-Text nach unseren Wünschen umgestalten. Dabei hat natürlich jeder andere Vorlieben … In meinem Beispiel wandelt “NaviAnpassen();” die Tabelle in einen Absatz um, die Inhalte der beiden Tabellen-Zellen werden extrahiert und bekommen jeweils “eigene” spans.

Eine Schleifen-Konstruktion für die Abfrage der Werte aus dem $Treffer-Array können wir uns in diesem Fall sparen, denn wir wissen ja, dass es hier nur zwei Treffer geben kann. Am Ende werden noch die überflüssigen geschützten und doppelten Leerzeichen entfernt.

function NaviAnpassen($Input,$Echo='true') {

	preg_match_all("/<td[^>]*>(.*)<\/td>/",$Input,$Treffer,PREG_SET_ORDER);

	$Output = '<p class="Navigation">'.
		'<span class="Left">'.$Treffer[0][1].'</span> '.
		'<span class="Right">'.$Treffer[1][1].'</span>'.
		'</p>';
	$Output = str_replace("&nbsp;", ' ', $Output);
	$Output = trim( preg_replace("/\s\s+/", ' ', $Output) );

	if ( strtolower($Echo) != 'false' )
		echo $Output;
	else
		return $Output;
}

So oder so ähnlich kann man auch etliche andere Dinge nach eigenem Geschmack umbauen - ohne dafür an xt:Commerce System-Dateien herumbasteln zu müssen. Einfach die Inhalte der zu bearbeitenden Template-Tags in PHP weiterreichen - und das dann wie gewünscht verändern.

Allgemeiner Tipp: Derlei Layout-Funktionen kann man (wie hier gezeigt) so anlegen, dass sie auf Wunsch auch nur Werte mit “return” zurückgeben und nicht zwangsläufig eine HTML-Ausgabe erzeugen. Der Mehraufwand dafür ist nicht besonders groß - und oft genug braucht man einige Daten nicht nur “zum Anzeigen”, sondern auch zum “Weiterverarbeiten” - zum Beispiel in anderen Funktionen …

 

Beispiel 2 - “Spezialfunktionen” mit Sonderpreisen

Dazu gehören u.a. “Angabe der Ersparnis als Prozentwert” oder einfach das “Aufteilen” verschiedener Preis-Angaben, damit man nicht alles “in einem Rutsch” ausgeben muss.

Wenn man also ein wenig mehr mit seinen Preisen anstellen möchte, als sie einfach “nur” anzuzeigen, kann man analog zu Beispiel 1 vorgehen. Der Preis muss wieder in einer Variablen gespeichert und an zusätzliche Funktionen weitergegeben werden, die global für das jeweilige Template gültig sind.

Wie schon erwähnt gibt es das fertige Funktions-Set in diesem Beitrag zum Download. Dort finden Sie natürlich auch eine genauere Anleitung mit einigen Anwendungs-Beispielen.

Lösung - Ansatz

Auch die Artikelpreise, ganz gleich aus welcher Box oder aus welchem Content-Modul sie kommen, kann man (analog zum ersten Beispiel) an eigene Funktionen weiterreichen. In diesem Fall reicht aber eine Funktion nicht aus, wir erstellen das “Template PlugIn” am Besten Schritt für Schritt auf mehrere Funktionen verteilt.

Wie Preise aufgerufen werden, ist von Box zu Box und von Modul zu Modul immer leicht unterschiedlich - Hier das Beispiel für die “product_info_v1.html”

{php}$PREIS = $this->_tpl_vars['PRODUCTS_PRICE'];{/php}

Für $PREIS müssen wir also etwas programmieren, das folgende Voraussetzungen erfüllt:

  • Es muss automatisch erkannt werden, ob ein Sonderpreis vorliegt.
  • Dieser muss “extrahiert” werden: Einmal als Text inklusive Währungs-Symbol etc. “zum Anzeigen” - und einmal als “numerischer Wert” für unsere Prozentrechnung
  • Auch der Normalpreis muss uns in beiden Varianten zur Verfügung gestellt werden
  • Differenz und Prozentwerte müssen berechnet werden
  • Die Ergebnisse müssen vor der Rückgabe “formatiert” werden, und zwar nach den gleichen Regeln wie die anderen Preise im Shop. Also nicht einfach “pauschal” ein Euro-Zeichen hinter die Differenz schreiben - Vielleicht guckt sich jemand anders die Preise ja grad in Dollar an…

Der Eingabewert ist immer ähnlich - Nämlich ein komplett von xt:Commerce formatierter Artikelpreis, mit allem drum und dran - also mit sämtlichen HTML-Tags, mit allen Textbausteinen, Einheiten, Prozenten, Hinweisen - eben komplett. Und dort steckt auch schon alles drin, was man zum Berechnen von Prozenten, Differenzen und zum Herausziehen einzelner “Werte” braucht.

Lösung - Prinzip

Im Grunde genommen ist es ganz einfach: Wir nehmen also unseren $PREIS und durchsuchen ihn mit regulären Ausdrücken nach einem “alten Preis”, nach einem “neuen Preis”, speichern die Fundstellen in eigenen Variablen - Dann bilden wir (wenn sinnvoll) eine Differenz und einen Prozentwert - und geben das Ganze wieder an unser Template zurück.

1) einen “alten Preis” suchen

Das Suchmuster für “get_productOldPrice();” war leicht zu entwickeln, denn xt:Commerce gibt “alte Preise” freundlicherweise immer in einem deutlich gekennzeichneten span aus. Wir brauchen also bloß nach diesen OldPrice-spans zu suchen - und alles, was nicht dazwischen liegt, gehört schon nicht mehr zum “alten Preis”.


function get_productOldPrice($Input){
	$OldPrice = false;
	if (preg_match("/<span class=\"productOldPrice\">([^<]*)<\/span>/",$Input,$Treffer)) {
		$OldPrice['TEXT'] = trim($Treffer[1]);
		$OldPrice['WERT'] = get_Value($Treffer[1]);
	}
	return $OldPrice;
}
	

Unsere Funktion gibt ‘false’ zurück, wenn kein “alter Preis gefunden wird. Wird einer Gefunden, werden (wie oben gefordert) zwei Preise erzeugt - Einmal der “Textpreis” und einmal der numerische - Letzteres passiert mit der Funktion “get_Value();”

2) Preise zum “Weiterverrechnen” vorbereiten

Die Funktion “get_Value();” muss also Werte im Stile von statt 89,90 &euro; und Ähnliches verarbeiten - und “echte” Zahlen draus machen. Also die Buchstaben dürfen allesamt verschwinden, mit dem Komma kann PHP auch nichts anfangen - und die anderen Sonderzeichen müssen auch weg.


function get_Value($Input) {
	$Value = preg_replace("/(&[^;]*;)/", '', $Input);
	$Value = preg_replace("/[^0-9]/", '', $Value);
	return ($Value/100);
}
	

Grob gesagt: Alles bis auf die Ziffern von Null bis Neun entfernen - das was übrig bleibt, durch 100 teilen - und schon haben wir eine weiterverwertbare Zahl.

Vorsichtshalber löschen wir aber nicht sofort alle “Nichtziffern”, denn es kann ja sein, dass in unserem Preis ein paar HTML-Entities stecken, die mit Zahlen gebildet werden. Wenn meinetwegen 3 Ziffern zusätzlich zum eigentlichen “alten Preis” übrigbleiben würden, würde mit einem mindestens 1.000 Mal so hohen Wert weitergerechnet werden. Und das muss ja nicht sein.

3) Aktuelle Währung nutzen

Der aktuell gültige Artikelpreis wird natürlich auch noch gebraucht. Passend zur unserer allerersten Funktion schreiben wir eine “get_productNewPrice();” - bei der das Suchmuster ein bisschen komplizierter ist.

Denn im Gegensatz zum “alten Preis” wird der aktuelle nicht in so schön auffindbaren HTML-Tags ausgegeben. Und weil in einigen Fällen auch noch der “Sie sparen”-Hinweis auftauchen kann, reicht es nicht, einfach nach dem alten Preis zu suchen und ihn zu löschen. Denn was dann “übrig bleibt”, muss nicht unbedingt nur der Preis sein.

Wir führen also vorher eine neue Hilfsfunktion ein, die wir sowieso noch brauchen werden - Und zwar müssen wir die Shop-Einstellungen für Währung und Preisanzeige wissen:


function get_Waehrung() {
	$WaehrungsQuery = "SELECT * FROM ".TABLE_CURRENCIES;
	$WaehrungsQuery = xtDBquery($WaehrungsQuery );
	$Waehrung = xtc_db_fetch_array($WaehrungsQuery, true);
	return $Waehrung;
}
	

Zurückgegeben wird ein Array mit den Antworten zu allen Fragen in Sachen Preis-Format: Also “Tausenderpunkt oder Tausenderkomma?”, “Welche Währung gilt?”, “Welches Währungssymbol wird?”, “Auf wieviele Nachkommastellen soll gerundet werden?”, “Währungssymbol links oder rechts vom Preis?”

4) Aktuellen Artikelpreis finden

Zunächst wird natürlich (sofern vorhanden) der “alte Preis” aus der zu durchsuchenden Zeichenkette gelöscht.

Danach bleiben eine mehrere Zeilen übrig, von denen aber nur eine “unser Preis” ist, auch wenn die andere(n) Zeile(n) ebenfalls Ziffern beinhalten können. Aber nachdem von all diesen Zeilen nur eine ein Preis ist - hat auch nur diese Zeile ein Währungssymbol. Und welches das sein muss, finden wir mit “get_Waehrung();” heraus.


function get_productNewPrice($Input){
	$Waehrung = get_Waehrung();
	$Input = trim( preg_replace("/<span class=\"productOldPrice\">([^<]*)<\/span>/",'',$Input) );
	if (preg_match("/[^>]*".$Waehrung['symbol_left']."[^<>]*".$Waehrung['symbol_right']."[^<]*/",$Input,$Treffer)) {
		$NewPrice['TEXT'] = trim( preg_replace("/<[^>]*>/",'',$Treffer[0]) );
		$NewPrice['WERT'] = get_Value($NewPrice['TEXT']);
	}
	return $NewPrice;
}
	

Unser Suchmuster findet also die erste Zeichenkette, in der ein Währungssymbol enthalten ist und die sich zwischen einer schließenden sowie einer öffnenden Dreiecksklammer befindet - zwischen zwei HTML-Tags, beispielsweise.

Damit sollten wir mittels “get_productNewPrice();” unseren “formatierten”, aktuellen Preis erhalten. Das umschließende HTML wird laut Suchmuster ausgeschlossen. Und dieses Ergebnis wieder durch get_Value(); geschickt - damit wir “endlich” mit dem Rechnen anfangen können.

5) Preisvorteil ausrechnen

Den neuen Preis vom alten Preis abziehen, die “nackte” Zahl einmal für eine mögliche Weiterverwendung unverändert lassen - und für eine HTML-Ausgabe formatieren. Dazu benutzen wir die Vorgaben, die uns über unsere “get_Waehrung();” mitgeteilt werden:


function get_savedMoney($OldPrice,$NewPrice) {
	$SavedMoney = false;
	if ($NewPrice && $OldPrice) {
		if ($NewPrice < $OldPrice) {
			$SavedMoney['WERT'] = $OldPrice-$NewPrice;
			$Waehrung = get_Waehrung();
			$SavedMoney['TEXT'] = round(
				$SavedMoney['WERT'],
				$Waehrung['decimal_places'] );
			$SavedMoney['TEXT'] = number_format(
				$SavedMoney['TEXT'],
				$Waehrung['decimal_places'],
				$Waehrung['decimal_point'],
				$Waehrung['thousands_point'] );
			$SavedMoney['TEXT'] = trim(
				$Waehrung['symbol_left'].' '.
				$SavedMoney['TEXT'].' '.
				$Waehrung['symbol_right'] );
		}
	}
	return $SavedMoney;
}
	

Eigentlich muss nicht unbedingt gerundet werden, da für unsere Berechnungen die ursprüngliche HTML-Ausgabe benutzt wird - in der ja bereits alles in gerundeter Form vorliegt. Aber tut nicht weh, die Rundung drin zu lassen, vielleicht braucht man sie ja irgendwann noch einmal …

6) Prozentuale Ersparnis

Genau wie die “get_savedMoney();” soll auch die “get_savedPercent();” wieder zweierlei “Ergebnisse” bieten: Formatiert, gerundet und mit Prozentzeichen hintendran, für Shop-Buttons, Hinweis-Boxen - oder wofür auch immer. Und wir brauchen wiederum einen ungerundeten Zahlenwert ohne jegliche Sonderzeichen.


function get_savedPercent($OldPrice,$NewPrice) {
	$SavedPercent = false;
	if ($NewPrice && $OldPrice) {
		if ( ($NewPrice < $OldPrice) && ($OldPrice != 0) ) {
			$SavedPercent['WERT'] = ($OldPrice-$NewPrice) / $OldPrice * 100;
			$Waehrung = get_Waehrung();
			$SavedPercent['TEXT'] = round(
				$SavedPercent['WERT'],
				$Waehrung['decimal_places'] );
			$SavedPercent['TEXT'] = number_format(
				$SavedPercent['TEXT'],
				$Waehrung['decimal_places'],
				$Waehrung['decimal_point'],
				$Waehrung['thousands_point'] );
			$SavedPercent['TEXT'] = trim(
				$SavedPercent['TEXT'].' %' );
		}
	}
	return $SavedPercent;
}
	

Und damit hätten wir alle Berechnungen und Formatierungs-Funktionen zusammen. Jetzt fehlen nur noch die letzten Schritte - Zum Einen muss anhand eines eingegebenen Preises alles korrekt ausgführt werden - und zum Anderen benötigen wir noch eine zentrale Ausgabe-Funktion.

7) Alles zusammenfügen

Wichtig ist ein Output, der uns alle sich ergebenden Werte “getrennt” liefert - Denn nur so kann man die “Preis-Teile” unabhängig voneinander einsetzen. Der erste Schritt ist eine reine “Rückgabe-Funktion”, die anhand eines $Input-Wertes weiteren Berechnungen miteinander in Zusammenhang bringt:


function return_gunnartPricing($Input) {
	$Input = str_replace("/\n\b\r\t/", ' ', $Input);
	$Input = preg_replace("/\s\s+/", ' ', $Input);
	$Output['OldPrice']	= get_productOldPrice($Input);
	$Output['NewPrice'] = get_productNewPrice($Input);
	$Output['SavedM'] = get_savedMoney($Output['OldPrice']['WERT'],$Output['NewPrice']['WERT']);
	$Output['SavedP'] = get_savedPercent($Output['OldPrice']['WERT'],$Output['NewPrice']['WERT']);
	return $Output;
}
	

Bei der Gelegenheit kann man auch gleich noch ungewünschte Doppel-Leerzeichen sowie Zeilenumbrüche, Tabulatoren vor der Aus- bzw. Rückgabe entfernen.

8) Output-Funktion

Und damit es bei der Arbeit im Template erst einigermaßen praktisch wird, schalten wir eine Ausgabe-Funktion vor, der man einfach auf Wunsch ein paar Parameter “mitgeben” kann - so dass man am Ende nur noch recht knappe Anweisungen zu schreiben braucht.

Mein Vorschlag hierzu (natürlich hat jeder andere Vorstellungen von “praktisch”) wäre dieser:


function gunnartPricing($Input,$Which='check',$How='TEXT',$Echo='true') {
	if (strtolower($Which)=='check') {
		if (get_productOldPrice($Input)) return true;
		else return false;
	} else {
		$Output = return_gunnartPricing($Input);
		if($Output) {
			if (strtolower($Echo)=='false') $Echo = false;
			else $Echo = true;
			if($Output[$Which][$How]) {
				if($Echo) echo $Output[$Which][$How];
				else return $Output[$Which][$How];
			}
		} else {
			return false;
		}
	}
}
	

Fertig: Jetzt steht in jeder Template-Datei die Funktion “gunnartPricing();” zur Verfügung - und sollte eine flexiblere Gestaltung Ihrer Preis-Angaben ermöglichen.

 

Diese Beispiele nur als “Anregung” …

Denn so lassen sich selbstverständlich auch viele weitere Zusatz-Funktionen realisieren, die direkt mit dem Template in Zusammenhang stehen. Durch den “Zugriff” auf $this->_tpl_vars und die Möglichkeit, auch in den “.html”-Dateien des Templates PHP einzusetzen, sind die dazu nötigen Daten leicht zu erreichen und zu bearbeiten.