媒體庫文件夾是WordPress 一直缺失的一個(gè)功能,對于喜歡讓一切東西分類存放整齊的用戶來說,把所有媒體放在一起很難受,查找不方便不說,還很容易導(dǎo)致媒體文件的重復(fù)上傳,浪費(fèi)服務(wù)器的空間。今天,我將向你展示如何在向 WordPress 媒體庫上傳媒體文件時(shí)添加文件夾選擇,效果如圖。
如何在網(wǎng)站上添加媒體庫文件夾
目前有兩種選擇:
- 我們可以在 WordPress 數(shù)據(jù)庫中創(chuàng)建一個(gè)自定義表,用它來建立文件夾結(jié)構(gòu)。
- 或者為
附件文章類型注冊一個(gè)分類方法,并將其也用于文件夾。
在我看來,在這里為媒體文件夾創(chuàng)建一個(gè)新的數(shù)據(jù)庫表并不合理,因?yàn)榉诸惙椒ū硪呀?jīng)擁有了我們需要的所有數(shù)據(jù):名稱、標(biāo)題、父類、計(jì)數(shù),而且我們可以使用term_group來按自定義順序顯示文件夾。
另外,如果選擇自定義數(shù)據(jù)庫表,我們將需要開發(fā)一個(gè)管理文件夾的界面,這會(huì)為我們帶來很多不必要的麻煩。
為附件注冊分類方法
很好,我們決定在網(wǎng)站上使用自定義分類法作為文件夾結(jié)構(gòu)。這意味著,是時(shí)候在這里使用register_taxonomy()函數(shù)了。對了,別忘了把它放在 init 鉤子里面。
register_taxonomy(
'folders',
array( 'attachment' ),
array(
'hierarchical' => true,
'labels' => array(
'name' => 'Folders',
'singular_name' => 'Folder',
'search_items' => 'Search folders',
'all_items' => 'All folders',
'parent_item' => 'Parent folder',
'edit_item' => 'Edit folder',
'update_item' => 'Update folder',
'add_new_item' => 'Add new folder',
'new_item_name' => 'New folder name',
'menu_name' => 'Folders',
),
'show_ui' => true,
'show_admin_column' => true,
'query_var' => true,
'rewrite' => false,
'update_count_callback' => 'wprs_update_folder_attachment_count',
)
);
register_taxonomy()函數(shù)的大部分參數(shù)取決于你是否要使用標(biāo)準(zhǔn)分類管理界面來管理媒體庫文件夾。
例如,如果你決定以自定義方式顯示文件夾,你可以跳過整個(gè)labels參數(shù),將show_ui設(shè)為false(甚至將public設(shè)為false)等。
除此之外,我們還需要做以下其中一件事:
- 使用
update_count_callback參數(shù)修改標(biāo)準(zhǔn)的_update_post_term_count函數(shù)。 - 或者直接使用
update_post_term_count_statuses鉤子,允許在計(jì)算文件夾中的附件總數(shù)時(shí)計(jì)算繼承狀態(tài)。
在 WordPress 中,默認(rèn)情況下分類條件中的附件數(shù)只能正確計(jì)算附加到文章中的附件。
以下是 update_count_callback 方法:
function wprs_update_folder_attachment_count( $terms, $taxonomy ) {
global $wpdb;
foreach ( (array) $terms as $term_id ) {
$count = 0;
$count += (int) $wpdb->get_var(
$wpdb->prepare(
"
SELECT COUNT(*)
FROM $wpdb->term_relationships, $wpdb->posts p1
WHERE p1.ID = $wpdb->term_relationships.object_id
AND post_status = 'inherit'
AND post_type = 'attachment'
AND term_taxonomy_id = %d
",
$term_id
)
);
do_action( 'edit_term_taxonomy', $term_id, $taxonomy->name );
$wpdb->update(
$wpdb->term_taxonomy,
compact( 'count' ),
array( 'term_taxonomy_id' => $term_id )
);
do_action( 'edited_term_taxonomy', $term, $taxonomy->name );
}
}
有趣的是,就在創(chuàng)建這個(gè)代碼段之后,我發(fā)現(xiàn)我們可以使用 WordPress 的另一個(gè)標(biāo)準(zhǔn)回調(diào)函數(shù),即 _update_generic_term_count,它似乎對附件分類法非常有效。
'update_count_callback' => '_update_generic_term_count',
總之,這里也有 update_post_term_count_statuses 方法:
add_filter( 'update_post_term_count_statuses', function( $statuses, $taxonomy ) {
if( 'folders' === $taxonomy->name ) {
$statuses[] = 'inherit';
$statuses = array_unique( $statuses );
}
return $statuses;
}, 25, 2 );
像 WordPress 常規(guī)術(shù)語一樣添加、重命名或刪除媒體庫文件夾
老實(shí)說,這里似乎沒有什么具體的內(nèi)容可以寫。你只需進(jìn)入媒體 > 文件夾,就可以像編輯普通分類術(shù)語一樣編輯 WordPress 媒體庫文件夾:

