Hoe kan ik mijn connectie class aanroepen binnen andere classes

Overzicht

Sponsored by: Vacatures door Monsterboard

Ventilatiesysteem Productontwikkelaar HBO WO Verwa

Samengevat: Zij bieden flexibele ventilatiematerialen, geluidsdempers, rookgasafvoer producten en industrieslangen. Ben jij een technisch productontwikkelaar? Heb jij ervaring met het ontwikkelen van nieuwe producten? Vaste baan: Technisch Productontwikkelaar HBO WO €3.000 - €4.000 Zij bieden een variëteit aan flexibele ventilatiematerialen, geluiddempers, rookgasafvoer producten, industrieslangen en ventilatieslangen voor de scheepsbouw. Met slimme en innovatieve materialen zorgen wij voor een gezonde en frisse leefomgeving. Deze werkgever is een organisatie die volop in ontwikkeling is met hardwerkende collega's. Dit geeft goede ontwikkelingsmogelijkheden. De branche van dit bedrijf is Techniek en Engineering. Functie: Voor de vacature als Technisch Productontwikkelaar Ede Gld HBO WO ga

Bekijk vacature »

Snelle Jaap

Snelle Jaap

05/04/2018 12:26:09
Anchor link
Ik heb een connectie class die prima werkt, nu wil een beetje OOP gaan leren en een menu script bouwen.

Dit is mijn connectie script (connection.php):
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
<?PHP
session_start();

class Connection {
    // Configure Database Vars
    private $host     = 'localhost';
    private $username = 'user';
    private $password = 'pass!';
    private $db_name  = 'dbname';
    public $db;

    function
__construct() {
        // Create connection
        $db = new mysqli($this->host, $this->username, $this->password, $this->db_name);
        // Check connection
        if ($db->connect_errno > 0) {
            die('Unable to connect to the database: '.$db->connect_error);
        }

        $this->db = $db;
    }


    public function query($query) {
        $db = $this->db;
        $this->db->query('SET NAMES utf8');
        if (!$result = $this->db->query($query)) {
            die('There was an error running the query ['.$db->error.']');
        }
else {
            return $result;
        }
    }


    public function multi_query($query) {
        $db = $this->db;
        if (!$result = $this->db->multi_query($query)) {
            die('There was an error running the multi query ['.$db->error.']');
        }
else {
            return $result;
        }
    }


    public function real_escape_string($value) {
        return $this->db->real_escape_string($value);
    }


    public function inserted_id() {
        return $this->db->insert_id;
    }
}


$conn = new Connection;
?>


