WordPress Custom Post Type
01.Mrz.2021 WordPress Tutorial

WordPress Custom Post Type

Was genau ist eigentlich ein WordPress Custom Post Type und was kann man damit alles machen? Wer sich als Web-Designer oder Entwickler noch nie mit Custom Post Types auseinandergesetzt hat, hat den größten Vorteil von WordPress noch nie genutzt. Moment, doch! Denn wir benutzen sie alle! Denn wenn wir das Wort „Custom“ einmal weg nehmen und darüber nachdenken was ein Post Type überhaupt ist, stellen wir fest, dass es quasi die Hauptfunktion von WordPress ist.

Bereits der Core bringt verschiedene Post Types mit. Wie jetzt?
Folgende Post Types sind bereits vorhanden:

  • Post / Artikel
  • Page / Seiten
  • Attachment / Medien
  • Revision / Revisionen
  • Navigation menu / Navigation Menüs

„Custom“ also nur weil wir eigene Post Types erstellen können, die jede erdenkliche Form von Inhalten abbilden können. Ein kleines Beispiel gefällig? Kein Problem, eines der bekanntesten Plugins in der WordPress Welt, Woocommerce, nutzt einen Custom Post Type um die Produkte abzubilden. Sehr viele weitere Möglichkeiten sind vorhanden, so kann man dynamisch seine Mitarbeiter auf der Website darstellen, ein Referenzen-Portfolio realisieren, oder auch einen seperaten Blog für einzelne Sparten eines Sportvereins integrieren.

Custom Post Type erstellen

Die erste Frage die wir wieder klären müssen – mit oder ohne Plugin?
Selbstverständlich erkläre ich in diesem Tutorial die Variante ohne Plugin. Denn wofür, wenn es doch eigentlich ganz einfach ist.

Die zweite Frage – schreiben wir ein Plugin, oder fügen wir unseren Code in die functions.php unseres Child-Themes?
Für dieses Tutorial entschied ich mich für die Plugin Version, denn so kann ich euch am Ende das Plugin zur Verfügung stellen. Du kannst den Code aber auch in dein Theme integrieren.

Die vorerst letzte Frage – Was wollen wir mit dem Custom Post Type machen?
Ich denke, eine gute Möglichkeit euch die Verwendung näher zu bringen, ist eine Art Mitarbeiter – Auflistung inkl. Mitarbeiter-Detail-Seite.

WordPress Custom Post Type Plugin Vorbereitungen

Um ein Plugin zu erstellen, erstellen wir als erstes einen neuen Ordner im WordPress Plugin Ordner. Ich nenne ihn einfach mal „myFirstPostType“ und erstelle dann eine leere PHP-Datei namens „main.php“ in diesem Ordner. Um zu verhindern, dass ein Benutzer den Inhalt des Ordners im Browser aufrufen kann, legen wir noch eine leere „index.html“ Datei mit in den Ordner.

Damit WordPress nun unser Plugin als Plugin erkennt, braucht unsere PHP-Datei als erstes einen Header. Zusätzlich fügen wir noch die Grundstruktur für unser Plugin ein.

<?php
/*
Plugin Name: my-first-custom-post-type
Plugin URI: https://api.tobier.de/?p=265&preview=true
Description: Custom Post Tyoe example
Version: 1.0
Author: Tobier
Author URI: https://api.tobier.de
*/

// Call an instance from our class
$plugin_start = new my_first_post_type();

class my_first_post_type {

	/*
	 * Constructor - the brain of our class
	 * */
	public function __construct() {

	}

}

In Zeile 2-8 steht unser Plugin Header, anhand diesem übergeben wir eine Information wie z.B. den Plugin-Namen, die Version und den Autoren. Diesen Header kann man noch erweitern, doch mehr Informationen müssen wir für unseren Custom Post Type erst einmal nicht übergeben. Wer hierzu noch mehr Wissen möchte, kann sich im Codex mehr Informationen über den File Header einholen.

Als nächstes erstellen wir eine Klasse in Zeile 14 und fügen einen Konstruktor ein. Der Konstruktor wird das „Gehirn“ unseres Plugins sein und all unsere Funktionen verwalten.