上傳媒體時(shí)選擇文件夾
好了,簡單的部分已經(jīng)結(jié)束?,F(xiàn)在要做的是更有趣的事情。我們將使用 JavaScript 對 WordPress Plupload 上傳器和媒體模式 wp.media 進(jìn)行大量定制。
這就是我們應(yīng)該得到的結(jié)果:

讓我們從 PHP 代碼段和pre-upload-ui操作鉤子開始:
add_action( 'pre-upload-ui', 'wprs_select_folder' );
function wprs_select_folder() {
wp_dropdown_categories( array(
'hide_empty' => 0,
'hide_if_empty' => false,
'taxonomy' => 'folders',
'name' => 'folder_id',
'id' => 'folders',
'orderby' => 'name',
'hierarchical' => true,
'show_option_none' => 'Choose folder',
) );
}
JavaScript 代碼將根據(jù)我們當(dāng)前執(zhí)行文件夾選擇的頁面而有所不同。
媒體 > 添加新頁面:
if( $( 'body.wp-admin' ).hasClass( 'media-new-php' ) && 'object' == typeof window.uploader ) {
window.uploader.bind( 'BeforeUpload', function( up ) {
const settings = up.settings.multipart_params;
settings.folder_id = $( '#folder_id' ).val()
})
}
在 WordPress 媒體模式窗口內(nèi),編輯文章時(shí),也不僅限于此:
if( 'function' == typeof wp.Uploader ) {
$.extend( wp.Uploader.prototype, {
init: function() {
let selectedFolder = -1;
// this part is a little bit tricky, but we need it without a doubt
$( 'body' ).on( 'change', '#folder_id', function() {
selectedFolder = $(this).val()
} )
this.uploader && this.uploader.bind( 'BeforeUpload', function( up, file ) {
up.settings.multipart_params = up.settings.multipart_params || {}
up.settings.multipart_params.folder_id = selectedFolder
})
}
})
}
這里還有一件事:除非你在 setTimeout() 函數(shù)(任意時(shí)間間隔)內(nèi)運(yùn)行,否則很可能不會(huì)觸發(fā)上述代碼。
好了,現(xiàn)在該在上傳附件時(shí)處理 folder_id 參數(shù)了。幸運(yùn)的是,這部分很容易,可以通過 WordPress 的鉤子 add_attachment 實(shí)現(xiàn)。
add_action( 'add_attachment', 'wprs_add_attachment_to_folder' );
function wprs_add_attachment_to_folder( $attachment_id ) {
if(
! isset( $_POST[ '_wpnonce' ] )
|| ! wp_verify_nonce( $_POST[ '_wpnonce' ], 'media-form' )
) {
return;
}
$folder_id = ! empty( $_POST[ 'folder_id' ] ) ? (int) $_POST[ 'folder_id' ] : 0;
if( ! $folder_id ) ) {
return;
}
wp_set_object_terms( $attachment_id, $folder_id, 'folders' );
}
不要忘記驗(yàn)證 nonce。這很容易,因?yàn)閮商幍?$action 操作值相同。
編輯媒體時(shí)更改文件夾
這里有 3 種不同的情況:
- 從媒體庫的 “列表視圖 “編輯媒體文件時(shí),媒體庫文件夾會(huì)像經(jīng)典編輯器中的自定義分類法一樣顯示。在這里我們不需要做什么,開箱即用。
- 從 “網(wǎng)格視圖 “編輯媒體文件時(shí),默認(rèn)情況下不會(huì)正確顯示文件夾結(jié)構(gòu)。只會(huì)顯示一個(gè)字段,這并不好。而這正是我們需要做的大部分工作。
- 你可以隨時(shí)創(chuàng)建一堆批量操作,用于向特定媒體庫文件夾添加或移除媒體文件。
好了,現(xiàn)在我們的目標(biāo)是顯示如下截圖所示的媒體庫文件夾結(jié)構(gòu):

