WordPress-Themes mit Twig und Timber erstellen

Für WordPress ist eine große Auswahl an fertigen Themes verfügbar. Aber oft gibt es Sonderwünsche oder Bedarf an einem individuellen Design. Dann ist etwas Handgestricktes oft unvermeidlich.

In WordPress werden Templates so programmiert, dass HTML und PHP vermischt werden. Um ein eigenes Theme entwickeln zu können, sind daher bestimmte grundlegende PHP-Kenntnisse oder zumindest die Bereitschaft zur Beschäftigung mit der Programmiersprache erforderlich. Dazu kommt auch noch, dass WordPress sehr viele Funktionen in der Form von sogenannten Template-Tags anbietet.

Für manche Personen kann das eine Hürde sein, in WordPress einzusteigen.

Zum Beispiel:

  • ist nicht jeder Frontend Entwickler/- in oder Webdesigner/-in bereit, viel Zeit in das Lernen PHP zu investieren.
  • kennen viele PHP-Entwickler sich mit WordPress nur begrenzt aus und finden es, sagen wir mal, etwas gewöhnungsbedürftig.
  • müssen Entwickler, die von anderen Content-Management-Systeme kommen, komplett umdenken.

Hinzu kommt, dass das Mischen von PHP und HTML auch ein gewisses Risiko in sich birgt. Weil PHP auf dem Server ausgeführt wird, können versehentlich Sicherheitslücken entstehen, wenn Daten nicht korrekt validiert oder bereinigt werden. Eine Möglichkeit die genannte Probleme zu lösen, wäre eine Template-Engine. Damit kann nach dem „Model View Controller“-Muster, kurz MVC, gearbeitet werden.

Die meisten modernen Frameworks basieren auf diesem Muster. Vorteil
ist, dass Programm- und Layout-Logik getrennt werden. In der PHP-Welt ist Twig derzeit sehr beliebt. Seinen Ursprung hat das Projekt im Symfony-Framework, aber es wird inzwischen auch von vielen anderen Projekten genutzt. Das bekannteste Beispiel ist wahrscheinlich Drupal 8. Aber auch Systeme wie EZ Publish, Bolt, Grav, Craft oder Sculpin setzen es ein.

Twig sieht eine klare Trennung von PHP- und HTML-Code vor. Das HTML wird in sogenannten „Views“ ausgelagert. Twig selber nimmt Daten entgegen und rendert über die Views das finale HTML. Hierdurch können Frontend- und Backend-Entwickler unabhängig von einander arbeiten. Zusätzlich lässt sich Object-Orientiertes PHP so einfacher schreiben, weil die Programmlogik ohne Layout-Code auskommt. Um Twig mit WordPress zu nutzen gibt es mehrere Plugins. Aber am bekanntesten ist Timber. Das liegt wohl daran, dass es hierfür eine ausführliche Dokumentation gibt und auch eine Reihe von Blogposts, WordCamp-Talks und Videos. Weiterer Vorteil ist die Unterstützung des Advanced-Custom-Fields-Plugins, sowohl
in der Free-, als auch in der Pro-Variante. Auch eine WooCommerce-Unterstützung ist vorhanden.

Die Syntax von Twig ist von Jinja2 abgeleitet, eine Python Template-Engine. Aber auch für andere Programmiersprachen gibt es Varianten von Jinja2. Daher wird es vielen Entwickler/innen, die Erfahrung mit anderen Programmiersprachen oder Systemen haben, vertraut vorkommen.
Ganz nebenbei erleichtert Twig Entwicklern ohne WordPress-Vorkenntnissen den Einstieg, da zumindest die Template-Engine schon vertraut ist. Ein praktisches Beispiel ist das Redesign von The Toast. Der Entwickler hatte kaum Erfahrung mit WordPress, konnte aber trotzdem das Projekt ohne Probleme umsetzen.

Um Twig einzusetzen, muss das Timber-Plugin in WordPress installiert werden. Twig wird mit dem Plugin mitgeliefert. Es gibt zwei Möglichkeiten Timber zu installieren. Am einfachsten ist es, das Plugin direkt in WordPress über die „Plugin installieren“-Funktion hinzuzufügen. Alternativ kann Timber mit dem Paketmanager Composer installiert werden. Entweder als MU-Plugin oder als Teil des Themes.

