33

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?

3
  • You can porbably set request variables according to your URL, instantiate WP_Query and obtain the information from it.
    – hakre
    Commented Jan 10, 2011 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. Commented Jan 10, 2011 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. Commented Jan 10, 2011 at 18:46

6 Answers 6

31

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;
    }
}
7
  • 1
    Can you explain why you query both _wp_attached_file and _wp_attachment_metadata? Commented Jul 1, 2014 at 22:11
  • 4
    @StephenHarris because URL can point to any of the image's sizes, which all have different file names
    – Rarst
    Commented Jul 1, 2014 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
    Commented Sep 27, 2016 at 23:21
  • 2
    @ChrisRae if you look at the source the core function will not work on image sizes, only on main image.
    – Rarst
    Commented Sep 28, 2016 at 8:48
  • 1
    @SyedPriom if URL you are searching for is inconsistent with your stored data then it won't work indeed, wasn't something I accounted for when writing this, but likely can be improved for protocols. As above the main issue with native function is that it won’t work on image sizes.
    – Rarst
    Commented Jan 19, 2017 at 8:25
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.

2
  • 7
    Notably this will not work on image sizes, core version only searches "main" attached file.
    – Rarst
    Commented Sep 28, 2016 at 8:48
  • This solution didn't work with image size variations. Only works with the main "full" size image.
    – Azamat
    Commented Nov 9, 2021 at 7:18
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>
1

Since wordpress version 4.0 there is a wordpress core function attachment_url_to_postid that "Tries to convert an attachment URL into a post ID." But these function has huge flaw cos it can't convert custom image url sizes.

Almost all top answers before trying solve these problem, but all of them have a very heavy database query trying to get all existing attachments and then loop through all of them trying to get attachment metadata and find custom sizes in it. It works well only if you have small amount of attachments in your wordpress project. But you definitely will get performance problem if you have big project with 10000 + attachments.

But you can extend attachment_url_to_postid core function through existing filter attachment_url_to_postid then trying to force attachment_url_to_postid function search among custom image sizes too. Just add code below to functions.php file of your active wordpress theme. and then use attachment_url_to_postid function as you use before but with additional custom sizes functionality.

    add_filter( 'attachment_url_to_postid', 'change_attachment_url_to_postid', 10, 2 );
    function change_attachment_url_to_postid( $post_id, $url ) {
        if ( ! $post_id ) {
            $file  = basename( $url );
            $query = array(
                'post_type'  => 'attachment',
                'fields'     => 'ids',
                'meta_query' => array(
                    array(
                        'key'     => '_wp_attachment_metadata',
                        'value'   => $file,
                        'compare' => 'LIKE',
                    ),
                )
            );

            $ids = get_posts( $query );

            if ( ! empty( $ids ) ) {

                foreach ( $ids as $id ) {
                    $meta = wp_get_attachment_metadata( $id );

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

        return $post_id;
    }
1
  • This should have more upvotes! Or better yet, it should work like that in WP Core... Commented Apr 21, 2022 at 8:47
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 and acknowledge you have read our privacy policy.

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