En dit is mijn poging tot een menu script (menuClass.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
require_once('includes/connection.php');
class Menu extends Connection
{
  public function selectCats()
  {

    $cats = "SELECT * FROM snm_categories WHERE published = '1'";
    $catscon = $this->db->query($cats);
    return $cats;
  }
}

?>


Dit geeft geen errors, alleen ik hoorde van iemand dat het slecht is om de connectie class te extenden omdat een menu iets heel anders is dan een connectie.

Mijn vraag is dus, hoe kan ik de functie query() gebruiken binnen mijn menu class?

Ik heb het volgende al geprobeerd:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
require_once('includes/connection.php');
$menucon = new Connection;
class Menu
{
  public function selectCats()
  {

    $cats = "SELECT * FROM snm_categories WHERE published = '1'";
    $catscon = $menucon->db->query($cats);
    return $cats;
  }
}

?>


Maar dit geeft een fatal error: Call to a member function query() on null

Wat doe ik fout?
 
PHP hulp

PHP hulp

28/03/2024 21:28:38
 
- Ariën  -
Beheerder

- Ariën -

05/04/2018 12:47:39
Anchor link
Een connectie maakt geen query, dus ik zou het liever een Database class noemen.
 
Ben van Velzen

Ben van Velzen

05/04/2018 12:47:40
Anchor link
Geef je connection mee aan de class, of aan de methode. Als je het aan de class meegeeft kun je het zo doen:
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
<?php
class Menu
{
  private Connection $db;

  public function __construct(Connection $db)
  {

    $this->db = $db;
  }


  public function selectCats()
  {

    $cats = "SELECT * FROM snm_categories WHERE published = '1'";
    $catscon = $this->db->query($cats);
    return $cats;
  }
}

?>


Je selectCats methode is een beetje vreemd (lees: zal niet doen wat je verwacht) maar dat had je zelf ook al gezien.
NB: Het is niet nodig om voor iedere queries SET NAMES uit te voeren. Dat kun je doen vlak nadat je verbinding hebt gemaakt. Ook is het raadzaam om hiervoor de ingebouwde methodes te gebruiken, omdat niet alleen aan de database side instellingen gedaan moeten worden, maar ook aan de client side.
Gewijzigd op 05/04/2018 12:58:18 door Ben van Velzen
 
Ivo P

Ivo P

05/04/2018 15:26:32
Anchor link
Je class doet nu niet heel veel meer dan een verbinding maken met de database en hij stelt een 3- of 4-tal functies beschikbaar die ook al deel uitmaken van mysqli. Enige is, dat er een uitbeiding bij zit met het opvangen van fouten.

Stel dat je gebruik wilt maken van een andere feature van mysqli die nog niet in jouw class zit, dan moet je die weer uitbreiden met een doorgeefluik-functie.

Ik denk dat je beter af bent, als je mysqli extend.

Functies die je iets extra's wilt laten doen, zoals het weergeven van een foutmelding, kun je gewoon in jouw class zetten.

Ik denk trouwens dat ook de connectiegegevens niet in je class thuis horen.
Stel dat je voor je applicatie gebruik maakt van 2 databases. Dan zou jij 2 dezelfde classes nodig hebben, met als enig verschil de username password etc.

Je kunt dat beter in een config bestand zetten en in je scripts die gegevens meegeven bij het aanroepen van je class.

Mijn voorstel: (dit zou effectief precies hetzelfde moeten doen als jouw class, maar zou alle features van mysqli meteen beschikbaar stellen)
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
<?php
class database extends mysqli {
    // Configure Database Vars
    private $host     = 'localhost';
    private $username = 'user';
    private $password = 'pass!';
    private $db_name  = 'dbname';

    function
__construct($host=null, $username=null, $password=null, $db_name=null) {
        // Create connection
        $this->host = $host ?: $this->host;
        $this->username = $username ?: $this->username;
        $this->password = $password ?: $this->password;
        $this->db_name = $db_name ?: $this->db_name;
            
        parent::__construct($this->host, $this->username, $this->password, $this->db_name);
        
        // Check connection
        if ($this->connect_errno > 0) {
            die('Unable to connect to the database: '.$this->connect_error);
        }

        
        $this->query('SET NAMES utf8');

    }


    public function query($query) {
        
        if (!$result = $this->query($query)) {
            die('There was an error running the query ['.$this->error.']');
        }
else {
            return $result;
        }
    }


    public function multi_query($query) {

        if (!$result = $this->multi_query($query)) {
            die('There was an error running the multi query ['.$this->error.']');
        }
else {
            return $result;
        }
    }

}
[
/code]
Gewijzigd op 05/04/2018 15:27:31 door Ivo P
 
- Ariën  -
Beheerder

- Ariën -

05/04/2018 15:44:19
Anchor link
En dan zou ik die die() liever in een exception vervangen ;-)
 
Ivo P

Ivo P

05/04/2018 16:31:56
Anchor link
- Ariën - op 05/04/2018 15:44:19:
En dan zou ik die die() liever in een exception vervangen ;-)


Dat ook ja.
 
Thomas van den Heuvel

Thomas van den Heuvel

06/04/2018 00:59:44
Anchor link
Ay caramba...

Allereerst. Het definiëren van een klasse doe je doorgaans in een apart bestand, dat is goed. Maar het starten van een sessie en het instantiëren van een object van die klasse horen daar echt niet in thuis. Als je dan toch met OOP aan de slag gaat probeer je dan in te lezen in autoloaders, dan kunnen includes en requires ook meteen overboord.

Dat gezegd hebbende, laten we eens gaan kijken naar de code van @Ivo.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
// Configure Database Vars
private $host     = 'localhost';
private $username = 'user';
private $password = 'pass!';
private $db_name  = 'dbname';
?>

