[ alfa-pro @ 18.08.2011. 21:07 ] @
Pozdrav svim clanovima, imam nekoliko pitanja u vezi strukture izrade database abstraction layer-a (drivera). Citao sam dosta po netu i vecina mi preporucuje da skinem gotovu biblioteku jedna od preporuka je AdoDB. Problem je sto nisam zelo da koristim tudje biblioteke. Na nekim delovima php manuela preporuka koristiti PDO za izradu drivera.Koliko je to preporucljivo sa vase tacke? OOP u PHP se bavim nekih godinu i po dana. Nisam expert ali pisem slozenije klase, koristim razne Frameworkove tako da sam upoznat sa bazama i Activ Record-om.

Zanima me proces izraade, gledao sam u FW Codeigniter nacin kako su momci uradili je drugaciji od zend FW.
Mene sta najvise buni organizacija celog koda.

Napravim npr folder Database i u njemu imam poseban folder Driver u kome se nalaze MySql, MySqli, odbc,sqlite itd...
E sada nije mi jasno dali ja sada u MySQL driveru pisem sve radnje, metode za rad sa mysql-om? Tipa povezivanje sa bazom, zadavanje upita, SELECT, UPDATE, DELETE itd. Dali sve to navodim u Driver/MySQL ili moram posebnu klasu da definisem kao sto je u CI FW?

Nije mi jasna logika i grananje koda. U nekim primerima sam video prave posebnu classu van Driver foldera koja vrsi konekciju i sve upite. A u Mysql driveru ima sve isto kao i u predhodnoj klasi. Izgleda kao neki visak ponavljanja koda.

Nadam se da me kapirate potrebana mi logika komunikacije clase database sa driverima, i dali je obavezno da odvajam Select iskaze kao sto ima u drugim primerima koje sam susretao... Ili moram da napravim neku main klasu db koja ce upravljati sa driverima i vaditi odredjene metode/funkcije(tipa connect ili Select) iz predhodno definisanog drivera..

Unapred hvala.
[ Milos911 @ 18.08.2011. 22:53 ] @
Aj da ja probam, onako iz glave (bas me zanima da li cu biti u pravu, posto nikad to nisam radio :) ).

Mislim da treba da se napravi jedna glavna klasa, recimo "database". Ona treba da ima sve metode koje ce se koristi za rad sa razlicitim bazama (ali te metode moraju da postoje u svakoj bazi, ili da je moguce naci zaobilazno resenje)... Treba da ima jedna glava varijabla koja ce da govori klasi koju bazu da koristi. Iz aplikacije ti pozivas metode iz te klase, a metode na osnovu te glavne varijable pozivaju metode iz ostalih klasa. Naravno, ostale klase treba uvek da vracaju isti rezultat database klasi, tako da ne moras da brines o nacinu povezivanja koji koristis.

Inace, ne znam sta je praksa, ali ja bih to doveo na neki nivo da mi klasa ne vraca rezultat iz mysql_query(naprimer). Nego, ako mi treba array, stavio bih $database->get_array(upit ili identifikacija onoga sto treba da se smesti u array);. Pa onda database to posalje mysql ili kojoj god klasi za direktnu komunikaciju sa bazom, i kao odgovor dobije ili array ili false. Ako dobije false, pristupa toj klasi i iz error varijable uzima gresku i upisuje je u svoju varijablu, pa i ona vraca false (ovo verovatno znas, ali kad sam vec inspirisan :D).
I eto tako, nadam se da sam u pravu i da nisam nesto lupio (inace, ovo je mozda vise za art of programming?) :)
[ Nikola Poša @ 19.08.2011. 10:46 ] @
Nemoj se mučiti sa izmišljanjem tople vode, koristi PDO. PDO inače nije full DAL, već je njegova apstrakcija na data-access nivou. Popularni ORM-ovi, kao što je Doctrine, se u svom core-u oslanjaju upravo na PDO. Zend_Db komponenta Zend Framework-a na raspolaganje stavlja adaptere takodje bazirane na istoj toj PHP ekstenziji (Zend_Db_Adapter_Pdo_* klase).

