altre destinazioni

vedi anche

ultimi post

ultimi commenti

tag principali

categorie

archivi

powered by

  • WPFrontman + WP

friends

copyright

  • © 2004-2011
    Ludovico Magnocavallo
    tutti i diritti riservati

WP rant #1 - tassonomie

13 luglio 2011

13 commenti

tag

categorie

Chi ha seguito un po’ le mie evoluzioni in questi ultimi anni, sa che con WP ho da tempo in corso una specie di lotta personale: è il più diffuso sistema di blogging, ha un’infinità di funzioni e tantissimi plugin, l’interfaccia è ragionevolmente comoda da usare, ma il codice è un esempio straordinario di come non si dovrebbe programmare, sia dal punto dell’architettura (quale architettura?), del disegno dei dati, degli algoritmi (quali algoritmi?), che del codice vero e proprio. In breve, ogni volta che ci metto le mani mi viene il rigetto, e ogni volta riscrivo un pezzo grande (il frontend, prima in PHP e ora in Django) o piccolo (varie funzionalità del backend).

Questa volta è il turno delle categorie, con cui sto lottando da qualche giorno. Per preservare la mia (scarsa) sanità mentale ho deciso di sfogarmi qui, in modo da a) avere magari qualche feedback, anche ne se immagino già il tono (da “ma chi te lo fa fare? Sei rimbecillito?” a “WP è una figata sei tu che non capisci una fava” – possibile), e b) inaugurare una “galleria degli orrori”, sempre che non mi stufi prima e decida di scrivere un sistema di blogging da zero. Quei due o tre lettori interessati possono continuare dopo il salto.

La questione di oggi riguarda le tassonomie di WP, il disegno dei dati inaugurato con WP 2 che generalizza categorie, tag, e tassonomie specifiche ideate dall’utente. Idea non malvagia, anche se appesantisce parecchio il recupero e la manipolazione dei dati, perchè se ben realizzata apre prospettive interessanti specie per le ottimizzazioni SEO. Vi spiego come sono strutturate le tassonomie nella base dati, poi vediamo come potrebbero essere utilizzate, e alla fine perchè WP ha fucked up e del nuovo disegno dei dati ha gli svantaggi (più query, join, ricorsività pesante) ma non i vantaggi.

Il disegno delle tassonomie nella base dati.

L’architettura delle tassonomie di WP è piuttosto semplice: il dato centrale sono i termini, “etichette” descrittive che sono (anzi non sono, vedi sotto) univoche; questi termini vengono associati in ogni tassonomia (categorie, tag) con istanze definite dal tipo di tassonomia, il termine appunto, e un oggetto progenitore a seconda che la tassonomia sia gerarchica (categoria “Python” figlia di una categoria “Programmazione”) o meno (tag “Python”); la relazione tra un’istanza di una tassonomia (categoria, tag, ecc.) e gli oggetti in essa contenuti (post, pagine) è definita mediante un semplice rapporto molti a molti (un post ha molte tag, una tag ha molti post).

Nella base dati, le tassonomie sono strutturate in tre tabelle:

  • wp_terms, ospita i termini definiti da term_id, name, slug (nome mnemonico breve); i campi id e slug sono unique (e di fatto anche il nome, vedi in seguito)
  • wp_term_taxonomy, ospita le istanze delle tassonomie, ognuna identificata da un campo term_taxonomy_id; ogni istanza ha una relazione term_id verso un termine della tabella precedente, la tipologia di tassonomia cui appartiene taxonomy  ('category', 'post_tag', ecc.), e se la tassonomia è gerarchica ed esiste un antenato per questa istanza, parent è una relazione verso il term_taxonomy_id dell’antenato definito in questa stessa tabella
  • wp_term_relationships, ospita le relazioni tra una istanza di una tassonomia term_taxonomy_id e un post o pagina o altro oggetto object_id

