Como mostrar avatar y biografía personalizados con PHP en WordPress

Contents

Introducción

Este tutorial explica, con todo detalle y ejemplos completos en PHP y JavaScript, cómo añadir y mostrar un avatar y una biografía personalizados para los usuarios de WordPress. Incluye: campos en el perfil de usuario (URL o attachment ID), guardado seguro, integración con el Media Uploader, filtro para reemplazar get_avatar() y funciones de plantilla para mostrar avatar bio. Se asume que dispone de conocimientos básicos de WordPress y que trabajará en un child theme o en un plugin propio.

Resumen de la solución

  • Añadir campos personalizados en la página de perfil (avatar personalizado y biografía).
  • Guardar esos campos con comprobaciones de permisos y nonces.
  • Opcional: integrar el Media Uploader para seleccionar imágenes desde la librería.
  • Filtrar get_avatar() para devolver el avatar personalizado en vez del Gravatar.
  • Proveer funciones de plantilla para mostrar avatar y biografía con escapes y fallback.

Recomendaciones previas

  • Trabaje en un tema hijo o en un plugin para no perder cambios con actualizaciones.
  • Haga backup antes de modificar archivos.
  • Use siempre funciones de escape y saneamiento: esc_url(), esc_attr(), esc_html(), sanitize_text_field(), esc_textarea().

Paso 1 — Añadir campos al perfil de usuario

Agregar los campos en el formulario de perfil: campo para URL de avatar o para almacenar el ID del attachment (cuando use el Media Uploader), y un textarea para la biografía personalizada.

lt?php
// Añadir campos al perfil (show_user_profile y edit_user_profile)
add_action(show_user_profile, mi_perfil_campos_personalizados)
add_action(edit_user_profile, mi_perfil_campos_personalizados)

function mi_perfil_campos_personalizados(user) {
    // Obtiene valores previos
    custom_avatar_url = get_user_meta(user-gtID, custom_avatar_url, true)
    custom_avatar_id  = get_user_meta(user-gtID, custom_avatar_id, true) // opcional
    custom_bio        = get_user_meta(user-gtID, custom_bio, true)
    // Nonce para verificar al guardar
    wp_nonce_field(guardar_campos_perfil_personalizados, nonce_campos_perfil_personalizados)
    ?gt

    ltpgtltlabel for=custom_avatar_urlgtltbgtAvatar personalizado (URL)lt/bgtlt/labelgtltbr /gt
    ltinput type=url name=custom_avatar_url id=custom_avatar_url value=lt?php echo esc_attr(custom_avatar_url) ?gt class=regular-text /gt
    ltspan class=descriptiongtDirección completa de la imagen. Alternativamente use el Media Uploader con el campo ID (debajo).lt/spangtlt/pgt

    ltpgtltlabel for=custom_avatar_idgtltbgtAvatar personalizado (Attachment ID)lt/bgtlt/labelgtltbr /gt
    ltinput type=text name=custom_avatar_id id=custom_avatar_id value=lt?php echo esc_attr(custom_avatar_id) ?gt class=regular-text /gt
    ltspan class=descriptiongtID del attachment guardado por el Media Uploader (opcional).lt/spangtlt/pgt

    ltpgtltlabel for=custom_biogtltbgtBiografía personalizadalt/bgtlt/labelgtltbr /gt
    lttextarea name=custom_bio id=custom_bio rows=5 class=large-textgtlt?php echo esc_textarea(custom_bio) ?gtlt/textareagt
    ltspan class=descriptiongtTexto corto que se mostrará en los perfiles/autorías.lt/spangtlt/pgt

    lt?php
}
?gt

Paso 2 — Guardar los campos del perfil

Al guardar el formulario del perfil hay que validar nonce, permisos y sanear los datos antes de guardarlos con update_user_meta().

lt?php
add_action(personal_options_update, mi_guardar_campos_perfil_personalizados)
add_action(edit_user_profile_update, mi_guardar_campos_perfil_personalizados)

