• Hi.

    I’m trying to automatically add a custom field to post. The custom field contains the url to the ‘medium’ thumbnail. I wan’t to do this without defining a featured image and instead I want to use the first image from the post (image attachment). After an extensive search I’ve found the elements required to achieve this but I’m not able to customize it/make it work myself.

    1. FUNCTION: Create custom field

    The code used to create a custom field based on the featured image is the following:

    function w_thumbnail_src() {
        if (has_post_thumbnail()) {
            $image = wp_get_attachment_image_src(get_post_thumbnail_id( $post->ID ), 'four' );
            return $image[0]; // thumbnail url
        } else {
            return 'http://domain.com/wp-content/uploads/2013/08/image.jpg';  // or a default thumbnail url
        }
    }
    
    add_action('publish_page', 'add_custom_field_automatically', 'w_thumbnail_src');
    add_action('publish_post', 'add_custom_field_automatically');
    function add_custom_field_automatically($post_id) {
    	global $wpdb;
    	if(!wp_is_post_revision($post_id)) {
    		add_post_meta($post_id, 'thumbnail_rt', w_thumbnail_src(), true);
    	}
    }

    The best I can make of it in order to make it work with the post attached image is the following (wp_get_attachment_image_src):

    function w_thumbnail_src() {
        if (wp_attachment_is_image()) {
            $image = wp_get_attachment_image_src( $attachment_id, 'four' );
            return $image[0]; // thumbnail url
        } else {
            return 'http://domain.com/wp-content/uploads/2013/08/image.jpg';  // or a default thumbnail url
        }
    }
    
    add_action('publish_page', 'add_custom_field_automatically', 'w_thumbnail_src');
    add_action('publish_post', 'add_custom_field_automatically');
    function add_custom_field_automatically($post_id) {
    	global $wpdb;
    	if(!wp_is_post_revision($post_id)) {
    		add_post_meta($post_id, 'thumbnail_rt', w_thumbnail_src(), true);
    	}
    }

    As you can see wp_get_attachment_image_src requires an id ($attachment_id). This id is not associated with the post as is the case with a featured image id.

    2. FUNCTION: Get $attachment_id

    In order to find the $attachment_id the following code is used:

    function pn_get_attachment_id_from_url( $attachment_url = '' ) {
    
    	global $wpdb;
    	$attachment_id = false;
    
    	// If there is no url, return.
    	if ( '' == $attachment_url )
    		return;
    
    	// Get the upload directory paths
    	$upload_dir_paths = wp_upload_dir();
    
    	// Make sure the upload path base directory exists in the attachment URL, to verify that we're working with a media library image
    	if ( false !== strpos( $attachment_url, $upload_dir_paths['baseurl'] ) ) {
    
    		// If this is the URL of an auto-generated thumbnail, get the URL of the original image
    		$attachment_url = preg_replace( '/-\d+x\d+(?=\.(jpg|jpeg|png|gif)$)/i', '', $attachment_url );
    
    		// Remove the upload path base directory from the attachment URL
    		$attachment_url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $attachment_url );
    
    		// Finally, run a custom database query to get the attachment ID from the modified attachment URL
    		$attachment_id = $wpdb->get_var( $wpdb->prepare( "SELECT wposts.ID FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = '_wp_attached_file' AND wpostmeta.meta_value = '%s' AND wposts.post_type = 'attachment'", $attachment_url ) );
    
    	}
    
    	return $attachment_id;
    }

    3. How to combine these two functions?

    How do I make one function out of these two functions, or how do i make it work. I want the output of the $attachment_id function to be used in the custom field function.

    So to recap:

    – how to make the custom field function work with image attachment instead of featured image/post_thumbnails?
    – how to use the output from the $attachment_id function in the custom field function?

    I’ve been dealing with this issue for a long time now and I feel like these bits of code are the solution. I just don’t know how to put them together. Could use some help, thanks.

