How to create a media library folder without using a plugin
Media library folder is a feature that WordPress has been missing, for users who like to keep everything categorized and neatly stored, it's hard to put all the media together, it's not easy to find, not to mention, it's also easy to lead to duplicate uploads of media files, which is a waste of space on the server. Today, I will show you how to add folder selection when uploading media files to WordPress media library, the effect is as shown in the picture.
How to add a media library folder to your website
There are currently two options:
- We can create a custom table in the WordPress database and use it to build the folder structure.
- Or for
attachment (email)The article type registers a taxonomy and uses it for folders as well.
It doesn't make sense to me to create a new database table for the media folder here, since the taxonomy table already has all the data we need:name (of a thing)、caption、parent class、reckoningand we can use theterm_groupto display folders in a customized order.
Also, if we choose to customize the database tables, we will need to develop an interface to manage folders, which will bring us a lot of unnecessary trouble.
Registration of classification methods for annexes
Great, we've decided to use a custom taxonomy as a folder structure on our website. This means that it's time to use theregister_taxonomy()function up. By the way, don't forget to put it in the init Inside the hook.
register_taxonomy(
'folders',
array( 'attachment' ),
array(
'hierarchical' => true,
'labels' => array(
'name' => 'Folders',
'singular_name' => 'Folders', 'search_items' =>
'search_items' => 'Search folders',
'all_items' => 'All folders', 'parent_item' =>
'edit_item' => 'Edit folder', 'update_item' =>
), 'show_ui' => true
'show_admin_column' => true,
'query_var' => true,
'rewrite' => false, 'update_count_call' => true
'update_count_callback' => 'wprs_update_folder_attachment_count',
)
);
register_taxonomy()Most of the function's arguments depend on whether you want to use the standard category management interface for media library folders.
For example, if you decide to display folders in a customized way, you can skip the entirelabelsparameter, which sets theshow_uiset up asfalse(and will evenpublicset up asfalse), etc.
On top of that, we need to do one of the following:
- utilization
update_count_callbackParameter modification of the standard_update_post_term_countfunction. - Or just use the
update_post_term_count_statuseshook that allows the total number of attachments in a folder to be counted when thepredecessorStatus.
In WordPress, by default, the number of attachments in a category condition is only counted correctlyAttach to articleAnnex to the report of the Secretary-General on the work of the Organization for Security and Cooperation in Europe。
following update_count_callback method::
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 );
}
}
Interestingly, right after creating this snippet, I realized that we can use another standard WordPress callback function, the _update_generic_term_count, it seems to work very well for the Annex Taxonomy.
'update_count_callback' => '_update_generic_term_count',
Anyway, here too update_post_term_count_statuses method::
add_filter( 'update_post_term_count_statuses', function( $statuses, $taxonomy ) {
if( 'folders' === $taxonomy->name ) {
$statuses[] = 'inherit';
$statuses = array_unique( $statuses );
}
return $statuses;
}, 25, 2 );
Add, rename or delete media library folders like regular WordPress terminology
Honestly, there doesn't seem to be anything specific to write about here. All you have to do is go toMedia > FoldersIf you want to edit the WordPress Media Library folder, you can edit it just like a normal category term:

Selecting a folder when uploading media
Okay, the easy part is over. Now it's time for the more interesting stuff. We're going to use JavaScript for the WordPress Plupload uploader and media mode. wp.media Make a lot of customizations.
That's what we deserve:

Let's start with the PHP snippet andpre-upload-uiOperation Hook Start:
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
'hierarchical' => true, 'show_option_none'
'show_option_none' => 'Choose folder', .
) );
}
The JavaScript code will vary depending on which page we are currently executing the folder selection.
Media > Add New Page::
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()
})
}
It's not limited to editing posts within the WordPress Media Mode window either:
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
})
}
})
}
Here's another thing: unless you're in setTimeout() function (at arbitrary intervals) is run, otherwise the above code will most likely not be triggered.
Okay, now it's time to deal with attachments as they are uploaded. folder_id parameter too. Luckily, this part is easy and can be done with the WordPress hooks add_attachment Realization.
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' );
}
Don't forget to verify the nonce. this is easy because the two places where the $action OperationThe values are the same.
Changing folders when editing media
There are 3 different scenarios here:
- When editing media files from the Media Library's List View, the Media Library folder is displayed like a custom taxonomy in the Classic Editor. We don't need to do anything here out of the box.
- When editing media files from the Grid View, the folder structure is not displayed correctly by default. Only one field will be displayed, which is not good. And that's where we need to do most of our work.
- You can always create a bunch of batch actions for adding or removing media files to or from a specific media library folder.
Okay, now our goal is to display the media library folder structure as shown in the following screenshot:

Good news-This chapter does not use JavaScript; the PHP snippet is below:
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'
'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 ) { $taxonomy_name = 'folders'; // get the assigned media library folders from the cache.
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 = "' . ob_get_contents() . '
' ;
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\n";
}
function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat( "\t", $depth );
$output . = "$indent
\n';
}
function start_el( &$output, $term, $depth = 0, $args = array(), $id = 0 ) {
$output . = sprintf(
'\n",
$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( $erm->name )
).
}
function end_el( &$output, $term, $depth = 0, $args = array() ) {
$output . = " \n";
}
}
Are the folder checkboxes saved correctly? That's what I was thinking.
The problem is that WordPress expects the value of the folder field to be a comma-separated string, but we just provide an array. By the way, there are different ways to solve this problem. For example, we can use the following hook to set the value of thewp_ajax_save_attachment_compat()function to our customized function:
add_filter('wp_ajax_save-attachment-compat', 'custom_save_attachment_compat', 0);
Alternatively, we can use JavaScript to ensure that WordPress receives comma-separated strings of customized category (folder) entries.
Almost forgot, there's a little 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;
}
Create filters to display media in specific folders
If you think we can easily accomplish this using the restrict_manage_posts operation, you're half right, because it all depends on whether a “grid“ or “list“ view is used.
In the List view, use therestrict_manage_postsManipulating the hooks will suffice, and our code snippet will look like the following:
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,
)
);
}
In the Grid view, the solution is much more complex:
(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: {
props: {
folders: value.slug, {
}
}
})
filters.all = {
text: 'All folders',
props: {
folders: ''
}, priority: 10
priority: 10
}
this.filters = filters
}
})
// add the current AttachmentsBrowser into a constant
const AttachmentsBrowser = wp.media.view.
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.
model: this.collection.props, priority: -75
priority: -75
}).render()
)
}
})
})()
If you're wondering. FolderTaxonomyTerms What is it - it's just a folder object that I use to wp_localize_script()和 get_terms() function prints it.
Here are the results:

With the help of this code, the filter will not only appear in theMedia > Librarypage, it will also appear in the media modal box, where you can select an image when you want to insert it in your post.

