Implementera strukturerad data (Schema.org) i ditt Shoporama-tema
Komplett guide till JSON-LD-strukturerad data i ett Shoporama-tema. Smarty-exempel för Product, Store, BreadcrumbList och AggregateRating med korrekta säkra metoder.
Strukturerade data (Schema.org) hjälper Google och andra sökmotorer att förstå innehållet på dina sidor. För en webbutik är det särskilt viktigt att markera produkter, butiksinformation, brödsmulor och produktrecensioner, så att du kan få ”rich snippets” i sökresultaten med till exempel pris, lagerstatus och stjärnbetyg.
Tips: Shoporama lägger automatiskt till grundläggande Schema.org-markup för produkter via våra inbyggda SEO-funktioner. Läs mer om automatisk strukturerad data och SEO i Shoporama. Den här artikeln riktar sig till dig som vill bygga eller utöka markupen själv i ditt tema.

Vad är strukturerade data?
Strukturerade data är ett standardiserat sätt att beskriva sidans innehåll i ett format som sökmotorerna förstår. JSON-LD är det rekommenderade formatet och placeras i en <script type="application/ld+json">-tagg. Det påverkar inte sidans utseende, men ger sökmotorerna strukturerad information som kan användas för rich snippets.
De rätta safe-metoderna för SafeProduct, SafeWebshop och SafeCompany
Innan du skapar din egen JSON-LD-markup är det viktigt att känna till de safe-metoder som faktiskt finns på objekten i ett Shoporama-tema. Dessa är de stabila metoderna som du kan använda i Smarty.
På $product (SafeProduct):
- getName(), getDescription(), getOwnId(), getGtin(), getMpn(), getBrandName()
- getPrice(), getSalePrice(), getRealPrice(), getLowest30DayPrice()
- getStockCount(), getIsInStock() och getAllowNegativeStock()
- getImage() och getImages() returnerar SafeImage med getSrc($w, $h, $type)
- getAvgRating($no_round), getReviewCount(), getProductReviews($limit)
- getUrl() ger produktens relativa sökväg
På $webshop (SafeWebshop):
- getName(), getDescription(), getCurrency(), getUrl(), getLogo()
- getCompany() returnerar SafeCompany
På $webshop->getCompany() (SafeCompany):
- getName(), getRegNr(), getEmail(), getPhone()
- getAddress(), getZipcode(), getCity(), getCountry()
Observera: Använd inte $product->getStock(), $product->getAverageRating(), $webshop->getCurrencyCode(), $webshop->getDomain(), $webshop->getEmail() eller $webshop->getPhone(). De finns inte som säkra metoder, och ditt tema kommer att ge fel. Använd istället getStockCount(), getAvgRating(), getCurrency(), getUrl() och getCompany()->getEmail()/getPhone().
Escaping: använd @json_encode, inte escape:javascript
JSON-LD är JSON, och JSON har sina egna regler för hur specialtecken skrivs. Det är den klart vanligaste orsaken till att en i övrigt korrekt markering avvisas av Google, så det är värt att sätta sig in i detta innan du skriver din första block.
Använd inte |escape:'javascript' i JSON-LD. Det omvandlar en apostrof till \, vilket är giltigt i JavaScript men inte i JSON, där den escape-sekvensen inte finns alls. En enda produkt med en apostrof i namnet, t.ex. ”Bo’s Kaffe”, räcker därför för att Google ska kasta bort hela blocket, och sidan förlorar då pris, lagerstatus och stjärnor på en gång. I Search Console visas detta som ”Bad escape sequence in string”.
Använd istället |@json_encode. Den skriver ut värdet som en färdig JSON-sträng inklusive citattecknen, så citattecknen måste tas bort från din mall:
Fel: "name": "<{$product->getName()|escape:'javascript'}>",
Rätt: "name": <{$product->getName()|@json_encode}>,
På så sätt hanteras apostrofer, citattecken, backslash och radbrytningar korrekt. Radbrytningar är värda att notera: en recensionstext som sträcker sig över flera rader förstör JSON-koden om den inte är eskapad, och detta fångas varken upp av |escape eller av <{strip}>. Dessutom escapar metoden snedstreck, så att ett </script> i ett produktnamn inte kan stänga ditt skriptblock i förtid.
- Om fältet kan vara tomt, lägg in |default:"" före, t.ex. <{$product->getOwnId()|default:""|@json_encode}>. Annars blir resultatet null istället för en tom sträng.
- Om HTML måste tas bort först lägger du till |strip_tags:true före, alltså |strip_tags:true|@json_encode.
- Det finns inget |escape:'json' i Smarty. Om du använder det sker ingen escapning alls.
- Siffror och fasta värden, t.ex. pris, valuta och schema.org-URL:er, ska fortfarande anges inom vanliga citattecken. De innehåller inga specialtecken.
- |escape:'javascript' är fortfarande det rätta valet i vanligt JavaScript, t.ex. i en dataLayer-push. Det är endast i JSON-LD som det är fel.
Produktmarkup (Product)
Den viktigaste typen för en webbshop är produktmarkering. Infoga blocket i din produktmall (vanligtvis product/view.html):
<{assign var="availability" value=$product->getStructuredDataAvailability()}>
<{assign var="availabilityStarts" value=$product->getStructuredDataAvailabilityStarts()}>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": <{$product->getName()|@json_encode}>,
"description": <{$product->getDescription()|strip_tags|@json_encode}>,
"sku": <{$product->getOwnId()|@json_encode}>,
<{if $product->getGtin()}>
"gtin": <{$product->getGtin()|@json_encode}>,
<{/if}>
<{if $product->getMpn()}>
"mpn": <{$product->getMpn()|@json_encode}>,
<{/if}>
<{if $product->getBrandName()}>
"brand": {
"@type": "Brand",
"name": <{$product->getBrandName()|@json_encode}>
},
<{/if}>
<{if $product->getImage()}>
"image": "<{$product->getImage()->getSrc(800, 800, 'fit')}>",
<{/if}>
"offers": {
"@type": "Offer",
"url": "<{$webshop->getUrl()}><{$product->getUrl()}>",
"price": "<{$product->getRealPrice()|string_format:"%.2f"}>",
"priceCurrency": "<{$webshop->getCurrency()}>",
"availability": <{$availability|@json_encode}><{if $availabilityStarts}>,
"availabilityStarts": <{$availabilityStarts|@json_encode}><{/if}>
}
}
</script>
Tre viktiga punkter i exemplet:
- Priset hämtas med getRealPrice(), så att eventuellt rea-pris och rabatt ingår, och momsen läggs till.
- Valuta hämtas med getCurrency() på $webshop (returnerar t.ex. "DKK" eller "EUR").
- Lagerstatus hämtas med getStructuredDataAvailability(), som returnerar den färdiga schema.org-URL:en, det vill säga https://schema.org/InStock, OutOfStock eller BackOrder. Metoden använder exakt samma logik som vårt Google Shopping-flöde, så att produktsidan och flödet alltid visar samma information. Bygg inte den själv med getIsInStock() och getAllowNegativeStock(): den kombinationen bortser från att butiken själv bestämmer om restorder-varor ska skickas som ”backorder”, och då uppstår just den avvikelse du ville undvika.
- getStructuredDataAvailabilityStarts() ger det förväntade leveransdatumet för en artikel i restorder, det vill säga fältet availabilityStarts. Den returnerar ett tomt värde när varan inte är i restorder eller när du inte har angett någon leveranstid i Google Shopping, så placera den i ett <{if}> -tagg som i exemplet.
Viktigt om du använder Google Shopping: Google jämför lagerstatusen på produktsidan med den du skickar i produktfeeden. Om de två inte stämmer överens kan Google inaktivera varan, så att den försvinner från både Shopping-annonser och de kostnadsfria produktvisningarna. Om du använder getStructuredDataAvailability() kan de två inte komma i konflikt med varandra, eftersom flödet och produktsidan bygger på samma metod. Om du skriver din egen logik för lagerstatus riskerar du att sidan visar ”slutsåld” för en artikel som feedet anger som tillgänglig för köp. Shoporamas egna teman hanterar redan detta. Läs mer i Konfigurera Google Shopping-feed.
Tidigare pris (Omnibusdirektivet)
Om produkten är på rea rekommenderar Google att du visar det lägsta priset under de senaste 30 dagarna som referenspris. Använd getLowest30DayPrice() för att hämta det. Funktionen returnerar null om det inte finns tillräckligt med prishistorik.
Produktrecensioner (AggregateRating)
Om du använder Shoporamas inbyggda recensionssystem kan du lägga till ett genomsnittsbetyg direkt i Product-markupen. Det kan ge stjärnor i Googles sökresultat. Infoga blocket inuti Product-objektet:
<{if $product->getReviewCount() > 0}>
,
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "<{$product->getAvgRating(true)|string_format:"%.1f"}>",
"reviewCount": "<{$product->getReviewCount()}>"
}
<{/if}>
Parametern true till getAvgRating() ger ett icke-avrundat genomsnitt, vilket Google föredrar (t.ex. 4,3 istället för bara 4).
Butiksinformation (Store / LocalBusiness)
Du kan också markera själva webbutiken, t.ex. i din sidfotsmall. Hämta kontaktuppgifter från $webshop->getCompany(), så att de uppgifter du redan har angett under Företag, Företagsinformation, inkluderas automatiskt:
<{$company = $webshop->getCompany()}>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Store",
"name": <{$webshop->getName()|@json_encode}>,
"url": "<{$webshop->getUrl()}>"
<{if $company}>
,
<{if $company->getEmail()}>
"email": <{$company->getEmail()|@json_encode}>,
<{/if}>
<{if $company->getPhone()}>
"telephone": <{$company->getPhone()|@json_encode}>,
<{/if}>
"address": {
"@type": "PostalAddress",
"streetAddress": <{$company->getAddress()|@json_encode}>,
"postalCode": <{$company->getZipcode()|@json_encode}>,
"addressLocality": <{$company->getCity()|@json_encode}>,
"addressCountry": <{$company->getCountry()|@json_encode}>
}
<{/if}>
<{if $webshop->getLogo()}>
,
"logo": "<{$webshop->getLogo()->getSrc(400, 400, 'fit')}>"
<{/if}>
}
</script>
Se till att företagsuppgifterna är korrekt ifyllda i admin under Företag, Företagsuppgifter. Om du har en fysisk butik kan du använda LocalBusiness eller en mer specifik undertyp (t.ex. ClothingStore) som @type.
Brödsmulor (BreadcrumbList)
På produkt- och kategorisidor är det god SEO-praxis att markera brödsmulor. Exempel för en produkt med huvudkategori:
<{if $product->getMainCategory()}>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": <{$webshop->getName()|@json_encode}>,
"item": "<{$webshop->getUrl()}>"
},
{
"@type": "ListItem",
"position": 2,
"name": <{$product->getMainCategory()->getName()|@json_encode}>,
"item": "<{$webshop->getUrl()}><{$product->getMainCategory()->getUrl()}>"
},
{
"@type": "ListItem",
"position": 3,
"name": <{$product->getName()|@json_encode}>,
"item": "<{$webshop->getUrl()}><{$product->getUrl()}>"
}
]
}
</script>
<{/if}>
Testa dina strukturerade data
Använd verktyg från Google och Schema.org för att validera din markering innan du går live:
- Rich Results Test kontrollerar om din markering ger rich snippets i Google
- Schema Markup Validator validerar JSON-LD-syntaxen
- I Google Search Console kan du se rapporter om dina produkter och rich results efter att sidorna har indexerats
Tips: Om du redan har aktiverat automatisk strukturerad data i Shoporama, se till att undvika dubbletter. Kontrollera vad som redan finns på sidan innan du lägger till din egen markering.
Vanliga frågor
Måste jag överhuvudtaget koda JSON-LD själv?
Nej, inte nödvändigtvis. Shoporama har inbyggda automatiska strukturerade data för produkter, och det täcker det mesta. Att koda själv är främst relevant om du vill lägga till ytterligare fält eller använda en mer specifik @type, t.ex. för hotell, restauranger eller evenemang.
Vilka schematyper kan jag använda utöver Product?
I ett Shoporama-tema kan du bygga alla Schema.org-typer som finns som JSON-LD. De vanligaste för webbutiker är Product, Offer, AggregateRating, Review, BreadcrumbList, Store, LocalBusiness, Organization, WebSite med SearchAction och FAQPage för fasta FAQ-sidor.
Varför får jag fel i Rich Results Test?
De vanligaste felen är saknade fält (t.ex. price utan priceCurrency), felaktig formatering av siffror eller specialtecken som förstör JSON-syntaxen. Den absolut vanligaste orsaken är escaping: kör alla textvärden genom |@json_encode och inte |escape:'javascript', vilket gör att en apostrof blir ogiltig JSON. Se avsnittet om escaping längre upp.
Hur lång tid tar det innan jag ser stjärnor i Google?
Google måste först genindexera sidan innan din nya markering tas i bruk. Det kan ta från några dagar till ett par veckor. I Search Console kan du begära snabb genindexering av enskilda sidor.
Påverkar det mina placeringar i Google?
Strukturerade data är inte en direkt rankningsfaktor, men rich snippets med t.ex. pris och stjärnor gör ditt sökresultat mer synligt och kan öka klickfrekvensen. På sikt bidrar det till att förbättra din totala SEO.
Hur hanterar jag moms i prisfältet?
Använd getRealPrice(), som returnerar priset inklusive moms. Det är det pris som kunden möter i butiken, och det som Google förväntar sig i produktmarkupen på en B2C-butik. Om du ska visa pris exklusive moms (B2B), använd istället getPriceExVat().
Måste jag ha recensioner innan jag visar AggregateRating?
Ja. Lägg endast till AggregateRating om produkten faktiskt har recensioner. I exemplet ovan kontrolleras att getReviewCount() > 0, så att markup inte läggs till för produkter utan recensioner. Falska eller tomma betyg kan leda till en straffåtgärd från Google.
Var hittar jag mina företagsuppgifter för Store-markup?
Gå till admin under Företag, Företagsinformation. De uppgifter du fyller i här blir tillgängliga via $webshop->getCompany() i ditt tema, så att Store-blocket fylls i automatiskt.
Var ska JSON-LD placeras på sidan?
Du kan placera den antingen i <head> eller i <body>. Båda alternativen är giltiga enligt Google. På produktsidor är det vanligt att placera den längst ner i produktmallen.
Min webbshop säljer på flera språk. Behöver jag ha markup för varje språk?
Ja, namn och beskrivningar i markeringen ska spegla den sida som kunden ser. Eftersom $product->getName() och $product->getDescription() automatiskt returnerar den översatta texten för det aktiva språket sker detta helt automatiskt när du använder exemplen här.
Behöver du hjälp med strukturerade data? Kontakta oss på support@shoporama.dk.
Relaterade artiklar
SEO i Shoporama
Komplett guide till SEO-funktionerna i Shoporama - från metataggar och webbplatskarta till Google Shopping och AI Assistant.
Produktrecensioner
Lär dig hur du använder produktrecensioner i Shoporama. Samla automatiskt in kundrecensioner, moderera dem och visa stjärnbetyg på dina produktsidor.
Variabler i ett Shoporama-tema
Översikt över globala och sidspecifika Smarty-variabler som är tillgängliga i Shoporama-teman.
Implementera spårning i ett Shoporama-tema
Utvecklarguide för e-handelsspårning i ett Shoporama-tema: datalager, Google Tag Manager, anpassade händelser och korrekta produkt-ID:n med...
Blogg länkad till din butik
Komplett guide till bloggfunktionen i Shoporama - skapa inlägg, schemalägg publicering, länka produkter, optimera för sökmotorer och använd...
Prishistorik och Omnibusdirektivet
Shoporama loggar automatiskt prisändringar och beräknar det korrekta förpriset enligt Omnibus-direktivet. Omnibus-direktivet (EU 2019/2161)....
Ställ in noindex på en sida, produkt eller kategori
Guide för att ställa in noindex i Shoporama så att sökmotorer inte indexerar vissa sidor.
Konfigurera Google Shopping-feed
Guide till hur man konfigurerar Google Shopping-feeden i Shoporama: kategorier, anpassade etiketter, produkt-ID och katalogmatchning samt lands-...
Få stjärnor i Google Shopping med produktrecensioner
Så här visar du dina produktrecensioner som stjärnor i Google Shopping med Shoporamas inbyggda recensionsflöde: aktivering, krav från Google och...
Produkt-ID:n och katalogmatchning: så här matchar dina annonser ditt produktflöde
Förklaring av vilket produkt-ID Shoporamas spårning skickar, varför det alltid stämmer överens med Google Shopping-feeden, vad katalogmatchning...
Relaterade funktioner
Strukturerad data - automatisk Schema.org-markup
Automatisk Schema.org-markup för produkter, recensioner, brödsmulor och företagsinformation. Rich snippets i Google med stjärnor och utmärkelser....
SEO i Shoporama - bli synlig i Google
Shoporama har inbyggda SEO-funktioner: snabb hastighet, full kontroll över titlar och metabeskrivningar, dynamiska taggar, automatiska...