A ako baš nameravaš da sam pišeš nešto, a da to stvarno valja i da bude upotrebljivo, onda to ti preporučujem da najpre proučiš malo naprednije stvari koje se tiču dizajna klasa. Najbolje ti je da preuzmeš neku od gore pomenutih biblioteka i vidiš kako su oni realizovali sve te stvari. Pokazalo se da je najbolji pristup za pravljenje database apstrakcije ta neka implementacija zasnovana na Adapter design pattern-u. Dakle definišeš neki ugovor, u vidu interfejsa, kojeg svaki taj adapter mora da ispunjava da bi sebe nazivao db adapterom, npr. ovako nešto za početak:
Code:
interface DatabaseAdapterInterface
{
    public function connect();
    public function disconnect();
    public function query($sql);
    public function fetch();
    public function insert($table, array $data);
    public function update($table, array $data, $where);
    public function delete($table, $where);
    public function lastInsertId();
    public function quote($value);
}

I posle na osnovu njega gradiš logiku za svaki RDBMS. Evo kako bi npr. izgledale implementacije nekih od metoda tog interfejsa u slučaju MySQL adaptera:
Code:
class MysqlAdapter implements DatabaseAdapterInterface
{
    protected $_config = array();

    protected $_connection;

    protected $_lastResult;

    public function __construct(array $config)
    {
        $this->_config = $config;
    }

    public function connect()
    {
        if ($this->_connection === null) {
            list($host, $username, $password, $database) = $this->_config;
            if (!$this->_connection = @mysqli_connect($host, $username, $password, $database)) {
                throw new Exception('An error occurred: ' . mysqli_connect_error());
            }

            unset($host, $username, $password, $database);
        }

        return $this->_connection;
    } 

    public function query($sql)
    {
        $this->connect();

        if (!$this->_lastResult = mysqli_query($this->_connection, $sql)) {
            throw new Exception('An error occurred: ' . $sql . mysqli_error($this->_connection));
        }

        return $->_lastResult;
    }

    public function fetch($mode = MYSQLI_ASSOC)
    {
        if ($this->_lastResult === null) {
            if (($row = mysqli_fetch_array($this->_lastResult, $mode)) === false) {
                return $row;
            }
        }

        return null;
    }
 
    public function insert($table, array $data)
    {
        $cols = implode(',', array_keys($data));
        $values = implode(',', array_map(array($this, 'quote'), array_values($data)));

        $query = 'INSERT INTO ' . $table . ' (' . $cols . ') ' . ' VALUES (' . $values . ')';
        $this->query($query);

        return $this->lastInsertId();
    }

    //Update, delete...



    public function quote($value)
    {
        if (!is_numeric($value)) {
            $this->connect();

            $value = "'" . mysqli_real_escape_string($this->_connection, $value) . "'";
        }

        return $value;
    }

    public function lastInsertId()
    {
        return ($this->_connection !== null) ? mysqli_insert_id($this->_connection) : null;
    }

    public function disconnect()
    {
        if ($this->_connection !== null) {
            mysqli_close($this->_connection);
            $this->_connection = null;
        }
    }

    public function __destruct()
    {
        $this->disconnect();
    }
}

Ovo je naravno daleko od upotrebljivog i predstavlja samo neki osnovni mockup konkretne implementacije.

I opet kažem, nemoj izmišljati toplu vodu...
[ alfa-pro @ 19.08.2011. 16:30 ] @
hvala na odgovorima, do pre nego sto sam postavio ovu temu proucavao sam dosta zend DB dokumentaciju njihovih klasa. I ucinilo mi se da je to nesto od bolje sto sam izgooglao do sada... I opet kazem hteo sam koristiti pdo ali nisam video da se u vecini FW koristi i nisam siguran koliko je pametno, zato sam odmah bio napomenuo PDO... Ovo je lep primer sto si naveo ali opet imam neke nerazjasnjene stvari u glavi...
Zasto ja moram da imam ud dbAdapterInt naredbe koje rade sa upitima i sve to isto moram imati u mySql adapteru?... U svakom slucaju hvala na pomoci

DOpuna:
Sada sam lepo procitao http://en.wikipedia.org/wiki/Adapter_pattern tako da sam sada ukapirao sustinu.. Lepo je objasnjeno kako adapteri rade.. Hvala na linku

[Ovu poruku je menjao alfa-pro dana 19.08.2011. u 17:48 GMT+1]