Zum Inhalt springen Zur Fußzeile springen

webfluence hat 5 von 5 Sternen bei 11 Bewertungen auf Google My Business.

Kostenfreier Erstkontakt

Telefon 0345 52485943

Mail-Adresse r.rosanke@webfluence.de

0345 52485943 r.rosanke@webfluence.de

Custom Post Types

Erfahre, was Custom Post Types sind und welche Vorteile sie bieten. Inklusive Code-Beispiel zum erstellen eines eigenen.

„Mit Custom Post Types (CPT) kannst du die verschiedensten Inhaltsformen und Funktionen deiner Webseite klar voneinander trennen und somit effizient verwalten.

In diesem Artikel lernst du, was ein CPT ist und wie du einen eigenen anlegst. Weiterführende Infos, mit denen du eine Metabox und eigene Templates für deinen Custom Post Type erstellen kannst, gibt’s obendrauf.“

Robert Rosanke,
Webdesigner

Inhaltsverzeichnis

  1. Vorab-Exkurs: Was ist ein Post Type?
  2. Was sind Custom Post Types?
  3. Custom Post Type erstellen
  4. Metabox für Custom Post Type erstellen
  5. Eigene Templates für deinen CPT anlegen

Vorab-Exkurs: Was ist ein Post Type?

Einige von WordPress mitgelieferte Post Types sind:

Für jeden Post Type können individuelle Einstellungen hinzugefügt werden.

Für Beiträge sind beispielsweise eigene Templates und Archivseiten gängig, um die Ausgabe im Frontend zu steuern.

Doch auch die Bearbeitungsoberfläche im Backend kann beispielsweise durch Metaboxen individualisiert werden.

Und an dieser Stelle kommen Custom Post Types ins Spiel.

Was sind Custom Post Types?

Custom Post Types sind eigens erstellte Beitragstypen, mit denen eine WordPress Installation übersichtlich erweitert werden kann.

Du kannst zum Beispiel einen eigenen Post Type erstellen, die Bearbeitungsoberfläche um ein paar Metaboxen für zusätzliche Daten erweitern, ein eigenes Template bereitstellen und ihm einen individuellen Menüpunkt namens „Produkte“ geben.

Dadurch wird die Verwaltung der Webseiteninhalte deutlich vereinfacht, weil die verschiedenen Themengebiete und Inhaltsformen im Backend visuell voneinander getrennt sind und einzelne Menüpunkte und angepasste Bearbeitungsoberflächen für die verschiedenen Inhalte der Seite erhalten.

Redakteure können sich dadurch super einfach zurechtfinden und die Inhalte effizient verwalten..

(Das oben beschrieben Beispiel könnte der erste Schritt für eine Produkt-Übersichtsseite, wie in einem Online Shop oder auf einer Affiliate-Seite, sein.)

Echte-Welt Beispiele für Custom Post Types:

Einsatzgebiete und Grenzen

Das alles klingt auf den ersten Blick super.

Ist es auch.

Doch wie so oft, gibt es auch bei Custom Post Types irgendwann Grenzen.

Die sehe ich persönlich bei dynamischen Anwendungen, die kein Caching ermöglichen, und Daten aus vielen individuellen Metafeldern ziehen.

Denn dann wird ein Seitenaufruf alleine schon zahlreiche Datenbankabfragen zur Folge haben.

(Das ist an sich eher weniger ein Problem.)

Doch ohne Caching und mit einer hohen Zahl an gleichzeitigen Webseitenbesuchern wird viel Rechenleistung benötigt, um den Content weitehrin „on the fly“ zusammenzustellen und geringe Ladezeiten bereitzustellen.

Da viele Menschen und Unternehmen aber genau beim Hosting sparen, kann das zu viel für einen leistungsschwachen Server sein und ziemlich auf die Performance drücken.

Mögliche Lösungsansätze:

Meine Meinung: Solange Caching möglich ist, ist alles in Butter. Für alles mit vielen Datenabfragen ohne Caching-Möglichkeit muss ein klarer Plan her, damit die Webseite auch mit steigendem Besucheraufkommen effizient funktionieren kann

Custom Post Type erstellen

