ugrás a tartalomhoz

OOP-s adatbázis kezelés minta

mindfull · 2017. Ápr. 14. (P), 13.14
Sziasztok!

Próbálok ismerkedni az OOP rejtelmeivel, még csak kezdő szinten, és szeretném kikérni a véleményetek az alábbi kóddal kapcsolatban, mely egy adatbázis kezelő osztály kezdeti verziója lenne, csupán az OOP gyakorlása végett.

De jó úton haladok-e, nem bonyolítom-e túl, mi az amit rosszul csináltam?
Azért kérdem, mert annyi a témába vágó minta kódot találtam, de mindegyik egy kicsit másképp van megalkotva, ezért gondoltam, hogy akkor a saját logikám szerint megalkotnám.

Olyan kódot szerettem volna létrehozni, ami alkalmas PDO, mySQLi vagy egyéb API felhasználására is. Természetesen még nincs kész, de jó-e a szemlélet, szervezés?


config.php
<?php
    DEFINE('DB_HOST', 'localhost');
    DEFINE('DB_NAME', 'asasd');
    DEFINE('DB_USER', 'sdssdsd');
    DEFINE('DB_PASS', 'dfgdfgfgdf');
?>
db.php
<?php
    require_once('config.php');
    
    interface DBConnection{
        public function db_connect();
        //public function db_disconnect();
        //public function db_is_connected();
    }
    
    interface DBQuery{
        public function select($query);
        //public function select_all($query);
        //public function delete($query);
        //public function update($query);
    }
    
    abstract class DB implements DBConnection{
        protected $db_host = DB_HOST;
        protected $db_name = DB_NAME;
        protected $db_user = DB_USER;
        protected $db_pass = DB_PASS;
        protected $db_conn = null;
    }
    
    final class PDOConnection extends DB implements DBQuery{
        private $stmt = null;
        
        function __construct(){
            $this->db_connect();
        }
        
        function db_connect(){
            $this->db_conn = new PDO("mysql:host={$this->db_host};dbname={$this->db_name}", $this->db_user, $this->db_pass);
        }
        
        function select($query){
            $this->stmt = $this->db_conn->prepare($query);
            $this->stmt->execute();
            return $this->stmt->fetchAll(); 
        }
    }
    
    
    //final class SQLiConnection extends DB implements DBQuery{}
    
?>
index.php
<?php
    ini_set("display_errors", true);
    error_reporting(E_ALL);
    
    require_once('db.php');
    
    $pdo = new PDOConnection;
    
    $result = $pdo->select("SELECT name FROM user");
?>   
Köszönöm, ha segítetek!


u.i.: Tudtok esetleg olyan oldalt ajánlani, ahol minta feladatok vannak fent (jóféle) megoldásokkal.
 
1

Hibak

janoszen · 2017. Ápr. 14. (P), 16.10
Amire sztem itt erdemes lenne figyelni az az, hogy az OOP celja, hogy az objektumokat onalloan lehessen hasznalni. Ehhez kepest Te a DB-t globalis konstansokkal inicializalod, ezzel rejtett fuggoseget letrehozva.

Ezen felul erdemes lenne kulonbseget tenni a kulonbozo DB motort tamogato konnektorok kozott, hiszen nem fognak ugyanugy viselkedni.

Ha szeretnel egy peldat latni egy OOP elveket koveto SQL libre, nezd meg ezt a projekt-kezdemenyt. Ugyan nincs kesz, de a lenyeg remelhetoleg ertheto belole.

Altalanos jotanacsok:

- Minden osztalyod legyen kulon fajlban a PSR-4 szabvany szerint. Ennek megfeleloen hasznalj PSR-4 betoltot.
- Ha mar atalltal PHP 7-re, dokumentald a fuggvenyeid visszateresi tipusat. Ha nem, akkor PHPDocban ird le.
- A konfigodat es fuggosegeidet add at konstruktor parameterben, ne implicit modon legyen beleegetve.
2

Ilyenek tanácsokra gondoltam,

mindfull · 2017. Ápr. 14. (P), 16.52
Ilyenek tanácsokra gondoltam, csak nem mind értem :)

Nagyon köszönöm! Utánanézek, aztán visszajövök!
3