Uno schema molto flessibile, che presenta però un po’ di problemi. Ovviamente l’integrità dei dati, dato che il tipo di tassonomia nella seconda tabella non è una relazione ma una semplice stringa, e che come è abitudine di WP non esistono foreign key da nessuna parte, e peggio ancora i NULL non sono NULL ma stringhe ’0′, rendendo impossibile per chi usa InnoDB crearsi foreign key da solo. Altro problema è la complessità che serve per estrarre informazioni tutto sommato banali da questo schema, specie dato il livello molto mediocre del codice di WP che fa queste operazioni. Ma torniamo a noi, e vediamo invece quali vantaggi potremmo avere da una struttura così flessibile.

Come potrebbero essere utilizzate le tassonomie

Non sono assolutamente un esperto SEO, ma quello che sto iniziando a capire frequentando chi ne capisce è che la densità di relazioni e contenuti rilevanti su un tema sono molto importanti, e il vantaggio di uno schema di tassonomie come quello di WP dovrebbe essere — oltre a permettere la definizione di tassonomie arbitrarie — quello di poter aggregare istanze di una stessa tassonomia o di tassonomie diverse che hanno come radice lo stesso termine. Ad esempio, immaginiamo di avere un blog dedicato alla fotografia, e di stare mostrando a un visitatore i post della categoria Nikon, sottocategoria di Reflex digitali.

E’ probabile che chi è interessato a corpi macchina Nikon sia anche interessato a ottiche Nikon, e forse più in generale ad argomenti relativi a prodotti Nikon (accessori, foto realizzate con, fotografi che usano, ecc.). E dato che la categoria che stiamo visualizzando ha come radice Nikon, e che questo termine dovrebbe essere univoco, dovremmo poter estrarre facilmente dalla base dati le altre istanze che usano lo stesso termine come radice indipendemente dalla tassonomia cui appartengono (categoria, tag, ecc.) o dal loro antenato. Partendo da Reflex digitali>Nikon potremmo quindi mostrare all’utente argomenti correlati come le categorie Obiettivi>Nikon, Accessori>Nikon, la tag nikon, la categoria di link Nikon, generando così contenuti più ricchi e incrementando la densità di link e keyword rilevanti. Peccato che in WP questo non sia possibile, per una serie di motivi.

Perchè le tassonomie di WP sono fucked up

Il primo problema che incontriamo è sui termini: WP in teoria fa la cosa giusta, definendo il campo slug — che è traslitterato e minuscolo — come univoco, in modo da ricondurre ad una sola entità termini uguali con una differenza di maiuscole o minuscole. In pratica però le cose sono diverse: in caso di problemi di integrità referenziale dovuti al tentativo di inserire un nuovo termine con slug uguale a uno esistente, invece di ricondurre il nuovo termine al vecchio WP ne crea uno nuovo, estendendo lo slug con un numero progressivo. Nel nostro caso, inserendo una tag nikon dopo una categoria Nikon (o viceversa), la tag sarebbe stata associata a un nuovo termine con slug nikon-2. Problema non gravissimo dato che basta attenersi a regole uguali per inserire i nomi di tag e categorie, ma abbastanza scocciante e soprattutto senza alcuna giustificazione.

Il secondo problema è molto più serio, e vanifica in gran parte i vantaggi di relazione tra istanze diverse che sono uno dei benefici di uno schema come questo. WP infatti definisce le istanze delle tassonomie (le singole categorie e tag) con il loro id (term_taxonomy_id) nella tabella wp_term_taxonomy, e usa correttamente questo stesso id per metterle in relazione con gli oggetti (post, pagine) nella tabella wp_term_relationships, peccato che

  • nella tabella wp_term_taxonomy ci sia un indice unico sulla coppia di campi (term_id, taxonomy)
  • le istanze delle tassonomie sono identificate con l’id del termine cui sono in relazione e non con il loro proprio id durante l’inserimento e aggiornamento di un post, sia nelle form dell”interfaccia di amministrazione che nel codice che interagisce con la base dati

Cosa significano le due situazioni precedenti?