Im folgenden ein Beispielcode zum registrieren eines Custom Post Types.

Ziel ist es Ferienwohnungen übersichtlich zu verwalten und jedem Objekt mit Metaboxen Eingabefelder für die Kerneigenschaften bereitzustellen.

Kerneigenschaften sind beispielsweise:

Schritt 1: Funktion vorbereiten

Erstelle zuerst eine neue Funktion, die deinen Custom Post Type beinhaltet.

Über die init() Hook wird die Funktion, und damit auch der Custom Post Type, später ins System integriert.

function wf_register_custom_post_type_apartment() {

}
add_action( 'init', 'wf_register_custom_post_type_apartment', 0 );

wf_register_custom_post_type_apartment ist der Name der Funktion. Du kannst diesen natürlich frei anpassen.

Nutze einen aussagekräftigen Namen und stelle einen Prefix voran, um die Wahrscheinlichkeit einer Kollision mit einem anderen Plugin oder Theme zu minimieren.

Schritt 2: Beschriftungen vorbereiten

Der Custom Post Type erhält einen eigenen Menüpunkt im Backend.

Dementsprechend müssen die Label dafür vorbereitet werden.

$labels = array(
  'name'                  => _x( 'Apartments', 'Post Type General Name', 'wf-apartments' ),
  'singular_name'         => _x( 'Apartment', 'Post Type Singular Name', 'wf-apartments' ),
  'menu_name'             => __( 'Apartments', 'wf-apartments' ),
  'name_admin_bar'        => __( 'Apartments', 'wf-apartments' ),
  'archives'              => __( 'Apartment archives', 'wf-apartments' ),
  'all_items'             => __( 'All apartments', 'wf-apartments' ),
  'add_new_item'          => __( 'Add new apartment', 'wf-apartments' ),
  'add_new'               => __( 'Add new', 'wf-apartments' ),
  'new_item'              => __( 'New apartment', 'wf-apartments' ),
  'edit_item'             => __( 'Edit apartment', 'wf-apartments' ),
  'update_item'           => __( 'Update apartment', 'wf-apartments' ),
  'view_item'             => __( 'View apartment', 'wf-apartments' ),
  'view_items'            => __( 'View apartments', 'wf-apartments' ),
  'search_items'          => __( 'Search apartments', 'wf-apartments' ),
);

Ich habe mich dazu entschieden, nur die notwendigsten Label anzupassen, um das Tutorial kurz und verständlich zu halten. Die WordPress Ressourcen bieten eine vollständige Liste aller Post Type Label (engl).

Falls du dich wunderst, was __() und _x() machen: Diese Funktionen markieren übersetzungsfähigen Text. Mehr dazu im Tutorial WordPress übersetzen.

Schritt 3: Editor-Ansicht vorbereiten

Nun ist es an der Zeit festzulegen, welche Funktionen in der Bearbeitungsansicht einer Ferienwohnung zu sehen sein sollen.

Ich entscheide mich für

$supports = array(
  'title',
  'editor',
  // 'excerpt',
  // 'author',
  'thumbnail',
  //'trackbacks',
  // 'custom-fields',
  'revisions',
  // 'page-attributes',
  // 'post-formats'
  // 'comments'
);

Der Vollständigkeit halber habe ich mal alle Möglichkeiten aus der Liste aller supports-Optionen (engl) aufgezeigt.

Die nicht benötigten Optionen sind auskommentiert und werden nicht im Backend angezeigt. Du kannst diese Zeilen auch einfach entfernen, um den Code übersichtlicher zu halten.

Damit die Beitragsbild-Metabox angezeigt wird, muss das Theme post-thumbnails unterstützen. Mehr dazu: add_theme_support()

Schritt 4: Permalinks konfigurieren

Die URL-Struktur soll am Ende wie folgt aussehen:

domain.de/apartments/haus-am-strand

$rewrite = array(
  'slug'                  => 'apartments',
  'with_front'            => true,
  'pages'                 => false,
  'feeds'                 => true,
);

Code-Erklärung:

Gut zu wissen: Aktuell ist es scheinbar nicht möglich den Slug standardmäßig übersetzungsfähig zu machen.

