Преобразование URL в ID вложения или записи

10 янв. 2011 г., 07:43:54
Просмотры: 16.6K
Голосов: 33

Есть ли способ получить ID вложения или записи изображения в базе данных по его URL?

Вот ситуация:

Я перебираю в цикле все теги 'img', окружённые тегами 'a' в контенте записи. Если атрибут src тега 'img' не совпадает с атрибутом href внешнего тега 'a', я хочу заменить тег 'img'. При этом, если заменяемое изображение находится в галерее, мне нужно удалить соответствующую запись и вставить новое изображение на её место. Я попробовал использовать такую функцию:

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;
}

Однако это не работает корректно, потому что guid (ирония) на самом деле не является глобально уникальным. Ранее в этом же скрипте я загрузил файл с тем же именем (зачем? потому что это была версия с более высоким разрешением, и я пытаюсь заменить изображения с низким разрешением). Хотя WordPress сохраняет изображение под другим именем в директории, guid оказывается одинаковым (возможно, это баг).

Есть ли другой способ решить эту задачу?

3
Комментарии

Вы можете установить переменные запроса в соответствии с вашим URL, создать экземпляр WP_Query и получить из него нужную информацию.

hakre hakre
10 янв. 2011 г. 14:38:51

Было бы полезно, если бы вы обновили свой вопрос и предоставили примеры вашего HTML, включая URL-адреса, которые вы хотите заменить, чтобы мы могли их обсудить.

MikeSchinkel MikeSchinkel
10 янв. 2011 г. 18:46:21

Майк прав. Находятся ли большие изображения, на которые вы ссылаетесь, на внешних сайтах? Если нет, то вам просто нужно выбрать полный размер при добавлении изображения в запись, и у вас есть возможность не связывать его ни с чем, если в этом больше нет смысла.

sanchothefat sanchothefat
10 янв. 2011 г. 20:46:36
Все ответы на вопрос 6
7
31

Значительно улучшенная функция, разработанная для плагина с большим количеством изображений:

if ( ! function_exists( 'get_attachment_id' ) ) {
    /**
     * Получает ID вложения для заданного URL изображения.
     *
     * @link   http://wordpress.stackexchange.com/a/7094
     *
     * @param  string $url
     *
     * @return boolean|integer
     */
    function get_attachment_id( $url ) {

        $dir = wp_upload_dir();

        // baseurl никогда не имеет завершающего слеша
        if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
            // URL указывает на место вне директории загрузки
            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 );

        if ( ! empty( $ids ) ) {

            foreach ( $ids as $id ) {

                // первый элемент возвращаемого массива - это URL
                if ( $url === array_shift( wp_get_attachment_image_src( $id, 'full' ) ) )
                    return $id;
            }
        }

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

        // повторный запрос вложений
        $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;
    }
}
17 янв. 2011 г. 09:13:18
Комментарии

Можете объяснить, почему вы запрашиваете и _wp_attached_file, и _wp_attachment_metadata?

Stephen Harris Stephen Harris
2 июл. 2014 г. 01:11:12

@StephenHarris потому что URL может указывать на любой из размеров изображения, у которых разные имена файлов

Rarst Rarst
2 июл. 2014 г. 01:14:08

Это отлично работает, но стоит отметить, что начиная с WordPress 4 есть встроенная функция для этого, как упомянул Gabriel в другом ответе. Она работает точно так же, как и этот вариант.

Chris Rae Chris Rae
28 сент. 2016 г. 02:21:37

@ChrisRae если посмотреть исходный код, основная функция не будет работать с размерами изображений, только с главным изображением.

Rarst Rarst
28 сент. 2016 г. 11:48:12

Я думаю, встроенная функция WordPress работает лучше. В моем продакшене это не сработало, но сработало на Staging (где нет SSL-сертификата). Встроенная функция (как указал Ego Ipse ниже) работает в обоих окружениях.

Syed Priom Syed Priom
18 янв. 2017 г. 23:17:36

@SyedPriom если URL, который вы ищете, не соответствует вашим сохраненным данным, то действительно не сработает, я не учитывал это при написании кода, но вероятно можно улучшить для протоколов. Как указано выше, основная проблема с нативной функцией в том, что она не работает с размерами изображений.

Rarst Rarst
19 янв. 2017 г. 10:25:44