Warum tun wir das? Wir könnten auch alle Funktionen einzeln in diese PHP-Datei schreiben und diese dann mit unseren Action-Hooks aufrufen. Das wollen wir aber nicht, denn wir wollen unseren Code auch später bei großen Projekten gut warten, mehrfach verwenden und einfach erweitern können. Der Fachbegriff dafür lautet „objektorientiertes Programmieren“ (OOP), auch wenn ich in diesem Tutorial nicht wirklich die Vorteile einer OOP zeige, denn dann würden, denke ich, einige Leute nicht mehr so leicht durchsteigen.

Wer sich noch nicht mit OOP auseinander gesetzt hat, dem empfehle ich, es unbedingt zu tun. Es spart Arbeit und Zeit.

Die Custom Post Type Methode

Um einen Post Type zu erstellen, liefert uns WordPress eine einfache Methode – die register_custom_post_type()“-Methode, welche wiederum einmal den Post Type Slug und ein Array mit verschiedenen Parametern mitgegeben haben möchte.

Bevor wir diese Methode also nutzen können, brauchen wir ein Array mit den benötigten Parametern. Um dies in unsere Klasse zu integrieren, erstellen wir also eine Methode unter unserem Konstruktor und erstellen ein Array mit den benötigen Argumenten.

public function register_custom_post_type(){
// args for the new post_type
$args = array(
           // Sichtbarkeit des Post Types
           'public'              => true,
           // Standart Ansicht im Backend aktivieren (Wie Artikel / Seiten)
           'show_ui'             => true,
           // Soll es im Backend Menu sichtbar sein?
           'show_in_menu'        => true,
           // Position im Menu
           'menu_position'       => 5,
           // Post Type in der oberen Admin-Bar anzeigen?
           'show_in_admin_bar'   => true,
           // in den Navigations Menüs sichtbar machen?
           'show_in_nav_menus'   => true,
			
           // Hier können Berechtigungen in einem Array gesetzt werden
           // oder die standart Werte post und page in form eines Strings gesetzt werden
           'capability_type'     => 'post',

           // Soll es im Frontend abrufbar sein?
           'publicly_queryable'  => true,

           // Soll der Post Type aus der Suchfunktion ausgeschlossen werden?
           'exclude_from_search' => true,

           // Welche Elemente sollen in der Backend-Detailansicht vorhanden sein?
           'supports'            => array( 'title', 'editor', 'thumbnail', 'custom-fields', 'revisions' ),

           // Soll der Post Type Archiv-Seiten haben?
           'has_archive'         => false,
           
           // Soll man den Post Type exportieren können?
           'can_export'          => false,
			
           // Slug unseres Post Types für die redirects
           // dieser Wert wird später in der URL stehen
           'rewrite'             => array('slug' => 'mitarbeiter' ),
);
}

Aber nicht erschrecken, das war schon ein Array mit ziemlich vielen neuen Informationen für dich xD. Durch meine Kommentare über jedem Array Element sollte eigentlich jedes Element klar sein, wenn nicht, sind sie alle und noch ein paar mehr hier im Codex beschrieben.

Nachdem wir nun unsere Argumente in einem Array zusammengefasst haben, können wir die oben gennante Methode unter unserem Array aufrufen. Zu beachten ist hier, dass der Name unseres Post Types einen Prefix bekommen sollte, einfach um eine Kollision mit anderen Plugins / Themes zu vermeiden.

// Custom Post Type registrieren
register_post_type( 'tobier_mitarbeiter', $args );

Aktuell wird unsere Function aber noch nicht ausgeführt, denn in unserem Konstruktor gibt es noch keine Anweisung dafür. Fügen wir unserem Konstruktor also eine Action-Hook hinzu, mit der wir unsere Function aufrufen.

/*
* Constructor - the brain of our class
* */
public function __construct() {
	add_action( 'init', array( $this, 'register_custom_post_type' ) );
}

Aktivieren wir nun unser Plugin, sehen wir im Backend-Menü einen weiteren „Beiträge“ Menüpunkt. Allerdings ohne die Unterpunkte „Kategorien“ und „Schlagwörter“, welche wir mit unserem Parameter-Array deaktiviert haben.

wordpress custom post type
wordpress custom post type

