Преобразование URL в ID вложения или записи
Есть ли способ получить 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 оказывается одинаковым (возможно, это баг).
Есть ли другой способ решить эту задачу?
Значительно улучшенная функция, разработанная для плагина с большим количеством изображений:
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;
}
}

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

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

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

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

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

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

Это отличная функция, но иногда она выдает уведомление "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;

Все эти сложные функции можно свести к одной простой функции:
Вам нужно только разобрать URL изображения, чтобы получить ID вложения:
$attachment_id = attachment_url_to_postid( $image_url );
echo $attachment_id;
Это всё, что вам нужно.

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

Я модифицировал код 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;
}

Отлично, я нашел ответ, которого ни у кого нет в сети. Я искал его несколько дней. Имейте в виду, это работает только если ваша тема или плагин использует 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>

Начиная с версии 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;
}

Вот альтернативное решение:
$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()
Для справки ознакомьтесь с этой ссылкой.