function mi_guardar_campos_perfil_personalizados(user_id) {
    if (!isset(_POST[nonce_campos_perfil_personalizados])) {
        return
    }
    if (!wp_verify_nonce(_POST[nonce_campos_perfil_personalizados], guardar_campos_perfil_personalizados)) {
        return
    }
    if (!current_user_can(edit_user, user_id)) {
        return
    }

    // Guardar URL de avatar
    if (isset(_POST[custom_avatar_url])) {
        url = trim(_POST[custom_avatar_url])
        url = esc_url_raw(url)
        update_user_meta(user_id, custom_avatar_url, url)
    }

    // Guardar attachment ID (solo números)
    if (isset(_POST[custom_avatar_id])) {
        id = intval(_POST[custom_avatar_id])
        if (id gt 0) {
            update_user_meta(user_id, custom_avatar_id, id)
        } else {
            delete_user_meta(user_id, custom_avatar_id)
        }
    }

    // Guardar biografía personalizada
    if (isset(_POST[custom_bio])) {
        bio = sanitize_text_field(_POST[custom_bio])
        update_user_meta(user_id, custom_bio, bio)
    }
}
?gt

Paso 3 (opcional) — Integrar el Media Uploader

Para una mejor UX use la Librería de Medios para seleccionar imágenes. Esto requiere encolar scripts y añadir un botón que abra el uploader. A continuación un ejemplo breve de cómo encolar y el JS necesario.

lt?php
// Encolar scripts en el admin para el Media Uploader
add_action(admin_enqueue_scripts, mi_encolado_media_uploader)
function mi_encolado_media_uploader(hook_suffix) {
    // Solo en la pantalla de perfil y edición de usuario
    if (profile.php !== hook_suffix  user-edit.php !== hook_suffix) {
        return
    }
    wp_enqueue_media()
    wp_enqueue_script(mi-uploader-profile-js, plugin_dir_url(__FILE__) . mi-uploader-profile.js, array(jquery), 1.0, true)
}
?gt

Archivo JavaScript (mi-uploader-profile.js):