Dein erster WordPress Custom Post Type ist nun also registriert und theoretisch im Backend einsatzbereit. Aber halt, zweimal „Beiträge“? Wir wollten doch unsere Mitarbeiter darstellen, da wäre es schon schön wenn der Menüpunkt so heißt und die Buttons sollten auch angepasst werden.

Was natürlich kein großes Problem ist, wir müssen unserem Array lediglich sagen, das wir ein weiteres Array mit diesen Strings haben. Und das machen wir, indem wir folgende Zeile in unser Array einfügen:

'labels'              => $labels,

OK OK, wir sagen unserem Custom Post Type also, das unsere Labels in der Variablen „labels“ sind. Damit das auch funktioniert, erstellen wir nun über unserem $args Array ein weiteres Array in dem wir die geänderten Strings bereitstellen.

// Backend string values
$labels = array(
	'name'               => _x( 'Mitarbeiter', 'post type general name' ),
	'singular_name'      => _x( 'Mitarbeiter', 'post type singular name' ),
	'add_new'            => __( 'Neuen Mitarbeiter anlegen'),
	'add_new_item'       => __( 'Neuen Mitarbeiter anlegen' ),
	'edit_item'          => __( 'Mitarbeiter Daten bearbeiten' ),
	'new_item'           => __( 'Neuer Mitarbeiter' ),
	'all_items'          => __( 'Alle Mitarbeiter' ),
	'view_item'          => __( 'Mitarbeiter ansehen' ),
	'search_items'       => __( 'Mitarbeiter durchsuchen' ),
	'not_found'          => __( 'Keine Mitarbeiter gefunden' ),
	'not_found_in_trash' => __( 'Keine Mitarbeiter im Papierkorb gefunden' ),
	'parent_item_colon'  => '',
	'menu_name'          => 'Mitarbeiter'
);

Nachdem wir nun unsere geänderten Strings bereitstellen, aktualisieren wir unseren Browser-Tab mit dem WordPress Backend. Wir sehen nun unter unserem „Beiträge“ Menüpunkt, den Menüpunkt „Mitarbeiter“. Klicken wir auf diesen, sehen wir, dass alle Strings aus unserem Array aktualisiert wurden.

Custom Post Typ mit geänderten Strings
Custom Post Typ mit geänderten Strings

Permalinks aktualisieren

Erstellen wir im Backend nun einen Mitarbeiter, sehen wir unter dem Title den Permalink. Wie konfiguriert, wird zwischen der Domain und dem Mitarbeiter-Name nun der Slug „mitarbeiter“ ausgegeben.

post type permalink
post type permalink

Veröffentlichen wir diesen Mitarbeiter und rufen den Permalink in einem neuem Tab auf, bekommen wir die 404 Seite zu sehen. Diesen Fehler können wir beheben, indem wir unter „Einstellungen -> Permalinks“ ohne Änderungen der Einstellungen auf „Speichern“ klicken.

Ein erneuter Aufruf des Mitarbeiter-Permalinks bestätigt uns die Behebung des Fehlers.

WordPress Custom Post Type Frontend-Ausgabe

Um unseren Custom Post Type im Frontend auszugeben, brauchen wir theoretisch zwei Templates. Einmal die Übersichtseite mit allen „Mitarbeitern“ und einmal die Detailansicht, wenn sie sich von den normalen „Post“ Beiträgen unterscheiden soll.

Hier kommt ein weiterer Grund warum ich mich für ein Plugin entschlossen habe. Theoretisch müssten wir in unserem Theme jetzt zwei Templates anlegen und zwischen den verschiedenen Post Types differenzieren. Entfernen wir den Post Type später wieder, haben wir die Templates im Theme Ordner rumfliegen und denken bestimmt nicht mehr daran, sie ebenfalls zu entfernen. In unserem Plugin hingegen sind diese Templates wieder entfernt, sobald wir es deaktivieren.

Shortcode für die Übersichtsseite erstellen

Für die Übersichtsseite werden wir in unserem Plugin also nun einen Shortcode für die Ausgabe einfügen. Bitte beachte das du die HTML-Struktur an deine Website anpassen musst. Die von mir verwendeten CSS-Klassen funktionieren nur, wenn dein Theme auf Bootstap 4 basiert.

