PHP o abordare obiectuala a dialogului cu baza de date

Posted by | November 11, 2008 | Uncategorized | 3 Comments

Dupa cum bine se stie PHP-ul este un limbaj orientat pe cod iar suportul pentru clase a aparut abia mai tarziu.
Astazi m-am gindit sa va arat un mic tutorial, si ca sa fiu mai practic am sa realizez citeva clase ce implementeaza majoritatea operatiilor necersare in lucrul cu baza de date in principiu este vorba de patru clase
MysqlQuerry MysqlConnector MysqlController si MysqlResource.
Si ca sa profitam la maxim de flexibilitatea oferita de progrmarea obiectuala haideti ca si clasele noastre sa implementeze fiecate cite o interfata…poate considerati acest pas inutil dar sa ne inchipuim ca dorim sa realizam o aplicatie ce foloseste un server de Mysql dar in timp s-ar putea sa facem o migratie catre un alt server tot ce trebuie sa facem este sa realizam clase ce implementeaza interfetele si modificariile in aplicatia noastra vor fi minime minime atita vreme cit interfata este respectata.
Mai jos este dat codul pentru fiecare dintre interfete

DbQuerry

Cod:
interface DbQuerry {

/**
* @ReturnType void
* @ParamType querry
*/
public function execQuery($querry);
}


DbController

Cod:
interface DbController {

/**
* @ReturnType void
* @ParamType name string
* @ParamType colomns
*/
public function createTable($name, $colomns);

/**
* @ReturnType void
* @ParamType name string
*/
public function deleteTable($name);

/**
* @ParamType name string
* @ParamType clone string
*/
public function cloneTable($name, $clone);

/**
* @ParamType name string
* @ParamType connector Database.DbConnector
*/
public function createDatabase($name);

/**
* @ParamType name string
* @ParamType connector Database.DbConnector
*/
public function deleteDatabase($name);

/**
* @ReturnType Database.DbResource
*/
public function getLink();
}


DbConnector

Cod:
interface DbConnector {

/**
* @ReturnType Database.DbResource
*/
public function getLink();

/**
* @ReturnType void
*/
public function disconnect();

/**
* @ReturnType void
*/
public function reconnect();
}


DbTable

Cod:
interface DbTable {

/**
* @ParamType colomns
* @ParamType values
*/
public function insert($colomns, $values);

/**
* @ParamType condition
*/
public function delete($condition,$operation=’AND’);

/**
* @ParamType colomns
* @ParamType values
* @ParamType conditions
*/
public function update($colomns, $values, $conditions);

/**
* @ParamType condition
*/
public function select($colomns,$condition=””);

/**
* @ReturnType Database.DbResource
*/
public function getData();
/**
* @ReturnType integer
*/
public function getRowsNumber();
}


Aplicatia va lucra doar cu interfatele (de fapt cu implementari ale interfetelor) si in acest mod se respecta bine cunoscutul principuiu al segregarii interfetelor de implementare.
Acum haideti sa arunacam o privire asupra implemetarii interfetelor

MysqlQuerry

Cod:
class MysqlQuerry implements DbQuerry {
/**
* @AttributeType Database.MysqlResource
*/
private $resource;
/**
* @AttributeType boolean
* */
public static $debug=false;

/**
* @ParamType resource Database.MysqlResource
*/
public function setResource(MysqlResource $resource) {
$this->resource=$resource;
}

/**
* @ParamType resource Database.MysqlResource
*/
public function MysqlQuerry(MysqlResource $resource) {
$this->setResource($resource);
}

/**
* @ParamType querry
*/
public function execQuery($querry) {
if(MysqlQuerry::$debug==true)
echo($querry);

$result=mysql_query($querry,$this->resource->getResource());
if(!$result){
$error=mysql_error();
throw new MysqlSintaxException($error);
}

$resource=new MysqlResource($result);

if(MysqlQuerry::$debug==true)
echo(mysql_info($this->resource->getResource()));

return $resource;
}
}

MysqlController

