Cómo establecer una imagen destacada en una entrada personalizada desde fuera programáticamente
Estoy tratando de obtener e insertar imágenes desde fuera del entorno de WordPress a una entrada personalizada mediante PHP.
¿Cómo mover/subir esa imagen al directorio de subidas de WordPress con el formato de carpetas año/fecha tal como lo hace WordPress y establecer esa imagen como imagen destacada para la entrada personalizada?
¿Y también cómo subir la imagen a la galería de la entrada personalizada?
Este es mi código:
$filename = $image['name'];
$target_path = "../wp-content/uploads/";
$target_path = $target_path . $filename;
$wp_filetype = wp_check_filetype(basename($filename), null );
$wp_upload_dir = wp_upload_dir();
$attachment = array(
'guid' => $wp_upload_dir['baseurl'] . '/' . basename( $filename ),
'post_mime_type' => $wp_filetype['type'],
'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),
'post_content' => '',
'post_status' => 'inherit',
);
$attach_id = wp_insert_attachment( $attachment, $target_path, $post_id );
$attach_data = wp_generate_attachment_metadata( $attach_id, $filename );
wp_update_attachment_metadata( $attach_id, $attach_data );
set_post_thumbnail( $post_id, $attach_id );
He logrado subir la imagen a mi directorio de uploads pero no puedo crear la carpeta de año y fecha. ¿Existe alguna función de WordPress para esto?