Mehr dazu in einem interessanten Thread auf StackExchange.

Schritt 5: Funktion vorbereiten

So. Nun wird alles zusammengeführt und die letzten Einstellungen gesetzt.

$args = array(
  'label'                 => __( 'Apartments', 'wf-apartments' ),
  'description'           => __( 'Manage your apartments', 'wf-apartments' ),
  'labels'                => $labels,
  'supports'              => array( 'title', 'editor' ),
  'show_in_rest'          => true,
  'taxonomies'            => array( 'category', 'post_tag' ),
  'hierarchical'          => false,
  'public'                => true,
  'show_in_menu'          => true,
  'menu_position'         => 5,
  'menu_icon'             => 'dashicons-admin-home',
  'has_archive'           => true,
  'capability_type'       => 'page',
  'rewrite'               => $rewrite,
);

Code-Erklärung:

Schritt 5: Post Type final registrieren

Folgende Zeile Code registriert nun den Custom Post Type.

register_post_type( 'wf_cpt_apartments', $args );

Mit der Registrierung des Custom Post Types wird der eigene Eintrag in der Menüleiste sichtbar.

Dieses Bild zeigt den neu entstandenen Menüpunkt Apartments im WordPress Backend.

Zusammenfassung

Zum Abschluss der ganze Code an einem Stück

function wf_register_custom_post_type_apartment() {
  
  $labels = array(
    'name'                  => _x( 'Apartments', 'Post Type General Name', 'wf-apartments' ),
    'singular_name'         => _x( 'Apartment', 'Post Type Singular Name', 'wf-apartments' ),
    'menu_name'             => __( 'Apartments', 'wf-apartments' ),
    'name_admin_bar'        => __( 'Apartments', 'wf-apartments' ),
    'archives'              => __( 'Apartment archives', 'wf-apartments' ),
    'all_items'             => __( 'All apartments', 'wf-apartments' ),
    'add_new_item'          => __( 'Add new apartment', 'wf-apartments' ),
    'add_new'               => __( 'Add new', 'wf-apartments' ),
    'new_item'              => __( 'New apartment', 'wf-apartments' ),
    'edit_item'             => __( 'Edit apartment', 'wf-apartments' ),
    'update_item'           => __( 'Update apartment', 'wf-apartments' ),
    'view_item'             => __( 'View apartment', 'wf-apartments' ),
    'view_items'            => __( 'View apartments', 'wf-apartments' ),
    'search_items'          => __( 'Search apartments', 'wf-apartments' ),
  );
  
  $supports = array(
    'title',
    'editor',
    // 'excerpt',
    // 'author',
    'thumbnail',
    //'trackbacks',
    // 'custom-fields',
    'revisions',
    // 'page-attributes',
    // 'post-formats'
    // 'comments'
  );
  
  $rewrite = array(
    'slug'                  => 'apartments',
    'with_front'            => true,
    'pages'                 => false,
    'feeds'                 => true,
  );
  
  $args = array(
    'label'                 => __( 'Apartment', 'wf-apartments' ),
    'description'           => __( 'Manage your apartments', 'wf-apartments' ),
    'labels'                => $labels,
    'supports'              => array( 'title', 'editor' ),
    'show_in_rest'          => true,
    'taxonomies'            => array( 'category', 'post_tag' ),
    'hierarchical'          => false,
    'public'                => true,
    'show_in_menu'          => true,
    'menu_position'         => 5,
    'menu_icon'             => 'dashicons-admin-home',
    'has_archive'           => true,
    'capability_type'       => 'page',
    'rewrite'               => $rewrite,
  );
  
  register_post_type( 'wf_cpt_apartments', $args );
  
}
add_action( 'init', 'wf_register_custom_post_type_apartment', 0 );

Metabox für deinen Custom Post Type erstellen

Sobald der Custom Post Type registriert ist, geht es daran das Backend entsprechend zu erweitern.

Dazu eignen sich Metaboxen wunderbar.

Für eine Ferienwohnung könnten beispielsweise die Anzahl der Zimmer, die Wohnfläche in m² oder auch der Preis pro Nacht in Metaboxen festgehalten werden.

