OOP Database

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Pagina: 1 2 volgende »

Niels K

Niels K

01/06/2010 17:19:34
Quote Anchor link
Beste lezer,

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)
PHP script in nieuw venster Selecteer het PHP script
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
<?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 );
    }
}
Gewijzigd op 01/06/2010 17:20:28 door Niels K
 
PHP hulp

PHP hulp

17/04/2024 00:37:26
 
Niels K

Niels K

01/06/2010 19:17:29
Quote Anchor link
Hierbij nog de MySQLi variant.. Wel zit ik met een probleempje. Bij het 'normale' PHP gebruik ik altijd $mysqli->affected_rows. maar bij de functie rows kan dat natuurlijk niet omdat hij alleen met $result tot zijn beschikking heeft. Hoe kan ik de $connection van de MySQLi classe bereiken?

Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?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 );
    }
}
Gewijzigd op 01/06/2010 19:19:01 door Niels K
 
Hipska BE

Hipska BE

01/06/2010 19:22:20
Quote Anchor link
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
<?
  /**
     * Run Query
     */

    public function query( $query ){
        $result = $this->connection->query( $query );
        if( !$result ) {
            throw new databaseException( $this->connection->error );
        }

        $this->affected_rows = $this->connection->affected_rows;    
        return new MySQLiResult( $result );
    }

?>
 
Niels K

Niels K

01/06/2010 19:24:57
Quote Anchor link
Sorry voor alle dubbel posts, maar anders wordt het zo onoverzichtelijk. Ik denk zelf dat het tijd wordt om een bepaald iets te maken waarin een database type kan worden ontworpen bepaalde adapters, want ik loop nu tegen het probleem aan dat niet bij elke soort database type de query's hetzelfde werken.

@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
 
Jelmer -

Jelmer -

01/06/2010 21:09:36
Quote Anchor link
Gewoon meegeven aan de constructor toch?
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
return new MySQLiResult($result, $this->connection->affected_rows);
?>


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)
 
Niels K

Niels K

01/06/2010 21:28:10
Quote Anchor link
@jelmer. Ja klopt, maar ik wil affected_rows eigenlijk alleen hebben voor mysqli. Maar nu een ander verhaal, als ik PDO wil toevoegen moet ik andere query's gaan schrijven. Is het niet beter als ik iets maak wat dat afhangt? zoja heb je een idee?

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
 
Pim -

Pim -

01/06/2010 21:41:59
Quote Anchor link
Misschien mag het niet omdat mysqli al bestaat?
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)?
 
Jelmer -

Jelmer -

01/06/2010 22:07:07
Quote Anchor link
Ik zou me niet te druk maken over de verschillen in SQL tussen verschillende databases. In de praktijk moet je voor die queries waar de notatie verschilt toch andere queries willen schrijven om gebruik te kunnen maken van de voor die database specifieke features. Daarom kies je immers voor die ene of toch die andere database. SQL abstraheren doet meer kwaad dan goed is mijn ervaring.

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.
 
Niels K

Niels K

01/06/2010 22:24:28
Quote Anchor link
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?
 
Pim -

Pim -

01/06/2010 22:28:14
Quote Anchor link
Nee joh, de database abstractie die jij probeert te maken is precies wat PDO levert: gewoon queries uitvoeren, resultaten fetchen (in stdClass, custom classes, of gewoon arrray's), exceptions gooien e.d.
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.
 
Jelmer -

Jelmer -

01/06/2010 22:32:27
Quote Anchor link
Het Active record pattern implementeren is inderdaad denk ik wel een goeie uitdaging. Het is iets waar je al die typische OOP dingen als interfaces en andere design patterns zoals inderdaad adaptors kan implementeren, en je hebt er misschien ook nog wat aan.
Gewijzigd op 01/06/2010 22:32:54 door Jelmer -
 
Niels K

Niels K

01/06/2010 22:37:34
Quote Anchor link
"Active record pattern"

Die snap ik niet.
 
Pim -

Pim -

01/06/2010 22:43:27
Quote Anchor link
Pff lang verhaal.
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 -
 
Hipska BE

Hipska BE

02/06/2010 10:01:35
Quote Anchor link
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?

Inderdaad, maar je kan hier rond lopen door de class in je eigen namespace te zetten..
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
namespace Database; // dit moet bovenaan het script!

class MySQLi {
// blabla
}
?>

Het gebruik ervan gaat dan simpel via:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
$db
= new Database\MySQLi();
?>

of
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
use Database\MySQLi; // dit moet bovenaan het script!

$db = new MySQLi();
?>


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
 
Niels K

Niels K

02/06/2010 13:01:09
Quote Anchor link
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?

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?
 
Hipska BE

Hipska BE

02/06/2010 13:16:13
Quote Anchor link
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?

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?

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)
 
Niels K

Niels K

02/06/2010 13:21:05
Quote Anchor link
"Beter.. Volgens mij is het gewoon persoonlijke voorkeur. Oja, let wel dat dit enkel vanaf php 5.3 werkt."

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?
 
Pim -

Pim -

02/06/2010 15:21:31
Quote Anchor link
Er zijn nog andere argumenten tegen een 'automatische active query' (heet dit zo?).

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)
PHP script in nieuw venster Selecteer het PHP script
1
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();

?>


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 -
 
Hipska BE

Hipska BE

02/06/2010 15:26:04
Quote Anchor link
Bij uw systeem heb je dan wel telkens een Datamapper extra waar telkens ongeveer dezelfde code in zal zitten en dus wel heleboel extra werk is bij het aanmaken.

Ik toon vanavond wel eens hoe een newsarticle class er bij mij uit ziet..
 
Pim -

Pim -

02/06/2010 15:29:34
Quote Anchor link
Als deze extra regel echt erg is kan je de mapper ook nog statisch maken:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
<?php
$user
= Datamapper_User::findById($id);
Datamapper_User::save($user);
?>

Kost nog maar een paar karakters meer ;)
 
Hipska BE

Hipska BE

02/06/2010 15:35:18
Quote Anchor link
ik bedoelde de code van class Datamapper_User { .. hierzo .. } waar je bij active records dit vaak maar enkelmalig hebt
 

Pagina: 1 2 volgende »



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.