Newsgroup: it.comp.www.php
Titolo: PGSQL 9.1, PHP e ARRAY multilivello

Salve a tutti,
 
	vorrei da voi un giudizio su una struttura di una tabella su postgresql 
9.1.
 
Obiettivo:
Devo archiviare, possibilmente in array multilivello al fine di non 
creare strutture troppo statiche, una serie di informazioni che non è 
detto esistano per tutti i record di una tabella.
 
Esempio:
 
ho una tabella Utenti dove archivio SOLO i record veramente necessari in 
si e no 10 colonne.
 
Poi volevo creare una seconda tabella o anche solo una colonna con gli 
attributi o valori opzionali.
Nella fattispecie stavo pensando di utilizzare una struttura di array di 
almeno 3 livelli.
 
Quindi in una tabella fatta di ID, Nome, attributi :
1, 'Test1', array(
		'generali', array(
			'Indirizzi',array('Via Pippo,1','viapluto,5'),
			'telefoni', array('12345678','
+39-339-321234
')),
		'links', array(
			'nomeunivoco', array(
				'titolo',array('Mio Link'),
				'descr',array('è una tabella'),
		 		'url',array('www.casa.nh')
			)
		);
 
Ammesso che abbia rispettato tutti gli ordini delle parentesi essendo 
andato a memoria, pensate che sia una cosa fattibile? e se si come mi 
consigliate di procedere? Oppure mi conviene affrontare il problema in 
altro modo?
 
Grazie!

Una cosa del genere si presta molto ai database a oggetti come MongoDb; Altrimenti su postgresql hai 3 strade:
 
1. Usi la de/normalizzazione, per ogni array di sottolivello salvi i dati in una tabella a parte con chiave/valore e, avendo anche l'indice per la parentela Postgresql ti permette di avere le query ricorsive (manuale: 
http://www.postgresql.org/docs/9.1/static/queries-with.html
 - esempio pratico: 
http://old.storytotell.org/blog/2009/08/11/postgresql84-recursive-queries.html
)
 
2. Salvi gli attributi come json, ma ti dimentichi di poterci fare sopra delle query
 
3. Salvi gli attributi in json, e provi l'estensione per il json: 
http://pgfoundry.org/projects/json
 per potervi fare sopra delle query

 1. Usi la de/normalizzazione, per ogni array di sottolivello salvi i dati in una tabella a parte con chiave/valore
 
Sottoscrivo, io andrei sicuramente per questa.
Ciao!

> 1. Usi la de/normalizzazione, per ogni array di sottolivello
 
A dir poco PERFETTO!!!!
Con qualche personalizzazione č venuto un capolavoro :D
 
Solo che per renderlo superlativo piuttosto che riciclare anche in PHP, 
esiste una funzione che da una stringa tipo:
 
'links||link1||title', 'Mio titolo'
'links||link1||url', 'www.pippo.n'
'links||link1||descr', 'mia descr'
 
Mi genera una struttura di array tipo
 
attr['links']['link1']['title'] = 'Mio titolo'
attr['links']['link1']['url'] = 'www.pippo.n'
attr['links']['link1']['descr'] = 'mia descr'
 
Grazie!!!!!!!

che io sappia no.. purtroppo quando recuperi un dato da postgres (e anche da mysql) te lo becchi in stringa/intero; Postgres gestisce gli array ma quando li recuperi li vedi come stringa.
Dai un'occhio a mongodb, li ci salvi direttamente l'array (in json) e, se la tua situazione è agibile per le query che permette di fare mongodb, ti risolvi un sacco di problemi.
 
Lato php invece potresti giocartela con array_map e qualche explode

 attr['links']['link1']['title'] = 'Mio titolo'
 
Se non ti serve che sia leggibile da altre fonti (banalmente: la console 
di postgres) puoi usare serialize() (e unserialize()) oppure, un po' più 
leggibile, json_encode() (e json_decode())
 
serialize() ti permette di salvare anche oggetti, mentre json_encode 
vuole solo array.
 
Bye.

 vuole solo array.
 
chiaramente dovrei poi richiamare il contenuto. Ma ci sto mettendo 
troppo tempo e non arrivo a nulla. Mi faresti un esempio di come dovrei 
muovermi?
 
In pratica ora mi trovo su PHP con una variabile che contiene il 
recordset della tabella con un campo PATH che contiene una stringa 
formata da "links||link1||title" oppure "links||link2||title" per un 
certo numero di record che provengono dalla query di PG, formattati come 
ho detto sopra. Al momento non riesco proprio a farmi generare un array 
strutturato in php. Sto costruendo alcune ricerche ricorsive ma sbaglio 
la logica probabilmente.
 
Grazie!
 

 la logica probabilmente.
 
Se hai gia` la stringa formattata in quel modo e` molto complicato. :/
Avevo capito che stavi decidendo anche il formato con cui salvarli.
 
Penso che la cosa piu` rapida sia fare una funzione ricorsiva che lavora 
sull'explode('||', ...) e costruisce l'array (tanto comunque verrebbe 
richiamata un numero abbastanza limitato di volte).
 
Non mi fido a scrivertela al volo perche` ho paura di scrivere schifezze, 
a quest'ora. :D
E anche perche` non ho capito benissimo cosa ti torna il DB e come 
vorresti trasformarlo.
 
Bye.
 

Ti ringrazio innanzitutto!
 
A me il db torna una tabella di 2 campi: path e value
 
Ed è formattata così:
 
"general||info||nome", "Tizio1"
"general||info||cognome", "CognomeTizio1"
"general||info||indirizzo", "IndirizzoTizio1"
 
"links||link1||title", "Questo è il link1"
"links||link1||url", "www.pippo.h"
"links||link1||description", "Una bella descrizione"
 
"links||link2||title", "Questo è il link2"
"links||link2||url", "www.pluto.h"
"links||link2||description", "Una bella descrizione del link 2"
 
Ora il problema è il seguente.
Tutto questo lavoro mi serve perchè vorrei archiviare per gli utenti 
solo le informazioni che loro archiviano e oltretutto vorrei evitare 
tabelle statiche perchè le info che si possono archiviare sono sempre tante.
Su consiglio di altri colleghi ho creato una tabella padre/figlio e mi 
sono generato una specie di tree ricorsivo che ha generato i path come 
leggi nell'esempio sopra. Il problema ora è far capire a PHP che i 
record li deve splittare in un array. Io ho trovato Molti esempi 
ricorsivi ma in nessuno viene usata la chiave ma a tutti l'indice 
numerico che a me non serve visto che vorrei arrivare a un risultato del 
genere:
 
$userInfo['general']['info']['nome'] = "Tizio1"
$userInfo['general']['info']['cognome'] = "CognomeTizio1"
$userInfo['general']['info']['indirizzo'] = "IndirizzoTizio1"
 
$userInfo['links']['link1']['title'] = "Questo è il link1"
$userInfo['links']['link1']['url'] = "www.pippo.h"
$userInfo['links']['link1']['description'] = "Una bella descrizione"
 
$userInfo['links']['link2']['title'] = "Questo è il link2"
$userInfo['links']['link2']['url'] = "www.pluto.h"
$userInfo['links']['link2']['description'] = "Una bella descrizione del 
link 2"
 
Spero di essermi spiegato meglio.
Nella pagina che visualizzo ho molti + campi questi sono solo di esempio 
e per alcuni utenti non ho gli stessi valori.
 
Secondo te è una buona soluzione e se si come mi districo, oppure devo 
pensare ad altro?
 
Grazie!

 $userInfo['general']['info']['nome'] = "Tizio1"
 
Se il formato della "key" e` sempre identico (3 campi) separati da || e 
un valore, e` abbastanza semplice (anche se brutto):
 
$userInfo = array();
 
$row = mysql_fetch_row(blabla);
 
$keys = explode('||', $row[0]);
 
if (!isset($userInfo[$keys[0]])) {
  $userInfo[$keys[0]] = array();
}
 
if (!isset($userInfo[$keys[0]][$keys[1]])) {
  $userInfo[$keys[0]][$keys[1]] = array();
}
 
$userInfo[$keys[0]][$keys[1]][$keys[2]] = $row[1];
 
Il problema e` appunto se il livello delle chiavi e` dinamico.
 
Bye.

> Il problema e` appunto se il livello delle chiavi e` dinamico.
 
E si è proprio lì che sta il problema. E' dinamico.
 
Ma xche brutto?
 

 Ma xche brutto?
 
Perche` tutto hardcoded in quel modo e` poco modificabile, poco 
generalizzabile, poco leggibile. E` un hack veloce per risolvere il 
problema in fretta, ma crea problemi in futuro quando devi lavorarci 
sopra.
 
Bye.
 

> Il problema e` appunto se il livello delle chiavi e` dinamico.
 
Credo di aver risolto in maniera un pelo macchino ma vedo di 
trascriverla. Spero sia chiara xche messa di seguito ha un'indentazione 
pessima!!!
 
// Recorset dal DB contenente la colonna path e value
$tree= $db->query($select)->fetchAll();
 
$string = "";
$myarr = array();
$c = array();
for ($i=0;$i<count($tree);$i++) {
	$myarr[] = 
$usersAttibutesMapper->stringToArray($tree[$i]['path'],$tree[$i]['value']);
	if ($i>0) {
		$merged = $usersAttibutesMapper->array_merge_recursive_distinct( 
$merged, $myarr[$i]);
	}else{
		$merged = $myarr[$i];
	}
}
$UsersAttributes = $merged;
 
 
public function stringToArray($path,$value, $separator = '||')
	{
		$pos = strpos($path, $separator);
 
		if ($pos === false) {
			return array($path=>$value);
		}
 
		$key = substr($path, 0, $pos);
		$path = substr($path, $pos + strlen($separator));
 
		$result = array(
				$key => $this->stringToArray($path,$value,$separator),
		);
 
		return $result;
	}
 
	public function &array_merge_recursive_distinct(array &$array1, 
&$array2 = null)
	{
		$merged = $array1;
		if (is_array($array2)) {
			foreach ($array2 as $key => $val)
				if (!isset($merged[$key])) {
					$merged[$key] = array();
				}
				if (is_array($array2[$key])) {
					$merged[$key] = is_array($merged[$key]) ? 
$this->array_merge_recursive_distinct($merged[$key], $array2[$key]) : 
$array2[$key];
				}else{
					$merged[$key] = $val;
				}
		}
	  return $merged;
	}
 

 pessima!!!
 
Sto facendo dei test e sembra che faccia il suo lavoro!!!
 
Chissà se posso migliorarla.
 
 

 Chissà se posso migliorarla.
 
come ti ha suggerito alessandro, una cosa del genere hard-coded è difficile da mantenere in futuro: metti che un domani ti cambia il formato di ingresso o di uscita, devi rimettere mano al codice..
 
Fossi in te farei una funzione che prende in ingresso i dati di postgresql e un formato, e ti restituisce l'array strutturato in base al formato (che può essere una stringa, una maschera di array, quello che vuoi), così puoi riutilizzare il tutto e al cambiare dei dati cambi solo il formato

 > Il 22/06/2012 14:19, Strae ha scritto:
 
Mi pare che io lo habbia fatto nel codice che ho postato. Certo devo 
mettere un limite alle sotto sezioni ma penso che possa andare bene un 3 
max 4 livelli no?