1

I’ve been writing some class and functions to modify the path of thumbnails. I extended the original WP_Image_Editor class to achieve custom structure.

What i want: to store the thumbnails in different folders based on their slugs, in the upload directory. such as : http://example.com/uploads/medium/image.jpg

What i have already done:

class WP_Image_Editor_Custom extends WP_Image_Editor_GD {
    public function generate_filename($prefix = NULL, $dest_path = NULL, $extension = NULL) {
        global $current_size_slug;
        // If empty, generate a prefix with the parent method get_suffix().
        if(!$prefix)
            $prefix = $this->get_suffix();

        // Determine extension and directory based on file path.
        $info = pathinfo($this->file);
        $dir  = ABSPATH."/media/";
        $ext  = $info['extension'];

        // Determine image name.
        $name = wp_basename($this->file, ".$ext");

        // Allow extension to be changed via method argument.
        $new_ext = strtolower($extension ? $extension : $ext);

        // Default to $_dest_path if method argument is not set or invalid.
        if(!is_null($dest_path) && $_dest_path = realpath($dest_path))
            $dir = $_dest_path;

        // Return our new prefixed filename.
        $slug = $current_size_slug; 
        return trailingslashit($dir)."{$slug}/{$name}.{$new_ext}";
    }
    function multi_resize($sizes) {
        $sizes = parent::multi_resize($sizes);
        foreach($sizes as $slug => $data)
            $sizes[$slug]['file'] = $slug."/".$data['file'];
            $current_size_slug = $slug;
        return $sizes;
    }
}

When i upload the image, the thumbnails are created properly, however the filenames are not. The $slug value is not passed from multi_resize() to generate_filename.

I tried to write the multi_resize() function as below:

class WP_Image_Editor_Custom extends WP_Image_Editor_GD {
    public function generate_filename($prefix = NULL, $dest_path = NULL, $extension = NULL) {
        global $current_size_slug;
        // If empty, generate a prefix with the parent method get_suffix().
        if(!$prefix)
            $prefix = $this->get_suffix();

        // Determine extension and directory based on file path.
        $info = pathinfo($this->file);
        $dir  = ABSPATH."/media/";
        $ext  = $info['extension'];

        // Determine image name.
        $name = wp_basename($this->file, ".$ext");

        // Allow extension to be changed via method argument.
        $new_ext = strtolower($extension ? $extension : $ext);

        // Default to $_dest_path if method argument is not set or invalid.
        if(!is_null($dest_path) && $_dest_path = realpath($dest_path))
            $dir = $_dest_path;

        // Return our new prefixed filename.
        $slug = $current_size_slug; 
        return trailingslashit($dir)."{$slug}/{$name}.{$new_ext}";
    }
    function multi_resize($sizes) {
        $sizes = parent::multi_resize($sizes);
        foreach($sizes as $slug => $data)
            $sizes[$slug]['file'] = $slug."/".$data['file'];
            $current_size_slug = $slug;
        return $sizes;
    }
}

Now the $slug is passed to generate_filename() but the thumbnails are all generated in uploads folder, overwriting each other. How can i do this?

I'm clueless here, any help is appreciated.

2
+50

Try adding this to functions.php:

add_filter("wp_image_editors", "my_wp_image_editors");

function my_wp_image_editors($editors) {
    array_unshift($editors, "WP_Image_Editor_Custom");
    return $editors;
}

// Include the existing classes first in order to extend them.
require_once ABSPATH . WPINC . "/class-wp-image-editor.php";
require_once ABSPATH . WPINC . "/class-wp-image-editor-gd.php";

class WP_Image_Editor_Custom extends WP_Image_Editor_GD {

