Newsgroup: it.comp.www.php
Titolo: Piccola classe per gestire il database

Ho scritto questa piccola classe (che verrà istanziata all'interno di
altre classi) per gestire il database. So benissimo che è una "vera e
propria zozzeria", ma vorrei avere qualche suggerimento per
migliorarla (non mi sono ancora preoccupato di integrare tutti i
metodi necessari, per ora solo quelli che utilizzo...):
<?php
class Database
{
	function __construct()
	{
		$this->link = mysqli_connect(DB_SERVER, DB_USER, DB_PASSWORD) or
die(Index::error(4, NULL, FALSE));
		mysqli_select_db($this->link, DB_NAME) or die(Index::error(5, NULL,
FALSE));
	}
	function __destruct()
	{
		//Libera la memoria dalla query
		Database::free_result();
		//Chiude la connessione al database e cancella il link
		mysqli_close($this->link);
		unset($this->link);
	}
	//Libera la memoria dalla query
	function free_result()
	{
		//Se è stata eseguita una query, ne libera la memoria
		if(isset($this->query) AND $this->query==TRUE)
			mysql_free_result($this->query);
	}
	//Esegue la query
	function get_query($sql)
	{
		//Libera la memoria da precedenti query
		Database::free_result();
		$this->query = mysqli_query($this->link, $sql);
	}
	//Trasforma la query in array associativo
	function get_array()
	{
		//Se la query è stata effettuata, la trasforma in array associativo
		if(isset($this->query) AND $this->query==TRUE)
			$this->rows = mysqli_fetch_array($this->query, MYSQL_ASSOC);
		//Altrimenti, genera errore
		else
			Index::error(7, NULL);
	}
	//Restituisce il numero di righe impiegate dalla query
	function get_num_rows()
	{
		//Se la query è stata effettuata, restituisce il numero di righe
impiegate dalla query
		if(isset($this->query) AND $this->query==TRUE)
			return(mysqli_num_rows($this->query));
		//Altrimenti, genera errore
		else
			Index::error(8, NULL);
	}
}
?>
---------------------------------------
Questa, invece, è una classe che utilizza la classe precedente:
<?php
Index::include_lib('database');
class Read extends Index
{
	function __construct()
	{
		//Istanzia Database
		$this->db = new Database();
	}
	function get_data()
	{
		//Esegue la query
		$this->db->get_query("SELECT * FROM posts WHERE post_id='".
$_GET['id']."'");
		$this->db->get_array();
	}
}
//Controlla che sia stato passato l'ID del post, altrimenti genera
errore
if(empty($_GET['id']) OR !is_numeric($_GET['id']))
	Index::error(3);
$my_module = New Read;
$my_module->get_data();
$my_module->assign('post', $my_module->db->rows);
?>
-----------------
Che ne pensate?
Piano con le critiche, vi prego, è la prima volta che programmo ad
oggetti e sto solo "sperimentando".

 
Come mai richiami staticamente free_result, se in esso utilizzi le 
variabili di istanza?
Poi, per rendere le cose più chiare, definisci le variabili e la 
visibilità delle stesse e dei metodi, togli le costanti dalla 
connessione e mettili come parametri del costruttore (hai maggior 
portabilità).
Avresti tantissime altre cose da fare ma se è per scopo didattico, vai 
avanti, altrimenti scegli una dei tanti layer di astrazione già esistenti!
Ah, tra che ci sei, utilizza le eccezioni per gestire gli errori, ne 
trarrai innumerevoli vantaggi!
	Francesco

 migliorarla [...]
Alcune mie note sparse:
- Dipende dalle costanti esterne DB_SERVER DB_USER DB_PASSWORD DB_NAME che
non appaiono in modo evidente dal sorgente (uno se ne accorge perché usanso
la classe dà un errore). Meglio mettere questi valori come argomenti del
costruttore, così puoi usare la classe in diversi contesti e puoi istanziare
due oggetti su diversi DB, per esempio per trasferire o confrontare dati.
- Non vengono dichiarate esplicitamente le proprietà della classe, né viene
indicato quali sono pubbliche e quali private. Mi sembra che ci siano:
    public /*. mixed  .*/ $rows;
    private /*. mysqli .*/ $link;
    private /*. string .*/ $sql;
    private /*. mysqli_result .*/ $query;