Nergens in de code worden deze waarden verder gebruikt. In de constructor worden deze als parameter meegegeven. Waarom zou je deze informatie dan nog als klasse-variabelen op willen slaan. Ik zou zeggen: verwijderen maar, dit is op dit moment een loze kopieeractie. Ook is het gebruik van "private" discutabel. Het hele doel van OOP is mede herbruikbaarheid en uitbreidbaarheid. Het gebruik van "private" belemmert dit. Als je dan toch willens en wetens deze variabelen wilt hebben (Joost mag weten waarom), maak ze dan protected.

En als je dan toch met een wrapper bezig bent. Zou je misschien nog meer informatie door kunnen pasen / of expliciete initiële waarden kunnen optekenen. Zoals een database-poort, een character encoding et cetera. Dan wordt het misschien wel lastig om te extenden van MySQLi, maar goed.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
parent::__construct($this->host, $this->username, $this->password, $this->db_name);
?>

Het nadeel hiervan is, als je dit kunt beschouwen als een nadeel, dat je vanaf dit moment de instantie van deze class kunt gebruiken als een MySQLi-object. Niets dwingt je om de database-communicatie te laten verlopen via de wrapper. Misschien is dat niet zo verstandig. Alternatief: een protected parameter "connection" die het mysqli-object bevat, die alleen toegankelijk is voor de wrapper (of nazaten) zodat je via de wrapper moet werken. Deze bepaalt dan tot welke functionaliteit je de beschikking hebt.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
if ($this->connect_errno > 0) {
    // something wrong
}
?>

Mogelijk beter: controleer of $this->connection->connect_error verschilt van NULL, $this->connection->connect_errno vertelt je dan vervolgens wat er mis is. Maar dit is wellicht persoonlijke voorkeur. EDIT: beide worden genoemd als controle (bron):
Quote:
OO syntax only: If a connection fails an object is still returned. To check if the connection failed then use either the mysqli_connect_error() function or the mysqli->connect_error property as in the preceding examples.


Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
$this
->query('SET NAMES utf8');
?>

Dit wordt niet aanbevolen omdat dat geen invloed heeft op escaping-functionaliteit aan de PHP-zijde. Gebruik hiervoor set_charset(). bron.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
if (!$result = $this->query($query)) {
    // something wrong
}
?>

Ik zou dit expliciet vergelijken met === false. Of wellicht real_query() gebruiken.

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
public function multi_query($query) {
    if (!$result = $this->multi_query($query)) {
        // etc
    }
}

?>

Ik heb elders al eens opgetekend dat hier een heleboel haken en ogen aan zitten in het gebruik. Allereerst is het een ramp om te beveiligen (hoe geef je parameters door?). Daarnaast: als het de bedoeling is dat je een batch queries uitvoert dan is het meestal de bedoeling dat deze in het geheel worden doorgevoerd, of in het geheel niet, hier bestaat al iets voor: transacties. Persoonlijk zou ik multi_query() vermijden als de pest.

Tot slot: een wrapper moet je ook werk uit handen nemen en dus in zekere zin een toegevoegde waarde in het gebruik hebben. Met bovenstaande code schiet je niet zoveel op.

Kijk anders eens voor inspiratie in het volgende draadje (hier zijn de eerdere opmerkingen over exceptions nog steeds van toepassing). Ik zeg overigens niet dat het bovenstaande niet prima kan voldoen aan wat je er mee probeert te doen, het lijkt mij alleen verdomd belangrijk dat je uiteindelijk iets creëert wat goed in elkaar zit, veilig werkt, of in ieder geval de potentie heeft om dat te kunnen doen, en makkelijk werkt. En hier mag best wat tijd in gestoken worden dus.

Alternatieven: kijk ook eens naar MySQLi (i.c.m. prepared statements) en/of naar PDO. Uiteindelijk heb ik gekozen voor een eigen wrapper met manuele escaping omdat dat gewoon fijner werkt (maar daarbij heb je dus zelf ook wat meer eigen verantwoordelijkheid).

EDIT: om antwoord te geven op je oorspronkelijke vraag: als je de beschikking wilt hebben over je Database class in een ander object dan moet je die (de instantie dus - het database-object) op een of andere manier ophalen of doorgeven bij creatie.
Gewijzigd op 06/04/2018 15:48:02 door Thomas van den Heuvel
 
Ward van der Put
Moderator

Ward van der Put