好消息–本章不使用 JavaScript,PHP 代碼段如下:
add_filter( 'attachment_fields_to_edit', 'wprs_edit_attachment_fields', 25, 2 );
function wprs_edit_attachment_fields( $form_fields, $post ) {
// prepare an array for new folder fields here
$folder_fields = array(
'label' => 'Folders',
'show_in_edit' => false,
'input' => 'html',
'value' => '',
);
$taxonomy_name = 'folders';
// get the assigned media library folders from the cache
$terms = get_the_terms( $post->ID, $taxonomy_name );
if( $terms ) {
$folder_fields[ 'value' ] = join( ', ', wp_list_pluck( $terms, 'slug' ) );
}
ob_start();
wp_terms_checklist(
$post->ID,
array(
'taxonomy' => $taxonomy_name,
'checked_ontop' => false,
'walker' => new Rudr_Folders_Walker()
)
);
$html = '<ul class="term-list">' . ob_get_contents() . '</ul>';
ob_end_clean();
$folder_fields[ 'html' ] = $html;
$form_fields[ $taxonomy_name ] = $folder_fields;
return $form_fields;
}
class Rudr_Folders_Walker extends Walker {
var $db_fields = array(
'parent' => 'parent',
'id' => 'term_id',
);
function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat( "\t", $depth );
$output .= "$indent<ul class='children'>\n";
}
function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat( "\t", $depth );
$output .= "$indent</ul>\n";
}
function start_el( &$output, $term, $depth = 0, $args = array(), $id = 0 ) {
$output .= sprintf(
"\n<li id='%s-%s'><label class='selectit'><input value='%s' type='checkbox' name='%s' id='%s' %s %s /> %s</label>",
$term->taxonomy,
$term->term_id,
$term->slug,
"tax_input[{$term->taxonomy}][{$term->slug}]",
"in-{$term->taxonomy}-{$term->term_id}",
checked( in_array( $term->term_id, $args[ 'selected_cats' ] ), true, false ),
disabled( empty( $args[ 'disabled' ] ), false, false ),
esc_html( $term->name )
);
}
function end_el( &$output, $term, $depth = 0, $args = array() ) {
$output .= "</li>\n";
}
}
文件夾復(fù)選框是否正確保存?我也是這么想的。
問題在于 WordPress 希望文件夾字段的值是一個(gè)逗號(hào)分隔的字符串,但我們只是提供了一個(gè)數(shù)組。順便說一下,有不同的方法可以解決這個(gè)問題。例如,我們可以使用下面的鉤子將wp_ajax_save_attachment_compat()函數(shù)改為我們自定義的函數(shù):
add_filter('wp_ajax_save-attachment-compat', 'custom_save_attachment_compat', 0);
或者,我們也可以使用 JavaScript 來確保 WordPress 接收到以逗號(hào)分隔的自定義分類(文件夾)詞條字符串。
差點(diǎn)忘了,還有一點(diǎn) CSS:
.compat-field-folders .term-list {
margin-top: 5px;
}
.compat-field-folders .children {
margin: 5px 0 0 20px;
}
.compat-field-folders .field input[type=checkbox] {
margin-top: 0;
}
創(chuàng)建過濾器以顯示特定文件夾中的媒體
如果你認(rèn)為我們可以使用 restrict_manage_posts 操作輕松實(shí)現(xiàn)這一目標(biāo),那么你說對了一半,因?yàn)橐磺卸既Q于使用的是 “網(wǎng)格 “還是 “列表 “視圖。
在 “List “視圖中,使用restrict_manage_posts操作鉤子就足夠了,我們的代碼片段將如下所示:
add_action( 'restrict_manage_posts', 'wprs_list_view_filter' );
function wprs_list_view_filter() {
global $typenow;
if( 'attachment' !== $typenow ) {
return;
}
$selected = isset( $_GET[ 'folders' ] ) ? $_GET[ 'folders' ] : false;
wp_dropdown_categories(
array(
'show_option_all' => 'All folders',
'taxonomy' => 'folders',
'name' => 'folders',
'orderby' => 'name',
'selected' => $selected,
'hierarchical' => true,
'value_field' => 'slug',
'depth' => 3,
'hide_empty' => true,
)
);
}
在 “網(wǎng)格 “視圖中,解決方案要復(fù)雜得多:
(function(){
const MediaLibraryTaxonomyFilter = wp.media.view.AttachmentFilters.extend({
// literally anything here
id: 'rudr-grid-taxonomy-filter',
createFilters: function() {
const filters = {}
_.each( FolderTaxonomyTerms.terms || {}, function( value, index ) {
filters[ value.term_id ] = {
text: value.name,
props: {
folders: value.slug,
}
}
})
filters.all = {
text: 'All folders',
props: {
folders: ''
},
priority: 10
}
this.filters = filters
}
})
// add the current AttachmentsBrowser into a constant
const AttachmentsBrowser = wp.media.view.AttachmentsBrowser;
wp.media.view.AttachmentsBrowser = AttachmentsBrowser.extend({
createToolbar: function() {
AttachmentsBrowser.prototype.createToolbar.call( this )
this.toolbar.set(
'MediaLibraryTaxonomyFilter',
new MediaLibraryTaxonomyFilter({
controller: this.controller,
model: this.collection.props,
priority: -75
}).render()
)
}
})
})()
如果你想知道 FolderTaxonomyTerms 是什么–它只是一個(gè)文件夾對象,我使用 wp_localize_script()和 get_terms() 函數(shù)打印了它。
下面就是結(jié)果:

在這段代碼的幫助下,過濾器不僅會(huì)出現(xiàn)在媒體 > 庫頁面,還會(huì)出現(xiàn)在媒體模態(tài)框中,當(dāng)你想在文章中插入圖片時(shí),可以在這里選擇圖片。