Cod:
class MysqlController extends MysqlQuerry implements DbController {
/**
* @AttributeType string
*/
private $databaseName;

/**
* @AttributeType DbResource
*/
private $resource;

/**
* @ParamType dbName
* @ParamType connector Database.MysqlConnector
*/
public function MysqlController($dbName, MysqlConnector $connector) {
parent::MysqlQuerry($connector->getLink());
$this->databaseName=$dbName;
$this->resource=$connector->getLink();

}

/**
* @ReturnType void
* @ParamType name string
* @ParamType colomns
*/
public function createTable($name, $colomns) {

if(is_array($colomns))
$colomns=implode(“,”,$colomns);
$this->execQuery(“CREATE TABLE $name ($colomns)”);
}

/**
* @ReturnType void
* @ParamType name string
*/
public function deleteTable($name) {
$this->execQuery(“DELETE TABLE $name”);
}

/**
* @ParamType name string
* @ParamType clone string
*/
public function cloneTable($name, $clone) {
$this->execQuery(“CREATE TABLE $clone LIKE $name”);
$this->execQuery(“INSERT $clone SELECT * FROM $name”);
}

/**
* @ParamType name string
*/
public function createDatabase($name) {
$this->execQuery(“CREATE DATABASE $name”);
}

/**
* @ParamType name string
*/
public function deleteDatabase($name) {
$this->execQuery(“DELETE DATABASE $name”);
}
/**
* @ParamType querry
*/
public function execQuery($querry) {
mysql_select_db($this->databaseName,$this->resource->getResource());
return parent::execQuery($querry);

}
/**
* @ReturnType Database.DbResource
*/
public function getLink(){
return $this->resource;
}
};


MysqlConnector

Cod:
lass MysqlConnector implements DbConnector {
/**
* @AttributeType string
*/
private $password;
/**
* @AttributeType Database.DbResource
*/
private $link;
/**
* @AttributeType string
*/
private $user;
/**
* @AttributeType string
*/
private $host;

/**
* @ReturnType boolean
*/
public function pingServer() {
$result= mysql_ping($this->getLink());
return $result;
}

/**
* @ParamType host
* @ParamType user
* @ParamType password
*/
public function MysqlConnector($host=null, $user=null, $password=null) {
$this->host=$host;
$this->user=$user;
$this->password=$password;
$this->reconnect();
}

/**
* @ReturnType Database.DbResource
*/
public function getLink() {
return $this->link;
}

/**
* @ReturnType void
*/
public function disconnect() {
mysql_close($this->getLink()->getResource());
}

/**
* @ReturnType void
*/
public function reconnect() {
$result=mysql_connect($this->host,$this->user,$this->password);
if($result==false){
$error=mysql_error();
throw new MysqlConnectionException($error);
return;
}

$this->link=new MysqlResource($result);
}
/**
* @ReturnType void
*/
public function __destruct(){
$this->disconnect();
}
};


MysqlResource

Cod:
class MysqlResource implements DbResource {
private $resource;

/**
* @ParamType resource
*/
public function MysqlResource($resource) {
$this->resource=$resource;
}

public function getResource() {
return $this->resource;
}

}


MysqlTable

Cod:
class MysqlTable extends MysqlQuerry implements DbTable {
/**
* @AttributeType string
*/
private $name;
/**
* @AttributeType string
*/
private $tableResource=null;
/**
* @AttributeType DbController
*/
private $controllerLink;
/**
* @ParamType name
* @ParamType connector Database.MysqlConnector
*/
public function MysqlTable($name, MysqlController $controler) {
parent::MysqlQuerry($controler->getLink());
$this->name=$name;
$this->controllerLink=$controler;
}

/**
* @ParamType colomns
* @ParamType values
*/
public function insert($colomns, $values) {
if(is_array($colomns))
$colomns=implode(“,”,$colomns);
if(is_array($values))
$values=implode(“,”,$values);

$this->execQuery(“INSERT INTO $this->name ($colomns) VALUES (‘$values’)”);
}

/**
* @ParamType condition
*/
public function delete($condition,$operation=’AND’) {
if(is_array($condition))
$condition=implode(” $operation “,$condition);
$this->execQuery(“DELETE FROM $this->name WHERE $condition”);
}

/**
* @ParamType colomns
* @ParamType values
* @ParamType conditions
*/
public function update($colomns, $values, $conditions) {
if(!is_array($colomns))
$colomns=explode(“,”,$colomns);
if(!is_array($values))
$values=explode(“,”,$values);
if(is_array($conditions))
$conditions=implode(” AND “,$conditions);
if(count($colomns)!=count($values))
throw new SintaxErrorException(“values and colomn must have the same size.”);

$rez=array();

for($i=0;$i<count($colomns)-1;$i++){
$rez.=$colomns[$i]=”=’$values[$i]’,”;
}
$rez.=$colomns[$i]=”=’$values[$i]'”;

$this->execQuery(“UPDATE $this->name SET $rez WHERE $conditions”);

}

/**
* @ParamType condition
*/
public function select($colomns,$condition=””) {
if(is_array($colomns))
$colomns=implode(“,”,$colomns);
if(is_array($condition))
$condition=implode(” AND “,$condition);
if($condition!=””)
$this->tableResource=$this->execQuery(“SELECT $colomns FROM $this->name WHERE $conditions”);
else
$this->tableResource=$this->execQuery(“SELECT $colomns FROM $this->name “);

}

/**
* @ReturnType Database.DbResource
*/
public function getData() {
if($this->tableResource==null)
throw new MysqlTableException(“no data selected in table”);

$i=0;
$result=array();

while($row=mysql_fetch_object($this->tableResource->getResource()))
{
$result[$i++]=$row;
}

return new MysqlResource($result);
}
/**
* @ReturnType integer
*/
public function getRowsNumber() {
if($this->tableResource==null)
throw new MysqlTableException(“no data selected in table”);

return mysql_num_rows($this->tableResource->getResource());
}
/**
* @ParamType querry
*/
public function execQuery($querry) {
return $this->controllerLink->execQuery($querry);

}
};


