32

Is there any way I can take a URL of an image and find the attachment or post id of that image in the database?

Here is the situation:

I'm in a loop going over all the 'img' tags that are surrounded by 'a' tags in my post content. if the src attribute of the 'img' tag does not match the href attribute of the outer 'a' tag, then i want to replace the 'img' tag. In doing this, if the 'img' that is to be removed is in the gallery, i want to delete that post, and then put my replacement 'img' in its place. I tried using a function like this:

function find_image_post_id($url) {
  global $wpdb;
  $postid = $wpdb->get_var($wpdb->prepare("SELECT DISTINCT ID FROM $wpdb->posts WHERE guid='$url'"));
  if ($postid) {
    return $postid;
  }
  return false;
}

This apparently is not right because the guid is ironically not globally unique. I had (earlier in the same script) uploaded a file with the same name (why? because it was higher resolution and i am trying to replace low resolution versions of the same image) and although wordpress will save the image with a different name in the directory, the guid's were set to be the same. (possibly a bug).

Is there another technique I can use?

  • You can porbably set request variables according to your URL, instantiate WP_Query and obtain the information from it. – hakre Jan 10 '11 at 12:38
  • It would help if you can update your question and post some examples of your HTML that includes URLs you want to replace so we can discuss them. – MikeSchinkel Jan 10 '11 at 16:46
  • Mike is right there. Are the larger images you're linking to on external sites? If not then you just need to choose the full size when you add the image to your post and you have the option not to link it anywhere if it doesn't make sense to anymore. – sanchothefat Jan 10 '11 at 18:46
29

Massively improved function developed for plugin heavy on images:

if ( ! function_exists( 'get_attachment_id' ) ) {
    /**
     * Get the Attachment ID for a given image URL.
     *
     * @link   http://wordpress.stackexchange.com/a/7094
     *
     * @param  string $url
     *
     * @return boolean|integer
     */
    function get_attachment_id( $url ) {

        $dir = wp_upload_dir();

        // baseurl never has a trailing slash
        if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
            // URL points to a place outside of upload directory
            return false;
        }

        $file  = basename( $url );
        $query = array(
            'post_type'  => 'attachment',
            'fields'     => 'ids',
            'meta_query' => array(
                array(
                    'key'     => '_wp_attached_file',
                    'value'   => $file,
                    'compare' => 'LIKE',
                ),
            )
        );

        // query attachments
        $ids = get_posts( $query );

        if ( ! empty( $ids ) ) {

            foreach ( $ids as $id ) {

                // first entry of returned array is the URL
                if ( $url === array_shift( wp_get_attachment_image_src( $id, 'full' ) ) )
                    return $id;
            }
        }

        $query['meta_query'][0]['key'] = '_wp_attachment_metadata';

        // query attachments again
        $ids = get_posts( $query );

        if ( empty( $ids) )
            return false;

        foreach ( $ids as $id ) {

            $meta = wp_get_attachment_metadata( $id );

            foreach ( $meta['sizes'] as $size => $values ) {

                if ( $values['file'] === $file && $url === array_shift( wp_get_attachment_image_src( $id, $size ) ) )
                    return $id;
            }
        }

        return false;
    }
}
  • 1
    Can you explain why you query both _wp_attached_file and _wp_attachment_metadata? – Stephen Harris Jul 1 '14 at 22:11
  • 2
    @StephenHarris because URL can point to any of the image's sizes, which all have different file names – Rarst Jul 1 '14 at 22:14
  • 1
    This works great, but it is worth noting here that, since WordPress 4, there is a built in function to do it as mentioned by Gabriel in another answer. It works exactly the same way as this one does. – Chris Rae Sep 27 '16 at 23:21
  • 1
    @ChrisRae if you look at the source the core function will not work on image sizes, only on main image. – Rarst Sep 28 '16 at 8:48
  • I think the built in WordPress function works better. This did not work in my production but did work on Staging (which does not have a SSL certificate). The built in function (as pointed out by Ego Ipse below) works in both environments. – Syed Priom Jan 18 '17 at 21:17
15

All those complex functions can be reduced to one simple function:

attachment_url_to_postid()

You only need to parse the image URL to retrieve the attachment ID:

$attachment_id = attachment_url_to_postid( $image_url );
echo $attachment_id;

That's all you need.

  • 6
    Notably this will not work on image sizes, core version only searches "main" attached file. – Rarst Sep 28 '16 at 8:48
3

I modified Rarst's code to allow you to match just the filename instead of the full path. This is helpful if you are about to sideload the image if it does not exist. Currently this only works if file names are unique but I will be adding a hash check later to help with images that have the same filename.

function get_attachment_id( $url, $ignore_path = false ) {

if ( ! $ignore_path ) {

    $dir = wp_upload_dir();
    $dir = trailingslashit($dir['baseurl']);

    if( false === strpos( $url, $dir ) )
        return false;
}

$file = basename($url);

$query = array(
    'post_type' => 'attachment',
    'fields' => 'ids',
    'meta_query' => array(
        array(
            'key'     => '_wp_attached_file',
            'value'   => $file,
            'compare' => 'LIKE',
        )
    )
);

$ids = get_posts( $query );

foreach( $ids as $id ) {
    $match = array_shift( wp_get_attachment_image_src($id, 'full') );
    if( $url == $match || ( $ignore_path && strstr( $match, $file ) ) )
        return $id;
}

$query['meta_query'][0]['key'] = '_wp_attachment_metadata';
$ids = get_posts( $query );

foreach( $ids as $id ) {

    $meta = wp_get_attachment_metadata($id);

    foreach( $meta['sizes'] as $size => $values ) {
        if( $values['file'] == $file && ( $ignore_path || $url == array_shift( wp_get_attachment_image_src($id, $size) ) ) )
            return $id;
    }
}

return false;
}
3

Ok I found the answer that no one has on the net I been looking for days now. Keep in mine this only works if your theme or plugin is using the WP_Customize_Image_Control() if you are using WP_Customize_Media_Control() the get_theme_mod() will return the ID and not the url.

For my solution I was using the newer version WP_Customize_Image_Control()

A lot of posts on the forums have the get_attachment_id() which does not work anymore. I used attachment_url_to_postid()

Here is how I was able to do it. Hope this helps someone out there

// This is getting the image / url
$feature1 = get_theme_mod('feature_image_1');

// This is getting the post id
$feature1_id = attachment_url_to_postid($feature1);

// This is getting the alt text from the image that is set in the media area
$image1_alt = get_post_meta( $feature1_id, '_wp_attachment_image_alt', true );

Markup

<a href="<?php echo $feature1_url; ?>"><img class="img-responsive center-block" src="<?php echo $feature1; ?>" alt="<?php echo $image1_alt; ?>"></a>
0

Here is an alternative solution:

$image_url = get_field('main_image'); // in case of custom field usage
$image_id = attachment_url_to_postid($image_url);

// retrieve the thumbnail size of our image
$image_thumb = wp_get_attachment_image_src($image_id, 'thumbnail');

Since WP 4.0 they introduced a function attachment_url_to_postid() that behaves similarly to your's find_image_post_id()

Please check the this url for your reference.

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.