Um die Ergebnisse unseres Shortcodes zwischendurch zu überprüfen, lege im Backend 3 Mitarbeiter an und füge bei jedem Mitarbeiter ein Custom Field mit der Bezeichnung „position“ an. Das Kontrollfeld zum Custom Field anlegen findest du ganz unten auf der Mitarbeiter-Anlegen-Seite.

Custom Field anlegen
Custom Field anlegen

Um einen Shortcode zu erstellen, legen wir in unserer Klasse als erstes eine neue Methode an:

/*
* Creates shortcode to display all employee
* */
public function mitarbeiter_shortcode() {
}

Danach sorgen wir in unserem Konstruktor dafür, dass diese Methode als Shortcode registriert wird und benutzt werden kann.

public function __construct() {
    // registriert den neuen custom post type
    add_action( 'init', array( $this, 'register_custom_post_type' ) );
    // Shortcode für die Ausgabe aller Mitarbeiter
    add_shortcode( 'alle-mitarbeiter', array( $this, 'mitarbeiter_shortcode' ) );
}

Ab sofort kann also der Shortcode genutzt werden, unsere angelegte Methode wird aufgerufen.

Da unsere Methode noch keinen Inhalt hat, kann sie natürlich auch noch nichts ausgeben. Das Ziel ist es jetzt, alle Mitarbeiter mit Namen, Position, Bild und einem Button zur Detailseite darzustellen. Hierfür brauchen wir eine Custom Query um in den Kontext unseres Custom Post Types zu kommen. Hört sich kompliziert an, ist es aber nicht. Fügen wir also den benötigten Code in unsere eben erstellte Methode „mitarbeiter_shortcode“ ein.

public function mitarbeiter_shortcode() {  // Hier drunter Code einfügen

    // Loop Argumente
    $args = array(
        'post_type'         => 'tobier_mitarbeiter',
        'post_status'       => array( 'publish' ),
        'posts_per_page'    => -1
    );

    // Daten abfragen
    $loop = new WP_Query( $args );

}

Code Erklärung:

Wir erstellen ein Array Namens „$args“ und geben drei Argumente mit:

  1. Welcher Post Type soll abgefragt werden? -> tobier_mitarbeiter
  2. der Post/Mitarbeiter Status -> nur wenn er auf publish/veröffentlicht steht
  3. Anzahl der Mitarbeiter die ausgegeben werden sollen. Die -1 steht für „alle“, alternativ kann hier eine Anzahl angegeben werden.

Anschließend rufen wir eine neue Instanz der Klasse WP_Query auf und geben dieser unser Array mit Argumenten mit. Dieser Aufruf wird uns ein Array mit allen Mitarbeiter-Objekten zurück geben. Genau das was wir also brauchen um die Daten weiterzuverarbeiten.

Um diese Daten zu verarbeiten, gehen wir mit einer While-Schleife, oder auch „WordPress Loop“ genannt, durch unser Array und können so ein Template für jeden Mitarbeiter ausgeben. Der Code dafür sieht wie folgt aus:

// start des WordPress Loops für unseren post type
while ( $loop->have_posts() ) : $loop->the_post();
	// post id abfragen
	$post_id = get_the_ID();
	// abfrage unseres custom fields "position"
	$position = get_post_meta( $post_id, 'position' );

	// Template Ausgabe
	?>
	<div class="col-md-4 text-center">
		<img style="max-height: 100px;" class="img-fluid mx-auto d-block rounded-circle" src="<?php echo get_the_post_thumbnail_url( $post_id, 'full' ) ?>">
			
		<span style="font-size: 1.5rem; font-weight: 700; color: #0e7c7b;" class="text-center"><?php echo get_the_title( $post_id ) ?></span>
		<p class="text-center">
			<b><?php echo $position[0] ?></b><br>
			<a href="<?php echo get_the_permalink( $post_id ) ?>" class="btn btn-tobi2" >Mehr erfahren</a>
		</p>
	</div>
	<?php
// Ende unserer while-schleife
endwhile;

Da wir unseren Shortcode auf einer Seite, oder in einem Beitrag verwenden wollen, müssen wir noch dafür sorgen, dass jeglicher nachfolgender Code wieder im richtigen Kontext ausgefügt wird. Wir haben jetzt einen WordPress Loop in einem WordPress Loop und würden dafür sorgen, dass jeglicher Code im Kontext unserer Mitarbeiter ausgeführt wird. Um dies zu verhindern, müssen wir also die Post-Daten reseten, glücklicherweise stellt uns WordPress eine Methode dafür zur Verfügung, somit müssen wir nur folgende Zeile Code unter unsere Schleife setzen:

