Cómo crear un placeholder configurable para nuestros módulos en Magento

Hace un tiempo me tocó crear, para la tienda Best Kiteboarding, un módulo algo más complejo que lo que normalmente uno acostumbra a hacer.

El módulo en cuestión crea una nueva entidad EAV para la gestión de los riders dentro de la tienda, permitiendo gestionar cualquiera de los atributos, con soporte multi-store view, controllers dinámicos (permitiendo configurar las url’s de acceso), galerías de imágenes, relación con productos, etc, etc.

Seguramente, de lo hecho con el módulo, se irán desprendiendo unos cuantos posts, ya que cubrir todas las funcionalidades en uno solo sería demasiado extenso.

Más allá de las funcionalidades mencionadas, uno de los temas a cubrir fue el de permitir, vía configuración, poder gestionar hasta el más mínimo detalle, dando total independencia al usuario de la tienda.

Uno de esos detalles, que a nivel frontend se notan, fue el manejo de imágenes. En el caso de la tienda, para cada rider, se gestionan distintas imágenes (avatar, imagen principal, galería de imágenes y galería de videos).

Si miramos en la tienda, en el caso de los riders, en la lista general se ve lo que se llamó el avatar.

Listado de Riders en www.bestkiteboarding.com

Y si entramos a la página de detalle, se ve la imagen principal y el resto del contenido.

Ficha de rider en www.bestkiteboarding.com

Para el caso del avatar, ante la posibilidad que no existiera la imagen, se implementó uno de esos pequeños detalles que nos permiten hacer que los módulos sean más robustos y cuidados: un placeholder por defecto.

Básicamente, es replicar el comportamiento que tiene Magento con los productos. Al faltar la imagen, veremos el placeholder por defecto.

Llevado a nuestro módulo, el primer paso es agregar una nueva opción en la configuración del propio módulo. En el caso los riders, se ve de la siguiente manera.

Configuración de imágenes

A nivel funcional, el usuario sólo deberá subir la imagen que quiera usar. A nivel código, la forma de implementar esa configuración, necesita de algunas líneas XML y algo de PHP.

Por un lado, la definición XML de la configuración.

<config>
     <sections>
        <rider translate="label" module="rider">
 
            ...
 
            <groups>
                <images translate="label">
                    <label>Images</label>
                    <frontend_type>text</frontend_type>
                    <sort_order>1</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>0</show_in_website>
                    <show_in_store>0</show_in_store>
                    <fields>
 
                        ...
 
                        <avatar_placeholder translate="label comment">
                            <label>Avatar Placeholder</label>
                            <comment>Allowed file types: ICO, PNG, GIF, JPG, JPEG, APNG, SVG. Not all browsers support all these formats!</comment>
                            <frontend_type>image</frontend_type>
                            <backend_model>rider/system_config_backend_avatar_placeholder</backend_model>
                            <base_url type="media" scope_info="0">rider/placeholder</base_url>
                            <sort_order>2</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>0</show_in_website>
                            <show_in_store>0</show_in_store>
                        </avatar_placeholder>
 
                        ...
 
                     </fields>
                </images>
            </groups>
        </rider2>
    </sections>
</config>

Implementamos en la opción, un modelo específico, que se encargará de capturar el post y procesarlo según necesitemos.

class Dc_Rider_Model_System_Config_Backend_Avatar_Placeholder extends Mage_Adminhtml_Model_System_Config_Backend_Image
{
    const UPLOAD_DIR = 'rider/placeholder';
 
    const UPLOAD_ROOT = 'media';
 
    protected function _getUploadDir()
    {
        $uploadDir = $this->_appendScopeInfo(self::UPLOAD_DIR);
        $uploadRoot = $this->_getUploadRoot(self::UPLOAD_ROOT);
        $uploadDir = $uploadRoot . '/' . $uploadDir;
        return $uploadDir;
    }
 
    protected function _addWhetherScopeInfo()
    {
        return true;
    }
 
    protected function _getAllowedExtensions()
    {
        return array('ico', 'png', 'gif', 'jpg', 'jpeg', 'apng', 'svg');
    }
 
    protected function _getUploadRoot($token) {
        return Mage::getBaseDir($token);
    }
}

Si ahora probamos nuestra configuración, veremos que al grabar se sube el archivo y se asocia. Lo veremos en la configuración y lo encontraremos en el path que hayas definido.

Configuración del palceholder

Ahora nos resta agregar unas líneas más de código, para que a nivel de bloque, nos provea la URL correcta a la imagen o al placeholder.

Siguiendo con el ejemplo, en uno de los bloques que invoca la entidad en el frontend, le implementé una sencilla validación.

public function getAvatarUrl($avatar)
{
    if ($avatar) {
        return Mage::getBaseUrl('media') . Mage::app()->getStore()->getConfig('rider/images/avatar') . $avatar;
    } else {
        return Mage::getBaseUrl('media') . 'rider' . DS . 'placeholder' . DS . Mage::app()->getStore()->getConfig('rider/images/avatar_placeholder');
    }
}

El método, si recibe un valor del atributo avatar, compondrá la URL correcta. Caso contrario, nos devuelve el path al placeholder.

En el archivo phtml, la llamada (siempre discutible) se vería así.

<img src="<?php echo $this->getAvatarUrl($_rider->getAvatar()); ?>" alt="<?php echo $_rider->getName(); ?>" title="<?php echo $_rider->getName(); ?>">

Si la entidad tiene la imagen asociada, es lo que veremos.

Rider con avatar en www.bestkiteboarding.com

Caso contrario, nos mostrará el placeholder configurado.

Rider sin avatar en www.bestkiteboarding.com

Algo tan sencillo y minúsculo como un placeholder configurable, usando sólo algunas líneas de XML y de PHP, puede ayudarnos a entregar mejor funcionalidad a nuestras tiendas.