Zu Demonstrationszwecken erstelle ich ein einfaches Eingabefeld für den Titel des Hauses.

So kann der User seine interne Bezeichnung für das Haus von der für den Webseitenbesucher sichtbaren Bezeichnung trennen.

Das macht zum Beispiel Sinn, wenn der User die Objekte intern mit Immobilien-IDs als Titel anlegen will und dem Webseitenbesucher gleichzeitig eine attraktive h1-Überschrift angezeigt werden soll.

Schritt 1: Metabox „registrieren“ und mit CPT verknüpfen

Kurz vorab: Eine Metabox kann mehrere Eingabefelder haben. Für viele Einsatzzwecke reicht es also aus, eine zu registrieren.

function wf_add_apartment_metabox() {
  add_meta_box(
    'wf_apartment_metabox',
    _x( 'Apartment features', 'Backend: Title for Metabox panel' , 'wf-apartments' ),
    'wf_display_apartment_metabox',
    array( 'wf_cpt_apartments' ),
    'normal',
    'default'
	);
}

Code-Erklärung:

Teile dem Custom Post Type anschließend noch mit, dass er eine eigene Metabox hat.

Erweitere dazu $args um folgende Zeile:

'register_meta_box_cb' => 'wf_add_apartment_metabox',

Code Erklärung

Schritt 2: HTML-Gerüst der Metabox festlegen

Die bei der Registrierung der Metabox hinterlegte Callback-Funktion trägt das HTML-Gerüst.

Da es diese Funktion nicht gibt, musst du sie noch erstellen.

function wf_display_apartment_metabox( $post ) {
  wp_nonce_field( basename( __FILE__ ), 'wf_apartments_metabox_nonce' );

  $html = '<p><label>' . esc_html( 'Apartment title' , 'wf-apartments' ) . ' <input type="text" aria-describedby="title-description" name="wf_apartment_title" value="' . esc_attr( get_post_meta($post->ID, 'wf_apartment_title',true) )  . '" /></label></p>';
	$html .= '<p class="description" id="title-description">' . esc_html( 'This title will be shown to your website visitors.' , 'wf-apartments' ) . '</p>';

  echo $html;
}

Die Funktion besteht einfach aus dem nonce-Feld und deinem HTML.

Das nonce-Feld ist ein verstecktes Formularfeld, das zur Absicherung der Eingaben eingesetzt wird. Mehr dazu: wp_nonce_field()

Das HTML kann nun die Eingabefelder beinhalten, die du für deine Metabox benötigst.

Wichtig:

Schritt 3: Speichern der Daten erlauben

Da das HTML-Gerüst steht, kann es schon im Backend angezeigt werden.

Doch damit die Werte beim speichern des Beitrags an die Datenbank gesendet werden, ist noch eine letzte Funktion notwendig.

Kurze Erklärung, was die Funktion machen soll:

Erstelle dafür eine neue Funktion, die über die save_post()-Hook geladen wird.

function wf_save_apartments_post_meta( $post_id, $post ) {
  //...
  return $post_id;
}

add_action( 'save_post_wf_cpt_apartments', 'wf_save_apartments_post_meta', 10, 2 );

Um Ressourcen zu sparen, kannst du save_post_[cpt_name]() anstatt der einfachen save_post()-Hook verwenden. Dann wird das Script nur beim festgelegten Custom Post Type ausgeführt.

Nun wird die Funktion mit Inhalt gefüllt

nonce-Feld prüfen

if ( !isset( $_POST['wf_apartments_metabox_nonce'] ) || !wp_verify_nonce( $_POST['wf_apartments_metabox_nonce'], basename( __FILE__ ) ) ) {
  return $post_id;
}

Benutzerrechte prüfen

Wenn der Nutzer keine Beiträge bearbeiten darf, dann wird auch nichts gespeichert.

$post_type = get_post_type_object( $post->post_type );
if ( !current_user_can( $post_type->cap->edit_post, $post_id ) ) {
  return $post_id;
}

Automatisches Datenüberschreiben verhindern

Wenn der Speichervorgang eine automatische Speicherung ist, dann werden die Metadaten nicht überschrieben, damit der User die volle Kontrolle darüber hat, wann er die Daten veröffentlicht.

