SEO técnico para PrestaShop: Schema markup y datos estructurados avanzados
Introducción: Por qué el marcado semántico es clave en tu SEO PrestaShop
En el ecosistema del comercio electrónico, la competencia es feroz. No basta con tener un catálogo bien organizado y un buen hosting; necesitas que Google entienda cada producto, cada categoría y cada breadcrumb de tu tienda. Aquí es donde entran en juego los datos estructurados y el schema markup.
Para un SEO PrestaShop efectivo, implementar datos estructurados avanzados no es un lujo, es una necesidad. Mientras que la mayoría de las tiendas se conforman con el marcado básico de producto, tú puedes ir un paso más allá: desde la integración de reseñas con estrellas (rich snippets) hasta la marcación de ofertas, disponibilidad y precios dinámicos.
Este artículo está diseñado para administradores de sistemas y desarrolladores que quieren dominar el SEO técnico en PrestaShop. Vamos a desglosar cómo implementar, verificar y optimizar schema markup de forma nativa y mediante módulos, con ejemplos de código y configuraciones reales.
¿Qué son los datos estructurados y por qué importan en PrestaShop?
Los datos estructurados son un formato estandarizado (basado en schema.org) que permite a los motores de búsqueda interpretar el contenido de tu página. En lugar de leer texto plano, Google puede identificar que un elemento es un "Producto", un "Precio", una "Valoración" o una "Oferta".
En el contexto de SEO PrestaShop, esto se traduce en:
- Rich snippets: Estrellas de reseña, precio, disponibilidad, marca.
- Knowledge Graph: Mayor presencia en búsquedas de marca o categoría.
- Voice Search: Los asistentes virtuales se apoyan en datos estructurados para responder.
- Búsqueda visual: Google puede asociar imágenes de producto con su marcado.
Tipos de schema esenciales para PrestaShop
- Product: El más básico y obligatorio. Incluye nombre, descripción, SKU, imagen, precio, moneda y disponibilidad.
- Offer: Dentro de Product, define condiciones de oferta, precio con descuento, fecha de finalización.
- AggregateRating: Para mostrar la valoración media y número de reseñas.
- BreadcrumbList: Mejora la navegación en SERP.
- Organization: Para la marca o tienda (logo, dirección, redes sociales).
- Review: Cada reseña individual de producto.
[INFO] Google recomienda usar JSON-LD como formato de datos estructurados, ya que es más fácil de inyectar y mantener sin romper el HTML. PrestaShop 1.7+ ya incluye soporte nativo para Product schema en JSON-LD, pero suele ser básico.
Implementación básica vs. avanzada en PrestaShop
El marcado nativo de PrestaShop 1.7
Por defecto, PrestaShop 1.7 añade un schema básico de producto en las páginas de producto. Puedes verificarlo inspeccionando el código fuente o usando la herramienta de prueba de datos estructurados de Google.
<!-- Ejemplo de marcado nativo (simplificado) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Camiseta Algodón",
"image": "https://tutienda.com/img/camiseta.jpg",
"description": "Camiseta 100% algodón orgánico.",
"sku": "CAM-001",
"offers": {
"@type": "Offer",
"priceCurrency": "EUR",
"price": "29.99",
"availability": "https://schema.org/InStock"
}
}
</script>
Sin embargo, este marcado tiene limitaciones:
- No incluye AggregateRating (necesitas un módulo de reseñas que lo genere).
- No maneja ofertas temporales de forma dinámica.
- No añade BreadcrumbList en todas las plantillas.
- No personaliza Organization con tu logo y redes.
Estrategia avanzada: Personalización mediante override o módulo
Para ir más allá, tienes dos caminos:
- Módulo de terceros: Como "PrestaShop SEO Schema Pro" o "Rich Snippets PrestaShop". Estos módulos añaden opciones en el back office para rellenar campos extra y generan JSON-LD avanzado.
- Override manual: Modificando el fichero
ProductController.phpo añadiendo un hook en el theme para inyectar JSON-LD personalizado.
A continuación, te muestro cómo hacerlo mediante override (recomendado para desarrolladores).
Paso 1: Crear un override en el controlador de producto
Crea un archivo en /override/controllers/front/ProductController.php con el siguiente contenido:
<?php
class ProductController extends ProductControllerCore
{
public function initContent()
{
parent::initContent();
// Solo en páginas de producto
if (Validate::isLoadedObject($this->product)) {
$this->addJsonLdProduct();
}
}
protected function addJsonLdProduct()
{
$product = $this->product;
$id_lang = $this->context->language->id;
$id_shop = $this->context->shop->id;
// Obtener datos de reseñas (ejemplo con módulo de reseñas nativo)
$averageRating = Product::getProductAverageRating($product->id);
$reviewsCount = Product::getProductNbComments($product->id);
$jsonLd = [
'@context' => 'https://schema.org',
'@type' => 'Product',
'name' => $product->name[$id_lang],
'image' => $this->context->link->getImageLink($product->link_rewrite[$id_lang], $product->id),
'description' => strip_tags($product->description_short[$id_lang]),
'sku' => $product->reference ?: $product->id,
'brand' => [
'@type' => 'Brand',
'name' => $product->manufacturer_name ?: 'TuMarca'
],
'offers' => [
'@type' => 'Offer',
'priceCurrency' => $this->context->currency->iso_code,
'price' => $product->getPriceWithoutReduct(),
'priceValidUntil' => date('Y-m-d', strtotime('+30 days')),
'availability' => $product->quantity > 0 ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock',
'seller' => [
'@type' => 'Organization',
'name' => Configuration::get('PS_SHOP_NAME')
]
]
];
// Añadir AggregateRating solo si hay reseñas
if ($reviewsCount > 0) {
$jsonLd['aggregateRating'] = [
'@type' => 'AggregateRating',
'ratingValue' => $averageRating,
'bestRating' => 5,
'worstRating' => 1,
'ratingCount' => $reviewsCount
];
}
// Añadir al array de datos estructurados de la página
$this->context->smarty->assign('jsonLdProduct', json_encode($jsonLd, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
}
}
Luego, en tu product.tpl, añade al final del <head> o antes de </body>:
{if isset($jsonLdProduct)}
<script type="application/ld+json">
{$jsonLdProduct nofilter}
</script>
{/if}
[TIP] Siempre usa
JSON_UNESCAPED_SLASHESpara que las URLs no aparezcan escapadas, yJSON_UNESCAPED_UNICODEpara caracteres acentuados.
Paso 2: Añadir BreadcrumbList
En el mismo controlador o en un hook displayHeader, puedes generar el breadcrumb:
protected function addJsonLdBreadcrumb()
{
$breadcrumb = $this->getBreadcrumb();
$itemListElement = [];
$position = 1;
foreach ($breadcrumb['links'] as $link) {
$itemListElement[] = [
'@type' => 'ListItem',
'position' => $position,
'name' => $link['title'],
'item' => $link['url']
];
$position++;
}
$jsonLdBreadcrumb = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $itemListElement
];
$this->context->smarty->assign('jsonLdBreadcrumb', json_encode($jsonLdBreadcrumb, JSON_UNESCAPED_SLASHES));
}
Datos estructurados avanzados: Ofertas, precios dinámicos y reseñas
Marcado de ofertas con precio rebajado
Cuando un producto tiene precio especial, debes indicar el precio original y el rebajado, además de la fecha de validez. Modifica el array offers:
"offers": {
"@type": "Offer",
"priceCurrency": "EUR",
"price": "24.99",
"priceValidUntil": "2025-12-31",
"availability": "https://schema.org/InStock",
"seller": {
"@type": "Organization",
"name": "Tu Tienda"
},
"hasMerchantReturnPolicy": {
"@type": "MerchantReturnPolicy",
"applicableCountry": "ES",
"returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow",
"merchantReturnDays": 30,
"returnMethod": "https://schema.org/ReturnByMail"
}
}
Para productos con descuento, añade "priceSpecification" con "priceType": "SalePrice":
"offers": {
"@type": "Offer",
"price": "19.99",
"priceSpecification": {
"@type": "UnitPriceSpecification",
"price": "19.99",
"priceType": "SalePrice",
"priceCurrency": "EUR"
},
"priceValidUntil": "2025-06-30"
}
[WARNING] No uses precios falsos ni descuentos inflados. Google penaliza el marcado engañoso. Asegúrate de que el precio en schema coincida exactamente con el visible en la página.
Reseñas de producto (Review + AggregateRating)
Para mostrar estrellas en los resultados, necesitas dos tipos de datos:
- AggregateRating: Valoración media global.
- Review: Cada reseña individual (opcional pero muy potente).
Ejemplo de una reseña individual:
{
"@context": "https://schema.org",
"@type": "Review",
"itemReviewed": {
"@type": "Product",
"name": "Camiseta Algodón"
},
"reviewRating": {
"@type": "Rating",
"ratingValue": "5",
"bestRating": "5"
},
"author": {
"@type": "Person",
"name": "María López"
},
"datePublished": "2025-03-15",
"reviewBody": "Excelente calidad, muy suave al tacto."
}
Puedes inyectar múltiples reseñas en un array dentro del schema de producto.
Marcado de categoría y listado
Aunque Google se centra en páginas de producto, también puedes marcar páginas de categoría con ItemList:
{
"@context": "https://schema.org",
"@type": "ItemList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"item": {
"@type": "Product",
"name": "Producto A",
"url": "https://tutienda.com/producto-a"
}
},
{
"@type": "ListItem",
"position": 2,
"item": {
"@type": "Product",
"name": "Producto B",
"url": "https://tutienda.com/producto-b"
}
}
]
}
Esto ayuda a Google a entender la estructura de tu catálogo.
Verificación y monitorización de los datos estructurados
Una vez implementado, debes verificar que Google lo interpreta correctamente. Usa estas herramientas:
- Prueba de resultados enriquecidos de Google: https://search.google.com/test/rich-results
- Schema Markup Validator: https://validator.schema.org
- Google Search Console: Informe de "Mejoras" > "Datos estructurados".
Errores comunes y soluciones
| Error | Causa | Solución |
|---|---|---|
| "Falta el campo price" | No se envía el precio en el JSON-LD | Revisa que $product->getPriceWithoutReduct() devuelva un número. |
| "Valor de rating no válido" | AggregateRating sin bestRating o worstRating | Añade ambos campos explícitamente. |
| "Múltiples tipos de objeto" | Mezclas Product y Offer en el mismo bloque | Separa en bloques independientes. |
| "URL no canónica" | El schema apunta a una URL con parámetros | Usa $product->getLink() para obtener la URL canónica. |
[TIP] Activa el debug de Google en Search Console para recibir alertas cuando un schema sea inválido. Revisa cada semana.
Optimización avanzada para SEO PrestaShop
Velocidad y carga diferida de JSON-LD
El marcado JSON-LD puede ser pesado si incluyes muchas reseñas. Para no ralentizar la carga, puedes diferir su carga:
<script type="application/ld+json" id="product-schema">
// Aquí el JSON
</script>
<script>
// Opcional: cargar después de la interacción
window.addEventListener('load', function() {
var schema = document.getElementById('product-schema');
schema.type = 'application/ld+json';
});
</script>
Integración con módulos de reseñas populares
Si usas módulos como "Product Comments" (nativo) o "Yotpo", asegúrate de que el hook displayFooterProduct inyecte las reseñas en el schema. Puedes modificar el módulo para que añada el JSON-LD directamente.
Internacionalización y multilingüismo
Si tu tienda tiene varios idiomas, el schema debe reflejar el idioma de la página. Usa $this->context->language->iso_code para definir inLanguage:
"inLanguage": "es-ES"
Además, para cada oferta, puedes añadir "availableLanguage" si vendes contenido digital.
Conclusión: El futuro del SEO técnico en PrestaShop
Los datos estructurados no son una moda pasajera. Con la evolución hacia la búsqueda semántica y la inteligencia artificial, Google premia cada vez más a las páginas que proporcionan información clara y estructurada. Implementar schema markup avanzado en tu SEO PrestaShop te dará una ventaja competitiva real: más visibilidad, mejores CTR y mayor confianza del usuario.
Recuerda:
- Empieza con el marcado básico de producto y breadcrumb.
- Añade AggregateRating y reseñas individuales.
- Personaliza ofertas y precios dinámicos.
- Verifica con herramientas oficiales.
- Monitoriza errores en Search Console.
No dejes tu SEO en manos del marcado por defecto. Toma el control, escribe tu propio JSON-LD y convierte tu tienda PrestaShop en una máquina de rich snippets.