    public function generate_filename($suffix = null, $dest_path = null, $extension = null) {
        // $suffix will be appended to the destination filename, just before the extension
        if (!$suffix) {
            $suffix = $this->get_suffix();
        }

        $dir = pathinfo($this->file, PATHINFO_DIRNAME);
        $ext = pathinfo($this->file, PATHINFO_EXTENSION);

        $name = wp_basename($this->file, ".$ext");
        $new_ext = strtolower($extension ? $extension : $ext );

        if (!is_null($dest_path) && $_dest_path = realpath($dest_path)) {
            $dir = $_dest_path;
        }
        //we get the dimensions using explode, we could have used the properties of $this->file[height] but the suffix could have been provided
        $size_from_suffix = explode("x", $suffix);
        //we get the slug_name for this dimension
        $slug_name = $this->get_slug_by_size($size_from_suffix[0], $size_from_suffix[1]);

        return trailingslashit($dir) . "{$slug_name}/{$name}.{$new_ext}";
    }

    function multi_resize($sizes) {
        $sizes = parent::multi_resize($sizes);

        //we add the slug to the file path
        foreach ($sizes as $slug => $data) {
            $sizes[$slug]['file'] = $slug . "/" . $data['file'];
        }

        return $sizes;
    }

    function get_slug_by_size($width, $height) {

        // Make thumbnails and other intermediate sizes.
        $_wp_additional_image_sizes = wp_get_additional_image_sizes();

        $image_sizes = array(); //all sizes the default ones and the custom ones in one array
        foreach (get_intermediate_image_sizes() as $s) {
            $image_sizes[$s] = array('width' => '', 'height' => '', 'crop' => false);
            if (isset($_wp_additional_image_sizes[$s]['width'])) {
                // For theme-added sizes
                $image_sizes[$s]['width'] = intval($_wp_additional_image_sizes[$s]['width']);
            } else {
                // For default sizes set in options
                $image_sizes[$s]['width'] = get_option("{$s}_size_w");
            }

            if (isset($_wp_additional_image_sizes[$s]['height'])) {
                // For theme-added sizes
                $image_sizes[$s]['height'] = intval($_wp_additional_image_sizes[$s]['height']);
            } else {
                // For default sizes set in options
                $image_sizes[$s]['height'] = get_option("{$s}_size_h");
            }

            if (isset($_wp_additional_image_sizes[$s]['crop'])) {
                // For theme-added sizes
                $image_sizes[$s]['crop'] = $_wp_additional_image_sizes[$s]['crop'];
            } else {
                // For default sizes set in options
                $image_sizes[$s]['crop'] = get_option("{$s}_crop");
            }
        }
        $slug_name = ""; //the slug name

        if($width >= $height){
          foreach ($image_sizes as $slug => $data) { //we start checking
            if ($data['width'] == $width) {//we use only width because regardless of the height, the width is the one used for resizing in all cases with crop 1 or 0
                $slug_name = $slug;
            }
            /*
             * There could be custom added image sizes that have the same width as one of the defaults so we also use height here
             * if there are several image sizes with the same width all of them will override the previous one leaving the last one, here we get also the last one
             * since is looping the entire list, the height is used as a max value for non-hard cropped sizes
             *  */
              if ($data['width'] == $width && $data['height'] == $height) {
                  $slug_name = $slug;
              }
          }
         }else{
           foreach ($image_sizes as $slug => $data) {
              if ($data['height'] == $height) {
                  $slug_name = $slug;
              }
              if ($data['height'] == $height && $data['width'] == $width ) {
                  $slug_name = $slug;
              }
            }
         }
        return $slug_name;
    }
}

i know you already know almost all of this code, notice that the generate_filename function has been updated to the current one, you will be more interested in the get_slug_by_size function which is the key part that you were missing. This is also working with custom image sizes, as can be seen here:

enter image description here

home-bottom is a image size i added. Right now wordpress has 4 different default image sizes:

    Array
    (
        [thumbnail] => Array // Thumbnail (150 x 150 hard cropped)
            (
                [width] => 150
                [height] => 150
                [crop] => 1
            )

        [medium] => Array // Medium resolution (300 x 300 max height 300px)
            (
                [width] => 300
                [height] => 300
                [crop] => 
            )

        [medium_large] => Array //Medium Large (added in WP 4.4) resolution (768 x 0 infinite height)
            (
                [width] => 768
                [height] => 0
                [crop] => 
            )

        [large] => Array // Large resolution (1024 x 1024 max height 1024px)
            (
                [width] => 1024
                [height] => 1024
                [crop] => 
            )

    )