if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
return $post_id;
}

Eingaben an die Datenbank senden

Wenn bis jetzt noch nicht abgebrochen wurde, und der richtige Post Type bearbeitet wird, dann kann gespeichert werden.

if ($post->post_type == 'wf_cpt_apartments') {
  update_post_meta($post_id, 'wf_apartment_title', sanitize_text_field( $_POST['wf_apartment_title'] ) );
}

Wenn du mehr als nur ein Eingabefeld benötigst, dann kannst du einfach das HTML-Grundgerüst erweitern und weitere Zeilen mit update_post_meta() in die if-Bedingung des Snippets einfügen.

Zusammenfassung: Speicherfunktion auf einen Blick

function wf_save_apartments_post_meta( $post_id, $post ) {

  // security: check nonce field
  if ( !isset( $_POST['wf_apartments_metabox_nonce'] )	|| !wp_verify_nonce( $_POST['wf_apartments_metabox_nonce'], basename( __FILE__ ) ) ) {
    return $post_id;
  }

  // security: check user permissions
  $post_type = get_post_type_object( $post->post_type );
  if ( !current_user_can( $post_type->cap->edit_post, $post_id ) ) {
    return $post_id;
}

  // prevent updating post meta on autosave
  if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
    return $post_id;
}

  // update post meta
  if ($post->post_type == 'wf_cpt_apartments') {
    update_post_meta($post_id, 'wf_apartment_title', sanitize_text_field( $_POST['wf_apartment_title'] ) );
  }

  return $post_id;
}

add_action( 'save_post_wf_cpt_apartments', 'wf_save_apartments_post_meta', 10, 2 );

Eigene Templates für deinen CPT anlegen

WordPress durchsucht standardmäßig das aktive Theme nach Templates.

Das betrifft sowohl die Basis-Templates, wie beispielsweise archive.php und single.php aber auch individuelle Templates, die von Themes bereitgestellt werden.

Theme Autoren können zur Template-Erstellung einfach eine PHP-Datei mit (fast) beliebigen Namen im Theme-Ordner hinterlegen und diese mit einer einfachen Info als Template kenntlich machen.

Fast beliebiger Name, weil WordPress einige Dateinamen für gängige Templates (engl) nutzt/reserviert. Diese zu verwenden, könnte für Probleme sorgen.

<?php
/**
* Template Name: Apartment
* Template Post Type: wf_cpt_apartments
*/

Code-Erklärung

Template-Infos können einfach in einem Kommentar hinterlegt werden. Ähnlich einem Plugin Header.

So. Nun habe ich über Themes gesprochen. Plugins durchsucht WordPress standardmäßig nicht nach Template-Dateien.

Für den Fall, dass du deinen Custom Post Type in einem Plugin hältst, kannst du eine Funktion schreiben, die folgendes macht:

  1. Dateinamen für deine CPT Template-Dateien festlegen. Zum Beispiel wf-apartment-single.php und wf-apartment-archive.php.
  2. Prüfen, ob das aktive Theme die Dateien besitzt.
    1. Wenn ja, dann diese Template-Dateien zur Ausgabe verwenden.
    2. Wenn nein, dann die im Plugin mitgelieferten Dateien nutzen.

Mit diesem Ansatz erlaubst du den Usern deines Plugins die Ausgabe zu steuern, weil sie deine Template-Dateien aus dem Plugin kopieren, anpassen, in ihr Theme laden, und die Ausgabe einfach anpassen können.

Einen Beispielcode dafür gibt es auf Pixelbar: Templates in Plugins

Die Daten der Metaboxen kannst du dann mit get_post_meta() erhalten und ganz einfach im Template verwenden:

<php
$price_per_night = get_post_meta( $post_id, $meta_key, true);
// more meta data...
?>

Die Ausgabe könnte wie folgt aussehen:

<p class="wf_apartment_data__pricing">
  <span class="wf_apartment_data__label"><php _e( 'Price per night' , 'wf-apartments' ); ?></span>
  <span class="wf_apartment_data__value"><php esc_html_e( $price_per_night ); ?></span>
</p>