Cómo agregar datos estructurados en Magento
Inserta datos estructurados en un Magento de una forma automatizada y efectiva con programación.

- Autor:
-
Carlos Sánchez
- Fecha de publicación:
- 2024-01-19
- Última revisión:
- 2026-01-30
Los datos estructurados son una de esas cosas que en Magento 2 vienen implementados de forma muy básica (por no decir pobre). La instalación por defecto con el tema Luma incluye algunos microdatos, pero le faltan campos importantes como availability, offers, shipping o reviews individuales.
Este artículo surge de una pregunta en mi comunidad de Discord, y como es algo que me preguntan bastante, he decidido desarrollarlo en profundidad.
¿Por qué importan los datos estructurados en ecommerce?
Los datos estructurados (o schema markup) son código que ayuda a los buscadores a entender el contenido de tu página. En el caso de productos, le estás diciendo a Google: "esto es un producto, este es su precio, esta es su disponibilidad, estas son sus valoraciones".
¿El resultado? Los famosos rich snippets en los resultados de búsqueda: estrellas de valoración, precios, disponibilidad... Según varios estudios, los rich snippets pueden aumentar el CTR hasta un 150%.
Google prefiere el formato JSON-LD sobre los microdatos (el formato que usa Magento por defecto). Las ventajas de JSON-LD:
- Separación del HTML: No ensucias el código de las plantillas.
- Fácil de depurar: Todo el schema está en un único bloque script.
- Fácil de mantener: Cambios en pocos archivos en vez de docenas de templates.
La forma correcta: crear un módulo personalizado
En Magento 2, la forma limpia y escalable de añadir datos estructurados es mediante un módulo propio. Esto te permite:
- No depender de extensiones de terceros que pueden dejar de actualizarse.
- Control total sobre qué datos incluyes y cómo.
- Que las actualizaciones de Magento no sobreescriban tus cambios.
Vamos paso a paso.
1. Estructura del módulo
Primero, creamos la estructura básica del módulo. En app/code/ creamos:
app/code/TuEmpresa/DatosEstructurados/
├── registration.php
├── etc/
│ └── module.xml
├── Block/
│ └── ProductSchema.php
├── view/
│ └── frontend/
│ ├── layout/
│ │ └── catalog_product_view.xml
│ └── templates/
│ └── product_schema.phtml
2. Archivos de registro del módulo
registration.php:
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'TuEmpresa_DatosEstructurados',
__DIR__
);
etc/module.xml:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="TuEmpresa_DatosEstructurados" setup_version="1.0.0">
<sequence>
<module name="Magento_Catalog"/>
</sequence>
</module>
</config>
3. El bloque PHP con la lógica
Es decir, donde construimos los datos estructurados. Block/ProductSchema.php:
<?php
namespace TuEmpresa\DatosEstructurados\Block;
use Magento\Framework\View\Element\Template;
use Magento\Catalog\Model\ProductRepository;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Review\Model\ReviewFactory;
use Magento\Framework\Registry;
class ProductSchema extends Template
{
protected $productRepository;
protected $storeManager;
protected $reviewFactory;
protected $registry;
public function __construct(
Template\Context $context,
ProductRepository $productRepository,
StoreManagerInterface $storeManager,
ReviewFactory $reviewFactory,
Registry $registry,
array $data = []
) {
$this->productRepository = $productRepository;
$this->storeManager = $storeManager;
$this->reviewFactory = $reviewFactory;
$this->registry = $registry;
parent::__construct($context, $data);
}
public function getProduct()
{
return $this->registry->registry('current_product');
}
public function getJsonLdData()
{
$product = $this->getProduct();
if (!$product) {
return null;
}
$store = $this->storeManager->getStore();
$currencyCode = $store->getCurrentCurrencyCode();
// Estructura básica del producto
$schemaData = [
"@context" => "https://schema.org/",
"@type" => "Product",
"name" => $product->getName(),
"description" => strip_tags($product->getDescription()),
"sku" => $product->getSku(),
"image" => $product->getMediaGalleryImages()->getFirstItem()->getUrl(),
"url" => $product->getProductUrl(),
];
// Marca (si existe)
$brand = $product->getAttributeText('manufacturer');
if ($brand) {
$schemaData['brand'] = [
"@type" => "Brand",
"name" => $brand
];
}
// Offer (precio y disponibilidad)
$schemaData['offers'] = [
"@type" => "Offer",
"url" => $product->getProductUrl(),
"priceCurrency" => $currencyCode,
"price" => number_format($product->getFinalPrice(), 2, '.', ''),
"availability" => $product->isAvailable()
? "https://schema.org/InStock"
: "https://schema.org/OutOfStock",
"itemCondition" => "https://schema.org/NewCondition"
];
// Reviews y ratings (si existen)
$this->reviewFactory->create()->getEntitySummary($product, $store->getId());
$ratingSummary = $product->getRatingSummary();
$reviewCount = $product->getReviewsCount();
if ($reviewCount > 0 && $ratingSummary) {
$schemaData['aggregateRating'] = [
"@type" => "AggregateRating",
"ratingValue" => round($ratingSummary->getRatingSummary() / 20, 1),
"bestRating" => "5",
"worstRating" => "1",
"reviewCount" => $reviewCount
];
}
return $schemaData;
}
public function getJsonLdString()
{
$data = $this->getJsonLdData();
if (!$data) {
return '';
}
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
}
4. El layout XML
view/frontend/layout/catalog_product_view.xml:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceContainer name="content">
<block class="TuEmpresa\DatosEstructurados\Block\ProductSchema"
name="product.schema.jsonld"
template="TuEmpresa_DatosEstructurados::product_schema.phtml"
after="-"/>
</referenceContainer>
</body>
</page>
5. La plantilla phtml
view/frontend/templates/product_schema.phtml:
<?php
$jsonLd = $block->getJsonLdString();
if ($jsonLd):
?>
<script type="application/ld+json">
<?= /* @noEscape */ $jsonLd ?>
</script>
<?php endif; ?>
6. Activar el módulo
Ejecuta los siguientes comandos desde la raíz de Magento:
bin/magento module:enable TuEmpresa_DatosEstructurados
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:clean
Extendiendo el módulo
El código anterior es una base. En mis proyectos suelo añadir condicionales para diferentes escenarios:
- Productos configurables vs simples
- Productos agrupados
- Diferentes schemas según la categoría
- Datos de envío (shippingDetails)
- Preguntas frecuentes (FAQPage)
La lógica puede complicarse bastante dependiendo del caso, pero siempre partiendo de esta estructura base. Esto es algo que vemos en detalle en mi máster de SEO técnico.
Alternativa: usar el buffer de PHP
Si no quieres o no puedes crear un módulo completo, existe otra opción: usar el todopoderoso buffer de PHP para inyectar los datos estructurados en el HTML antes de enviarlo al navegador.
Esta técnica es especialmente útil cuando:
- Necesitas añadir schema a páginas que no son de producto (CMS, categorías custom).
- Quieres modificar el comportamiento sin tocar los layouts de Magento.
- Tienes prisa y necesitas una solución rápida.
La idea es interceptar el HTML completo, detectar en qué página estamos, y añadir el JSON-LD correspondiente justo antes del </head> o del </body>.
Un ejemplo simplificado:
<?php
// En un observer o plugin que capture el output
public function addStructuredData($html)
{
// Detectar si es página de producto
if (strpos($_SERVER['REQUEST_URI'], '/product/') !== false) {
$schema = $this->buildProductSchema();
$schemaScript = '<script type="application/ld+json">' . json_encode($schema) . '</script>';
$html = str_replace('</head>', $schemaScript . '</head>', $html);
}
return $html;
}
Esta técnica requiere más cuidado porque trabajas con el HTML ya generado, pero te da flexibilidad total.
Lo que NO recomiendo: Tag Manager y JavaScript
Existe la opción de añadir datos estructurados desde Google Tag Manager. Google en su documentación te especifica cómo hacerlo. Y realmente sería insertando una etiqueta de HTML dentro de un contenedor de la herramienta.
El resumen de cómo funciona esto: si el usuario rastrea la página con JavaScript, una de las funciones de este Tag Manager va a ser poner el código en cuestión. Pero va a tener que renderizar el JavaScript antes. Por lo cual, en el caso en el que Google rastree la página sin JavaScript (lo que es bastante posible), no se verá dicho dato estructurado.
Lo que dice John Mueller al respecto
Según él, Google no tiene problemas a la hora de rastrearlo, pero aun así recomienda insertar los datos estructurados en la propia web y no en herramientas externas por mantenimiento.
En mi propia experiencia, he visto ocasiones donde Google no ha sido capaz de ver los datos estructurados cuando se han insertado por JavaScript con CSR (que es la forma habitual de JS). Se puede comprobar si lo lee por medio de la Search Console.
Mi opinión: ¿por qué insertar los datos estructurados de una forma arriesgada y con menor escalabilidad cuando puedes hacerlo directamente en el servidor?
Verificar que funciona
Después de implementar los datos estructurados, es fundamental verificar que todo está correcto.
1. Google Rich Results Test
La herramienta oficial de Google para validar datos estructurados: Rich Results Test. Introduce la URL de un producto y comprueba que detecta correctamente el schema de Product.
2. Schema.org Validator
Para una validación más exhaustiva contra el estándar schema.org: Schema Markup Validator.
3. Search Console
En Google Search Console, ve a Mejoras > Productos (o el tipo de schema que hayas implementado). Así verás si Google está detectando correctamente los datos estructurados y si hay errores o advertencias.
4. Ver el código fuente
Lo más básico: abre el código fuente de la página (Ctrl+U) y busca application/ld+json. Deberías ver tu bloque de JSON-LD con todos los datos del producto.
Errores comunes a evitar
- Datos duplicados: Si Magento ya incluye microdatos y tú añades JSON-LD, puedes tener duplicación. Google lo maneja, pero es más limpio desactivar los microdatos nativos o asegurarte de que no haya conflictos.
- Campos requeridos vacíos: Si el producto no tiene imagen, descripción o precio, el schema será inválido. Añade condicionales para manejar estos casos.
- Precios con formato incorrecto: El precio debe ser numérico sin símbolos de moneda. Usa
number_format()correctamente. - Reviews sin productos: No incluyas aggregateRating si no hay reviews. Google penaliza esto.
- URLs relativas: Todas las URLs en el schema deben ser absolutas.
Schemas adicionales para ecommerce
Además del schema de Product, considera implementar:
- Organization: Información de tu empresa (logo, contacto, redes sociales).
- BreadcrumbList: La ruta de navegación del producto.
- WebSite: Con SearchAction para el buscador interno.
- FAQPage: Si tienes preguntas frecuentes en las fichas de producto.
- LocalBusiness: Si tienes tienda física.
Resumen
- Método recomendado: Crear un módulo personalizado con JSON-LD.
- Alternativa: Usar el buffer de PHP para inyectar el schema.
- No recomendado: Tag Manager o JavaScript del lado del cliente.
- Siempre verificar: Rich Results Test y Search Console.
- Formato preferido: JSON-LD sobre microdatos.
Fuentes
Te falta mi máster. Accede a una formación avanzada que te permitirá aplicar e implementar SEO en cualquier tipo de WEB
¡Accede al Máster de SEO Técnico!