¿No se puede hacer esto simplemente con media_sideload_image()?
Parece bastante sencillo. El único detalle es que si no estás en el área de administración, debes incluir algunas bibliotecas de los includes de WordPress:
// solo necesitas estos si ejecutas fuera del entorno de administración
require_once(ABSPATH . 'wp-admin/includes/media.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
// imagen de ejemplo
$image = 'http://example.com/logo.png';
// media_sideload_image devuelve una imagen HTML, no un ID
$media = media_sideload_image($image, $post_id);
// por lo tanto debemos encontrarla para establecerla como ID destacado
if(!empty($media) && !is_wp_error($media)){
$args = array(
'post_type' => 'attachment',
'posts_per_page' => -1,
'post_status' => 'any',
'post_parent' => $post_id
);
// referencia la nueva imagen para establecer como destacada
$attachments = get_posts($args);
if(isset($attachments) && is_array($attachments)){
foreach($attachments as $attachment){
// obtiene la fuente de las imágenes a tamaño completo (para evitar rutas con tamaños como 300x150)
$image = wp_get_attachment_image_src($attachment->ID, 'full');
// determina si en la imagen $media que creamos existe la cadena de la URL
if(strpos($media, $image[0]) !== false){
// si es así, encontramos nuestra imagen. la establecemos como miniatura
set_post_thumbnail($post_id, $attachment->ID);
// solo queremos una imagen
break;
}
}
}
}

A partir de WordPress 4.8 puedes establecer el cuarto parámetro en media_sideload_image
como 'id'
y devolverá el ID del nuevo adjunto. Ejemplo: $new_att_id = media_sideload_image($image, $post_id, "descripción de la imagen...", 'id'); if(!is_wp_error($new_att_id)) { set_post_thumbnail($post_id, $new_att_id); }

Definitivamente esto funcionó para mí usando la sugerencia de @DonWilson, solo no olvides incluir las 3 bibliotecas de medios si lo usas en un formulario de front end (de lo contrario, media_sideload_image
no se encontrará). Necesitaba copiar imágenes destacadas de un sitio a un multisitio separado mediante switch_to_blog()

Prueba esta explicación sobre subir archivos usando una ruta y un ID de publicación.
Aquí está el código (para versión legacy):
/* Importar medios desde una URL
*
* @param string $file_url URL del archivo existente desde el sitio original
* @param int $post_id El ID de la publicación a la que se adjuntará el medio importado
*
* @return boolean True en éxito, false en fallo
*/
function fetch_media($file_url, $post_id) {
require_once(ABSPATH . 'wp-load.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
global $wpdb;
if(!$post_id) {
return false;
}
//directorio donde importar
$artDir = 'wp-content/uploads/2013/06';
//si el directorio no existe, crearlo
if(!file_exists(ABSPATH.$artDir)) {
mkdir(ABSPATH.$artDir);
}
//renombrar el archivo
$ext = array_pop(explode("/", $file_url));
$new_filename = 'blogmedia-'.$ext;
if (@fclose(@fopen($file_url, "r"))) { //verificar que el archivo realmente exista
copy($file_url, ABSPATH.$artDir.$new_filename);
$siteurl = get_option('siteurl');
$file_info = getimagesize(ABSPATH.$artDir.$new_filename);
//crear un array de datos de adjunto para insertar en la tabla wp_posts
$artdata = array();
$artdata = array(
'post_author' => 1,
'post_date' => current_time('mysql'),
'post_date_gmt' => current_time('mysql'),
'post_title' => $new_filename,
'post_status' => 'inherit',
'comment_status' => 'closed',
'ping_status' => 'closed',
'post_name' => sanitize_title_with_dashes(str_replace("_", "-", $new_filename)), 'post_modified' => current_time('mysql'),
'post_modified_gmt' => current_time('mysql'),
'post_parent' => $post_id,
'post_type' => 'attachment',
'guid' => $siteurl.'/'.$artDir.$new_filename,
'post_mime_type' => $file_info['mime'],
'post_excerpt' => '',
'post_content' => ''
);
$uploads = wp_upload_dir();
$save_path = $uploads['basedir'].'/2013/06/'.$new_filename;
//insertar el registro en la base de datos
$attach_id = wp_insert_attachment( $artdata, $save_path, $post_id );
//generar metadatos y miniaturas
if ($attach_data = wp_generate_attachment_metadata( $attach_id, $save_path)) {
wp_update_attachment_metadata($attach_id, $attach_data);
}
//opcional: hacerlo la imagen destacada de la publicación a la que está adjunto
$rows_affected = $wpdb->insert($wpdb->prefix.'postmeta', array('post_id' => $post_id, 'meta_key' => '_thumbnail_id', 'meta_value' => $attach_id));
}
else {
return false;
}
return true;
}

Tal vez esté entendiendo mal, pero ¿por qué querrías hacer eso fuera del entorno de WordPress? ¡Replicar esta funcionalidad sería mucho trabajo! WordPress hace mucho más que simplemente subir el archivo y colocarlo en un directorio específico, por ejemplo, controlar qué usuarios tienen permiso para subir archivos, añadir registros en la base de datos para las subidas, establecer relaciones de imágenes destacadas, ejecutar acciones y filtros para plugins externos que dependan de la subida de archivos, todo mientras se respetan los ajustes del sitio (como convenciones de nombres, ubicación de medios, etc.).
Si simplemente buscas subir archivos sin estar logueado en la sección de administración de WordPress, por ejemplo, para subir archivos desde un sitio externo, podrías echar un vistazo a la API XML-RPC y específicamente al método uploadFile
.
Otra opción podría ser escribir una mini API tú mismo. Básicamente, lo que querrías hacer es lo siguiente:
- Obtener el archivo en el servidor mediante una subida (o hacer que el servidor lo descargue desde una URL especificada).
- Usar
wp_upload_dir()
para obtener la ruta del directorio de subidas ysanitize_file_name()
para construir la ruta y escribir el archivo en la ubicación resultante. - Usar
wp_insert_attachment()
para guardar el adjunto en la base de datos (wp_check_filetype()
será útil para establecer elpost_mime_type
). Opcionalmente, también puedes establecerpost_parent
y la meta clave_thumbnail_id
si lo deseas. - Exponer tu API a usuarios externos o requerir inicio de sesión, según sea necesario. Si usas un formulario público, al menos utiliza
wp_create_nonce()
ywp_verify_nonce()
para hacer el formulario un poco más seguro.

Estoy escribiendo un servicio web para una aplicación. La aplicación me envía un array FILE a través del cual quiero insertar los datos del post y la imagen. He insertado los detalles del post en la base de datos pero estoy atascado en la parte de la imagen.

Revisa la documentación de wp_insert_attachment()
, creo que debería hacer gran parte de lo que necesitas. Desaconsejo encarecidamente modificar manualmente la base de datos fuera de WordPress si eso es lo que estás haciendo. En su lugar, revisa el código fuente de WordPress e intenta identificar las partes responsables de añadir datos de posts, manejar subidas de archivos e insertar adjuntos. Básicamente, lo que he descrito en mi respuesta anterior.

@Simon Tengo el mismo problema. Otra razón por la que podrías querer subir imágenes es cuando tienes un trabajo por lotes de imágenes que quieres adjuntar a diferentes posts y no hacerlo manualmente.

@hitautodestruct: Absolutamente, suelo hacer eso cuando migro datos de sitios existentes, sistemas heredados, exportaciones de bases de datos, etc. Mi punto es que siempre debes esforzarte por aprovechar la funcionalidad principal de WordPress para lograr esto, en lugar de simplemente escribir tu propio script que coloque las imágenes en la ubicación correcta (que hasta cierto punto era lo que percibí que trataba la pregunta).