(function(){
    var frame
    (document).on(click, #mi_seleccionar_avatar, function(e){
        e.preventDefault()
        // Si existe el frame, reusar
        if (frame) {
            frame.open()
            return
        }
        frame = wp.media({
            title: Seleccionar avatar,
            button: { text: Usar este avatar },
            multiple: false
        })
        frame.on(select, function(){
            var attachment = frame.state().get(selection).first().toJSON()
            (#custom_avatar_id).val(attachment.id)
            (#custom_avatar_preview).attr(src, attachment.sizes  attachment.sizes.thumbnail ? attachment.sizes.thumbnail.url : attachment.url).show()
        })
        frame.open()
    })
})(jQuery)

En el HTML del perfil (ya mostrado en el Paso 1) puede añadir un botón y una etiqueta img para previsualizar:

ltbutton id=mi_seleccionar_avatar class=buttongtSeleccionar avatar desde la libreríalt/buttongt
ltimg id=custom_avatar_preview src=lt?php echo esc_url(preview_url) ?gt style=max-width:100pxdisplay: alt=Preview /gt

Paso 4 — Filtrar get_avatar() para usar el avatar personalizado

Use el filtro get_avatar para devolver la imagen personalizada (attachment ID o URL). El ejemplo siguiente gestiona distintas entradas (WP_User, comment, email). Respeta el parámetro size y conserva el markup de avatar típico de WordPress.

lt?php
add_filter(get_avatar, mi_get_avatar_personalizado, 10, 6)

function mi_get_avatar_personalizado(avatar, id_or_email, size, default, alt, args) {
    user = false
    // Obtener el user object
    if (is_numeric(id_or_email)) {
        user = get_user_by(id, (int) id_or_email)
    } elseif (is_object(id_or_email)) {
        if (!empty(id_or_email->user_id)) {
            user = get_user_by(id, (int) id_or_email->user_id)
        }
    } else {
        user = get_user_by(email, id_or_email)
    }

    if (!user) {
        return avatar // Mantener comportamiento por defecto si no hay usuario
    }

    // Priorizar attachment ID, luego URL, luego fallback a Gravatar
    avatar_id = get_user_meta(user->ID, custom_avatar_id, true)
    avatar_url = get_user_meta(user->ID, custom_avatar_url, true)

    if (avatar_id) {
        img = wp_get_attachment_image_src(avatar_id, array(size, size))
        if (img) {
            src = esc_url(img[0])
        } else {
            src = 
        }
    } elseif (avatar_url) {
        src = esc_url(avatar_url)
    } else {
        return avatar
    }

    if (empty(src)) {
        return avatar
    }

    alt = alt ? alt : sprintf(esc_attr__(%s avatar, textdomain), user->display_name)
    class = isset(args[class]) ? args[class] : avatar avatar- . (int)size .  photo

    custom_avatar = sprintf(
        ltimg alt=%s src=%s class=%s width=%d height=%d /gt,
        esc_attr(alt),
        src,
        esc_attr(class),
        (int) size,
        (int) size
    )

    // Mantener envoltorio por si otros plugins esperan estructura similar
    return custom_avatar
}
?gt

Notas sobre el filtro

  • Si necesita conservar exactamente el HTML que WordPress genera (p. ej. envoltorios con enlace a author), puede construirlo aquí o aplicar el filtro en otro momento.
  • El ejemplo devuelve solo la etiqueta ltimggt. Si quiere el mismo markup completo de WP (con span), puede construirlo con sprintf usando clases de avatar.

Mostrar avatar y biografía en la plantilla

Puede crear una función reutilizable que muestre avatar y biografía de un usuario concreto en su tema.

lt?php
function mostrar_avatar_y_bio_personalizados(user_id = 0, size = 96) {
    if (!user_id) {
        user_id = get_the_author_meta(ID)
    }
    user = get_user_by(id, user_id)
    if (!user) {
        return
    }

    // Usar nuestro filtro get_avatar (se aplicará automáticamente)
    avatar_html = get_avatar(user_id, size)
    custom_bio = get_user_meta(user_id, custom_bio, true)
    // Si no hay bio personalizada, usar description o user meta por defecto
    if (empty(custom_bio)) {
        custom_bio = get_the_author_meta(description, user_id)
    }

    echo ltdiv class=mi-avatar-biogt
    echo ltdiv class=mi-avatargt . avatar_html . lt/divgt
    echo ltdiv class=mi-biogtlth4gt . esc_html(user->display_name) . lt/h4gt
    if (custom_bio) {
        echo ltpgt . esc_html(custom_bio) . lt/pgt
    }
    echo lt/divgtlt/divgt
}
?gt

Ejemplo completo: plugin mínimo que implementa la funcionalidad

El siguiente bloque resume todo en un único archivo de plugin para que lo pegue en wp-content/plugins/mi-avatar-bio/mi-avatar-bio.php (añádale encabezado de plugin, activo y pruebe).

lt?php
/
Plugin Name: Avatar y Bio Personalizados
Description: Añade avatar personalizado (URL o attachment ID) y biografía por usuario, filtra get_avatar y provee funciones de plantilla.
Version: 1.0
Author: Tu Nombre
/

if (!defined(ABSPATH)) exit

// Añadir campos al perfil
add_action(show_user_profile, mi_perfil_campos_personalizados)
add_action(edit_user_profile, mi_perfil_campos_personalizados)
function mi_perfil_campos_personalizados(user) {
    custom_avatar_url = get_user_meta(user-gtID, custom_avatar_url, true)
    custom_avatar_id  = get_user_meta(user-gtID, custom_avatar_id, true)
    custom_bio        = get_user_meta(user-gtID, custom_bio, true)
    wp_nonce_field(guardar_campos_perfil_personalizados, nonce_campos_perfil_personalizados)
    ?>
    ltpgtltlabel for=custom_avatar_urlgtltbgtAvatar personalizado (URL)lt/bgtlt/labelgtltbr /gt
    ltinput type=url name=custom_avatar_url id=custom_avatar_url value=lt?php echo esc_attr(custom_avatar_url) ?gt class=regular-text /gtlt/pgt

    ltpgtltlabel for=custom_avatar_idgtltbgtAvatar personalizado (Attachment ID)lt/bgtlt/labelgtltbr /gt
    ltinput type=text name=custom_avatar_id id=custom_avatar_id value=lt?php echo esc_attr(custom_avatar_id) ?gt class=regular-text /gt
    ltbutton id=mi_seleccionar_avatar class=buttongtSeleccionar desde libreríalt/buttongt
    ltimg id=custom_avatar_preview src=lt?php echo esc_url(custom_avatar_url ? custom_avatar_url : ) ?gt style=max-width:80px alt=Preview /gtlt/pgt

    ltpgtltlabel for=custom_biogtltbgtBiografía personalizadalt/bgtlt/labelgtltbr /gt
    lttextarea name=custom_bio id=custom_bio rows=5 class=large-textgtlt?php echo esc_textarea(custom_bio) ?gtlt/textareagtlt/pgt
    lt?php
}

// Guardar campos
add_action(personal_options_update, mi_guardar_campos_perfil_personalizados)
add_action(edit_user_profile_update, mi_guardar_campos_perfil_personalizados)
function mi_guardar_campos_perfil_personalizados(user_id) {
    if (!isset(_POST[nonce_campos_perfil_personalizados])) return
    if (!wp_verify_nonce(_POST[nonce_campos_perfil_personalizados], guardar_campos_perfil_personalizados)) return
    if (!current_user_can(edit_user, user_id)) return

    if (isset(_POST[custom_avatar_url])) {
        update_user_meta(user_id, custom_avatar_url, esc_url_raw(trim(_POST[custom_avatar_url])))
    }
    if (isset(_POST[custom_avatar_id])) {
        id = intval(_POST[custom_avatar_id])
        if (id gt 0) update_user_meta(user_id, custom_avatar_id, id)
        else delete_user_meta(user_id, custom_avatar_id)
    }
    if (isset(_POST[custom_bio])) {
        update_user_meta(user_id, custom_bio, sanitize_text_field(_POST[custom_bio]))
    }
}

// Filtrar avatar
add_filter(get_avatar, mi_get_avatar_personalizado, 10, 6)
function mi_get_avatar_personalizado(avatar, id_or_email, size, default, alt, args) {
    user = false
    if (is_numeric(id_or_email)) user = get_user_by(id, (int) id_or_email)
    elseif (is_object(id_or_email)  !empty(id_or_email->user_id)) user = get_user_by(id, (int) id_or_email->user_id)
    else user = get_user_by(email, id_or_email)

    if (!user) return avatar

    avatar_id = get_user_meta(user->ID, custom_avatar_id, true)
    avatar_url = get_user_meta(user->ID, custom_avatar_url, true)
    src = 

    if (avatar_id) {
        img = wp_get_attachment_image_src(avatar_id, array(size, size))
        if (img) src = img[0]
    } elseif (avatar_url) {
        src = avatar_url
    }

    if (empty(src)) return avatar

    alt = alt ? alt : sprintf(esc_attr__(%s avatar, textdomain), user->display_name)
    class = isset(args[class]) ? args[class] : avatar avatar- . (int)size .  photo

    return sprintf(ltimg alt=%s src=%s class=%s width=%d height=%d /gt,
        esc_attr(alt),
        esc_url(src),
        esc_attr(class),
        (int) size,
        (int) size
    )
}

// Función de plantilla
function mostrar_avatar_y_bio_personalizados(user_id = 0, size = 96) {
    if (!user_id) user_id = get_the_author_meta(ID)
    user = get_user_by(id, user_id)
    if (!user) return
    avatar_html = get_avatar(user_id, size)
    custom_bio = get_user_meta(user_id, custom_bio, true)
    if (empty(custom_bio)) custom_bio = get_the_author_meta(description, user_id)

    echo ltdiv class=mi-avatar-biogt
    echo ltdiv class=mi-avatargt . avatar_html . lt/divgt
    echo ltdiv class=mi-biogtlth4gt . esc_html(user->display_name) . lt/h4gt
    if (custom_bio) echo ltpgt . esc_html(custom_bio) . lt/pgt
    echo lt/divgtlt/divgt
}
?gt

Buenas prácticas y seguridad

  • Use nonces y current_user_can al guardar para evitar CSRF y escalado de privilegios.
  • Saneamiento: esc_url_raw para URLs, sanitize_text_field para textos y esc_textarea/esc_html al mostrar.
  • Compruebe que el attachment ID corresponde a una imagen antes de usarlo (wp_get_attachment_image_src devuelve false si no existe).
  • Si muestra HTML en front-end, evite imprimir contenido sin escapar para prevenir XSS.
  • Cache: si su sitio carga muchos avatares personalizados, considere usar transients o un CDN para las imágenes.

Compatibilidad y consideraciones finales

  • Si el usuario no tiene avatar personalizado, deje el comportamiento por defecto (Gravatar) o use una imagen por defecto alojada localmente.
  • Respete tamaños y proporciones. Use funciones de imagen de WordPress para obtener tamaños generados: wp_get_attachment_image_src(id, size).
  • Si tiene multi-site, verifique que las rutas y permisos de media sean correctos.
  • Si otros plugins ya filtran get_avatar, ajuste prioridades o implemente condiciones para evitar conflictos.

Recursos útiles



Acepto donaciones de BAT's mediante el navegador Brave 🙂



Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *