Convertir una URL en un ID de Adjunto o Entrada
¿Existe alguna forma de tomar la URL de una imagen y encontrar el ID de adjunto o entrada de esa imagen en la base de datos?
Esta es la situación:
Estoy en un bucle recorriendo todas las etiquetas 'img' que están rodeadas por etiquetas 'a' en el contenido de mi entrada. Si el atributo src de la etiqueta 'img' no coincide con el atributo href de la etiqueta 'a' exterior, entonces quiero reemplazar la etiqueta 'img'. Al hacer esto, si la 'img' que se va a eliminar está en la galería, quiero borrar esa entrada y luego poner mi imagen de reemplazo en su lugar. Intenté usar una función como esta:
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;
}
Esto aparentemente no funciona correctamente porque el GUID (Global Unique Identifier) irónicamente no es globalmente único. Había (antes en el mismo script) subido un archivo con el mismo nombre (¿por qué? porque era de mayor resolución y estoy intentando reemplazar versiones de baja resolución de la misma imagen) y aunque WordPress guarda la imagen con un nombre diferente en el directorio, los GUIDs se establecieron iguales (posiblemente un error).
¿Existe otra técnica que pueda usar?

Función mejorada masivamente desarrollada para plugins con gran cantidad de imágenes:
if ( ! function_exists( 'get_attachment_id' ) ) {
/**
* Obtiene el ID de adjunto para una URL de imagen dada.
*
* @link http://wordpress.stackexchange.com/a/7094
*
* @param string $url
*
* @return boolean|integer
*/
function get_attachment_id( $url ) {
$dir = wp_upload_dir();
// baseurl nunca tiene una barra diagonal al final
if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
// La URL apunta a un lugar fuera del directorio de subida
return false;
}
$file = basename( $url );
$query = array(
'post_type' => 'attachment',
'fields' => 'ids',
'meta_query' => array(
array(
'key' => '_wp_attached_file',
'value' => $file,
'compare' => 'LIKE',
),
)
);
// consultar adjuntos
$ids = get_posts( $query );
if ( ! empty( $ids ) ) {
foreach ( $ids as $id ) {
// la primera entrada del array devuelto es la URL
if ( $url === array_shift( wp_get_attachment_image_src( $id, 'full' ) ) )
return $id;
}
}
$query['meta_query'][0]['key'] = '_wp_attachment_metadata';
// consultar adjuntos nuevamente
$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;
}
}

¿Puedes explicar por qué consultas tanto _wp_attached_file
como _wp_attachment_metadata
?

@StephenHarris porque la URL puede apuntar a cualquiera de los tamaños de la imagen, los cuales tienen nombres de archivo diferentes

Esto funciona muy bien, pero vale la pena mencionar aquí que, desde WordPress 4, existe una función integrada para hacerlo como mencionó Gabriel en otra respuesta. Funciona exactamente de la misma manera que esta.

@ChrisRae si miras el código fuente, la función principal no funcionará en tamaños de imagen, solo en la imagen principal.

Creo que la función integrada de WordPress funciona mejor. Esto no funcionó en mi entorno de producción pero sí funcionó en Staging (que no tiene un certificado SSL). La función incorporada (como señaló Ego Ipse abajo) funciona en ambos entornos.

@SyedPriom si la URL que estás buscando es inconsistente con tus datos almacenados, entonces no funcionará efectivamente, no era algo que tuve en cuenta al escribir esto, pero probablemente se pueda mejorar para protocolos. Como se mencionó arriba, el principal problema con la función nativa es que no funcionará en tamaños de imagen.

Es una gran función, pero a veces arroja un aviso "Notice: Only variables should be passed by reference" Para solucionarlo, simplemente asigna el resultado de array_keys a una variable y pasa esa variable
$image_src = wp_get_attachment_image_src( $id, $size ); if ( $values['file'] === $file && $url === array_shift( $image_src ) ) return $id;

Todas esas funciones complejas se pueden reducir a una simple función:
Solo necesitas analizar la URL de la imagen para obtener el ID del adjunto:
$attachment_id = attachment_url_to_postid( $image_url );
echo $attachment_id;
Eso es todo lo que necesitas.

Cabe destacar que esto no funcionará en los tamaños de imagen, la versión principal solo busca el archivo adjunto "principal".

Modifiqué el código de Rarst para permitir coincidir solo con el nombre del archivo en lugar de la ruta completa. Esto es útil si estás a punto de cargar una imagen lateralmente si no existe. Actualmente esto solo funciona si los nombres de archivo son únicos, pero agregaré una verificación de hash más adelante para ayudar con imágenes que tengan el mismo nombre de archivo.
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;
}

Ok encontré la respuesta que nadie tiene en la red, he estado buscando durante días. Ten en cuenta que esto solo funciona si tu tema o plugin está usando WP_Customize_Image_Control()
. Si estás usando WP_Customize_Media_Control()
, el get_theme_mod()
devolverá el ID y no la URL.
Para mi solución, estaba usando la versión más nueva WP_Customize_Image_Control()
Muchos posts en los foros tienen el get_attachment_id()
que ya no funciona. Yo usé attachment_url_to_postid()
Aquí está cómo pude hacerlo. Espero que esto ayude a alguien por ahí.
// Esto obtiene la imagen / URL
$feature1 = get_theme_mod('feature_image_1');
// Esto obtiene el ID del post
$feature1_id = attachment_url_to_postid($feature1);
// Esto obtiene el texto alternativo de la imagen que se establece en el área de medios
$image1_alt = get_post_meta( $feature1_id, '_wp_attachment_image_alt', true );
Marcado
<a href="<?php echo $feature1_url; ?>"><img class="img-responsive center-block" src="<?php echo $feature1; ?>" alt="<?php echo $image1_alt; ?>"></a>

Desde la versión 4.0 de WordPress, existe una función principal llamada attachment_url_to_postid que "Intenta convertir una URL de un archivo adjunto en un ID de publicación". Pero esta función tiene un gran defecto porque no puede convertir URLs de imágenes con tamaños personalizados.
Casi todas las respuestas principales intentan resolver este problema, pero todas tienen una consulta a la base de datos muy pesada que intenta obtener todos los archivos adjuntos existentes y luego los recorre para obtener los metadatos del adjunto y buscar los tamaños personalizados en ellos. Esto funciona bien solo si tienes una pequeña cantidad de archivos adjuntos en tu proyecto de WordPress. Pero definitivamente tendrás problemas de rendimiento si tienes un proyecto grande con 10,000+ archivos adjuntos.
Sin embargo, puedes extender la función principal attachment_url_to_postid mediante el filtro existente attachment_url_to_postid para forzar a la función a buscar también entre los tamaños personalizados de imágenes. Simplemente añade el siguiente código al archivo functions.php de tu tema activo de WordPress. y luego usa la función attachment_url_to_postid como lo hacías antes, pero con la funcionalidad adicional de tamaños personalizados.
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;
}

Aquí hay una solución alternativa:
$image_url = get_field('main_image'); // en caso de uso de campos personalizados
$image_id = attachment_url_to_postid($image_url);
// recuperar el tamaño miniatura de nuestra imagen
$image_thumb = wp_get_attachment_image_src($image_id, 'thumbnail');
Desde WP 4.0 introdujeron una función attachment_url_to_postid()
que se comporta de manera similar a tu find_image_post_id()
Por favor revisa esta url como referencia.
