OOP Database
Ik heb database handler geschreven in OOP. Ben ik zo goed op weg? Wat kan er beter? En hebben jullie nog ideeën wat ik nog meer kan bouwen? Als ik hem helemaal af heb zal ik hem in tussen de scripts zetten.
Alvast bedankt,
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
<?php
/**
* @package database
*
* @author Niels Kieviet <[email protected]>
* @version $Revision v1.00$
* @copyright Copyright (c) 2010, Niels Kieviet
*/
/*
* Database interface
*/
interface database
{
/**
* Make connection with the database
*
* @param string $dbHost
* @param string $dbUser
* @param string $dbPass
* @param string $dbName
*/
public function connect( $db_host, $db_user, $db_pass, $db_name );
/**
* Run a Query
*/
public function query( $query );
}
/**
* DatabaseResult interface
*/
interface databaseResult
{
/**
* @return array
*/
public function fetch_assoc();
/**
* @return array
*/
public function fetch_all();
/**
* @return int
*/
public function rows();
/**
* @return int
*/
public function last_id();
}
/**
* Database exception class.
*/
class databaseException extends Exception
{
}
/**
* MySQL class.
*/
class MySQL implements database
{
/**
* @var array
*/
private $connection;
/**
* Make connection with a MySQL database
*/
public function connect( $db_host, $db_user, $db_pass, $db_name )
{
if( !$this->connection = mysql_connect( $db_host, $db_user, $db_pass, $db_name ) ) {
throw new databaseException( 'Connection with MySQL database failed' );
}
if( !mysql_select_db( $db_name, $this->connection ) ) {
throw new databaseException( 'Select database failed' );
}
}
/**
* Run Query
*
* @return MySQLResult
*/
public function query( $query )
{
$result = mysql_query( $query, $this->connection );
if( !$result ) {
throw new databaseException( mysql_error() );
}
return new MySQLResult( $result );
}
}
/**
* MySQLResult class.
*/
class MySQLResult implements databaseResult
{
/**
* @var array
*/
private $result;
/**
* Constructor.
*
* @param handler $result
*/
public function __construct( $result )
{
$this->$result = $result;
}
/**
* @return array
*/
public function fetch_assoc()
{
return mysql_fetch_assoc( $this->result );
}
/**
* @return array
*/
public function fetch_all()
{
$result = array();
while( $row = $this->fetch_assoc() ) {
$result[] = $row;
}
return $result;
}
/**
* @return int
*/
public function rows()
{
return mysql_num_rows( $this->result );
}
/**
* @return int
*/
public function last_id()
{
return mysql_insert_id( $this->result );
}
}
/**
* @package database
*
* @author Niels Kieviet <[email protected]>
* @version $Revision v1.00$
* @copyright Copyright (c) 2010, Niels Kieviet
*/
/*
* Database interface
*/
interface database
{
/**
* Make connection with the database
*
* @param string $dbHost
* @param string $dbUser
* @param string $dbPass
* @param string $dbName
*/
public function connect( $db_host, $db_user, $db_pass, $db_name );
/**
* Run a Query
*/
public function query( $query );
}
/**
* DatabaseResult interface
*/
interface databaseResult
{
/**
* @return array
*/
public function fetch_assoc();
/**
* @return array
*/
public function fetch_all();
/**
* @return int
*/
public function rows();
/**
* @return int
*/
public function last_id();
}
/**
* Database exception class.
*/
class databaseException extends Exception
{
}
/**
* MySQL class.
*/
class MySQL implements database
{
/**
* @var array
*/
private $connection;
/**
* Make connection with a MySQL database
*/
public function connect( $db_host, $db_user, $db_pass, $db_name )
{
if( !$this->connection = mysql_connect( $db_host, $db_user, $db_pass, $db_name ) ) {
throw new databaseException( 'Connection with MySQL database failed' );
}
if( !mysql_select_db( $db_name, $this->connection ) ) {
throw new databaseException( 'Select database failed' );
}
}
/**
* Run Query
*
* @return MySQLResult
*/
public function query( $query )
{
$result = mysql_query( $query, $this->connection );
if( !$result ) {
throw new databaseException( mysql_error() );
}
return new MySQLResult( $result );
}
}
/**
* MySQLResult class.
*/
class MySQLResult implements databaseResult
{
/**
* @var array
*/
private $result;
/**
* Constructor.
*
* @param handler $result
*/
public function __construct( $result )
{
$this->$result = $result;
}
/**
* @return array
*/
public function fetch_assoc()
{
return mysql_fetch_assoc( $this->result );
}
/**
* @return array
*/
public function fetch_all()
{
$result = array();
while( $row = $this->fetch_assoc() ) {
$result[] = $row;
}
return $result;
}
/**
* @return int
*/
public function rows()
{
return mysql_num_rows( $this->result );
}
/**
* @return int
*/
public function last_id()
{
return mysql_insert_id( $this->result );
}
}
Gewijzigd op 01/06/2010 17:20:28 door Niels K
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?php
/**
* MySQLi class.
*/
class MySQLi implements database
{
/**
* @var array
*/
private $connection;
/**
* Make connection with a MySQL database with MySQLi extention
*
* @param string $dbHost
* @param string $dbUser
* @param string $dbPass
* @param string $dbName
*/
public function connect( $db_host, $db_user, $db_pass, $db_name )
{
if( !$this->connection = new mysqli( $db_host, $db_user, $db_pass, $db_name ) ) {
throw new databaseException( 'Database connection failed' );
}
}
/**
* Run Query
*/
public function query( $query )
{
$result = $this->connection->query( $query );
if( !$result ) {
throw new databaseException( $this->connection->error );
}
return new MySQLiResult( $result );
}
}
/**
* MySQLiResult class
*/
class MySQLiResult implements databaseResult
{
/**
* @var array
*/
private $result;
/**
* Constructor.
*
* @param handler $result
*/
public function __construct( $result )
{
$this->result = $result;
}
/**
* @return array
*/
public function fetch_assoc()
{
return $this->result->fetch_assoc();
}
/**
* @return array
*/
public function fetch_all()
{
$result = array();
while( $row = $this->fetch_assoc() ) {
$result[] = $row;
}
return $result;
}
/**
* @return int
*/
public function rows()
{
return mysqli_affected_rows( $this->result );
}
/**
* @return int
*/
public function last_id()
{
return mysqli_insert_id( $this->result );
}
}
/**
* MySQLi class.
*/
class MySQLi implements database
{
/**
* @var array
*/
private $connection;
/**
* Make connection with a MySQL database with MySQLi extention
*
* @param string $dbHost
* @param string $dbUser
* @param string $dbPass
* @param string $dbName
*/
public function connect( $db_host, $db_user, $db_pass, $db_name )
{
if( !$this->connection = new mysqli( $db_host, $db_user, $db_pass, $db_name ) ) {
throw new databaseException( 'Database connection failed' );
}
}
/**
* Run Query
*/
public function query( $query )
{
$result = $this->connection->query( $query );
if( !$result ) {
throw new databaseException( $this->connection->error );
}
return new MySQLiResult( $result );
}
}
/**
* MySQLiResult class
*/
class MySQLiResult implements databaseResult
{
/**
* @var array
*/
private $result;
/**
* Constructor.
*
* @param handler $result
*/
public function __construct( $result )
{
$this->result = $result;
}
/**
* @return array
*/
public function fetch_assoc()
{
return $this->result->fetch_assoc();
}
/**
* @return array
*/
public function fetch_all()
{
$result = array();
while( $row = $this->fetch_assoc() ) {
$result[] = $row;
}
return $result;
}
/**
* @return int
*/
public function rows()
{
return mysqli_affected_rows( $this->result );
}
/**
* @return int
*/
public function last_id()
{
return mysqli_insert_id( $this->result );
}
}
Gewijzigd op 01/06/2010 19:19:01 door Niels K
Code (php)
@hipska,
En hoe vang ik dat dan op in mijn MySQLiResult klasse? $this->affected_rows?
Gewijzigd op 01/06/2010 19:26:24 door Niels K
Het zou wel leuk zijn als je het dan vervolgens in de interface van DatabaseResult opneemt, zodat die functie niet mysqli specifiek wordt. (Als je hem dan gebruikt in je script, houd je je niet meer slechts aan die interface, en kan je niet langer erop vertrouwen dat alle classes die die interface implementeren zullen werken met je code)
EDIT: Ik krijg wel deze error
Fatal error: Cannot redeclare class MySQLi in C:\xampp\htdocs\OOP\databaseClass\database_class.php on line 169
Komt dat omdat MySQLi al bestaat ofzoiets?
Gewijzigd op 01/06/2010 21:30:23 door Niels K
Of include je hem gewoon twee keer?
Zo'n systeem maken is natuurlijk een leuke oefening, maar in een echte applicatie is PDO toch veel makkelijker (en sneller, PDO hoeft niet geïnterpreteerd te worden)?
Dat is ook een beetje het idee achter PDO: de interface moet gelijk zijn voor alle databases. Dat ben jij nu ook aan het maken.
En inderdaad, je kan MySQLi niet als naam gebruiken omdat PHP vaak al komt met de class MySQLi.
Ok, ja ik ben een leek omtrent OOP en ik wou mezelf erin verbeteren vandaar dat ik wat aan het oefenen ben. Wat zou ik volgens jouw moet doen dan Pim? Een PDO handler maken?
Gewoon direct gebruiken.
Of als je je nog eens in OOP wilt verdiepen in een Datamapper of een active record oid stoppen, waarbij ik je de eerste aanraad.
Gewijzigd op 01/06/2010 22:32:54 door Jelmer -
Die snap ik niet.
Design patterns geven specifieke oplossingen voor bepaalde problemen.
Het 'active record' en de 'datamapper' geven de oplossing voor het probleem van 'object persistence', het opslaan van objecten in een database.
De 'active record' is erop gebaseerd dat een model (een basisobject in een OOP systeem, dus bijv een Bericht of een User in een blog systeem) zichzelf opslaat ( $bericht->save() en Bericht::findById(112) ). Dit heeft het voordeel dat het het makkelijkst is, maar het gaat tegen een van de OOP principes in: 1 object heeft 1 taak. De taak van het model en haar persistence worden zo gemengd.
De datamapper doet dat apart (mijn voorkeur): $mapper = new Datamapper_User; $mapper->save($user); $mapper->findById(112); Dit heeft het voordeel dat alle SQL op een rijtje zit, maar voegt wel een extra laag toe.
De active record is de basis van ORM systemen (als Doctrine) die de hele persistence voor je doen, dus je maakt gewoon een User model, geeft deze properties en de ORM zorgt voor de rest. Wat jelmer, denk ik, bedoelt is dat je probeert zo'n systeem zelf te bouwen. Succes daarmee, dat is een hoop werk ;)
Gewijzigd op 01/06/2010 22:48:43 door Pim -
Niels Kieviet op 01/06/2010 21:28:10:
Fatal error: Cannot redeclare class MySQLi in C:\xampp\htdocs\OOP\databaseClass\database_class.php on line 169
Komt dat omdat MySQLi al bestaat ofzoiets?
Fatal error: Cannot redeclare class MySQLi in C:\xampp\htdocs\OOP\databaseClass\database_class.php on line 169
Komt dat omdat MySQLi al bestaat ofzoiets?
Inderdaad, maar je kan hier rond lopen door de class in je eigen namespace te zetten..
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
<?php
namespace Database; // dit moet bovenaan het script!
class MySQLi {
// blabla
}
?>
namespace Database; // dit moet bovenaan het script!
class MySQLi {
// blabla
}
?>
Het gebruik ervan gaat dan simpel via:
of
Edit:
Ben trouwens zelf bezig met zo'n Active Record systeem, veel denkwerk en moeilijke code. Maar het resultaat mag er wezen! En nadien heb je lekker minder werk..
Gewijzigd op 02/06/2010 10:06:05 door Hipska BE
Wat een waardevolle reactie's. Hier kan ik echt wat mee.
@hipska
Is het 'beter' om een namespace te gebruiken? of kan ik beter de naam veranderen?
En met zo'n Active Record systeem bedoel je dan bijvoorbeeld new user( 'naam', 'password', 'email' ); die dan direct registreert, activeert en dergelijke dingen? Of moet het helemaal niet uitmaken wat voor iets je maakt maar toch alles voor je doet?
@Pim
Ja inderdaad gaat dat tegen OOP regels in, ik heb even lopen googlen. Maar volgens mij heeft het wel veel voordelen als je het doet. Zijn er frameworks die gebruik maken van deze pattern?
Niels Kieviet op 02/06/2010 13:01:09:
Zo goedemiddag,
Wat een waardevolle reactie's. Hier kan ik echt wat mee.
@hipska
Is het 'beter' om een namespace te gebruiken? of kan ik beter de naam veranderen?
Wat een waardevolle reactie's. Hier kan ik echt wat mee.
@hipska
Is het 'beter' om een namespace te gebruiken? of kan ik beter de naam veranderen?
Beter.. Volgens mij is het gewoon persoonlijke voorkeur. Oja, let wel dat dit enkel vanaf php 5.3 werkt.
Niels Kieviet op 02/06/2010 13:01:09:
En met zo'n Active Record systeem bedoel je dan bijvoorbeeld new user( 'naam', 'password', 'email' ); die dan direct registreert, activeert en dergelijke dingen? Of moet het helemaal niet uitmaken wat voor iets je maakt maar toch alles voor je doet?
En met zo'n Active Record systeem bedoel je dan bijvoorbeeld new user( 'naam', 'password', 'email' ); die dan direct registreert, activeert en dergelijke dingen? Of moet het helemaal niet uitmaken wat voor iets je maakt maar toch alles voor je doet?
Ik bedoel dus meer dat mijn systeempje dit soort dingen doet, zonder daar telkens de code voor te schrijven:
$bericht->save() en Bericht::findByID(112)
Ja klopt had ik gezien op PHP.net
"Ik bedoel dus meer dat mijn systeempje dit soort dingen doet, zonder daar telkens de code voor te schrijven:
$bericht->save() en Bericht::findByID(112)"
Ik heb wat gegoogled en rondgekeken. Maar ik vind het wel in strijd staan met de OOP gedachte. En omdat ik die zeker wil blijven aanhouden, is er niet wat anders waardoor ik mezelf sterk kan verbeteren in OOP?
Het komt erop neer dat je met PHP een API maakt om daarmee SQL te schrijven. Dit wordt vaak afgeraden omdat je in SQL al heel veel complexiteit hebt, dit mappen naar PHP doet of af aan deze complexiteit, of de PHP wordt erg ingewikkeld. Doctrine bijvoorbeeld doet dit, en maakt zelfs een hele eigen SQL achtige taal aan, het werkt prima, maar is alleen enorm complex en langzaam wanneer geen gebruik wordt gemaakt van opcode cache (welke PHP code interpreteert en daarna die interpretatie opslaat, scheelt heel veel tijd bij het gebruiken van veel PHP).
En zeg nou zelf: hoeveel scheelt het om het eerste ipv het tweede te schrijven:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<?php
$mapper = new Datamapper_User;
$user = $mapper->findById($id);
$mapper->save($user);
// IPV
$user = User::findById($id);
$user->save();
?>
$mapper = new Datamapper_User;
$user = $mapper->findById($id);
$mapper->save($user);
// IPV
$user = User::findById($id);
$user->save();
?>
Ik raad iedereen aan gewoon handmatig een data mapper te schrijven, kost een kwartiertje per model. En nog een voordeel: je modellen hoeven niet met je tabellen te matchen, wat in veel situaties erg handig is.
Gewijzigd op 02/06/2010 15:21:48 door Pim -
Ik toon vanavond wel eens hoe een newsarticle class er bij mij uit ziet..
Kost nog maar een paar karakters meer ;)
ik bedoelde de code van class Datamapper_User { .. hierzo .. } waar je bij active records dit vaak maar enkelmalig hebt