Queries in een php class

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Michael R

Michael R

04/04/2015 10:51:40
Quote Anchor link
Hallo,

Waarom kan dit niet?


Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
<?php

    include_once('databaseclass.php');
    
    class user {
        
        private $query = mysqli_query($database->connect(),"SELECT * FROM users WHERE login_hash = '".$_SESSION['user_hash']."' LIMIT 1");
        private $query_array = mysqli_fetch_array($query);

}

?>


Ik krijg deze error:
Parse error: syntax error, unexpected '(', expecting ',' or ';' in C:\xampp\htdocs\php_forum\imports\classes\userclass.php on line 7
Gewijzigd op 06/04/2015 13:13:24 door - Ariën -
 
PHP hulp

PHP hulp

23/11/2024 10:53:30
 
NOLot -

NOLot -

04/04/2015 12:47:13
Quote Anchor link
Waarom dat niet kan staat hier: http://php.net/manual/en/language.oop5.properties.php

Quote:
Class member variables are called "properties". You may also see them referred to using other terms such as "attributes" or "fields", but for the purposes of this reference we will use "properties". They are defined by using one of the keywords public, protected, or private, followed by a normal variable declaration. This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.


Hoe je dit wel kunt doen is zo:

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

    include_once('databaseclass.php');
    
    class user {
        
        private $query;
        private $query_array;

        public function __construct()
        {

            $this->query = //... assign je value;
            $this->query_array = //... assign je value;
        }

}

?>
 
Michael R

Michael R

04/04/2015 14:30:43
Quote Anchor link
Ik heb nu dit:

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

    include_once('databaseclass.php');
    
    class user {
        
        private $query;
        private $query_array;
        
        public function __construct() {
            $this->query = mysqli_query($database->connect(),"SELECT * FROM users WHERE login_hash = '".$_SESSION['user_hash']."' LIMIT 1");
            $this->query_array = mysqli_fetch_array($query);
        }

?>


En nu krijg ik deze errors:

Notice: Undefined variable: database in C:\xampp\htdocs\php_forum\imports\classes\userclass.php on line 11

Fatal error: Call to a member function connect() on a non-object in C:\xampp\htdocs\php_forum\imports\classes\userclass.php on line 11

Hij herkent op een een of andere manier de $database variabele niet die zich in 'databaseclass.php' bevindt.
 
Pipo Clown

Pipo Clown

04/04/2015 14:37:43
Quote Anchor link
Waar heb je de variabele $database geinitialiseerd ?
Volgens mij heb jedeze niet als databaseclass geinitialiseerd, dan kan je hem ook niet als zodanig gebruiken.
 
Michael R

Michael R

04/04/2015 14:45:37
Quote Anchor link
Ik heb hem toch echt aangemaakt in mijn database class:

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
<?php
    
    class database {
        
        function
connect() {
            
            $mysql_host = "localhost";
            $mysql_user = "root";
            $mysql_password = "";
            $mysql_database = "database";
            
            $db_connection = mysqli_connect($mysql_host,$mysql_user,$mysql_password,$mysql_database);
            
            if($db_connection) {
                return $db_connection;
                exit;
            }
else {
                die("Couldn't connect to the database");
            }
        }
    }

    
    $database = new database();
?>


Is dit trouwens een goede manier om te verbinden met de database of kan het beter?
 
Thomas van den Heuvel

Thomas van den Heuvel

04/04/2015 15:08:00
Quote Anchor link
@moderator(s) wellicht is dit topic beter op zijn plaats in de categorie "Object-Oriented Programming"?

Als je van object A (van klasse A) gebruik wilt maken in object B (van klasse B), dan moet op een of andere manier object A bekend zijn bij object B. Je zou dit bijvoorbeeld kunnen bereiken door bij creatie een referentie van object A aan object B door te geven:
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
<?php
// *** declaratie ***
// NB: namen van classes zouden met een Hoofdletter moeten beginnen

class User
{
    protected $db;

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

    // na creatie van een object van deze klasse kun je in je methoden
    // gebruik maken van je database via $this->db

}

// *** aanroep ***
// creatie database object

$db = new Database(...);

// creatie user object
$user = new User($db);
?>

Quote:
Is dit trouwens een goede manier om te verbinden met de database of kan het beter?

Dit kan (stukken) beter, want deze class heeft op dit moment geen toegevoegde waarde. De parameters waarmee je een verbinding maakt zijn hard gecodeerd, wat de klasse niet herbruikbaar maakt. Daarnaast wordt er geen character encoding geselecteerd bij het maken van de connectie wat een bron van veel ellende is. Ook bevat de klasse geen verdere (shorthand) methoden voor het uitvoeren van queries, het escapen van DATA binnen je SQL, het starten, committen en terugdraaien van transacties, het ophalen van resultaten (waar je een aparte klasse aan zou moeten wijden) of insert-id's en eventueel het bijhouden van statistieken (als je dat leuk vindt).

Het gebruik van klasses zou je (op den duur) werk uit handen moeten nemen en taken eenvoudiger moeten maken. De bovenstaande database-klasse doet dit niet.
Gewijzigd op 04/04/2015 15:46:04 door Thomas van den Heuvel
 
Michael R

Michael R

04/04/2015 16:45:50
Quote Anchor link
Tegen welke problemen loop je dan op als ik het op de oude manier doe?
 
Thomas van den Heuvel

Thomas van den Heuvel

04/04/2015 16:49:39
Quote Anchor link
Wat is "de oude manier"? Een variant waarbij je je code helemaal uitschrijft zonder gebruikmaking van klasses (oftewel "procedureel" in plaats van "object georienteerd")?
 
Michael R

Michael R

04/04/2015 16:50:12
Quote Anchor link
Precies

gewoon met mysqli_connect()
Gewijzigd op 04/04/2015 16:52:45 door Michael R
 
Thomas van den Heuvel

Thomas van den Heuvel

04/04/2015 17:36:36
Quote Anchor link
Daar is in principe niks op tegen als je dat in een los script doet ofzo, maar als je iets vaker (her)gebruikt dan schrijf je dat vast niet elke keer opnieuw uit neem ik aan, maar zet je het een en ander in functies of klasses. Op die manier heb je op den duur een collectie van bouwstenen waarmee je sneller kunt bouwen.

Een bijkomend voordeel daarvan (het gebruik maken van bouwstenen) is dat je dingen ook elke keer op precies dezelfde manier aanpakt. Als je je code elke keer helemaal opnieuw uitschrijft heb je die garantie niet. En als je dan toch elke keer alles knipt en plakt kun je er beter voor zorgen dat je dezelfde (en één) bron gebruikt niet?

En nog een voordeel is is dat je het maar 1x hoeft uit te denken en je kunt het daarna (zonder al teveel na te denken) gebruiken. Dan hoef je er ook maar 1x voor te zorgen dat je een aanroep van mysqli_set_charset() (of de OOP-variant daarvan) niet vergeet :).
 