dove solo $rows è accessibile dall'esterno.
- Si mescolano costanti e funzioni proprie di mysql con quelle di mysqli,
non so se è una cosa sana. Per esempio MYSQL_ASSOC è invece MYSQLI_ASSOC,
mysql_free_result() è invece mysqli_free_result(), ecc.
- Dipende da una classe Index di cui non sappiamo niente.
- Sarebbe bene usare le eccezioni per tutti quei casi che sono errori,
trasformando in eccezione anche i warning, così da avere un meccanismo
uniforme di gestione degli errori.
- L'uso di mysqli_free_result() (non mysql_free_result()!) mi pare inutile
perché tutto viene disallocato quando l'oggetto scompare, e quando si
riassegna la proprietà $query il valore precedente viene disallocato.
Per cui eliminerei semplicemente il metodo free_result() e il distruttore.
- Complessivamente la tua classe è solo un wrapper che poco aggiunge alla
classe base di mysqli, tanto vale usare direttamente mysqli. A meno che
non estendi ulteriormente questo nucleo base in modo da personalizzarlo in
qualche direzione. E comunque, è un'utile esperienza provarci.
Non ho provato l'esempio perché non può funzionare "out of the box"
e anche perché non c'ho proprio MySQL :-)
Ciao,
 ___ 
/_|_\  Umberto Salsi
\/_\/  
www.icosaedro.it

 trarrai innumerevoli vantaggi!
Eccezioni? In che senso?
  Index::error(8, NULL);
Questo metodo, rimanda al modulo degli errori, in cui vengono stampate
informazioni e l'utente ha la possibilità di inviarmi un report... che
ne dici?

 Ciao,
Grazie!

 ne dici?
Brutto...Ribadisco le eccezioni e con quelle (definendo un'eccezione 
personalizzata myException extends Exception, fai i metodi che vuoi e 
tratti l'errore come vuoi)
	Francesco

 tratti l'errore come vuoi)
Mmh... per me è un po' troppo complicato, almeno per ora.
Tra l'altro non capisco nemmeno tutti gli esempi del manuale, mi ci
avvicinerò piano piano.
p.s. per quanto riguarda la storia della chiusura della connessione e
dello svuotamento della memoria, accennato da umberto?

 
su questo di do ragione, passare al costruttore permette di chiamare
la classe anche per altri database
se vuoi leggere 2 db lo puoi fare se invece hai la conf in un file
con delle definizioni non puoi aprire 2 db contemporaneamente
usando la stessa classe ma ne devi includere e istanziare un altra
il bello degli oggetti e che se devi fare la stessa cosa su
piu oggetti una volta istanziata la chimi n volte.
il brutto degl oggetti e che se la fai 1 volta nella vita (30 secondi)
hai una richiesta di risorse MOLTO superiore a quello che devi fare.


 dello svuotamento della memoria, accennato da umberto?
la chiusura della connessione avviene automaticamente alla fine dello 
script, per lo svuotamento:
mysql_free_result() only needs to be called if you are concerned about 
how much memory is being used for queries that return large result sets. 
All associated result memory is automatically freed at the end of the 
script's execution.
Ovviamente se, dopo aver eseguito un fetch di 5Mbyte carichi in memoria 
un file da 10Mbyte conviene flushare
	Francesco

 montagna pieno
Mi sa che hai frainteso la mia risposta. Io parlavo di definire le 
variabili di istanza nella classe. La tua obiezione non sussiste se, 
come è giusto che sia, ogni classe è definita in un file a parte (like 
java) e con l'autoload tiri su solo le definizioni che ti servono.
	Francesco

         Francesco
Thanks!

 
Il risultato che ottieni può essere il solito ma la via è diversa. 
Intanto le eccezioni sono un metodo "standard" per segnalare gli errori 
(e si torna alla possibilità di riciclare il codice) poi esistono già, e 
a questo punto che scopo ha scrivere un'altra classe per la gestione 
degli errori?
Stai facendo 30, arriva a 31!
	Francesco


Eheheh, hai ragione, ma sto cercando di fare tutto io, voglio imparare
tutto, dalle basi :p

 Vien fatto tutto automaticamente?
Esatto, è a questo che servono i linguaggi applicativi ad alto livello.