La prima, che in WP non è possibile avere due istanze della stessa tassonomia con antenati diversi riferite allo stesso termine; nel nostro esempio, non potremo mai avere due categorie Obiettivi>Nikon e Reflex digitali>Nikon, ognuna con il proprio id di istanza (term_taxonomy_id) ma riferite ad un unico termine (term_id); quello che fa WP è creare un nuovo termine come abbiamo visto sopra per la categoria che viene inserita più tardi, quindi termine nikon per la prima e nikon-2 per l’ultima.

La seconda situazione significa che, anche se modificassimo a mano l’indice univoco sulla base dati, non riusciremmo comunque ad utilizzare categorie riferite allo stesso termine: quando associa le categorie (o qualsiasi istanza di tassonomia gerarchica) ad un post, WP usa una corrispondenza 1:1 tra term_taxonomy_id e term_id, e ovviamente (se c’è una soluzione peggiore tra molte state certi che è quella che viene implementata in produzione) gli id che vengono passati tra form e database sono i secondi. Per modificare questo comportamento andrebbe riscritta parte della admin e parte delle funzioni che recuperano e impostano le tassonomie associate a post e pagine.

Dubito che questi problemi verranno rettificati in futuro, dato che la codebase di WP è troppo stratificata; quello che sto cercando di capire è se c’è modo di sostituirsi a WP con un plugin nella console di admin sia quando si genera il box di scelta delle categorie, che nella pagina di creazione e modifica delle categorie, che nelle funzioni che ricevono i dati di un post e li salvano in un database. E ovviamente va anche riscritto l’orribile classe Walker usata da WP per generare la lista gerarchica di tassonomie, che (avevate dubbi?) sballa la gerarchia lasciando orfane istanze che pure hanno un antenato. A furia di sostutuire pezzi di WP, forse facevo prima a scrivere un blogging engine da zero… Ma forse sono io che non ho la profondità per apprezzare la poetry del code di WP… (btw, poesia è materia obbligatoria nelle facoltà di CS oltreoceano, lo sapevate?).

Aggiornamento: the plot thickens

Dopo qualche altra ora passata sulle tassonomie di WP devo rivedere le mie considerazioni: non sono solo una implementazione maldestra di un disegno, sono una schifezza abominevole che dovrebbe essere mostrata agli studenti di CS come esempio negativo.

Avete presente il campo parent della tabella wp_term_taxonomy? Logica vorrebbe che, individuando un ogetto relazionato identico a quello da cui parte la relazione con le uniche ovvie differenze di avere attributi diversi (term_id) e livello gerarchico superiore, la foreign key fosse ricursiva e puntasse a un record della stessa tabella. Cioè che una categoria avesse come antenato un’altra categoria. Logico no? Beh, evidentemente non era abbastanza logico per gli sviluppatori che hanno lavorato alle tassonomie di WP: la chiave punta infatti alla tabella wp_terms!

In sintesi, con un colpo di mano sorprendente sono riusciti a commettere una serie di crimini contro logica: un livello di indirezione ulteriore completamente non necessario, per cui per trovare la categoria progenitrice occorre seguire la relazione alla tabella dei termini, e da questa ridiscendere alla tabella delle tassonomie tenendo conto della chiave primaria (però al contrario) e filtrando per tassonomia dato che a un termine possono corrispondere n tassonomie (tag, categoria, ecc.); forzare il disegno ad essere utilizzato con relazioni 1:1 tra tassonomie e termini; confondere chi prende in manimo codice e disegno dei dati, dato che non esiste una dichiarazione di foreign key da nessuna parte. Complimenti, bravi!