Viewing 9 replies - 1 through 9 (of 9 total)
  • Moderator bcworkz

    (@bcworkz)

    Attachments are typically associated with posts, even if not featured images. You can normally get all image attachments to a post by using get_posts(array('post_type' => 'attachment','post_parent' => $post_id, 'post_mime_type' => 'image/jpeg'));. You can then just grab the ID from the first object returned and use wp_get_attachment_image_src() to get the image URL.

    The problem arises when a post has an image inserted from the media library that was initially used in a different post. I know of no way to query for this type of image, AFAIK, one has to parse the content for image tags and hopefully the ID is in one of the assigned classes of the tag.

    If you are dynamically grabbing the first image of a post, why have it shown in a special field? Why not just use it directly for whatever the field’s purpose was? So the URL can be overridden manually if need be? If so, it should only be placed in postmeta if there is no value at all. If there were a value there, it may have been manually placed so your script must not override that.

    I’m afraid I’ve raised more questions rather than answer yours. If you could more specifically explain what you want to do, perhaps I can suggest a workable direction to pursue.

    Thread Starter jubot

    (@jubot)

    Thanks for the reply, you raise valid points. I figured that the way I was approaching the custom field was a bit of a detour but it was the only thing I could get to work. Before, I had it working with featured image, but only if I set one manually. Of course I wanted it automated so I installed an auto featured image plugin. but it would only set featured image after post publish, and the custom field function would fire before that so no image source grab. There I noticed it being an imperfect method.

    What I am trying to do is the following:

    I am using gd star rating widget to show post links with scores and thumbnail in the column. I had it working with the post content %IMAGE% tag (there are 3 options: none, %IMAGE%, custom field), but that only works when uploads are stored in the original wp directory. I want to load images from a subdomain so the gd star rating thumbnail generation does not work. another option is using a custom field. It does not fiend any custom fields so I had to create one. I could have saved my weekend and just use default upload folder, but it would be such a waste not to use this opportunity to load images from a subdomain right form the start.

    So bottom line: I need a custom field which contains the url to the thumbnail of the first (and only) image of a post. So image used in post would be “image.jpg”, custom field would contain “img-200×140.jpg”.

    Moderator bcworkz

    (@bcworkz)

    Ah, I see now. So overwriting the previous meta value is not a big deal, it’ll be the same value in most cases. But I’m also confused about sub-domain images. For a thumbnail to exist, the image would have been uploaded by the media uploader. Are you saying this was done, but not relative to the WP installation with the ratings list? So all the various image sizes are available in the sub-domain, which is not the uploads folder of this installation?

    How are you adding images from the sub-domain to posts? If you use the media uploader, it will copy the referenced file to uploads and regenerate all the registered sizes as well. If you manually enter an <img> tag in the post, the sub-domain image will be used, but it becomes very difficult to know what the thumbnail filename would be, because as far as this installation is concerned, it does not exist.

    You would need to exactly replicate the filename logic employed when the thumbnails were created in order to properly enter the correct URL in the custom field in postmeta.

    Am I misunderstanding something? Does any of this make sense?

    Thread Starter jubot

    (@jubot)

    Thanks for hanging in there. Ok, I think I got it. Let’s forget about gd star rating thumbnail generation. This is all about wp.

    Images are uploaded through the uploader which means that thumbnails will always be generated. The image is added to the post and is in between <img> tags. If the upload directory is on a subdomain then wp will know because it is defined in options.php. The path is there, regardless of where it is stored. So i figured using ‘<?php wp_get_attachment_image_src( $attachment_id, $size ); ?> ‘ would do the trick, you think so too if I understood correctly. $size Being one of the thumbnail sizes, lets say “medium”, which would grab “image-200×140.jpg”, a thumbnail generated after “image.jpg” was uploaded.

    You said it was possible to grab the image attachment:

    “Attachments are typically associated with posts, even if not featured images. You can normally get all image attachments to a post by using get_posts(array(‘post_type’ => ‘attachment’,’post_parent’ => $post_id, ‘post_mime_type’ => ‘image/jpeg’));. You can then just grab the ID from the first object returned and use wp_get_attachment_image_src() to get the image URL.”

    How would that code look like in a function? See I’m not able to put the two parts of code you mention together in one function (I’m in the deep-end php wise).

    Moderator bcworkz

    (@bcworkz)

    I don’t use subdomains, but if your logic is correct, this should work:

    add_action('publish_post', 'add_custom_field_automatically');
    function add_custom_field_automatically($post_id) {
    	$attachments = get_posts(array('post_type' => 'attachment','post_parent' => $post_id, 'post_mime_type' => 'image/jpeg'));
    	$src = wp_get_attachment_image_src($attachments[0]->ID, 'thumbnail');
    	add_post_meta($post_id, 'thumbnail_rt', $src[0], true);
    }

    I did test this and it works fine for images in /uploads/, except that (at least for the test post I used) it seems the first image in the array returned is actually the last image in the post. It may just be the images of this particular post were added in an odd order. The script may need to be modified to find the size of the array and grab the last image, a bit more testing is required.

    And of course I can’t know if it will work for subdomain images. No matter what, it should be a step in the right direction.

    Thread Starter jubot

    (@jubot)

    brilliant stuff, looks like it is working! thank you! works with default upload folder and sub-domain referring to default upload folder. going to do more testing.

    so far there is only one issue: it only works for newly uploaded images. so selecting an image which is already in the library does not work. could it be that there is only attachment association with a post after upload?

    additionally, would it be possible to make it work on edit/update of the post? by re-initiating the code, or maybe a separate function, ‘update_post_meta()’?

    Moderator bcworkz

    (@bcworkz)

    Great news! Progress- gotta love it.

    Yeah, the reused image issue is a big one, which I alluded to in my 1st post. It is true there is only one attachment per image, regardless of the number of times the image is used. As such there is no easy way to query for reused images and establish the attachment ID. The best I can come up with is search each post’s content for an <img> tag. The ID number should be contained in one of the assigned classes. Failing that, we’d have to extract the src URL and query for a matching attachment that way. Either approach strikes me as rather fragile, but I don’t have a better idea.

    As for working on updates, in theory it does. Or at least the action fires and the function runs on updates. The problem is related to the reused image issue. Here is what I believe is happening, though I haven’t confirmed by actual testing. When you remove an image from the post, it remains in the image library and the attachment post remains in the DB with the post as parent, even though it does not appear in the post content. So when the function runs, it finds the first image attached instead of the one recently added for the update. Thus it appears to not work on updates, but it actually does… just not the way we want.

    So once again we find ourselves needing to search content in order to be sure we find an image that is actually used in the current version of the post. The attachments are simply unreliable except when one structures their blog to only use new images and never goes back and fiddles with the images used in content.

    Thread Starter jubot

    (@jubot)

    We went full circle indeed. Technically my site is structured as you mention in the last paragraph so this solution will work (for now). I’m now fiddling with the code from my first post. Have a look if you want, though if not that’s ok because you already helped me out.

    // add automated custom field on post: image thumbnail for rating - find attachment id
    
    // get attachment id
    function pn_get_attachment_id_from_url( $attachment_url = '' ) {
    
    	global $wpdb;
    	$attachment_id = false;
    
    	// If there is no url, return.
    	if ( '' == $attachment_url )
    		return;
    
    	// Get the upload directory paths
    	$upload_dir_paths = wp_upload_dir();
    
    	// Make sure the upload path base directory exists in the attachment URL, to verify that we're working with a media library image
    	if ( false !== strpos( $attachment_url, $upload_dir_paths['baseurl'] ) ) {
    
    		// If this is the URL of an auto-generated thumbnail, get the URL of the original image
    		$attachment_url = preg_replace( '/-\d+x\d+(?=\.(jpg|jpeg|png|gif)$)/i', '', $attachment_url );
    
    		// Remove the upload path base directory from the attachment URL
    		$attachment_url = str_replace( $upload_dir_paths['baseurl'] . '/', '', $attachment_url );
    
    		// Finally, run a custom database query to get the attachment ID from the modified attachment URL
    		$attachment_id = $wpdb->get_var( $wpdb->prepare( "SELECT wposts.ID FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = '_wp_attached_file' AND wpostmeta.meta_value = '%s' AND wposts.post_type = 'attachment'", $attachment_url ) );
    
    	}
    
    	return $attachment_id;
    }
    
    // add automated custom field on post: image thumbnail for rating - custom field
    function w_thumbnail_src() {
    		$attachment_id = pn_get_attachment_id_from_url( $attachment_url = '' );
            $image = wp_get_attachment_image_src( $attachment_id, 'thumbnail' );
            return $image[0]; // thumbnail url
    }
    
    add_action('publish_page', 'add_custom_field_automatically', 'w_thumbnail_src');
    add_action('publish_post', 'add_custom_field_automatically');
    function add_custom_field_automatically($post_id) {
    	global $wpdb;
    	if(!wp_is_post_revision($post_id)) {
    		add_post_meta($post_id, 'thumbnail_rt', w_thumbnail_src(), true);
    	}
    }

    So I define the $attachment_id variable in the second (custom field) function by ‘calling’ the first function: $attachment_id = pn_get_attachment_id_from_url( $attachment_url = ” );

    Not working though. Is this the correct way to of using a return variable from one function in another?

    Moderator bcworkz

    (@bcworkz)

    Close but not quite. The problem is the $attachment_url = '' portion. This syntax is proper when defining a function to assign a default value to a parameter should it be omitted from the calling function. It is improper when actually calling the function because it overwrites the value you intended to pass.

    Which brings us to another problem. When you call pn_get_attachment_id_from_url(), the variable $attachment_url has not been assigned a value, so the entire process serves no purpose.

    But as for assigning the return value to $attachment_id, which was your actual question, that part is fine, that’s the way to do it.

    Finally, w_thumbnail_src() is never called because it was not added as an action callback correctly. You cannot list multiple callback functions like that, you need a separate add_action() call for each callback you wish to add. For the position you placed the w_thumbnail_src parameter, the add_action() function is expecting a priority argument, an integer.

    These are what I saw, there may be other issues. Take care of those and see if you get anywhere. Good luck!

Viewing 9 replies - 1 through 9 (of 9 total)
  • The topic ‘How to define a custom field by using two functions?’ is closed to new replies.