Während außerhalb von WordPress bereits Twig 2.0 zum Einsatz kommt, setzt Timber im Moment noch auf Twig 1.x. Die neueste Twig-Version setzt PHP 7.0 voraus, während Timber noch Support von PHP 5.3 bietet. Als zusätzliche Hilfe kann beim Entwickeln auch die Timber Debug Bar von Nutzen sein, um zu sehen was in WordPress geladen wurde. So sind eventuell auftretende Fehler schneller zu finden.
Die Unterschiede zwischen WordPress-PHP und Twig lassen sich am einfachsten im direkten Vergleich zeigen. Folgende Beispiele zeigen ein Seitentemplate, einmal in PHP und einmal in Twig.

WordPress-Seitentemplate

PHP-Template

<?php
	/* page.php */
	get_header();
	if ( have_posts() ) :
		while ( have_posts() ) :
			the_post(); ?>
			<h2>
				<?php echo mb_strtoupper( get_the_title(), get_bloginfo( 'charset' ) ); ?>
			</h2>

			<?php if ( has_post_thumbnail() ):
				the_post_thumbnail();
			endif; ?>

			<div class="content">
				<?php the_content(); ?>
			</div> <?php

 		endwhile;
 	endif;
 
 get_footer(); ?>

Twig-Seitentemplate

