SEO programmatico: meta tag e JSON-LD strutturato — articolo

> SEO programmatico: meta tag e JSON-LD strutturato

Deep dive su come il framework genera automaticamente meta tag Open Graph, structured data schema.org e breadcrumb per ogni pagina.

Luigi Iadicola
~5 min lettura
#Produzione #SEO #Frontend
SEO programmatico: meta tag e JSON-LD strutturato
SEO programmatico: meta tag e JSON-LD strutturato

SEO non e solo contenuto: e anche markup

Puoi scrivere il miglior articolo del mondo, ma se la pagina non comunica correttamente con i motori di ricerca, quel contenuto rimane invisibile. Google, Bing e i social network non leggono le pagine come un umano: cercano markup strutturato — meta tag per titolo e descrizione, Open Graph per le preview social, JSON-LD per i rich snippet nei risultati di ricerca. Senza questo markup, la pagina e una scatola chiusa per i crawler.

In Soft PHP MVC, due classi si occupano di questo: Seo per i meta tag classici e Open Graph, e JsonLd per i dati strutturati schema.org. Insieme, coprono tutto cio che serve per una buona indicizzazione senza dover scrivere markup manualmente in ogni template.

La classe Seo: defaults intelligenti

Seo::make() accetta un array opzionale con title, description, image e url, e restituisce un array normalizzato con valori di default per tutto cio che manca. Il titolo viene formattato come Titolo pagina | Iadicola - CRM | ERP | Software Aziendali dev — il nome del sito e sempre in coda, che e la convenzione piu comune per il branding nei risultati di ricerca.

Se non passi una descrizione, viene usata una descrizione generica del sito. Se non passi un'immagine, viene usata la favicon. Se non passi un URL, viene costruito automaticamente da $_SERVER['REQUEST_URI']. Questo significa che puoi chiamare Seo::make() senza parametri e ottenere comunque meta tag validi — utile per pagine secondarie dove non vale la pena personalizzare ogni campo.

Il metodo baseUrl() costruisce l'URL base del sito dal protocollo e dall'host corrente, gestendo correttamente il caso HTTPS (controllando $_SERVER['HTTPS']). Questo URL viene usato come base per costruire URL assoluti nei meta tag — i meta tag Open Graph richiedono URL assoluti, non relativi.

JsonLd: dati strutturati per i rich snippet

JSON-LD (JavaScript Object Notation for Linked Data) e il formato che Google preferisce per i dati strutturati. Invece di annotare il markup HTML esistente (come Microdata), JSON-LD inserisce un blocco <script type="application/ld+json"> separato con un oggetto JSON che descrive il contenuto della pagina secondo il vocabolario schema.org.

La classe JsonLd offre metodi statici per i tipi di schema piu comuni:

  • blogPosting() — per le pagine articolo: titolo, descrizione, autore, date di pubblicazione e modifica, keywords, conteggio parole. Questo e lo schema che abilita i rich snippet "Article" nei risultati di Google, con data e autore visibili
  • website() — per la homepage: nome del sito, URL, e opzionalmente una SearchAction che abilita il sitelink search box nei risultati di Google. Se configurato, l'utente puo cercare direttamente nel sito dalla pagina dei risultati Google
  • person() — per pagine personali: nome, URL, job title, e link ai profili social (LinkedIn, GitHub). Questo aiuta Google a collegare il sito alla persona e mostrare un knowledge panel
  • breadcrumbList() — per la navigazione gerarchica: una lista ordinata di voci con nome e URL. Google mostra i breadcrumb nei risultati di ricerca al posto dell'URL grezzo, migliorando la leggibilita e il CTR

Come si integra nel controller

Nel BlogController::show() l'integrazione SEO avviene in poche righe ma produce molto markup. Il controller:

  • Costruisce la descrizione SEO dal sottotitolo dell'articolo o, in mancanza, dai primi 160 caratteri dell'overview senza tag HTML
  • Genera l'URL canonico dell'articolo da slug o ID
  • Chiama Seo::make() con titolo, descrizione, immagine, URL, tipo Open Graph (article), date di pubblicazione/modifica, e tag come article_tags
  • Genera il JSON-LD BlogPosting con headline, descrizione, immagine assoluta, date ISO 8601, autore, URL, keywords dai tag, e word count calcolato dall'overview
  • Genera il JSON-LD BreadcrumbList con la gerarchia Home → Blog → Titolo articolo
  • Costruisce l'array breadcrumbs per il componente visuale nella pagina

Tutto questo viene passato alla view con compact(): la view non deve sapere come si generano i meta tag o i JSON-LD, li riceve gia pronti e li inserisce nel template.

Conversione date e formati

I motori di ricerca richiedono date in formato ISO 8601 (2026-04-12T14:30:00+02:00). Nel database le date sono in formato MySQL (2026-04-12 14:30:00). Il metodo privato toIso8601() in JsonLd gestisce questa conversione tramite strtotime() e date('c'), con un fallback alla stringa originale se il parsing fallisce. E un dettaglio piccolo ma critico: una data in formato sbagliato nel JSON-LD puo far ignorare l'intero blocco strutturato al crawler.

I JSON-LD vengono serializzati con JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE per produrre output leggibile e compatto. Gli slash negli URL non vengono escaped (evita il brutto https:\/\/), e i caratteri Unicode italiani restano intatti invece di diventare sequenze \uXXXX.

Reading time e word count

Nella view dell'articolo, il tempo di lettura stimato viene calcolato con str_word_count(strip_tags($article->overview)) / 200, arrotondato per eccesso con un minimo di 1 minuto. Il divisore 200 e la velocita media di lettura in parole al minuto per un testo tecnico — leggermente sotto la media generale di 250 wpm, perche il codice e i concetti tecnici rallentano la lettura.

Il word count viene anche incluso nel JSON-LD BlogPosting tramite la proprieta wordCount. Google puo usare questa informazione per valutare la profondita del contenuto — articoli piu lunghi e approfonditi tendono a posizionarsi meglio per query informative.

Breadcrumb: navigazione e SEO

I breadcrumb hanno un doppio ruolo: navigazione per l'utente e structured data per i motori di ricerca. Nel controller vengono costruiti due set paralleli: l'array $breadcrumbs per il componente visuale nella pagina (con URL relativi), e il JSON-LD BreadcrumbList per Google (con URL assoluti). La separazione e necessaria perche i JSON-LD richiedono URL assoluti mentre il componente visuale funziona meglio con URL relativi.

La gerarchia e sempre la stessa: Home → Sezione → Pagina corrente. L'ultimo elemento non ha URL — e la pagina attuale, e Google lo capisce dalla posizione nella lista. Questa struttura aiuta anche Google a capire la relazione gerarchica tra le pagine del sito, il che puo influenzare positivamente come vengono mostrati i risultati con i sitelink.

altri articoli