Michael R

Michael R

04/04/2015 17:38:51
Quote Anchor link
Wat ik altijd doe is dat ik een connectie opzet in een bestand. Die stop ik in een variabele en die gebruik ik dan overal in al mijn codes om queries uit te voeren die ik overigens wel in functies heb staan.
 
NOLot -

NOLot -

04/04/2015 19:06:39
Quote Anchor link
Ja dat deed ik ook ... 10 jaar geleden. Hier staat een goede tutorial over OOP. http://www.phphulp.nl/php/tutorial/overig/oop-beginnershandleiding-php5/701/ Probeer die eerst door te lezen, en lees daarna de reply van Thomas nog eens terug met codevoorbeeld.
 
Michael R

Michael R

06/04/2015 12:21:08
Quote Anchor link
Top, bedankt. Ik zal het even doornemen.
 
- Ariën  -
Beheerder

- Ariën -

06/04/2015 13:14:21
Quote Anchor link
Topic verhuisd naar het juiste forum.
 
Michael R

Michael R

06/04/2015 13:53:48
Quote Anchor link
Maar wat zou ik dan het beste in de Database class kunnen zetten?
 
Thomas van den Heuvel

Thomas van den Heuvel

06/04/2015 18:02:10
Quote Anchor link
De vraag stellen is hem beantwoorden :).

Je zou eerst een (abstracte) Database klasse kunnen maken waarin je definieert wat je wilt kunnen doen met deze klasse, zoals:

- het opbouwen van een connectie (kun je in de __construct() regelen)
hierbij zou je uit kunnen gaan van enkele default waarden voor bepaalde instellingen zoals host, poort en character encoding (waarvan je wel kunt afwijken als je afwijkende instellingen hebt maar standaard hoef je niets extra's hiervoor te doen)
- het uitvoeren van een query (doh)
- het starten, committen en rollbacken van transacties (als je database engine dit ondersteunt)
- het ophalen van het laatst toegevoegde auto-increment id
- het ophalen van het aantal "affected" records na uitvoering van een query

Nu zou je zeggen: "waarom zou ik hiervoor een database klasse moeten maken, deze functies / methoden zijn er al in native PHP".

Deze opzet heeft één belangrijk voordeel: je koppelt het aanspreken van je database los van database-specifieke functies, of liever gezegd, je maakt indirect gebruik van deze database-specifieke functies. Dit is eigenlijk precies wat PDO doet.

Ik ga er een beetje stilzwijgend vanuit dat je gebruik maakt van een MySQL-database, en dat je hiermee wilt communiceren via MySQLi-functies of -methoden die je wilt organiseren in een Database klasse. Dit kan, maar er is dus ook een alternatief: PDO. Beide oplossingen hebben voor- en nadelen. Zo is PDO niet specifiek geshreven voor een specifieke database (en dus ook niet voor MySQL), en als je enkel MySQL-databases gebruikt kun je net zo goed MySQLi gebruiken. Als je PDO gebruikt voor communicatie met je MySQL-database dan zul je je enigszins moeten verdiepen in de werking van de PDO_MySQL driver.

Iets wat beide varianten niet oplossen is een abstractie van de SQL; beide varianten (PDO of je eigen database-toegang-abstractie-laag) bieden een uniforme manier om met je database te communiceren maar de onderliggende SQL die je in beide varianten gebruikt is nog steeds database-specifiek. Als je een nog verdere abstractie wilt zul je een soort van data-abstractie-laag moeten realiseren, bijvoorbeeld door middel van Object Relational Mapping (als ik het goed heb?).

Maar goed, terug naar het eerdere genoemde voordeel.

Stel je hebt deze verouderde code met mysql_query aanroepen:

some_code.php
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
<?php
// do stuff
$res = mysql_query(<stuff>);
while ($row = mysql_fetch_assoc($res)) {
    <
do stuff>
}

// ...
$res = mysql_query(<more stuff>);
while ($row = mysql_fetch_assoc($res)) {
    <
do more stuff>
}

?>


Wat nu als je op een gegeven moment besluit dat je wilt overstappen naar MySQLi. Een hele "eenvoudige" manier zou zijn: doe een search-and-replace door heel je code op alle mysql_ functies.

Maar wat als je deze mysql_ functies nou al in een database-klasse had gevangen? Dan hoef je niet alle code aan te passen in alle verschillende bestanden maar enkel de implementatie van deze klasse te veranderen. Als alle methoden van de klasse goed omschreven zijn zou dit redelijk eenvoudig bewerkstelligd kunnen worden. Dit is veel minder werk en veel minder foutgevoeling omdat je je "definities" maar op één plek aanpast. Het probleem wat je hiermee dus voorkomt is hardcoding van database-specifieke functies overal in je code.

Uiteraard kom je voor hele andere problemen te staan als je naar een andere database overstapt...

Zoals gezegd: nadat je in je abstracte klasse hebt bedacht wat voor functionaliteit je wilt hebben (nog even los van hoe je dit doet dus) schrijf je vervolgens een klasse die hiervan afgeleid is en een database-(driver-)specifieke implementatie geeft van je abstracte klasse. Hier stop je dus het hoe in en geef je precies aan hoe de abstracte methoden afgehandeld zouden moeten worden.

Op eenzelfde wijze zou je ook een abstracte klasse (of interface) kunnen definieren voor een result-set. Hier definieer je welke methoden je verwacht bij implementaties van die klasse (dit is dus in feite ook een specificatie van wat je wilt kunnen doen met een result-set, bijvoorbeeld:
- het ophalen van een resultaatrij
ik gebruik hierbij enkel fetch_assoc, ik ben niet geinteresseerd in andere varianten
- het ophalen van een enkele kolom
als handige shorthand
- het teruggeven van het aantal resultaatrijen
- het "scrollen" naar een specifiek resultaat (dataSeek)
- het vrijgeven van een resultaat

Na de declaraties van je abstracte klasses implementeer je deze bijvoorbeeld met MySQLi, je krijgt dan zoiets:
Database klasse
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
<?php
abstract class Database
{
    protected $connection;
    protected $transactionStarted;

    abstract public function __construct($username, $password, $database, $settings=array());

    abstract public function escape($input);

    abstract public function query($query);

    abstract public function startTransaction();

    abstract public function commitTransaction();

    abstract public function rollbackTransaction();

    abstract public function insertId();

    abstract public function affectedRows();
}

?>


Database Result klasse
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
<?php
interface Database_Result
{
    public function fetchRow();

    public function fetchValue();

    public function numRows();

    public function dataSeek();

    public function freeResult();
}

?>


En de implementaties:
Database MySQLi klasse
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
<?php
class Database_MySQLi extends Database
{
    protected static $queryCount;

    public function __construct($username, $password, $database, $settings=array()) {
        // Default settings are overridden by $settings.
        $settings = $settings + array(
            'host'      => '127.0.0.1',
            'port'      => '3306',
            'charset'   => 'utf8',
        );

        $this->connection = new MySQLi($settings['host'], $username, $password, $database, $settings['port']);
        if ($this->connection->connect_error) {
            // @todo throw exception instead?
            die('[error] '.$this->connection->connect_errno.', '.$this->connection->connect_error);
        }

        if (!$this->connection->set_charset($settings['charset'])) {
            // @todo throw exception instead?
            die('[error] failed to set charset: '.$this->connection->error);
        }

        $this->transactionStarted = false;
        self::$queryCount = 0;
    }


    public function escape($input) {
        return $this->connection->real_escape_string($input);
    }


    public function query($query) {
        if ($this->connection->real_query($query)) {
            self::$queryCount++;
            return new Database_Result_MySQLi($this->connection);
        }
else {
            // Note that the text that the error returns may contain data provided by the user, so it should be escaped.
            throw new Exception($this->connection->error."\n".$query);
        }
    }


    public function startTransaction() {
        if ($this->transactionStarted) {
            throw new Exception('transaction already running');
        }
else {
            $this->connection->autocommit(false);
            $this->transactionStarted = true;
            register_shutdown_function(array($this, 'shutdownCheck'));
        }
    }


    public function shutdownCheck() {
        if ($this->transactionStarted) {
            $this->rollbackTransaction();
        }
    }


    public function commitTransaction() {
        // Commit pending queries.
        $this->connection->commit();

        // The next line both commits queries in queue (the transaction) and turns autocommit back on.
        // Note that with the addition of commit() it no longer commits any pending queries (as there are none).

        $this->connection->autocommit(true);
        $this->transactionStarted = false;
    }


    public function rollbackTransaction($stopTransaction=true) {
        $this->connection->rollback();
        if ($stopTransaction) {
            // Afterwards, turn back on autocommitting.
            // It is up to the user to decide whether (s)he wants to continue after a rollback though...

            $this->connection->autocommit(true);
            $this->transactionStarted = false;
        }
    }


    public function insertId() {
        return $this->connection->insert_id;
    }


    public function affectedRows() {
        return $this->connection->affected_rows;
    }


    public function getQueryCount() {
        return self::$queryCount;
    }
}

?>


Database Result MySQLi klasse
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
<?php
class Database_Result_MySQLi extends MySQLi_Result implements Database_Result
{
    // Returns an associative array.
    public function fetchRow() {
        return $this->fetch_assoc();
    }


    // Returns a single value (first column of current row), for COUNT queries and such.
    // If it is not guaranteed a query has (at least) one result, YOU need to check for this yourself.
    // Here we assume the query does have at least one result.
    // return $this->fetch_row()[0] seems slightly faster, requires PHP 5.4 or later
    // @see https://wiki.php.net/rfc/functionarraydereferencing (FAD)

    public function fetchValue() {
        $row = $this->fetch_row();
        return $row[0];
    }


    public function numRows() {
        return $this->num_rows;
    }


    public function dataSeek($offset=0) {
        return $this->data_seek($offset);
    }


    public function freeResult() {
        $this->free();
    }
}

?>

De toegevoegde waarde is vervolgens ook de hoeveelheid code die je (minder) moet kloppen bij gebruikmaking van deze klasses, de definities zijn korter (en regelen soms meer dingen tegelijkertijd op de achtergrond) dan wanneer je dit elke keer helemaal uitschrijft.

Dit is mijn "wiel", veel plezier met het maken van je eigen wiel.
Gewijzigd op 06/04/2015 18:13:08 door Thomas van den Heuvel
 
Michael R

Michael R

07/04/2015 13:26:35
Quote Anchor link
Bedank voor je zeer uitgebreid antwoord :). Ik probeer hier zo veel mogelijk informatie uit te halen.


Toevoeging op 07/04/2015 13:26:58:

Al begrijp ik nog lang niet alles :P
 



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.