{# page.twig #}

	{% extends "base.twig" %}
	{% block content %}
		<h2>{{ post.title|upper }}</h2>

		{% if post.thumbnail %}
			<img src="{{ post.thumbnail.src }}">
		{% endif %}
		
		<div class="content">
			{{ post.content }}
		</div>
	{% endblock %}

Wie oben bereits erwähnt, enthält das Twig-Template keinen PHP-Code. Dafür gibt es eine eigene Syntax, die aus geschweiften Klammern in drei Ausprägungen besteht. Daten stehen als Variablen zur Verfügung und werden mit {{ }} ausgegeben. Template-Logiken werden mit {% %} aufgerufen und {# #} wird für Kommentare genutzt. In Twig können Inhalte über Filter verändert werden. Im Beispiel Seitentemplate wird der Titel mit dem upper-Filter in Großbuchstaben umgewandelt. Es gibt eine ganze Reihe von Standard-Filtern. Zusätzlich können auch eigene Filter über die functions.php (oder als WordPress-Plugin) definiert werden. Es wäre zum Beispiel möglich, ein bestimmtes Wort zu ersetzen. Das könnte wie folgt aussehen: {{ post. content|trump }}. Über den WordPress-Filter get_twig wird dann der Twig-Filter hinzugefügt.

function kp_trump_filter( $twig ) {
	$twig->addFilter(
		new Twig_SimpleFilter('trump', function( $text ) {
		return preg_replace( '/(\b)Trump(\b)/um', '${1}"The Orange One"${2}', $text );
	} ) );
	return $twig;
}
add_ filter( 'get_twig', 'kp_trump_filter' );

Für Twig gibt es fertige Erweiterungen die eigene Filter bieten. Zum Beispiel liefert die Text-Erweiterung die Filter truncate um Text abzuschneiden und wordwrap um Text automatisch umzubrechen.

function kp_extension_text( $twig ) { 
	$twig->addExtension( new Twig_Extensions_Extension_Text() );
	return $twig;
}
add_filter( 'get_twig', 'kp_extension_text' );

Neben Filter gibt es auch noch Funktionen. Diese werden unabhängig von Variablen eingesetzt um Inhalte zu generieren. Timber stellt zum Beispiel die Übersetzungsfunktion von WordPress in Twig zur Verfügung. {{ __('Sorry, no posts.', 'textdomain') }}

Außerdem hat Timber auch noch die function-Funktion, um PHP-Funktionen direkt aufzurufen. Hier wird ein wenig gemogelt, weil WordPress so viele Hilfsfunktionen und Template-Tags bietet. Alle WordPress-Funktionen in Twig nachzubauen wäre einfach zu aufwendig gewesen. Zum Beispiel kann die hilfreiche Funktion capital_P_dangit aufgerufen werden, um immer die korrekte Schreibweise für „WordPress“ zu erzwingen. {{ function('capital_P_dangit', post.content) }} Eine wichtiges Feature ist die Vererbung von Templates. Meistens wird ein Basis-Template definiert, das die HTML-Hauptstruktur für Seiten enthält.

Durch {% block block-name %} {% endblock %} können unterschiedliche Blöcke markiert werden. Diese können dann von anderen Templates überschrieben werden. Mit {% extends "template-name.twig" %} wird ein Template referenziert.
Definieren und Überschreiben von Blöcken wird auf genau der gleichen Art und Weise umgesetzt. So können schnell unterschiedliche Varianten erstellt werden, ohne jedes Mal ein komplettes Template schreiben zu müssen.

Außerdem ist es möglich Konditionen mit {% if %} {% endif %} abzufragen. Im Beispiel wird ein Thumbnail nur dann ausgegeben, wenn ein solches verfügbar ist.

„The Loop“ gibt es in Twig nicht, was ich persönlich als sehr angenehm empfinde. Ich kann mir das Ding einfach nicht merken und muss jedes Mal den Aufbau nachschlagen. Und bei einem einzelnen Post macht ein Loop auch keinen Sinn.
Um Inhalte tatsächlich auszugeben, müssen Variablen in Twig-Templates mit WordPress-Daten befüllt werden. Dafür stehen eine Reihe von PHP-Klassen zur Verfügung, die als Schnittstelle zu WordPress dienen. Timber folgt hierbei der Template-Hierarchie von WordPress und ruft die gleichen PHP-Dateien auf, wie ein normales Theme. Der Unterschied ist die Menge an benötigtem PHP-Code, der mit Twig überschaubar ist.

page.php mit Timber

<?php
	$context = Timber::get_context();
	$context['post'] = new TimberPost();
	Timber::render( 'page.twig', $context );

Mit Timber::get_context() werden einige globale WordPress-Daten geladen, wie der Titel oder die URL der Website. Die $context Variable ist ein Array und kann mit beliebigen, weiteren Daten befüllt werden. Im Beispiel wird mit new TimberPost() der aktuelle Post geladen. Mit Timber::render wird dann das gewünschte Template geladen und die Daten übergeben.

In Twig wird ein „Loop“ nur dann benötigt, wenn tatsächlich eine Liste durchlaufen werden muss, zum Beispiel für die Start- oder Archiv-Seite.

Beispiel Archivseite

{% extends "base.twig" %}
	{% block content %}
		{% for post in posts %}
			<article>
				<h2>
					{{ post.title|upper }}
				</h2>

				<div class="excerpt">
					{% if post.excerpt %} 
						{{post.excerpt}}
					{% else %}
						{{post.content|excerpt(55)}}
					{% endif %} 
				</div>
			</article>
		{% endfor %}
	{% endblock %}

Für das Aufrufen von mehreren Posts gibt es eine separate Klasse.

$context['posts'] = new TimberPosts();

Sowohl hier, als auch bei TimberPost gibt es die Möglichkeit die WP_Query-Syntax zu nutzen.

$args = array(
	'post_type' => 'custom_post_type',
	'post_status' => 'publish',
	'posts_per_page' => 5,
);
$context['posts'] = Timber::get_posts( $args );

Über die functions.php können Daten hinzugefügt werden, um sie allen Templates zur Verfügung zu stellen. Ein Navigationsmenü wäre so ein typischer Fall und Timber hat dafür „zufällig“ eine PHP- Klasse.

function kp_context( $data ) {
	$data['menu'] = new TimberMenu();
	return $data; }
add_filter( 'timber_context', 'kp_context' );

Wenn HTML-Fragmente mehrfach in unterschiedlichen Templates genutzt werden, dann können „Includes“ genutzt werden. Zum Beispiel können die Header- und Footer-Bereiche ausgelagert werden.

{% include 'header.twig' %}

{% block content %}
	Keine Inhalte gefunden.
{% endcontent %}

{% include 'footer.twig' %}

Falls ihr auf dem Geschmack gekommen seid und mit Twig und Timber loslegen wollt, gibt es ein Starter-Theme auf Github. Es gibt auch weitere Starter-Themes wie Lumberjack, das modernes PHP nutzt, inklusive Namespaces, und mindestens PHP 5.4 voraussetzt. Beispiele von Webseiten die mit Timber umgesetzt wurden, gibt es auf der Showcase-Seite, teilweise mit den kompletten Quellcode.
Übrigens kann man sich für Twig zertifizieren lassen. Ob das sinnvoll ist, muss jeder selber entscheiden.

Ein Kommentar

  1. Sehr schöner Artikel. Wird bestimmt denen helfen, wie auch mir, mal selbst zu versuchen ein Theme für WordPress zu erstellen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.