// Full resolution (original size uploaded) this one is not in the array.

if you upload an image of width 310 only thumbnail and medium images would be created WordPress will not create bigger ones, so with the code above, only 2 folders will be created.

| improve this answer | |
  • I updated the code for vertical images, as for small images that are more small than the minimun image size (thumbnail by default given that you didnt add a custom one more small) there is not going to be a "resized" image only the original. – David Lee Feb 15 '17 at 13:00
  • np, i think i saw 2, i feel this structure for the images better than the default since we would need to change only 1 var (the name of the folder) to change a entire set of images versus the name with dimensions... – David Lee Feb 15 '17 at 13:30
1

A code sample explaining its use case will be more helpful. Not sure how you're going to use it, but to simply access all the slugs inside $sizes you can use this.

The multi_resize() gets its 'sizes' as a parameter, which is provided to it by wp_generate_attachment_metadata() here. A simple approach to get the sizes array will be to store it in a class property. Such as

Add this before all functions.

public $current_size_slug = '';

This is generate_filename() function with necessary modifications and comments to help you understand how it works:

public function generate_filename( $prefix = null, $dest_path = null, $extension = null ) {
// <-- Now we have the current thumbnail slug.-->
$slug = $this->current_size_slug;
// If empty, generate a prefix with the parent method get_suffix().
if ( ! $prefix ) {
    $prefix = $this->get_suffix();
}

// Determine extension and directory based on file path.
$info = pathinfo( $this->file );
$dir  = $info['dirname'];
$ext  = $info['extension'];

// Determine image name.
$name = wp_basename( $this->file, ".$ext" );

// Allow extension to be changed via method argument.
$new_ext = strtolower( $extension ? $extension : $ext );

// Default to $_dest_path if method argument is not set or invalid.
if ( ! is_null( $dest_path ) && $_dest_path = realpath( $dest_path ) ) {
    $dir = $_dest_path;
}

// Return our new prefixed filename.
// <-- Replaced prefix with slug. -->
return trailingslashit( $dir ) . "{$slug}/{$name}.{$new_ext}"; 

}

This multi_resize() function which actually sets the $current_size_slug before calling _save()

function multi_resize( $sizes ) {
$metadata  = array();
$orig_size = $this->size;

foreach ( $sizes as $size => $size_data ) {
    if ( ! isset( $size_data['width'] ) && ! isset( $size_data['height'] ) ) {
        continue;
    }

    if ( ! isset( $size_data['width'] ) ) {
        $size_data['width'] = null;
    }
    if ( ! isset( $size_data['height'] ) ) {
        $size_data['height'] = null;
    }

    if ( ! isset( $size_data['crop'] ) ) {
        $size_data['crop'] = false;
    }

    $image     = $this->_resize( $size_data['width'], $size_data['height'], $size_data['crop'] );
    $duplicate = ( ( $orig_size['width'] == $size_data['width'] ) && ( $orig_size['height'] == $size_data['height'] ) );

    if ( ! is_wp_error( $image ) && ! $duplicate ) {
        // We set the current slug before calling the save function.
        $this->current_size_slug = $size;

        $resized = $this->_save( $image );

        imagedestroy( $image );

        if ( ! is_wp_error( $resized ) && $resized ) {
            unset( $resized['path'] );
            $metadata[ $size ] = $resized;
        }
    }

    $this->size = $orig_size;
}

return $metadata;
}

The _save() function actually saves the file on the server, to do so it calls the generate_filename(). I hope this answers your question.
Please consider hiring a developer for custom jobs like these if you're not comfortable with PHP and WordPress
Do Not Copy and Paste code unless you know what you're doing.

| improve this answer | |
  • Replace your function multi_resize($sizes) with mine. and use $this->all_sizes where you want to use them. – devkabiir Feb 10 '17 at 14:09
  • I'm assuming this is what you want : example.com/thumbs/slug/image.jpg ? is this what you want? – devkabiir Feb 10 '17 at 14:13
  • You should avoid using Global variables, it's mostly harmless in this example. But it's a bad practice to use Global variables. – devkabiir Feb 10 '17 at 15:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.