Hoe kan ik mijn connectie class aanroepen binnen andere classes
Dit is mijn connectie script (connection.php):
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
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;
?>
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)
1
2
3
4
5
6
7
8
9
10
11
12
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;
}
}
?>
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}
}
?>
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?
Een connectie maakt geen query, dus ik zou het liever een Database class noemen.
Code (php)
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
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)
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
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]
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
En dan zou ik die die() liever in een exception vervangen ;-)
- Ariën - op 05/04/2018 15:44:19:
En dan zou ik die die() liever in een exception vervangen ;-)
Dat ook ja.
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)
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)
1
2
3
2
3
<?php
parent::__construct($this->host, $this->username, $this->password, $this->db_name);
?>
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.
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.
Dit wordt niet aanbevolen omdat dat geen invloed heeft op escaping-functionaliteit aan de PHP-zijde. Gebruik hiervoor set_charset(). bron.
Ik zou dit expliciet vergelijken met === false. Of wellicht real_query() gebruiken.
Code (php)
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
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.
Hoewel het een controversieel anti pattern is, handel ik de databaseverbinding af via een 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.
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.
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
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.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
?>
$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);
?>
Topic onnodig gekicked vanwege (inmiddels verwijderde) spam. Daarom gesloten. Topicstarter kan eventueel verzoek tot heropening indienen.