06/04/2018 09:51:29
Anchor link
Hoewel het een controversieel anti pattern is, handel ik de databaseverbinding af via een Singleton. Bij het testen van webwinkelsoftware viel me namelijk op dat daarin vaak meerdere instanties van databaseverbindingen rondzwerven. Vooral als je veel plug-ins, extensions en dergelijke installeert, loopt het aantal geopende databaseverbindingen al gauw op tot ergens tussen de 10 en 20.

Dat trekt een dubbele wissel op de performance: het openen van elke databaseverbinding vreet tijd en elke geopende verbinding is niet beschikbaar voor een andere instantie van je applicatie. Praktisch gevolg daarvan is dat je zelfs bij circa tien gelijktijdige bezoekers al hinderlijk trage webpagina's krijgt.

Om dit te voorkomen heb je naast het object voor de databaseverbinding zelf dus een object nodig dat controleert of er een geopende verbinding is, een bestaande verbinding hergebruikt en alleen een nieuwe verbinding opent als er nog geen verbinding is.
 
Snelle Jaap

Snelle Jaap

06/04/2018 11:30:20
Anchor link
Bedankt voor de reacties. Een hoop taal waar ik nog niks van begrijp (ik begin net met OOP leren) maar ik zal sowieso de tips voor de connectie class alvast doorvoeren.
 
Thomas van den Heuvel

Thomas van den Heuvel

06/04/2018 15:32:54
Anchor link
@Ward, maar dit is tegenwoordig toch al (onder de motorkap) geoptimaliseerd? Op het moment dat je een nieuwe connectie wilt maken met dezelfde connectie-parameters (dus in wezen eenzelfde DSN), dan wordt de bestaande connectie gebruikt (bron moet ik je even schuldig blijven, maar heb dit volgens mij op verschillende plaatsen gezien in documentatie, zowel in de (oorspronkelijke?) MySQL-driver alsmede bij PDO). EDIT: of was dit alleen bij persistente connecties, nu weet ik het niet meer zeker :p

Toegegeven, het maken van een connectie is een dure operatie, maar wat volgens mij vaker voor problemen zorgt zijn trage queries / het niet (tussentijds) vrijgeven van resultaten.
Gewijzigd op 06/04/2018 15:43:44 door Thomas van den Heuvel
 
Ward van der Put
Moderator

Ward van der Put

06/04/2018 15:55:49
Anchor link
Dit levert me drie extra verbindingen op in de server status monitor:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
$dbh_first
  = new \PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
$dbh_second = new \PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
$dbh_third  = new \PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
?>
 
Thomas van den Heuvel

Thomas van den Heuvel

06/04/2018 16:08:14
Anchor link
Dan was het toch persistente connecties waarschijnlijk. Maar meerdere connecties kun je voorkomen door op één vaste plek een connectie te maken, en vervolgens het object door te geven als parameter (dependency injection).

Als je nooit een tweede connectie op eenzelfde database nodig hebt (dit kan soms wel handig zijn) dan zou je het Singleton pattern kunnen gebruiken. Maar anders zet je jezelf buiten spel door gebruikmaking hiervan.
 
Ward van der Put
Moderator

Ward van der Put

06/04/2018 16:13:25
Anchor link
Ik heb de testopzet nog even uitgebreid. Dit geeft inderdaad drie aparte connecties met een continu oplopende ID:

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
<?php
$dbh_one
= new \PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
$dbh_two = new \PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');
$dbh_tri = new \PDO('mysql:dbname=test;host=127.0.0.1', 'root', '');

$stmt_one = $dbh_one->prepare('SELECT CONNECTION_ID()');
$stmt_two = $dbh_two->prepare('SELECT CONNECTION_ID()');
$stmt_tri = $dbh_tri->prepare('SELECT CONNECTION_ID()');

$stmt_one->execute();
$stmt_two->execute();
$stmt_tri->execute();

$row_one = $stmt_one->fetch();
$row_two = $stmt_two->fetch();
$row_tri = $stmt_tri->fetch();

echo '<pre>';
var_dump($row_one, $row_two, $row_tri);
?>
 
- Ariën  -
Beheerder

- Ariën -

31/01/2019 07:44:16
Anchor link
Topic onnodig gekicked vanwege (inmiddels verwijderde) spam. Daarom gesloten. Topicstarter kan eventueel verzoek tot heropening indienen.
 
 

Dit topic is gesloten.



Overzicht

 
 

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.