// stellt den ursprünglichen data Kontext wieder her
wp_reset_postdata();

Theoretisch ist unser Shortcode nun einsatzbereit, doch führen wir ihn aus, merken wir schnell, dass irgendetwas nicht stimmt. Der Content wird nicht an der Stelle ausgegeben, wo er eigentlich erscheinen sollte. Und das ist auch völlig korrekt, denn eigentlich sollte ein Shortcode seine Ausgabe immer mit einem „return“ wiedergeben.

Das könnten wir auch tun, indem wir eine Variable erstellen und in jedem Durchgang unseres Loops den neuen Inhalt zur Variable hinzufügen. Anschließend könnten wir diese dann mit return ausgeben. Das hat aber einen deutlichen Nachteil, wir haben in unserem Editor keine Syntax-Highlighting mehr. Alleine dieser Grund ist Grund genug zwei weitere Zeilen Code noch zum Shortcode hinzuzufügen und das Problem aus dem Weg zu schaffen.

Wir fügen nun also folgende Zeile am Anfang unserer Methode hinzu:

ob_start();

und am Ende unserer Methode returnen wie folgt:

return ob_get_clean();

Was machen diese Zeilen?
Was wir soeben gemacht haben nennt sich „Output Buffering“. Mit ob_start() aktivieren wir einen Buffer, man kann es sich vorstellen wie einen Arbeitsspeicher. Wir speichern unsere Ausgabe im „Arbeitsspeicher“ und geben ihn dann im richtigen Augenblick aus. Denn eine Ausgabe mit „echo“, oder wie in unserem Beispiel HTML durch eine Unterbrechung der PHP-Tags, wird der Content sofort ausgegeben. Da unser Shortcode (durch WordPress) ausgeführt wird, bevor unser Content ausgegen wird, steht er vor unserem Content und nicht an der Stelle wo wir ihn eigentlich haben wollen.

Mit einem „return ob_get_clean()“ beenden wir die Ausführung der Methode und bekommen einen Wert (unseren Content) zurück, außerdem löschen wir anschließend den Buffer („Arbeitsspeicher“). Wir geben den Inhalt unseres Shortcodes aber nicht sofort aus.

Der genaue Vorgang:
WordPress filtert den Content bevor er ausgegeben wird, unter anderem nach Shortcodes. Wie es das macht, lasse ich hier mal außen vor, da es sonst diesen Artikel sprengen würde. Findet WordPress einen Shortcode, führt WordPress diesen aus und erwatet einen Rückgabewert. Erhält WordPress diesen Rückgabewert, fügt es diesen Wert (unseren Content) anstelle unseres Shortcode-Codes an die Stelle des Shortcodes ein. Sind alle Filter über den Content gelaufen, wird der Content inkl. des Shortcode Contents ausgegeben.

Wenn du es noch nicht gemacht hast, lege nun einfach mal 3 Mitarbeiter im Backend an und schaue wie dein Shortcode im Frontend aussieht. Bei mir sieht es nun wie folgt aus:

WordPress Custom Post Type erstellen Bob Foo

Maskottchen
Mehr erfahren

WordPress Custom Post Type erstellen Muhament Salin

Marketing
Mehr erfahren

WordPress Custom Post Type erstellen Max Mustermann

Web-Entwickler
Mehr erfahren

Die Detailansicht – Custom Single Template

In unserem Shortcode für die Übersicht unseres WordPress Custom Post Types, haben wir einen Button mit einem Link zur Detailansicht angelegt. Öffnen wir diesen Link, sehen wir unseren Mitarbeiter im normalen Blogbeitrag-Design. Das ist bzw. ist nicht bei allen Anwendungsfällen optimal, aus diesem Grund brauchen wir ein neues Template.

Um ein neues Template einzubinden müssen wir zwei weitere Schritte durchlaufen.

  1. Kopiere das „single.php“-Template aus deinem Theme in das Plugin-Verzeichnis und benenne es in „mitarbeiter_single.php“ um.
  2. Anschließend müssen wir mit einer weiteren Methode, in unserem Plugin, WordPress beibringen, dass es das eben angelegte Template für unseren WordPress Custom Post Type verwenden soll.