Google

janoszen · 2017. Ápr. 14. (P), 17.27
Kulcsszavak amikre kereshetsz:

- SOLID elvek
- Dependency injection
7

Megnéztem amit linkeltél, de

mindfull · 2017. Ápr. 16. (V), 14.43
Megnéztem amit linkeltél, de jelenleg nehezen tudom magamévá tenni...

Viszont az elméleti dolgok nagyon hasznosak, köszönöm!
4

Kérdések

Hidvégi Gábor · 2017. Ápr. 14. (P), 17.40
Szerintem érdemes pár kérdést feltenni, mielőtt bármit csinálsz, hogy időt spórolhass meg magadnak.

Miért több API-van szeretnéd használni?

Azon a szinten, ahol most vagy, általában egyféle adatbázishoz szoktak csatlakozni. Ha már többhöz kell, az macera, még akkor is, ha az SQL dialektusait beszélik, mert a kompatibilitás nagyjából az alap SELECT, INSERT és társaikig tart, de már egy JOIN megfogalmazásánál is eltérőek lehetnek a különböző motorok, nem beszélve a típusosságról.

Miért MySQL? Igaz, hogy gyorsabb, mint a PostgreSQL, és kényelmesebb is benne lekérdezéseket írni, mert a típusellenőrzéseket elvégzi a háttérben, de ez egyben hátránya is, mert ha később komolyabban szeretnél SQL-lel foglalkozni, ott ezt nem úszhatod meg, azaz a PostgreSQL jobb belépő, ráadásul az SQL szabványt is pontosabban valósítja meg.

Emellett a MySQL GPL lincesszel fut, ami olyan, mint a rák, ha valamit megfertőz, soha nem fog szabadulni tőle. Ezzel szemben a PostgreSQL-t egy szabad MIT lincesszel adják, így azt teszel vele, amit akarsz. Példa: az általunk fejlesztett szolgáltatás nem csak központi szerveren fut, hanem az ügyfeleknek lehetősége van tőlünk egy minigépet venni, így az adatok ott vannak náluk. Ezt MySQL-lel nem tudnánk megcsinálni, azaz igen, de ügyfelenként félmilliós díjat kéne fizetni. Áttértünk PostgreSQL-re, és ez meg is oldódott.

Valóban szükség van arra, hogy a PHP-d meghívásakor több adatbázishoz is csatlakozz? Ha nem, akkor miért OOP módon teszed? Írhatsz egy egyszerű db_lekerdezes(), db_beszur(), db_torol() stb. függvényt, amit bárhonnan könnyen elérhetsz a kódból.

Mi például két-három adatbáziskapcsolatot használunk, a fenti függvényeknek pedig paraméterként adjuk át, hogy melyiken végezze el a műveletet.

De ha már PDO, akkor miért foglalkozol az eggyel egyszerűbb MySQLi-vel? A PDO egy absztrakciós réteg a natív adatbázisfüggvények felett. Viszont, mint fentebb írtam, tapasztalataim szerint elég nagy eltérések vannak már a MySQL és a PostgreSQL között is, nagyon nehéz az alap Móricka szint fölött olyan lekérdezéseket írni, amelyek mindkettőben egyformák.

Vagy fordítva: ha viszont nincs szükséged a PDO funkcionalitására, miért nem használod közvetlenül a MySQLi függvényeket? A PDO-t sokan azért használják, mert lehetőség van benne prepared statementek írására. Ezt viszont natív MySQLi függvényekkel is megteheted, így némileg gyorsabb lesz a programod.

A PHP PostgreSQL függvényei között vannak, amelyek – hasonlóan a prepared statementekhez – a kapott paramétereket escape-elik, azaz neked nem kell ezzel foglalkoznod (pg_insert, pg_update, pg_delete, pg_query_params).

A prepared statementek drágák, mert kétszer kell őket elküldeni az adatbázisszervernek (egyszer előkészíti őket, azaz elvégzi az escape-elést, egyszer pedig lefuttatja a lekérdezést). Persze van előnyük is, ha ugyanazt a lekérdezést többször is meghívod, csak más paraméterekkel, akkor végül gyorsabb lesz a program. Csak tapasztalatom szerint nem sok ilyen alkalom van.

