Nodi

Gli argomenti trattati fanno riferimento alla versione 6.x di Drupal

I nodi contengono il contenuto primario del sito.
L'elenco di tutti i nodi inseriti nel sistema è contenuto nella tabella NODE.
Le informazioni sono lette e codificate dal sistema, mediante la funzione menu_execute_active_handler(), descritta in Decodifica URL. La funzione individua la page callback e, tramite essa, torna la stringa del contenuto codificato.
Per i nodi sono disponibili due page callback:

La home page - node_page_default()

La prima pagina del sito mostra generalmente più di un nodo. Questi sono i nodi a cui è stato assegnato l'attributo di pubblicazione "promosso in prima pagina". Per selezionare tali nodi, la funzione node_page_default() esegue la seguente query sulla tabella NODE:

SELECT n.nid, n.sticky, n.created 
FROM node n 
WHERE n.promote = 1 AND n.status = 1 
ORDER BY n.sticky DESC, n.created DESC

Dalla query si vede che le condizioni affinché un nodo venga selezionato sono:

L'ordine di visualizzazione dei nodi è fornito invece dal valore sticky:più è alto, più il nodo salirà nell'elenco. A parità di valore l'ordine è dato dalla data di creazione. I nodi più recenti , sono visti per prima.
Il valore di sticky normalmente vale zero. Volendo quindi mostrare un nodo sempre per primo, indipendentemente dalla data di creazione, basterà assegnare un valore >0 al nodo, ad esempio 100.
Individuati i nodi da visualizzare , la funzione node_page_default(), inizia un ciclo di lettura e visualizzazione di tutti i nodi. Questo viene eseguito all'interno del ciclo contenuto nella funzione node_page_default().