Nachdem du das „single.php“-Template in dein Plugin-Verzeichnis kopiert hast, legen wir nun folgende Methode in unserem Plugin (innerhalb der Klasse) an:

public function custom_post_type_single_mapping($single) {

		global $post;

		/* Checks for single template by post type */
		if ( $post->post_type == 'tobier_mitarbeiter' ) {
		    if ( file_exists( plugin_dir_path( __FILE__ ) . '/mitarbeiter_single.php' ) ) {
			    return plugin_dir_path( __FILE__ ) . '/mitarbeiter_single.php';
		    }
		}

		return $single;
	}

Code Erklärung:

Als erstes erstellen wir eine Methode, diese Methode rufen wir später mit einer WordPress-Hook auf, die uns das Single-Template mitgibt. Um dieses Template wieder zurückgeben zu können, wenn der Post Type nicht unser erstellter ist, nehmen wir dieses mit der Variable $single in unserer Methoden-Deklaration auch mit.

Mit global $post stellen wir uns das aktuelle Post-Objekt zur Verfügung. Welches wir in der nächsten Zeile bei der IF-Abfrage brauchen, um den Post Type des aufgerufenen Posts abzufragen. Wenn der aufgerufene Artikel aus unserem Post Type ist, gelangen wir in eine weitere IF-Abfrage, welche überprüft ob unsere angelegte Template-File existiert. Warum wir das in zwei IF-Abfragen machen? Weil wir nicht überprüfen müssen, ob unser Template vorhanden ist, wenn der Post Type nicht unser Custom Post Type ist.

Ist der Post Type unser Mitarbeiter Post Type und existiert unser neues Template, returnen wir unser Template und geben somit den Post mit unserem gewünschten Template aus. Da ein return die Methode beendet, erreichen wir das letzte return nicht mehr. Das letzte return , returnt das Standard Template, falls die Bedingungen in unseren IF-Abfragen nicht erfüllt wurden.

Um die Methode nun in unserem Plugin aufzurufen, schreiben wir folgende Filter-Hook in unseren Konstruktor:

add_filter('single_template', array( $this, 'custom_post_type_single_mapping' ));

Mehr Informationen zur „single-template“ Filter-Hook

Rufen wir nun einen Mitarbeiter in der Detailansicht auf, sieht es trotzdem noch aus wie ein normaler Blogartikel. Richtig, wir haben das Template ja nur kopiert. Passe das Template in deinem Plugin nun einfach deinen Wünschen an. Ein Beispiel wie es bei mir aussieht, findest du hier.

 

Unser WordPress Custom Post Type ist nun fertig, doch das ist nur ein Beispiel gewesen, selbstverständlich kann man damit noch viel coolere und kompliziertere Sachen machen. Aber ich denke für den Einstieg in die WordPress Welt reicht das. Abschließend wünsche ich dir noch viel Spaß beim ausprobieren und stelle dir hier meinen Code als Download zur Verfügung:
Plugin Code Download

Gutenberg Editor für den Custom Post Type aktivieren

Wer in seinem Custom Post (CPT) mit dem WordPress Gutenberg Editor arbeiten möchte, muss diesen bereits beim anlegen aktivieren, oder nachträglich aktivieren. Da mich viele Anfragen erreicht haben wie dies gemacht werden kann, hier eine Aktualisierung des Beitrages.

Für den Aufruf der WordPress Methode register_post_type() haben wir bereits unser Argumente in unserem „$args“ Array angelegt. Um den Gutenberg Editor zu aktivieren müssen wir hier folgenden Eintrag ergänzen:

'show_in_rest'        => true,

Danach einfach speichern, hochladen und Seite neuladen. Der Gutenberg Editor sollte nun zur Verfügung stehen.

Du hast Fragen zu diesem Thema? Stell sie in den Kommentaren!

Tobias Keller Web Entwickler
Tobias Keller Web Entwickler

Mit Leib und Seele Web Entwickler, immer auf der Suche nach der nächsten Herrausforderung, offen für Neues, Autodidakt.

Kommentare

Geschlossen wegen DSGVO, Artikel 13 Gedöns