Fügen Sie Ihrem Menü Links zum Archivtyp hinzu
() translation by (you can also view the original English article)
Eine häufige Anforderung, insbesondere für diejenigen, die benutzerdefinierte Beitragstypen wie "Nachrichten" oder "Events" erstellt haben, besteht darin, in ihrem Navigationsmenü einen Link zur Archivseite ihres Beitragstyps hinzuzufügen. Derzeit kann dies jedoch nur durch manuelle Eingabe der Archiv-URL des Beitragstyps erfolgen. Abgesehen davon, dass diese Lösung ziemlich unelegant ist, weist sie einige Nachteile auf: Sie wird nicht immer als "aktuell" angezeigt. Wenn Sie Ihre Permalink-Struktur ändern, kann der Link beschädigt werden. Das manuelle Hinzufügen der URLs ist mühsam und der Link wird nicht als "aktuell" angezeigt. aktuell ', wenn auf einem Beitrag dieses Beitragstyps.
In diesem Tutorial werde ich Ihnen zeigen, wie Sie ein Plugin erstellen, das auf Ihrer Seite "Darstellung -> Menü" ein Meta-Feld erstellt, mit dem Sie Archivlinks vom Typ "Beitrag" hinzufügen können. Diese Links leiden nicht unter den oben genannten Nachteilen.
Schritt 1 Erstellen eines Plugins
Dieses Plugin heißt "My Post Type Archive Links". Zu diesem Zweck erstellen Sie zunächst einen Ordner mit dem Namen "my-post-type-archive-links" unter Ihrem Ordner /wp-content/plugins/ und erstellen darin eine Datei my-post-type-archive-links.php. Diese Datei ist die Haupt-Plugin-Datei. Wir werden es in eine Klasse einbinden - dies ist einfach so, dass wir uns keine Sorgen machen müssen, dass unsere Funktionsnamen mit WordPress oder anderen Plugins kollidieren: Wir müssen einfach sicherstellen, dass unser Klassenname eindeutig ist. Fügen Sie Folgendes zu my-post-type-archive-links.php hinzu
1 |
<?php
|
2 |
/*
|
3 |
Plugin Name: My Post Type Archive Links
|
4 |
Version: 1.0
|
5 |
Description: Adds a metabox to the Appearance -> Menu page to add post type archive links
|
6 |
Author: Stephen Harris
|
7 |
Author URI: https://profiles.wordpress.org/users/stephenh1988/
|
8 |
*/
|
9 |
|
10 |
class My_Post_Type_Archive_Link { |
11 |
//Everything will go here
|
12 |
}
|
13 |
My_Post_Type_Archive_Link::load(); |
14 |
?>
|
Alles in diesem Tutorial wird in dieser Klasse sitzen.
Schritt 2 Laden des Plugins
Wenn die Plugin-Datei geladen wird, wird die Klassenmethode load()
ausgelöst. Diese Methode ist für das Hinzufügen von Aktionen und Filtern zu verschiedenen WordPress-Hooks verantwortlich. Wir werden sie in den folgenden Schritten durchgehen, aber es gibt auch eine nützliche Zusammenfassung. Fügen Sie unserer Klasse die folgende Methode hinzu:
1 |
public function load(){ |
2 |
// Hook function to add the metabox to the Menu page
|
3 |
add_action( 'admin_init', array(__CLASS__,'add_meta_box')); |
4 |
|
5 |
// Javascript for the meta box
|
6 |
add_action( 'admin_enqueue_scripts', array(__CLASS__,'metabox_script') ); |
7 |
|
8 |
// Ajax callback to create menu item and add it to menu
|
9 |
add_action('wp_ajax_my-add-post-type-archive-links', array( __CLASS__, 'ajax_add_post_type')); |
10 |
|
11 |
// Assign menu item the appropriate url
|
12 |
add_filter( 'wp_setup_nav_menu_item', array(__CLASS__,'setup_archive_item') ); |
13 |
|
14 |
// Make post type archive link 'current'
|
15 |
add_filter( 'wp_nav_menu_objects', array(__CLASS__,'maybe_make_current')); |
16 |
}
|
Lassen Sie uns zusammenfassen, was jeder dieser Teile tut:
- Fügen Sie eine Meta-Box hinzu - Ziemlich selbsterklärend. Die Hook-Funktion ist für das Hinzufügen unserer Meta-Box verantwortlich.
- JavaScript in die Warteschlange stellen - Wir verwenden den Hook
admin_enqueue_scripts
, um unsere JavaScript-Datei in die Warteschlange zu stellen. Unser JavaScript löst beim Klicken auf "Zum Menü hinzufügen" eine AJAX-Anfrage aus. - AJAX-Rückruf - Diese Funktion ist für die Bearbeitung der obigen AJAX-Anforderung verantwortlich. Die Menüelemente werden erstellt und dem Menü hinzugefügt.
- Einrichten des Menüelements - Dies stellt sicher, dass der Archivlink, wenn er in Ihrem Menü angezeigt wird, korrekt auf das Archiv des Beitragstyps verweist.
- Vielleicht aktuell machen - Immer wenn ein Menü angezeigt wird, werden seine Elemente durch einen Filter geleitet, und wir stellen sicher, dass die Klasse '
current-menu-item
' dem entsprechenden Post-Typ-Link hinzugefügt wird.
Schritt 3 Hinzufügen der Metabox
Zuerst definieren wir unsere add_meta_box
-Methode, die einfach die WordPress-Funktion add_meta_box()
aufruft. Die Details dieser Funktion wurden bereits mehrfach behandelt. Wenn Sie sich jedoch nicht sicher sind, können Sie sie auf den Codex-Seiten nachlesen.
1 |
public function add_meta_box() { |
2 |
add_meta_box( 'post-type-archives', __('Post Types','my-post-type-archive-links'),array(__CLASS__,'metabox'),'nav-menus' ,'side','low'); |
3 |
}
|
Als nächstes definieren wir die Meta-Box-Rückruffunktion, die für die Anzeige der Innenseiten der Metabox verantwortlich ist:
1 |
public function metabox( ) { |
2 |
global $nav_menu_selected_id; |
3 |
|
4 |
//Get post types
|
5 |
$post_types = get_post_types(array('public'=>true,'_builtin'=>false), 'object');?> |
6 |
|
7 |
<!-- Post type checkbox list -->
|
8 |
<ul id="post-type-archive-checklist"> |
9 |
<?php foreach ($post_types as $type):?> |
10 |
<li><label><input type="checkbox" value ="<?php echo esc_attr($type->name); ?>" /> <?php echo esc_attr($type->labels->name); ?> </label></li> |
11 |
<?php endforeach;?> |
12 |
</ul><!-- /#post-type-archive-checklist --> |
13 |
|
14 |
<!-- 'Add to Menu' button -->
|
15 |
<p class="button-controls" > |
16 |
<span class="add-to-menu" > |
17 |
<input type="submit" id="submit-post-type-archives" <?php disabled( $nav_menu_selected_id, 0 ); ?> value="<?php esc_attr_e('Add to Menu'); ?>" name="add-post-type-menu-item" class="button-secondary submit-add-to-menu" /> |
18 |
</span>
|
19 |
</p>
|
20 |
<?php
|
21 |
}
|
Diese Methode ruft einfach alle öffentlichen benutzerdefinierten Beitragstypen mit get_post_types()
ab und durchläuft sie dann, um eine Liste von Kontrollkästchen zu erstellen. Jedes Kontrollkästchen hat den Namen des Beitragstyps als Wert. Im nächsten Schritt fügen wir Javascript hinzu, das ausgelöst wird, wenn ein Benutzer auf die Schaltfläche "Zum Menü hinzufügen" klickt.
Schritt 4 Das JavaScript
Wir möchten unser JavaScript nur auf der Seite "Darstellung - > Menüverwaltung" in die Warteschlange stellen. Wir haben den Hook admin_enqueue_scripts
verwendet, der nur auf Admin-Seiten ausgelöst wird und den Hook der Seite als Argument übergibt. Der Haken für die Seite Darstellung -> Menü lautet nav-menus.php. Nach dem Einreihen unseres Skripts verwenden wir wp_localize_script
, um das Nonce in unserem JavaScript verfügbar zu machen. Wir nehmen es in die AJAX-Anfrage auf, um zu überprüfen, ob die Aktion beabsichtigt war.
1 |
public function metabox_script($hook) { |
2 |
if( 'nav-menus.php' != $hook ) |
3 |
return; |
4 |
|
5 |
//On Appearance>Menu page, enqueue script:
|
6 |
wp_enqueue_script( 'my-post-type-archive-links_metabox', plugins_url('/metabox.js', __FILE__), array('jquery')); |
7 |
|
8 |
//Add nonce variable
|
9 |
wp_localize_script('my-post-type-archive-links_metabox','MyPostTypeArchiveLinks', array('nonce'=>wp_create_nonce('my-add-post-type-archive-links'))); |
10 |
}
|
Im vorherigen Schritt wurde der Schaltfläche "Zum Menü hinzufügen/Add to Menu" die ID "submit-post-type-archives
" zugewiesen. Wir verwenden jetzt jQuery, um auf diese Schaltfläche zu zielen, und senden beim Klicken eine AJAX-Anfrage, um den Menüpunkt zu erstellen und an das Menü anzuhängen. Das Folgende ist der einzige Teil dieses Tutorials, der außerhalb unserer Klasse lebt. Es sollte sich in einer Datei namens metabox.js in unserem Plug-In-Ordner befinden.
1 |
jQuery(document).ready(function($) { |
2 |
$('#submit-post-type-archives').click(function(event) { |
3 |
event.preventDefault(); |
4 |
|
5 |
/* Get checked boxes */
|
6 |
var postTypes = []; |
7 |
$('#post-type-archive-checklist li :checked').each(function() { |
8 |
postTypes.push($(this).val()); |
9 |
});
|
10 |
|
11 |
/* Send checked post types with our action, and nonce */
|
12 |
$.post( ajaxurl, { |
13 |
action: "my-add-post-type-archive-links", |
14 |
posttypearchive_nonce: MyPostTypeArchiveLinks.nonce, |
15 |
post_types: postTypes |
16 |
},
|
17 |
|
18 |
/* AJAX returns html to add to the menu */
|
19 |
function( response ) { |
20 |
$('#menu-to-edit').append(response); |
21 |
}
|
22 |
);
|
23 |
})
|
24 |
});
|
Beachten Sie die URL, an die wir die Anfrage senden: ajaxurl
. Wir haben es nirgendwo definiert. Es ist eine globale Variable, die von WordPress nur auf der Administratorseite festgelegt wird und auf die Seite verweist, auf der WordPress AJAX-Anforderungen verarbeitet. Wenn Sie auf die Schaltfläche "Senden" klicken, erden die Namen der aktivierten Beitragstypen, eine eindeutige Aktion und Nonce an diese URL gesendet. Wenn WordPress die Anfrage empfängt, löst es den Hook wp_ajax_my-add-post-type-archive-links
aus. Das Nonce ist eine Sicherheitsmaßnahme, um zu überprüfen, ob die Aktion beabsichtigt war.
Schritt 5 Der AJAX-Rückruf
Wir definieren nun die AJAX-Rückruffunktion ajax_add_post_type
.
1 |
public function ajax_add_post_type() { |
2 |
|
3 |
if ( ! current_user_can( 'edit_theme_options' ) ) |
4 |
die('-1'); |
5 |
|
6 |
check_ajax_referer('my-add-post-type-archive-links', 'posttypearchive_nonce'); |
7 |
|
8 |
require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; |
9 |
|
10 |
if(empty($_POST['post_types'])) |
11 |
exit; |
12 |
|
13 |
// Create menu items and store IDs in array
|
14 |
$item_ids=array(); |
15 |
foreach ( (array) $_POST['post_types'] as $post_type) { |
16 |
$post_type_obj = get_post_type_object($post_type); |
17 |
|
18 |
if(!$post_type_obj) |
19 |
continue; |
20 |
|
21 |
$menu_item_data= array( |
22 |
'menu-item-title' => esc_attr($post_type_obj->labels->name), |
23 |
'menu-item-type' => 'post_type_archive', |
24 |
'menu-item-object' => esc_attr($post_type), |
25 |
'menu-item-url' => get_post_type_archive_link($post_type) |
26 |
);
|
27 |
|
28 |
//Collect the items' IDs.
|
29 |
$item_ids[] = wp_update_nav_menu_item(0, 0, $menu_item_data ); |
30 |
}
|
31 |
|
32 |
// If there was an error die here
|
33 |
if ( is_wp_error( $item_ids ) ) |
34 |
die('-1'); |
35 |
|
36 |
// Set up menu items
|
37 |
foreach ( (array) $item_ids as $menu_item_id ) { |
38 |
$menu_obj = get_post( $menu_item_id ); |
39 |
if ( ! empty( $menu_obj->ID ) ) { |
40 |
$menu_obj = wp_setup_nav_menu_item( $menu_obj ); |
41 |
$menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items |
42 |
$menu_items[] = $menu_obj; |
43 |
}
|
44 |
}
|
45 |
|
46 |
// This gets the HTML to returns it to the menu
|
47 |
if ( ! empty( $menu_items ) ) { |
48 |
$args = array( |
49 |
'after' => '', |
50 |
'before' => '', |
51 |
'link_after' => '', |
52 |
'link_before' => '', |
53 |
'walker' => new Walker_Nav_Menu_Edit |
54 |
);
|
55 |
echo walk_nav_menu_tree( $menu_items, 0, (object) $args ); |
56 |
}
|
57 |
|
58 |
// Finally don't forget to exit
|
59 |
exit; |
60 |
}
|
Lassen Sie uns diesen Rückruf nacheinander durchgehen. Zuerst überprüfen wir die Berechtigungen des Benutzers, überprüfen die Nonce und laden die Seite nav-menu.php (wir benötigen einige der Funktionen).
1 |
if ( ! current_user_can( 'edit_theme_options' ) ) |
2 |
die('-1'); |
3 |
|
4 |
check_ajax_referer('my-add-post-type-archive-links','posttypearchive_nonce'); |
5 |
|
6 |
require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; |
7 |
|
8 |
if(empty($_POST['post_types'])) |
9 |
exit; |
Anschließend erstellen wir für jeden ausgewählten Beitragstyp einen Menüpunkt. Wir überprüfen zunächst, ob der Post-Typ, den wir erhalten haben, vorhanden ist, indem wir den von get_post_type_object()
zurückgegebenen Wert überprüfen. Wir können den Archivlink mit der Funktion get_post_type_archive_link()
erhalten.
Menüelemente sind tatsächlich Posts vom Post-Typ 'nav_menu_item
' mit integriertem Post-Meta, einschließlich Feldern, die sich auf 'url
', 'type
' und 'object
' beziehen. Der 'type
' des Elements ist normalerweise 'custom
', 'post_type
' oder 'taxonomy
' - aber wir werden seinen Wert auf 'post_type_archive
' setzen. Der Metawert 'objekt
' des Elements wird normalerweise nur für Elemente vom Typ 'post_type
' oder 'taxonomy
' verwendet und bezieht sich auf den Beitragstyp oder die Taxonomie, auf die sich der Link bezieht. Wir werden dies verwenden, um den Beitragstyp des Archivlinks zu speichern.
1 |
// Create menu items and store IDs in array
|
2 |
$item_ids=array(); |
3 |
foreach ( (array) $_POST['post_types'] as $post_type) { |
4 |
$post_type_obj = get_post_type_object($post_type); |
5 |
|
6 |
if(!$post_type_obj) |
7 |
continue; |
8 |
|
9 |
$menu_item_data= array( |
10 |
'menu-item-title' => esc_attr($post_type_obj->labels->name), |
11 |
'menu-item-type' => 'post_type_archive', |
12 |
'menu-item-object' => esc_attr($post_type), |
13 |
'menu-item-url' => get_post_type_archive_link($post_type) |
14 |
);
|
15 |
|
16 |
// Collect the items' IDs.
|
17 |
$item_ids[] = wp_update_nav_menu_item(0, 0, $menu_item_data ); |
18 |
}
|
19 |
|
20 |
// If there was an error die here
|
21 |
if ( is_wp_error( $item_ids ) ) |
22 |
die('-1'); |
Danach generieren wir einfach den HTML-Code, der dem Menü hinzugefügt wird. Wir verwenden das Array $item_ids
, um ein Array von Menüelementen abzurufen und dieses an eine WordPress-Walker-Klasse zu übergeben, um die harte Arbeit für uns zu erledigen.
1 |
//Set up menu items
|
2 |
foreach ( (array) $item_ids as $menu_item_id ) { |
3 |
$menu_obj = get_post( $menu_item_id ); |
4 |
if ( ! empty( $menu_obj->ID ) ) { |
5 |
$menu_obj = wp_setup_nav_menu_item( $menu_obj ); |
6 |
$menu_obj->label = $menu_obj->title; // don't show "(pending)" in ajax-added items |
7 |
$menu_items[] = $menu_obj; |
8 |
}
|
9 |
}
|
10 |
|
11 |
//This gets the HTML to returns it to the menu
|
12 |
if ( ! empty( $menu_items ) ) { |
13 |
$args = array( |
14 |
'after' => '', |
15 |
'before' => '', |
16 |
'link_after' => '', |
17 |
'link_before' => '', |
18 |
'walker' => new Walker_Nav_Menu_Edit |
19 |
);
|
20 |
echo walk_nav_menu_tree( $menu_items, 0, (object) $args ); |
21 |
}
|
22 |
|
23 |
//Finally don't forget to exit
|
24 |
exit; |
Schritt 6 Der Menüpunkt
Aufgrund eines Fehlers in WordPress wird die URL leider entfernt, wenn der Typ Ihres Elements nicht 'taxonomy
', 'custom
'oder 'post_type
' ist. Um dem entgegenzuwirken, fügen wir die URL manuell hinzu, wenn in einem Menü ein Link 'post_type_archive
' verwendet wird. Dadurch wird auch sichergestellt, dass der Archivlink auf dem neuesten Stand ist (falls Ihre Permalink-Struktur geändert wurde).
1 |
public function setup_archive_item($menu_item){ |
2 |
if($menu_item->type !='post_type_archive') |
3 |
return $menu_item; |
4 |
|
5 |
$post_type = $menu_item->object; |
6 |
$menu_item->url =get_post_type_archive_link($post_type); |
7 |
|
8 |
return $menu_item; |
9 |
}
|
Schritt 7 Verbindung herstellen
Schließlich müssen wir den Artikel "aktuell" machen, wenn wir uns auf der entsprechenden Seite befinden. Ich möchte, dass der Archivlink für den Beitragstyp als aktuell hervorgehoben wird, wenn wir uns auf dieser Archivseite befinden oder einen einzelnen Beitrag dieses Typs anzeigen. Dazu überprüfe ich:
Um den Artikel aktuell zu machen, müssen wir lediglich den Klassen des Artikels, die in $item->classes
gespeichert sind, den current-menu-item
hinzufügen. Wir müssen dann die übergeordneten Elemente im Menü durchlaufen und die Klassen current_item_parent
und current_item_ancestor
hinzufügen. Schauen wir uns jedes Bit einzeln an:
Wir durchlaufen jeden Menüpunkt:
1 |
public function maybe_make_current($items) { |
2 |
foreach ($items as $item) { |
3 |
// This is where we check the item
|
4 |
}
|
5 |
return $items; |
6 |
}
|
Wenn das Element nicht von 'post_type_archive
' ist oder wenn es ist, aber wir möchten nicht, dass es 'aktuell' wird, fahren wir einfach mit dem nächsten Element fort. Denken Sie daran, dass für unsere Archivlinks der Beitragstyp als Objekt des Elements gespeichert wird. Also innerhalb der foreach
-Schleife:
1 |
if('post_type_archive' != $item->type) |
2 |
continue; |
3 |
|
4 |
$post_type = $item->object; |
5 |
if(!is_post_type_archive($post_type)&& !is_singular($post_type)) |
6 |
continue; |
Wenn wir es aktuell machen wollen, geben wir ihm die entsprechende Klasse und nehmen dann seine Eltern in das Menü. Die übergeordneten Elemente eines Menüelements werden als Post-Meta mit dem Metaschlüssel _menu_item_menu_item_parent
gespeichert.
1 |
//Make item current
|
2 |
$item->current = true; |
3 |
$item->classes[] = 'current-menu-item'; |
4 |
|
5 |
//Get menu item's ancestors:
|
6 |
$_anc_id = (int) $item->db_id; |
7 |
$active_ancestor_item_ids=array(); |
8 |
|
9 |
while(( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) && ! in_array( $_anc_id, $active_ancestor_item_ids ) ) { |
10 |
$active_ancestor_item_ids[] = $_anc_id; |
11 |
}
|
Wir durchlaufen dann die Menüelemente und geben den Eltern und Vorfahren des "aktuellen" Elements die entsprechenden Klassen.
1 |
// Loop through the items and give ancestors and parents the appropriate class
|
2 |
foreach ($items as $key=>$parent_item) { |
3 |
$classes = (array) $parent_item->classes; |
4 |
|
5 |
// If menu item is the parent
|
6 |
if ($parent_item->db_id == $item->menu_item_parent ) { |
7 |
$classes[] = 'current-menu-parent'; |
8 |
$items[$key]->current_item_parent = true; |
9 |
}
|
10 |
|
11 |
// If menu item is an ancestor
|
12 |
if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) { |
13 |
$classes[] = 'current-menu-ancestor'; |
14 |
$items[$key]->current_item_ancestor = true; |
15 |
}
|
16 |
|
17 |
$items[$key]->classes = array_unique( $classes ); |
18 |
}
|
Diese Funktion zusammenstellen:
1 |
public function maybe_make_current($items) { |
2 |
foreach ($items as $item) { |
3 |
if('post_type_archive' != $item->type) |
4 |
continue; |
5 |
|
6 |
$post_type = $item->object; |
7 |
if(!is_post_type_archive($post_type)&& !is_singular($post_type)) |
8 |
continue; |
9 |
|
10 |
// Make item current
|
11 |
$item->current = true; |
12 |
$item->classes[] = 'current-menu-item'; |
13 |
|
14 |
// Get menu item's ancestors:
|
15 |
$_anc_id = (int) $item->db_id; |
16 |
$active_ancestor_item_ids=array(); |
17 |
|
18 |
while(( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) && ! in_array( $_anc_id, $active_ancestor_item_ids ) ) { |
19 |
$active_ancestor_item_ids[] = $_anc_id; |
20 |
}
|
21 |
|
22 |
// Loop through ancestors and give them 'ancestor' or 'parent' class
|
23 |
foreach ($items as $key=>$parent_item) { |
24 |
$classes = (array) $parent_item->classes; |
25 |
|
26 |
// If menu item is the parent
|
27 |
if ($parent_item->db_id == $item->menu_item_parent ) { |
28 |
$classes[] = 'current-menu-parent'; |
29 |
$items[$key]->current_item_parent = true; |
30 |
}
|
31 |
|
32 |
// If menu item is an ancestor
|
33 |
if ( in_array( intval( $parent_item->db_id ), $active_ancestor_item_ids ) ) { |
34 |
$classes[] = 'current-menu-ancestor'; |
35 |
$items[$key]->current_item_ancestor = true; |
36 |
}
|
37 |
|
38 |
$items[$key]->classes = array_unique( $classes ); |
39 |
}
|
40 |
|
41 |
}
|
42 |
return $items; |
43 |
}
|
Sie müssen nur noch auf die Administrationsseite Ihres Plugins gehen und das Plugin aktivieren.
Abschluss
Es gibt immer Raum für Verbesserungen. Mit etwas jQuery können Sie beispielsweise einen Link "Alle auswählen" unter den Kontrollkästchen hinzufügen oder ein "Laden"-Symbol anzeigen, während AJAX verarbeitet wird. Jetzt ist dieses Plugin nicht die einfachste Lösung - aber es funktioniert gut und vermeidet die Fallstricke, einfach einen benutzerdefinierten Link hinzuzufügen. Das obige Plugin in seiner Gesamtheit befindet sich auf meinem GitHub. Wenn Sie Kommentare oder Vorschläge haben, können Sie gerne einen Kommentar hinterlassen oder mich über Twitter kontaktieren.