<?php
function node_page_default() { 
......
 
$output = '';
 
$num_rows = FALSE;
  while (
$node = db_fetch_object($result)) {
   
$output .= node_view(node_load($node->nid),  1);
   
$num_rows = TRUE;
  }
// Il valore 1 in node_view indica che si tratta del contenuto teaser
.....
?>

All'interno del ciclo sono visibili le due funzioni di Drupal per il caricamento del nodo node_load() e per la visualizzazione del nodo node_view(). Al termine la stringa $output conterrà tutti i contenuti dei nodi da visualizzare in prima pagina.

Contenuto singolo - node_page_view()

Se la pagina richiesta non è la home page, ma un singolo contenuto, Drupal seleziona la page callback node_page_view(). Poichè la richiesta di visualizzazione è di un particolare nodo (ad esempio http://miosito.com/node/100), Drupal carica prima il nodo richiesto mediante la funzione node_load() e successivamente chiama la node_page_view() passando le informazioni sul nodo ($node) lette e un identificativo ($cid) che, se non nullo, indica che la pagina del nodo è già presente nella cache.
La funzione node_page_view() imposta il titolo della pagina (drupal_set_title(check_plain($node->title))) e chiama la funzione node_view($node, FALSE, TRUE). Il terzo parametro a TRUE indica che Drupal deve visualizzare l'intero contenuto del nodo.

Caricamento e visualizzazione del contenuto

In definitiva abbiamo visto che le due funzioni di base utilizzate da Drupal sia per la visualizzazione della home page sia per la visualizzazione di un singolo contenuto sono sempre node_load() e node_view().
Il funzionamento di queste due funzioni e quindi la logica del caricamento e della visualizzazione dei nodi è descritto nei successivi paragrafi: Caricamento, Visualizzazione.

Le funzioni principali

node_page_default()
node_page_view()
node_load()
node_view()
node_invoke()
node_invoke_api()

Caricamento

Lettura del contenuto di un nodo - node_load()

La lettura del contenuto del nodo, è eseguita mediante la funzione node_load($nid) a cui è passato il valore dell'identificativo univoco del nodo <nid> da caricare. Al suo interno viene generata la query indicata, che usa tre tabelle:

  • NODE da cui sono estratte informazioni sulla pubblicazione e tipo del nodo
  • NODE_REVISIONS da cui sono estratte informazioni sul contenuto del nodo (Il titolo del nodo è letto da questa tabella e non da NODE come si potrebbe pensare)
  • USERS da cui sono estratte informazioni sul proprietario del nodo

Query di selezione

SELECT n.nid, n.type, n.language, n.uid, n.status, n.created, n.changed, n.comment, n.promote, 
n.moderate,  n.sticky, n.tnid, n.translate, r.vid, r.uid AS revision_uid, r.title, r.body, r.teaser, r.log,
 r.timestamp AS revision_timestamp, r.format, u.name, u.picture, u.data 
FROM node n 
INNER JOIN users u ON u.uid = n.uid 
INNER JOIN node_revisions r ON r.nid = n.nid AND r.vid = <n.nid>
WHERE n.nid = <n.nid>

Dala funzione node_load() torna un oggetto ($node) contenente le informazioni lette mediante la query.
In particolare i valori $node->body e $node->teaser conterranno rispettivamente contenuto e sommario del nodo.

Oggetto $node generato dalla funzione.
(ND=da tabella NODE; NR da tabella NODE_REVISIONS; US da tabella USERS)

$node->nid ND identificativo univoco del nodo
$node->language ND lingua del contenuto:'it' , 'en', 'fr', …
$node->type ND tipo del nodo. Tabella di codifica NODE_TYPE
$node->uid ND identificativo utente del proprietario del nodo
$node->status ND =0 non visibile; =1 visibile
$node->created ND timestamp della data di creazione del nodo
$node->changed ND timestamp della data di modifica del nodo
$node->comment ND identificativo utente dell'ultimo commento
$node->promote ND =1 il nodo è visualizzato in prima pagina
$node->moderate ND =1 il nodo deve essere controllato prima della pubblicazione
$node->sticky ND numero d'ordine del nodo nella prima pagina
$node->tnid ND set ID traduzione (da capire meglio)
$node->translate ND Booleano.Indica se la traduzione deve essere aggiornata
$node->sticky ND numero d'ordine del nodo nella prima pagina
$node->vid NR identificativo del numero di revisione
$node->uid NR identificativo utente del proprietario del nodo
$node->title NR titolo del nodo per la revisione corrente
$node->body NR contenuto del nodo per la revisione corrente
$node->teaser NR sommario del nodo per la revisione corrente
$node->log NR Messaggio di log contenente le modifiche eseguite
$node->revision_timestamp NR data di modifica del nodo
$node->format NR formato del nodo da tabella FILTERS_FORMAT
$node->name US nome utente
$node->picture US percorso all'immagine utente
$node->data US serializzazione di un array che rappresenta i campi dela form utente

Lettura extra-contenuto di un nodo

Oltre alla lettura delle informazioni contenute specificatamente nelle tabelle NODE e NODE_REVISIONS, Drupal consente di aggiungere ulteriori informazioni ai nodi e quindi all'oggetto $node.
Potremmo decidere di definire un nuovo tipo di nodo con informazioni aggiuntive salvate in una tabella separata.
Pensiamo ad esempio ad una rubrica telefonica. Il nominativo e l'identificativo univoco potrebbero essere rispettivamente il campo <title> e il campo <nid> della tabella NODE, mentre tutte le informazioni aggiuntive come l'indirizzo, il telefono, l'email etc, potrebbero essere memorizzate in una tabella separata.
Si pone quindi il problema di leggere queste ulteriori informazioni associate ai nodi.
Drupal consente di leggere queste extra informazioni implementando funzioni particolari che gestiscano queste situazioni.
Dobbiamo distinguere due casi:

Implementazione della funzione legata alla tipologia del nodo

Nella tabella NODE_TYPES sono elencati il tipo di nodi definiti nel sistema. Tra questi sono presenti i nodi principali, forniti con il sistema, quali: page, book, story etc.
Dalla stessa tabella vediamo che il campo <module> ci dice da quale modulo sono gestiti i nodi.
Ora supponiamo di aggiungere il nuovo tipo di nodo "rubrica" e che questo tipo di nodo sia gestito dall'omonimo modulo con tutte le sue funzioni implementate in rubrica.module. Ora quando Drupal leggerà un nominativo dal nodo rubrica, nominativo e identificativo saranno letti come indicato nel precedente paragrafo, mentre l'ulteriore contenuto sarà letto chiamando la funzione rubrica_load($node). La funzione deve tornare un array associativo costituito dalle coppie chiave=valore. Un esempio di semplice codice è il seguente:

<?php
function rubrica_load($node) {

      

// rid = identificativo nominativo ;
      
$r = db_fetch_object(db_query('SELECT  telefono, indirizzo,citta  FROM {rubrica} WHERE rid = %d ', $node->nid));
      
info = array();
      
info['telefono'] = $r->telefono;
      
info['indirizzo'] = $r->indirizzo;
      
info['citta']        = $r->citta;
       return
$info;
}
?>

All'oggetto $node originale, Drupal aggiungerà le informazioni:

$node->telefono
$node->indirizzo
$node->citta

La funzione è chimata all'interno di node_load() mediante node_invoke($node,'load');
Attenzione:Nel caso il modulo di gestione sia quello di default node, il nome della funzione deve essere node_content_load() e non node_load() come dovrebbe.

Implementazione della funzione legata ad un modulo

In questo caso viene chiamata una funzione per avvisare altri moduli che si sta caricando un nodo.
Infatti la funzione non appartiene al modulo che gestisce il tipo di nodo come nel caso precedente , ma ad altri moduli attivi.
A questo punto la funzione chiamata potrebbe compiere due diverse operazioni:

  • aggiungere informazione al nodo. Si ricade nel caso precedente e quindi anche la funzione potrebbe essere implementata in modo analogo.
  • Utilizzare l'informazione di lettura del nodo contenute in $node per altre attività ad esempio per eseguire statistiche sulla frequenza di lettura di quel nodo.

In entrambi i casi possono dunque essere aggiunte o meno extra informazioni all'oggetto $node.
La funzione da implementare deve chiamarsi nomemodulo_nodeapi($node,$opz).
A questa funzione è passato l'oggetto $node e il valore $opz='load. che indica l'operazione che si sta eseguendo sul nodo .
Un esempio di semplice codice, nel caso in cui venga aggiunta informazione a $node, è il seguente:

<?php
function rubrica_nodeapi($node,$opz) {

    switch (

$opz)     {
        case
'load':
                          
info = array();
                          
info['telefono'] = $r->telefono;
                         
info['indirizzo'] = $r->indirizzo;
                         
info['citta']        = $r->citta;
                          return
$info;
            break;
        default:
            break;
}
?>

La funzione è chiamata all'interno di node_load() mediante node_invoke_nodeapi($node,'load');
La funzione node_load() per ogni nodo letto chiama tutte le funzioni implametate in questo modo.

Visualizzazione

Abbiamo visto che la funzione node_load() insieme ad altre funzioni di integrazione, genera l'oggetto $node con tutte le informazioni sul nodo e, tra queste, quelle sul corpo ($node->body) e sul sommario ($node->teaser). Drupal deve ora leggere queste informazioni,e prepararle alla visualizzazione finale. La fase di preparazione del contenuto usa quindi la funzione node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE). Il parametro $node è l'oggetto nodo; $teaser indica se visualizzare o meno il sommario; $page indica se visualizzare o meno l'intero contenuto del nodo; $links indica se visualizzare o meno i links associati al nodo.
Ma perchè è necessaria la fase di preparazione dei contenuti? Ci sono diversi motivi, ad esempio:

  • Necessità di codificare le informazioni in codice HTML
  • Eseguire codice PHP contenuto nei campi del nodo
  • Verificare che non ci sia codice malevolo
  • Consentire di tematizzare le informazioni del nodo
  • Tradurre le informazioni
  • Avviare eventi che avvisino la visualizzazione di quel nodo
  • Applicare il tema corrente al nodo

In questa fase Drupal genera l'array $node->content[<campo>] che contiene tutti i contenuti del nodo, sia del campo body sia di tutti i campi aggiunti dai moduli esterni a quella tipologia di nodo.
Vediamo quindi quali sono i passi che portano alla definizione del contenuto da visualizzare.
Per fare questo di seguito è riportato integralmente il codice della funzione node_view()

<?php
function node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE) {
 
$node = (object)$node;
 
// ############## FASE 1
 
$node = node_build_content($node, $teaser, $page);
 
// ############## FASE 2
 
if ($links) {
   
$node->links = module_invoke_all('link', 'node', $node, $teaser);
   
drupal_alter('link', $node->links, $node);
  }
 
// Set the proper node part, then unset unused $node part so that a bad
  // theme can not open a security hole.
  // ############## FASE 3
 
$content = drupal_render($node->content);
  if (
$teaser) {
   
$node->teaser = $content;
    unset(
$node->body);
  }
  else {
   
$node->body = $content;
    unset(
$node->teaser);
  }
 
// ############## FASE 4
  // Allow modules to modify the fully-built node.
 
node_invoke_nodeapi($node, 'alter', $teaser, $page);
 
// ############## FASE 5
 
return theme('node', $node, $teaser, $page);
}
?>

La funzione comprende cinque fasi:
1.Costruzione del contenuto
Per prima cosa la funzione node_build_content (costruzione del contenuto) genera l'array $node[content].
2. Costruzione dei links del contenuto
Se richiesti i links nel contenuto, Drupal chiama tutte le funzioni dei moduli esterni che implementano la funzione nomemodulo_link($node,$teaser) e successivamente consente di modificare i links costruiti chiamando la funzione, se esistente, nomemodulo_tiponodo_alter(). Ad esempio avendo un tipo nodo rubrica e gestito dal modulo node, la funzione si chiamerà node_rubrica_alter().
3. Rendering del contenuto
Drupal esegue il rendering dei campi definiti in $node->content. Il risultato è una stringa che viene copiata in $node->body o $node_teaser in funzione dei valori passati in $page e $teaser.
4. Modifica globale del contenuto
Qui Drupal dà l'ultima possibilità di modificare le informazioni del nodo, contenuto e links inclusi, prima di applicare il tema corrente e di visualizzare il nodo.i
5. Tematizzazione del contenuto
Al termine il contenuto costruito viene tematizzato per la definitiva visualizzazione. Per un nodo generalmente è usato il template node.tpl.php.

Fase 1: costruzione del contenuto nell'array $node[content]

Avvia la costruzione del contenuto del nodo mediante la node_build_content($node, $teaser, $page);
Qui, la preparazione dell'array $node[content] può avvenire mediante due funzioni:
1. Una funzione personalizzata
2. La funzione standard node_prepare()

Funzione personalizzata

Drupal legge il tipo di nodo corrente ($node->type) e dalla tabella NODE_TYPE ricava il modulo che gestisce il tipo di nodo, che supponiamo abbia il nome nomemodulo.
Cerca quindi la funzione nomemodulo_view($node, $teaser, $page) e se esiste la chiama.
Nel caso il nodo sia gestito dal modulo "node" la funzione si dovrebbe chiamare node_view, ma poichè questa funzione è quella che stiamo descrivendo, solo per questo caso la costruzione del nome della funzione non segue la regola generale, ma si dovrà chiamare node_content_view() (che comunque non esiste).
La costruzione dell' array $node->content[] per tutti i campi del nodo è quindi totalmente affidata alla funzione personalizzata.

Funzione standard node_prepare($node, $teaser)

Se non è fornita la funzione personalizzata , Drupal chiama la sua funzione node_prepare()
Qui viene elaborato il contenuto del corpo (body) o del sommario (teaser) del nodo applicando i fitri impostati tramite la funzione check_markup(), di cui parleremo in altra parte. Inoltre imposta il flag $node->readmore= True/False in funzione del valore di $teaser/$page. Al termine è generato il primo pezzo dell'array node->content e precisamente:
node->content['body']['#value'] che contiene il valore del corpo o del sommario del nodo,
node->content['body']['#weight']=0 posizione del corpo del nodo in fase di visualizzazione.
Si pone ora il problema di generare il contenuto di tutti quei campi non standard del nodo, aggiunti eventualmente da altri moduli. Ad esempio se è stato generato un nuovo tipo di nodo usando il modulo CCK, il nodo conterrà campi aggiuntivi che devono essere gestiti dal modulo CCK. Per fare questo Drupal esegue tutte le funzione nomemodulo_nodeapi($node,'view', $teaser, $page).
Gli array node->content['nome campo'] che vengono creati possono avere strutture complesse e non omogenee.
Ad esempio per un campo CCK immagine la struttura che si ottiene è la seguente

$node[content][field_immagine]	Array [6]	
	field	Array [11]	
		#type		content_field	
		#title		immagine	
		#field_name	field_immagine	
		#access		true	
		#label_display	above	
		#node		stdClass	
		#teaser		1	
		#page		false	
		#context	teaser	
		#single		true	
		items	Array [1]	
			0	Array [7]	
				#item	Array [12]	
					fid	20	
					list	0	
					data	Array [0]	
					uid	3	
					filename	foto.jpg	
					filepath	sites/files/foto.jpg	
					filemime	image/jpeg	
					filesize	123453	
					status	1	
					timestamp	1265803634	
					nid	7	
					#delta	0	
				#weight	0	
				#theme		filefield_formatter_default	
				#field_name	field_immagine	
				#type_name	nuovotipo	
				#formatter	default	
				#node		stdClass	
	#weight	1	
	#post_render	Array [1]	
			0	content_field_wrapper_post_render	
	#field_name	field_immagine	
	#type_name	nuovotipo	
	#context	teaser

All'oggetto $node sono associati i Links di navigazione del nodo che , generalmente, compaiono al termine del sommario di un nodo in prima pagina. Se presenti sono inseriti nell' array $node->links. Drupal consente di generare l'array $node->links chiamando due funzioni personalizzate:
- nomemodulo_link($type, $node, $teaser = FALSE)
Dove:
$type è il tipo di nodo (ad esempio 'node'),
$node l'oggetto in cui inserire l'array del link,
$teaser indica se il nodo è in prima pagina o meno.
La funzione è chiamata per tutti i moduli che la implementano. Ad esempio node_link(), comment_link() taxonomy_link() etc.
La funzione deve tornare un array contenente le componenti caratteristiche della funzione di costruzione del link: l().
Come esempio di implementazione riporto integralmente la funzione node_link().

<?php
function node_link($type, $node = NULL, $teaser = FALSE) {
 
$links = array();
  if (
$type == 'node') {
    if (
$teaser == 1 && $node->teaser && !empty($node->readmore)) {
     
$links['node_read_more'] = array(
       
'title' => t('Read more'),
       
'href' => "node/$node->nid",
       
// The title attribute gets escaped when the links are processed, so
        // there is no need to escape here.
       
'attributes' => array('title' => t('Read the rest of !title.', array('!title' => $node->title)))
      );
    }
  }
  return
$links;
}

?>

Come si vede , Il valore di ritorno è l'array $links che sarà aggiunta agli array già definiti. L'oggetto nodo non deve quindi essere modificato all'interno della funzione.
- nomemodulo_link_alter(&$data)
Dopo che l'intero array dei link è stato costruito, Drupal da una ultima opportunità di modificarlo chiamando ancora per tutti i moduli le funzioni nomemodulo_link_alter() passando in &$data il riferimento il valore $node->links.

Fase 3:rendering del contenuto

Il rendering del contenuto (generazione del codice HTML definitivo) avviene mediante la funzione drupal_render(&$elements) chiamata ricorsivamente su tutti i campi del nodo. Il parametro $elements corrisponde all'array precedentemente generata $node->content[]. La funzione torna la stringa $content contenente tutto il contenuto del nodo pronto per essere visualizzato. Il contenuto è copiato in $node->body o in $node->teaser a seconda dei valori $teaser e $page passati alla funzione node_view(). In $node , la variabile in cui non è copiato il contenuto è eliminata da Drupal.
Al''interno della funzione drupal_render(), Drupal ci consente di modificare il contenuto sia prima di eseguire il rendering sia dopo averlo eseguito. Vediamo come.
- Personalizzazione mediante $node->content['#pre_render']
Prima di eseguire il rendering del contenuto, Drupal chiama tutte le funzioni eventualmente definite nell'array $node->content['#pre_render']. Le funzioni possono avere un qualsiasi nome e ad esse Drupal passa tutta l'array $node->content precedentemente generata.
- Personalizzazione mediante $node->content[<campo>]['#pre_render']
Il valore [#pre_render] può essere assegnato ad ogni campo (Es.: $content[body][#pre_render]) per chiamare una o più funzioni da eseguire per il particolare elemento. In questo caso è passato alla funzione il solo elemento $content[<campo>].
- Personalizzazione mediante $node->content['#post_render']
Dopo aver eseguito il rendering del contenuto, Drupal chiama tutte le funzioni eventualmente definite nell'array $node->content['#post_render']. Le funzioni possono avere un qualsiasi nome e ad esse Drupal passa la stringa $content contenente il rendering del contenuto e l'array $node->content.
- Personalizzazione mediante $node->content[<campo>]['#post_render']
Il valore [#post_render] può essere assegnato ad ogni campo (Es.: $content[body][#post_render]) per chiamare una o più funzioni da eseguire per il particolare elemento. Le funzioni possono avere un qualsiasi nome e ad esse Drupal passa la stringa $content contenente il rendering del contenuto e l'array $node->content[<campo>].
- Personalizzazione mediante ['#prefix'] e ['suffix']
Il rendering finale di ogni singolo elemento del contenuto viene sempre racchiuso tra i valori #prefix e #suffix forniti per quell'elemento come vediamo dal codice stesso della funzione drupal_render(&$elements)

<?php

function drupal_render(&$elements) {
......
 
$prefix = isset($elements['#prefix']) ? $elements['#prefix'] : '';
 
$suffix = isset($elements['#suffix']) ? $elements['#suffix'] : '';
    return
$prefix . $content . $suffix;
...
}
?>

Fase 4:modifica globale del contenuto

In questa fase, l'oggetto $node contiene il rendering del contenut , l'array dei links se presenti e quanto modificato o aggiunto dai moduli che implementano la funzione nomemodulo_nodeapi($node,'view', $teaser, $page).
Qui il nodo può essere ulteriormente modificato prima che sia applicato il tema corrente, implementando nei moduli la funzione nomemodulo_nodeapi($node,'alter', $teaser, $page).

Fase 5:tematizzazione del contenuto

Al nodo è applicata la tematizzazione mediante la funzionetheme('node', $node, $teaser, $page) . Generalmente per tematizzare un nodo viene usato il file template node.tpl.php. Di questo ne parleremo in altra parte.

Eventi

Drupal durante le operazioni di caricamento, inserimento, aggiornamento, cancellazione e visualizzazione dei nodi , consente allo sviluppatore di eseguire funzioni personalizzate per alterare la modalità operativa standard.
Indichiamo con:

  • nomemodulo il nome di un modulo personallizzato che implementa le funzioni richieste.
  • nomemodulotipo il nome del modulo che gestisce il tipo di nodo, letto dalla tabella NODE_TYPE nel campo module. Nel caso che il tipo sia node, nomemodulotipo sarà uguale a node_content, e i nomi di funzione dovranno essere quindi node_content_load, node_content_view, node_content_save, questo perchè le funzioni node_load, node_view, node_save etc. sono funzioni standard di Drupal.
  • nometipo il nome del tipo di nodo.

Per ogni tipo di operazione, la sequenza con cui sono descritte le funzioni coincide con quella di esecuzione.
Di seguito riportiamo le funzioni che , se esistenti, sono chiamate da Drupal nei diversi contesti.

Caricamento

nomemodulotipo_load(&$node,$page,$teaser)

Implementata nel modulo che gestisce il tipo di nodo, consente di caricare dei campi aggiuntivi all'oggetto nodo.

nomemodulo _nodeapi($node,$op='load')

Consente a ogni modulo che la implementa di aggiungere ulteriori campi all'oggetto nodo non definiti nella tabella NODE.

Visualizzazione

nomemodulotipo_view($node,$page,$teaser)

Implementata nel modulo che gestisce il tipo di nodo, se presente sosituisce la funzione node_prepare() che è responsabile della generazione dell'array $node->[content].

nomemodulo _nodeapi(&$node,$op='view')

Consente a ogni modulo che la implementa di preparare alla visualizzazione i campi aggiunti al nodo.

nomemodulo_link($type, $node, $teaser = FALSE)

$type è il tipo di nodo (ad esempio 'node'),
Consente a ogni modulo che la implementa di aggiungere links da visualizzare nel nodo corrente.

nomemodulo_link_alter(&$node->links)

Consente a ogni modulo che la implementa di modificare l'intero array dei links da visualizzare nel nodo corrente.

nomemodulo_nodeapi($node,'alter', $teaser, $page)

Consente a ogni modulo che la implementa di modificare l'intero oggetto $node prima che il nodo sia passato alla funzione theme('node', $node).