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
  1. <?php  
  2.     DEFINE('DB_HOST''localhost');  
  3.     DEFINE('DB_NAME''asasd');  
  4.     DEFINE('DB_USER''sdssdsd');  
  5.     DEFINE('DB_PASS''dfgdfgfgdf');  
  6. ?>  
db.php
  1. <?php  
  2.     require_once('config.php');  
  3.       
  4.     interface DBConnection{  
  5.         public function db_connect();  
  6.         //public function db_disconnect();  
  7.         //public function db_is_connected();  
  8.     }  
  9.       
  10.     interface DBQuery{  
  11.         public function select($query);  
  12.         //public function select_all($query);  
  13.         //public function delete($query);  
  14.         //public function update($query);  
  15.     }  
  16.       
  17.     abstract class DB implements DBConnection{  
  18.         protected $db_host = DB_HOST;  
  19.         protected $db_name = DB_NAME;  
  20.         protected $db_user = DB_USER;  
  21.         protected $db_pass = DB_PASS;  
  22.         protected $db_conn = null;  
  23.     }  
  24.       
  25.     final class PDOConnection extends DB implements DBQuery{  
  26.         private $stmt = null;  
  27.           
  28.         function __construct(){  
  29.             $this->db_connect();  
  30.         }  
  31.           
  32.         function db_connect(){  
  33.             $this->db_conn = new PDO("mysql:host={$this->db_host};dbname={$this->db_name}"$this->db_user, $this->db_pass);  
  34.         }  
  35.           
  36.         function select($query){  
  37.             $this->stmt = $this->db_conn->prepare($query);  
  38.             $this->stmt->execute();  
  39.             return $this->stmt->fetchAll();   
  40.         }  
  41.     }  
  42.       
  43.       
  44.     //final class SQLiConnection extends DB implements DBQuery{}  
  45.       
  46. ?>  
index.php
  1. <?php  
  2.     ini_set("display_errors", true);  
  3.     error_reporting(E_ALL);  
  4.       
  5.     require_once('db.php');  
  6.       
  7.     $pdo = new PDOConnection;  
  8.       
  9.     $result = $pdo->select("SELECT name FROM user");  
  10. ?>     
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

inf · 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.
  1. class UserService {  
  2.   
  3.     public function __construct(iConnection $connection){  
  4.         $this->connection = $connection;  
  5.     }  
  6.   
  7.     public function readNames(){  
  8.         $result = $this->connection->select("SELECT name FROM user");  
  9.         // ...  
  10.     }  
  11.   
  12. }    
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:
  1. class UserService {  
  2.   
  3.     public function __construct(iConnection $connection){  
  4.         $this->connection = $connection;  
  5.     }  
  6.   
  7.     public function readNames(){  
  8.         $users = $this->connection->select('name')->from('user');  
  9.         foreach ($users as $user)  
  10.             $name = $user->name;  
  11.         // ...  
  12.     }  
  13.   
  14. }    
Ha valamilyen tulajdonság később változni fog, akkor azt érdemes injektálni:
  1. $connection = new MySqlDbConnection(array(  
  2.     'host' => 'localhost',  
  3.     'port' => 1234,  
  4.     'name' => 'mydb',  
  5.     'user' => 'root',  
  6.     'password' => 'bla'  
  7. ));  
  8. $service = new UserService($connection);  
  9. $service->readNames();  
Nyilván egy rendesen összerakott kódnál a connection-t valamilyen factory hozza létre a kapcsolatot:
  1. class DbConnectionFactory {  
  2.     public function createConnection(array $options){  
  3.         $dialect = $options['dialect'];  
  4.         $type = $options['type'];  
  5.         if ($dialect == 'mysql') {  
  6.             if ($type == 'pdo')  
  7.                 $connection = new MySqlPdoConnection($config);  
  8.             // ...  
  9.         }  
  10.         // ...  
  11.     }  
  12. }  
  13.   
  14. $factory = new DbConnectionFactory();  
  15. $connection = $factory->createConnection(array(  
  16.     'type' => 'pdo',  
  17.     'dialect' => 'mysql',  
  18.     'host' => 'localhost',  
  19.     'port' => 1234,  
  20.     'name' => 'mydb',  
  21.     'user' => 'root',  
  22.     'password' => 'bla'  
  23. ));  
  24.   
  25. $service = new UserService($connection);  
  26. $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!