---

Ha objektumorientált programozást szeretnél tanulni, a fentiekből látszik, hogy nem feltétlenül az adatbázisok kezelése a legjobb terep erre.
6

MySQLi

janoszen · 2017. Ápr. 15. (Szo), 20.32
A MySQLi-t inkabb ne ajanld. Orbitalis baromsagok vannak a megvalositasaban. Nem lehet tombkent normalisan atadni a behelyettesitendo parametereket, rejtett global scope van a beallitasokban, stb.
10

rejtett global scope van a

Hidvégi Gábor · 2017. Ápr. 17. (H), 09.53
rejtett global scope van a beallitasokban
Ez micsoda pontosan?
11

Pl

janoszen · 2017. Ápr. 17. (H), 13.07
Pl azt hogy dobjon-e exceptiont vagy ne az egy globalis beallitas. Vagyis ha van egy libraryd ami atallitja, akkor debugolhatsz.
8

Az utolsó mondatodban a

mindfull · 2017. Ápr. 16. (V), 14.51
Az utolsó mondatodban a lényeg, legalábbis abból a szempontból, hogy próbáltam egy olyan feladatot találni, amiben az OOP alapelveket és lehetőségit szerettem volna valamennyire kihasználni.

Szóval úgy néz ki, hogy nem a legjobb feladatot találtam erre, viszont nem találtam olyan oldalt, ahol kiírnak egy feladatot, melynek megvalósítása során nem csak 1 osztályra korlátozódik, illetve ki kell valamennyire használni az OOP lehetőségeit. No, meg persze, aztán kapok egy alternatívát a megoldást illetően, hogy összehasonlíthassam a saját kódommal. Mert, ha nincs, akkor review-hatom magam, nem biztos, hogy jól fog elsülni a dolog :).

Ha valaki ismert ilyen forrást, akkor azt szívesen venném. Vagy keressek egy könyvet? No, de melyiket?
5

Ha valaminek az

inf3rno · 2017. Ápr. 15. (Szo), 20.30
Ha valaminek az implementációját le akarod cserélni a kódban, akkor azt lazán kell csatolni.

class UserService {

    public function __construct(iConnection $connection){
        $this->connection = $connection;
    }

    public function readNames(){
        $result = $this->connection->select("SELECT name FROM user");
        // ...
    }

}  
Ha ORM-et használsz, akkor még az SQL rész is generálva lesz, hogy ne függj tőle, ha mondjuk lecseréled pgsql-re, oracle-re vagy bármi egyébre, pl:

class UserService {

    public function __construct(iConnection $connection){
        $this->connection = $connection;
    }

    public function readNames(){
        $users = $this->connection->select('name')->from('user');
        foreach ($users as $user)
            $name = $user->name;
        // ...
    }

}  
Ha valamilyen tulajdonság később változni fog, akkor azt érdemes injektálni:

$connection = new MySqlDbConnection(array(
    'host' => 'localhost',
    'port' => 1234,
    'name' => 'mydb',
    'user' => 'root',
    'password' => 'bla'
));
$service = new UserService($connection);
$service->readNames();
Nyilván egy rendesen összerakott kódnál a connection-t valamilyen factory hozza létre a kapcsolatot:

class DbConnectionFactory {
    public function createConnection(array $options){
        $dialect = $options['dialect'];
        $type = $options['type'];
        if ($dialect == 'mysql') {
            if ($type == 'pdo')
                $connection = new MySqlPdoConnection($config);
            // ...
        }
        // ...
    }
}

$factory = new DbConnectionFactory();
$connection = $factory->createConnection(array(
    'type' => 'pdo',
    'dialect' => 'mysql',
    'host' => 'localhost',
    'port' => 1234,
    'name' => 'mydb',
    'user' => 'root',
    'password' => 'bla'
));

$service = new UserService($connection);
$service->readNames();
Egy normális ORM-nél általában szétbontják driver-re, connection-re, query-re, results-ra, stb... az egészet. Nézd meg mondjuk a Doctrine kódját.
9

Ezt értem :), tök jó,

mindfull · 2017. Ápr. 16. (V), 14.55
Ezt értem :), tök jó, köszönöm a mintát!