13 commenti

  • Silvano
    13 luglio 2011 #

    Se l’obiettivo fosse solo "mostrare all’utente argomenti correlati come le categorie Obiettivi>Nikon, Accessori>Nikon, la tag nikon, la categoria di link Nikon", valuterei l’idea di effettuare ricerche all’interno di wp_terms, eventualmente anche utilizzando espressioni regolari, tenendo conto del numero incrementale accodato allo slug.

    Scenderei a compromessi con l’architettura fucked-up (mai definizione fu più appropriata) delle tassonomie ma eviterei compromessi peggiori che, immagino, mi aspetterebbero mettendo mano al grosso del codice.

    Ogni volta (per fortuna poche) che ho dovuto metterci le mani ho pensato che quella del codice di WP è poetry nel senso più stretto: per comprenderla a fondo serve una qualche forma di illuminazione…

  • ludo
    13 luglio 2011 #

    Beh si Silvano, la soluzione più "sana" è fare ricerche con regexp, ma non è molto elegante.

    Sulla poetry, mi hai fatto fare una risata, hai pienamente ragione!

  • Davide Salerno
    14 luglio 2011 #

    " Nel nostro caso, inserendo una tag nikon dopo una categoria Nikon (o viceversa), la tag sarebbe stata associata a un nuovo termine con slug nikon-2"

    A che versione di WP fai riferimento? A questo comportamento non capita proprio…

  • ludo
    14 luglio 2011 #

    Alla ultima versione rilasciata.

  • Davide Salerno
    15 luglio 2011 #

    Strano, a me non capita affatto.

    Riesco ad avere tranquillamente una categoria ed un tag con gli stessi nomi.

  • ludo
    15 luglio 2011 #

    Una tag e categoria con differenze nel nome tra maiuscole e minuscole, che hanno lo stesso slug nel database? Hai una copia di WP fortunata allora...

  • michele
    15 luglio 2011 #

    Ma chi te lo fa fare? WP è una figata, sei tu che non capisci una fava. ;)

    Ludo, dimenticati WP e fatti un blog engine in python; una settimana di hacking è sufficiente.

  • eV
    18 luglio 2011 #

    sei coraggioso.
    non capisco però una cosa. perché rifare il frontend? il settore di mercato da "aggredire" è quello degli utilizzatori di wordpress a cui piace l’interfaccia di admin, ma che sono disposti a rinunciare a temi e plugin per il frontend in favore di? velocità? scalabilità?
    visto come è fatto il codice (e il db) di wp… perché non fare un blogengine da zero?

    ti chiedo infine una cosa: perché wp fa tutto questo successo e a guardare il codice è tutto spaghetti-php? (no, non è sarcasmo, è una domanda a cui continuo a non riuscire a rispondere, se non "perché basta saper vendere e fare una bella interfaccia di admin")…

    ciao!

  • ludo
    19 luglio 2011 #

    Cerco di risponderti con ordine.

    Il progetto è nato, come spesso succede, per un’esigenza mia ("to scratch an itch" per dirla all’americana), ma il target sono le grosse installazioni commerciali di WP, per farti un paio di esempi italiani quotidiani come il Fatto o il Post, ma ce ne sono tanti. Per questo tipo di siti, avere un ambiente stabile, performante, e che si presta a modifiche rapide e facilmente testabili è un enorme vantaggio. Se poi aggiungi che nel mio progetto (come praticamente in tutte le applicazioni Django) codice e template sono separati, il che significa che chi si occupa del layout lavora su file HTML, chi si occupa di programmazione su file Python, hai un ulteriore vantaggio.

    Rifare solo il frontend, perchè WP è ormai un ecosistema, c’è dietro tantissimo lavoro e l’interfaccia di amministrazione è completa e molto ben fatta. Rifarla non ha senso, sia perchè gli utenti sono abituati ad usarla, che perchè servirebbe una mole di lavoro mostruosa, ma soprattutto perchè funziona. Se ci pensi poi, l’amminstrazione è tipicamente usata da poche persone, che fanno pochi accessi (scrivi un post, lo editi, lo pubblichi, ripeti per uno, dieci, venti persone per due, tre, cinque post al giorno), e ha un impatto marginale sulle performance. E tipicamente c’è poco da customizzare nell’admin, e quel poco si fa tranquillamente via plugin, che avranno prestazioni pessime ma girano appunto per i pochi accessi degli amminsitratori.

    Diverso ivnece il discorso del frontend: ogni sito serio basato su WP ha un tema suo, logiche sue di composizione dei percorsi e dei contenuti, e fare questo con WP è un’operazione complicata, lunga, con un sacco di passaggi inutili, e che genera un qualcosa dalla stabilità e dalle performance discutibili.

    E poi, chi ha già WP in casa difficilmente migra ad altro, mentre integrare un prodotto per la sola parte pubblica è un’operazione molto meno pesante, e molto meno rischiosa. Alla peggio, uno lo disattiva, riavvia il web server, e continua ad usare WP senza problemi.

    Il successo di WP è dovuto alla sua diffusione, sia in termini di utenti che in termini di sviluppatori (plugin, temi) che poi è la stessa cosa. Si è difuso perchè era uno dei primi, perchè è sempre stato facile da installare, perchè è sempre stato ababstanza completo come funzionalità, e alle spalle ha un lavoro di marketing intelligente.

  • eV
    19 luglio 2011 #

    Giuste considerazioni, non avevo valutato il fatto che "ogni sito serio basato su WP ha un tema suo, logiche sue di composizione dei percorsi e dei contenuti".
    In bocca al lupo per il progetto.

    Comunque è un peccato che sia molto diffuso, ma non sia un esempio di… programmazione. ;-)

    ciao!

  • fullo
    20 luglio 2011 #

    a prescindere dal fatto che il codice di wp è quello che sappiamo che sia… imho l’esempio da te usato è concettualmente sbagliato, in effetti hai un problema di architettura dell’informazione ancora prima di un problema di reperimento delle informazioni su db.

    per un uso corretto delle tassonomie quello che dovresti fare è creare una tassonomia marca, una accessori ed una tipo macchine. Non entro nel merito della gerarchizzazione o meno delle stesse (dovrebbe dircelo un vero architetto dell’informazione ;) )

    ti ritroveresti quindi con:
    marca>nikon
    accessori>cavalletti
    tipo macchine>reflex

    molto più gestibili, senza termini replicati, con possibilità di fare incroci di dati sensati. Ovvio poi hai una query un po’ più lunga da scrivere ma essendo query abbastanza semplici un buon sistema di caching (e so che lo hai) risolve tutto.

    ti do però un altro spunto per insultare WP imho molto più grave… http://core.trac.wordpress.org/ticket/16542
    partendo da quel semplice bug e scavando a fondo nel codice ho scoperto orrori lovercraftiani nascosti nella gestione dei commenti (dove commento, trackback e linkback sono la stessa cosa separati da tanti IF che ricalcolano più volte la stessa query)….

  • ludo
    20 luglio 2011 #

    Fullo, grazie del commento: quello che dici ha senso specie dal punto di vista SEO (e infatti è come sta strutturando alcuni siti il mio esperto di fiducia), ma il rischio è quello di usare WP per creare un catalogo, mentre io sto facendo un blog. :)

    La vista predefinita che mi interessa non è marca=nikon+tipo macchine=reflex+obiettivo=attacco Nikon, che è una tipica vista da "negozio" e con la struttura di wp obbliga a fare query ad hoc, che aggiungono complessità e per essere generalizzate costringono a soluzioni complesse. Mi serve una semplice categoria Obiettivi > Nikon, poi se sulla radice del termine sono "agganciate" altre cose le estraggo come contenuti secondari. Entrambi gli approcci sono validi,il mio è semplice e generalizzabile, e più che sufficiente per un sito di contenuti e non un un catalogo.

    Il bug che citi è uno degli orrori di WP, ce ne sono tanti altri.. :) Un blog "normale" con qualche funzionalità SEO che fa 30 query per mostrare una home con una lista di post e qualche informazione aggiuntiva nella sidebar è un’aberrazione. Ne bastano 5 o 6. E se penso alla mole di codice che gira per generare ogni pagina mi viene da ridere. Le "classi" di PHP poi sono tutto un programma…

  • luca sabato
    29 agosto 2011 #

    io ho abbandonato wp dalla versione 2.0.10 o qualcosa del genere, per passare a textpattern non so se hai mai avuto modo di provarlo o di vedere il suo codice io lo trovo adatto ai miei scopi e molto piacevole da usare.

    Sono curioso di conoscere la tua opinione a riguardo.