Это отличная функция, но иногда она выдает уведомление "Notice: Only variables should be passed by reference" Чтобы исправить это, просто присвойте результат array_keys переменной и передайте эту переменную

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

Oleg Apanovich Oleg Apanovich
24 окт. 2019 г. 18:41:39
Показать остальные 2 комментариев
2
15

Все эти сложные функции можно свести к одной простой функции:

attachment_url_to_postid()

Вам нужно только разобрать URL изображения, чтобы получить ID вложения:

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

Это всё, что вам нужно.

1 нояб. 2015 г. 08:58:45
Комментарии

Важно отметить, что это не будет работать с размерами изображений, базовая версия ищет только "главный" прикрепленный файл.

Rarst Rarst
28 сент. 2016 г. 11:48:54

Это решение не работало с вариациями размеров изображений. Работает только с основным изображением в полном размере ("full").

Azamat Azamat
9 нояб. 2021 г. 09:18:22
0

Я модифицировал код Rarst, чтобы можно было сравнивать только имя файла вместо полного пути. Это полезно, если вы собираетесь загрузить изображение, если оно не существует. В настоящее время это работает только для уникальных имен файлов, но позже я добавлю проверку хеша для изображений с одинаковыми именами.

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;
}
25 апр. 2012 г. 19:59:13
0

Отлично, я нашел ответ, которого ни у кого нет в сети. Я искал его несколько дней. Имейте в виду, это работает только если ваша тема или плагин использует WP_Customize_Image_Control(). Если вы используете WP_Customize_Media_Control(), то get_theme_mod() вернет ID, а не URL.

В моем решении я использовал более новую версию — WP_Customize_Image_Control().

Много постов на форумах упоминают get_attachment_id(), который больше не работает. Я использовал attachment_url_to_postid().

Вот как я это сделал. Надеюсь, это поможет кому-то:

// Получаем URL изображения
$feature1 = get_theme_mod('feature_image_1');

// Получаем ID записи вложения
$feature1_id = attachment_url_to_postid($feature1);

// Получаем альтернативный текст изображения, заданный в медиабиблиотеке
$image1_alt = get_post_meta( $feature1_id, '_wp_attachment_image_alt', true );

Разметка

<a href="<?php echo $feature1_url; ?>"><img class="img-responsive center-block" src="<?php echo $feature1; ?>" alt="<?php echo $image1_alt; ?>"></a>
29 апр. 2017 г. 05:26:18
1

Начиная с версии WordPress 4.0, в ядре WordPress появилась функция attachment_url_to_postid, которая "Пытается преобразовать URL вложения в ID записи". Однако у этой функции есть серьезный недостаток — она не может конвертировать URL изображений с пользовательскими размерами.

Почти все популярные решения до этого пытались решить данную проблему, но все они использовали очень тяжелые запросы к базе данных, пытаясь получить все существующие вложения, а затем перебирали их все, чтобы получить метаданные вложения и найти в них пользовательские размеры. Это хорошо работает только если у вас небольшое количество вложений в вашем проекте WordPress. Но вы определенно столкнетесь с проблемами производительности, если у вас большой проект с 10000+ вложениями.

Однако вы можете расширить функцию attachment_url_to_postid через существующий фильтр attachment_url_to_postid, чтобы заставить функцию искать среди пользовательских размеров изображений тоже. Просто добавьте код ниже в файл functions.php вашей активной темы WordPress. и затем используйте функцию attachment_url_to_postid как и раньше, но уже с дополнительной функциональностью для пользовательских размеров.

    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;
    }
4 янв. 2020 г. 19:27:52
Комментарии

Это заслуживает больше голосов! Или, еще лучше, так должно работать в ядре WP...

Sarah Groß Sarah Groß
21 апр. 2022 г. 11:47:26
0

Вот альтернативное решение:

$image_url = get_field('main_image'); // если используется произвольное поле
$image_id = attachment_url_to_postid($image_url);

// получаем миниатюру нашего изображения
$image_thumb = wp_get_attachment_image_src($image_id, 'thumbnail');

Начиная с версии WP 4.0 была добавлена функция attachment_url_to_postid(), которая работает аналогично вашей функции find_image_post_id()

Для справки ознакомьтесь с этой ссылкой.

21 мар. 2018 г. 12:46:43