Pentru a trata diversele erori ce pot aparea in timpul lucrului cu baza de date se lucreaza cu exceptii.Iata un exemplu ce foloseste clasele de mai sus, sa presupunem ca avem un tabel cu doua cimpuri id si nume:

Cod:
<?php

try
{
$connector=new MysqlConnector(“host”,”user”,”parola”);
$controller=new MysqlController(“nume_baza_de_date”,$connector);
$table=new MysqlTable(“nume_tabel”,$controller);
$table->select(“*”,”id=’1′”);
$data=$table->getData()->getResource();

for($i=0;$i<count($data);$i++)
{
echo($data->id);
}

}

catch $ex)
{
echo(“eroare de sintaxa:”.$ex->getMesage());
}
catch(MysqlTableException $ex)
{
echo(“eroare:”.$ex->getMessage());
}
catch(MysqlConnectionException $ex)
{
echo(“eroare :”.$ex->getMessage());
}
catch(MysqlException $ex)
{
echo(“eroare :”.$ex->getMessage());
}
..

?>

O observatie foarte importanta este ce in cazul catchurilo ordinea este importanta astfel de exmplu daca aveam primul chatch MysqlException de fiecare data cind aparea o exceptie se intra pe primul catch deoarece MysqlException este o calsa din care se deriveaza celelalte tipuri astfel o exceptie de tipul MysqlSintaxException este si de tipul MysqlException.
Pentru a va face o face o imagine asupra ierarhiei de clase am atasat o imagine

About Andrei Tara

Andrei is a developer/geek who loves creating high quality and efficient applications, always following the best practices in software engineering. He loves the challenge of learning new, exciting things and playing with new languages, frameworks, and tools.

3 Comments

  • Ce iti place sa te dai mare ca stii PHP si mai ales programare! sunt cu ochii pe tine 🙂 -> prin RSS

  • lae says:

    fain tare ….
    oarecum repros: ai fi putut incepe cu tutoriale generale despre clase …. si mai pe php …. cum poti salva o clasa intr-o variabila de tip session

    comentariu cretin: ai uitat o “(” la catch acolo unde dai exemplul practic. “catch $ex)”
    da’ exceptand faza cu exceptile care sunt datorate in majoritatea cazurilor, transmisiei gresite de parametrii, mi se pare oarecum greoaie programarea in felul asta … si cu ceva batai de cap pana te obisnuiesti
    sintaxa e cam aceeasi la toate bazele de date si astea difera in mare prin optiuni. gen: trigerele in oracle … care mai nou au aparut si pe mysql. sau autoincrementul din mysql si canch in oracle. desi cam poti implementa o solutie la problemele astea in clase … tot iti dau batai de cap. in fine ce-am vrut sa zic e ca: nu cred ca sintaxa e problema mare in cazul migrarii. desi, mai mult ca sigur, ma insel.

  • admin says:

    Chestia era ca de fapt nu se incapsuleaza sintaxa ci servicii 🙂 practic fiecare interfata garanteaza ca o anumita clasa ce o implementeaza furnizeaza serviciile respective, de exemplu s-ar putea trece pe un sistem de gestiune implementat pe baza de fisere indexate etc..posibilitati nelimitate….
    Si chestia cu sintaxa este intra-adevar o problema(una mare) in sensul ca fiecare server de DB aduce “mici inbunatatiri” la ce ar trebui sa insemne un script SQL sau DDL standard. Eu persoanl am patit-o.

Leave a Reply