[PHP] Netste manier om PHP functies $pdo te laten gebruiken

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Ook al Bezet
  • Registratie: Juli 2004
  • Laatst online: 06-09 19:51
Ik heb wat rondgezocht en ben verchilllende methodes tegengekomen om de verbinding met de database (PDO object) aan php functies door te geven (singletons, etc), zoals wel te verwachten viel lopen de meningen nogal uiteen. Zelf (ben een beginner met PHP/MySql) doe ik het op het moment zo,maar ik vraag me af of het niet beter kan.

Bovenaan het index bestand wordt een PDO object aangemaakt, $pdo, index.php bestand is trouwens het enige adres dat nodig is, andere pagina's worden ingevuld aan de hand van $_GET requests, met includes. Functies die iets met de database moeten doen zien er zo uit.
PHP:
1
2
3
function functienaam($connection, $andere_variabelen){
functiecode;
}

Die zo worden aangeroepen.
PHP:
1
functienaam($pdo, $variabele);


De vraag is dus of dit een acceptabele manier is om functies de database te laten gebruiken en, zo niet, hoe dat volgens de tweakers hier beter zou kunnen. Voordeel van mijn methode is, als ik het goed begrijp, dat er altijd maar één keer verbinding wordt gemaakt met de database, wanneer er een pagina geladen wordt.

Acties:
  • 0 Henk 'm!

  • wackmaniac
  • Registratie: Februari 2004
  • Laatst online: 23-09 08:36
Dat zou kunnen, maar ik zou zelf gewoon een singleton maken voor je database-zaken. Die kan je al dan niet laten erven van de PDO-klasse.

[ Voor 61% gewijzigd door wackmaniac op 19-07-2012 16:32 ]

Read the code, write the code, be the code!


Acties:
  • 0 Henk 'm!

  • HuHu
  • Registratie: Maart 2005
  • Niet online
wackmaniac schreef op donderdag 19 juli 2012 @ 16:31:
Dat zou kunnen, maar ik zou zelf gewoon een singleton maken voor je database-zaken. Die kan je al dan niet laten erven van de PDO-klasse.
Ik zou er absoluut geen singleton van maken.

Acties:
  • 0 Henk 'm!

  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
HuHu schreef op donderdag 19 juli 2012 @ 16:34:
[...]

Ik zou er absoluut geen singleton van maken.
+1

PDO meegeven aan de functie als je hem gebruikt is prima. Als je met objecten gaat werken kun je hem in de constructor meegeven.

Een mogelijk beter alternatief is je functie de database changes te laten returnen en die vervolgens buiten de functie om verwerken, maar dat is afhankelijk van wat je aan het maken bent. Voordeel hiervan is dat je de changes ook ergens anders op kan slaan als dat moet, of juist helemaal niet opslaan omdat je alleen wilt testen wat het resultaat is.

Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
Ook: Op zich is voor PHP het Service Locator pattern niet zo erg; je hebt een globaal toegangkelijk object (zij het een object als in OOP of, ik noem maar wat, een array), en je functionaliteit die het nodig heeft haalt daar de service (in dit geval $pdo) uit wanneer nodig. Het is eigenlijk een naam geven aan een global singleton, maar oké.

Acties:
  • 0 Henk 'm!

  • alex3305
  • Registratie: Januari 2004
  • Laatst online: 21:51
Ik doe het zelf ook meestal met een singleton. Misschien niet in alle situaties de meest ideale oplossing maar vooral voor kleinere websites werkt het snel en perfect.

Acties:
  • 0 Henk 'm!

Verwijderd

HuHu schreef op donderdag 19 juli 2012 @ 16:34:
[...]

Ik zou er absoluut geen singleton van maken.
Waarom geen singleton die de pdo instance teruggeeft?

Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 22-09 14:14

Matis

Rubber Rocket

Verwijderd schreef op donderdag 19 juli 2012 @ 21:47:
Waarom geen singleton die de pdo instance teruggeeft?
Precies. Zo doe ik dat ook, zeker voor klassen die met processen "buiten" zijn eigen applicatie communiceert, wil ik dit graag via een singleton doen.

Desnoods maak je een wrapper klasse om PDO heen die je dan via een instance/singleton teruggeeft.

[ Voor 13% gewijzigd door Matis op 20-07-2012 00:32 ]

If money talks then I'm a mime
If time is money then I'm out of time


Acties:
  • 0 Henk 'm!

  • Kajel
  • Registratie: Oktober 2004
  • Laatst online: 23-09 09:07

Kajel

Development in Style

Verwijderd schreef op donderdag 19 juli 2012 @ 21:47:
[...]

Waarom geen singleton die de pdo instance teruggeeft?
De reden waarom ik dit zelf nooit zo'n goed idee vindt: Singleton pattern is bedoeld om te voorkomen dat je een "duur" object meerdere keren instantieert, terwijl je het eigenlijk maar 1x nodig hebt, en verder kunt hergebruiken. PHP is inherent stateless tussen requests, dus bij elk nieuw request wordt die Singleton toch opnieuw geinstantieerd. Natuurlijk kan het zo zijn dat je een hoop db actie onderneemt binnen die ene request, en je dan gebruik kunt maken van slechts 1 connectie (via de Singleton), maar in de praktijk van overwegend standaard CRUD operaties, doe je vaak 1 query/update/insert etc per request, dus heeft die Singelton helemaal geen zin, maar maakt je applicatie wel weer ff net dat stukje minder makkelijk te debuggen.

Acties:
  • 0 Henk 'm!

  • Marientjuh
  • Registratie: Oktober 2004
  • Laatst online: 23-09 11:43

Marientjuh

Fullstack developer

Tenzij je de boel echt abstract gaat maken. Zo heb ik in mijn (grote) applicatie een 'query' laag welke op basis van basale parameters de database queried. Deze 'query' laag word door mijn 'data' laag aangeroepen waar de verschillende tabellen staan gedefinieerd. En de data laag word naar gelang van toepassing nog abstracter gemaak of direct aangeroepen. In dit hele verhaal roept mijn query laag via een singleton de database connectie op. In deze singleton kunnen meerdere connecties zijn welke aangepast worden op de server&database&credentials parameters. Hier gaat het wel over een grote applicatie ;-) Maar gebeurt wel via 'singleton' ;-) In de korte levensduur van een phpscript kan je wel degelijk singleton hebben :)

Respect begint waar eigen kunnen ophoudt! - Kinderkleding webshop van vrouwlief: coz-adore.nl


Acties:
  • 0 Henk 'm!

  • Kajel
  • Registratie: Oktober 2004
  • Laatst online: 23-09 09:07

Kajel

Development in Style

Je weerlegt mijn punt niet :) Ik zeg niet dat er tijdens de request geen Singleton kan bestaan, dat kan zeker. Ik zeg alleen dat er weinig performance voordelen zitten aan het gebruiken van een Singleton voor database connecties in PHP. In jouw geval is het eigenlijk vooral een kwestie van convenience/het goed kunnen houden van overzicht op die manier. Je kunt zo gevoelsmatig je database connectie vanuit een centraal punt beheren, en dat is prima. Maar daarmee heb je dus niet de rest van de voordelen van het Singleton pattern.

edit: Ik begrijp overigens niet de behoefte van tweakers om in dit soort discussies altijd te gaan vermelden hoe groot en complex hun applicaties wel zijn, en uit wat voor een briljante gelaagde architectuur ze bestaan. Soms maakt dat verschil, maar vaak leidt dat alleen maar af van de essentie van de discussie :)

[ Voor 23% gewijzigd door Kajel op 20-07-2012 23:32 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Ik gebruik zelf een simpele class met een statische verbinding om over mn hele project dezelfde verbinding te kunnen gebruiken.

Zie hier:
http://simple_pdo_implementation.onlinephpfunctions.com

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
 <?php
class sql
{
    public static $db = false;
    private $database_host = '127.0.0.1';
    private $database_user = 'username';
    private $database_pass = 'verySecretPassWord';
    private $database_db = 'database';

    function __construct()
    {
        if (self::$db === false) {
            $this->connect();
        }
        return self::$db;
    }

    private function connect()
    {
        $dsn = $this->database_type . ":dbname=" . $this->database_db . ";host=" . $this->database_host;
        try {
            self::$db = new PDO($dsn, $this->database_user, $this->database_pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''));
            self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            //Comment this out on a production environment
            //print_r($e->errorInfo);
            //echo 'Connection failed: ' . $e->getMessage();
        }
    }

}

Acties:
  • 0 Henk 'm!

  • YopY
  • Registratie: September 2003
  • Laatst online: 13-07 01:14
Mja, een paar opmerkingen daarover (miereneuk modus engage):

* Class SQL is geen goeie naam; het "is" geen SQL, een SQL is geen ding. ipv daarvan is het een PDODatabaseConnection of een PDODatabaseConnectionContainer of PDODatabaseConnectionUtility class oid. Een instantie van een class moet iets representeren.
* Het is niet self-containing; je kunt, van buitenaf, $sql->db = "henkie!"; doen en zo je hele applicatie stuq maken. $db moet op z'n minst final zijn.
* Rare / incorrecte implementatie van een singleton
* De constructor heeft een return value; constructors behoren geen waardes te returnen.
* Geen manier om configuratie te injecten; op z'n minst zou ik verwachten dat je DB connection parameters kunt doorgeven aan de constructor, zoals je dat ook kunt doen in de PDO constructor.
* Geen error handling.
* Foute opmerking dat je een regel moet uitcommentariëren in een productieomgeving; slecht onderhoudbaar
* Echo-statement om een foutmelding weer te geven (bad practice om 'rauwe' foutmeldingen naar de voorkant te sturen)
* Weinig toegevoegde waarde, veel 'boilerplate'.

Acties:
  • 0 Henk 'm!

  • Down
  • Registratie: Februari 2005
  • Laatst online: 22:38
YopY schreef op donderdag 19 juli 2012 @ 21:44:
Ook: Op zich is voor PHP het Service Locator pattern niet zo erg; je hebt een globaal toegangkelijk object (zij het een object als in OOP of, ik noem maar wat, een array), en je functionaliteit die het nodig heeft haalt daar de service (in dit geval $pdo) uit wanneer nodig. Het is eigenlijk een naam geven aan een global singleton, maar oké.
Ik vind Service Locator één van de lelijkste patterns (in tegenstelling tot Dependency Injection). Dat zit hem in de aard van de singleton. Persoonlijk ben ik dan ook erg gecharmeerd van Dependency Injection. Met het meegeven van het PDO-object aan de classes die het gebruiken maak je meteen de afhankelijkheden duidelijk (en hopelijk verplicht). Dit kan uiteraard met de hand, maar ik gebruik er het liefst een IoC container voor. Nu ben ik zelf niet thuis in PHP, maar b.v. voor .NET kun je veel IoC-containers zo configureren dat je dependencies als singleton laat meegeven. Op die manier heb je wel bereikt dat objecten één instantie hebben, maar niet een globale var zijn.

Mother north, how can they sleep while their beds are burning?


Acties:
  • 0 Henk 'm!

  • Barryvdh
  • Registratie: Juni 2003
  • Laatst online: 18:03
Ik gebruik voor kleine projecten vaak het volgende:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
class PDOClass {
    public static $db;
    public function createConnection($host, $database, $username, $password) {
        try {
            // Create a new PDO connection
            self::$db= new PDO("mysql:host=$host;dbname=$database", $username, $password);
            self::$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            //Output error - would normally log this to error file rather than output to user.
            die("Connection Error: " . $e->getMessage());
        }
    }
}

En dan in het begin dus ergens
PHP:
1
PDOClass::createConnection($host, $database, $username, $password);

En alle classes die gebruik willen maken van die verbinding:
PHP:
1
class Menu extends PDOClass {..}

En dan kan je dus self::$db gebruiken, of een aantal simpele altijd terugkerende handelingen in die class zetten ook, zoals het tellen/ophalen/verwijderen ed. van rijen.
(Werkt bij mij bij kleine projecten iig prima, hoef ik ook niet overal steeds de verbinding mee te geven)

Acties:
  • 0 Henk 'm!

  • Down
  • Registratie: Februari 2005
  • Laatst online: 22:38
De meest slechte ontwerpen zullen vast prima werken bij kleine projecten, maar dat lijkt me nauwelijks een overtuigend argument.
Barryvdh schreef op zaterdag 11 augustus 2012 @ 13:34:

PHP:
1
class Menu extends PDOClass {..}
Je menu erft over van een class met data access.. :X

Mother north, how can they sleep while their beds are burning?


Acties:
  • 0 Henk 'm!

  • ReenL
  • Registratie: Augustus 2010
  • Laatst online: 14-09-2022
Nu ben ik zelf niet thuis in PHP, maar b.v. voor .NET kun je veel IoC-containers zo configureren dat je dependencies als singleton laat meegeven.
Simpelste DI Container voor PHP is toch wel Pimple, hiermee kan dit ook. Komt van de maker van Symfony.
http://pimple.sensiolabs.org/

Acties:
  • 0 Henk 'm!

  • ID-College
  • Registratie: November 2003
  • Laatst online: 21:28
Barryvdh schreef op zaterdag 11 augustus 2012 @ 13:34:
En alle classes die gebruik willen maken van die verbinding:
PHP:
1
class Menu extends PDOClass {..}
Eeeh wat? Elke class die een DB connectie heeft extend je met PDOClass?
Ten eerste is dit compleet fout en ten tweede vraag ik mij af waarom jij dit doet?

Waarom zou je PDOClass uberhaupt extenden als je gewoon PDOClass::createConnection() kan doen als je PDOClass hebt ge-include?

[ Voor 3% gewijzigd door ID-College op 12-08-2012 00:19 ]


Acties:
  • 0 Henk 'm!

  • Barryvdh
  • Registratie: Juni 2003
  • Laatst online: 18:03
ID-College schreef op zondag 12 augustus 2012 @ 00:18:
[...]
Eeeh wat? Elke class die een DB connectie heeft extend je met PDOClass?
Ten eerste is dit compleet fout en ten tweede vraag ik mij af waarom jij dit doet?
Nouja, ik doe het niet perse omdat elke class dan een DB verbinding krijgt, maar omdat ik op deze manier omdat een aantal classes heb die steeds hetzelfde doen, dus met de PDO verbinding een paar rijen ophalen op basis van een id of taal/categorie, of het instellen van een taal/categorie, die voor elke class waar ik dat mee wil doen hetzelfde zijn. Dus ik wil niet alleen de verbinding overerven, maar juist ook de functies (die de verbinding gebruiken)
Dit leek mij toen op basis van wat voorbeelden het makkelijkste werken, maar misschien moet ik er nog eens goed naar kijken dan. (Komt volgens mij van het bovenste voorbeeld op http://www.php.net/manual/en/class.pdo.php)

[ Voor 5% gewijzigd door Barryvdh op 12-08-2012 23:51 ]


Acties:
  • 0 Henk 'm!

  • kokx
  • Registratie: Augustus 2006
  • Laatst online: 13-09 20:30

kokx

WIN

Down schreef op zaterdag 11 augustus 2012 @ 12:24:
[...]


Ik vind Service Locator één van de lelijkste patterns (in tegenstelling tot Dependency Injection). Dat zit hem in de aard van de singleton. Persoonlijk ben ik dan ook erg gecharmeerd van Dependency Injection. Met het meegeven van het PDO-object aan de classes die het gebruiken maak je meteen de afhankelijkheden duidelijk (en hopelijk verplicht). Dit kan uiteraard met de hand, maar ik gebruik er het liefst een IoC container voor. Nu ben ik zelf niet thuis in PHP, maar b.v. voor .NET kun je veel IoC-containers zo configureren dat je dependencies als singleton laat meegeven. Op die manier heb je wel bereikt dat objecten één instantie hebben, maar niet een globale var zijn.
Dependency Injection in PHP is een beetje overkill. Een echte DI implementatie vraagt veel te veel van je applicatie qua performance, aangezien PHP stateless is. En een Service Locator is misschien wel niet zo mooi, maar het werkt een stuk sneller.

Anyways, ontopic:

Ik zou gewoon een connectie parameter in je functie hebben. Dat maakt je functie een heel stuk generieker. Anders hangt je functie al snel af van andere dingen die in je dan perse in je applicatie moet hebben.

Acties:
  • 0 Henk 'm!

  • alienfruit
  • Registratie: Maart 2003
  • Laatst online: 03:15

alienfruit

the alien you never expected

En ik maar denken dat een 'echte' DI implementatie gewoon enkele parameters in de constructor van de klasse is ;) new PagesCollection(DataSource $connection)

[ Voor 6% gewijzigd door alienfruit op 13-08-2012 22:22 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Wat ik zelf altijd doe is een algemene PersistentStorage klasse die de verbinding managed en bovendien als Singleton benaderbaar. Vervolgens worden de queries in losse repository klassen ingedeeld. De repository klasse:
(in het voorbeeld enkel getRepository() geimplementeerd, rest spreekt voor zichzelf)
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
class PersistentStorage
{
    // Onmogelijk te instantieren zonder getInstance!
    private function __construct()
    
    // Verbinden en private pdo object bij houden
    public function connect($dsn, $username, $password)

    // Standaard pad voor repositories zetten, versimpelt het laden
    public function setRepositoryPath($path)

    // Returned het pdo object, word eigenlijk enkel bij instantiatie van de 
    // repository gebruikt
    public function getPdo()
    
    // Returned een instance van de genaamde repository
    public function getRepository($name)
    {
        if(array_key_exists($name, $this->_repositories))
            return $this->_repositories[$name];
            
        $classname = $name . $this->_classnameSuffix;
        if(!class_exists($classname))
        {
            $path = $this->_repositoryPath . DIRECTORY_SEPARATOR . $classname . ".php";
            require_once($path);            
        }
            
        $this->_repositories[$name] = new $classname($this);
        return $this->_repositories[$name];
    }
    
    // Returned de enige instance van deze class (singleton!)
    public static function getInstance()
}

Vervolgens heb ik dus voor elke table (of eigenlijk entiteit, maar de indeling is helemaal aan jezelf) een aparte class die alle queries bevat met functies die de queries uitvoert met de juiste parameters. Hier kun je ook conditionals maken, bijvoorbeeld het updaten/inserten van een record.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class UserRepository
{
    public function __construct($storage)
    {
        $this->_storage = $storage;
        $this->_pdo = $storage->getPdo();
    }

    // voorbeeld van een functie die de sql implementeert
    public function fetchUser($userId)
    {
        $stmt = $this->_pdo->prepare(self::FETCH_USER);
        $stmt->bindParam(':id', $userId);
        $stmt->execute();
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    // de daadwerkelijke query
    const FETCH_USER = 'SELECT id, username
FROM users
WHERE id = :id';
}


Voordelen:
• Je database code volledig gescheiden van je actieve code
• Al je SQL boilerplate (parameters binden enzo netjes weggewerkt van je andere code)
• Maar 1 globale klasse die je in de rest van je code gebruikt
• Caching van repositories zodat ze maximaal maar één keer geladen worden
• Geschikte plek voor je db logica (niet je bussiness logica!)
• Makkelijk om je queries te catagoriseren in verschillende repo's

Nadelen:
• Globale Singleton genaamd 'PersistentStorage' (nog steeds semantisch correctere naam dan de meeste database singletons ;))
• Je schrijft al je SQL alsnog gewoon volledig uit itt een 'echte' ORM
• Wat doe je met errors? Exceptions, logs of enkel een boolean return?

Voorbeeld van gebruik:
PHP:
1
2
3
4
5
6
7
8
// bootstrap.php
$storage = PersistentStorage::getInstance();
$storage->connect("mysql://localhost", "bla", "bla");
$storage->setRepositoryPath(ROOT . "/db");

// waar je de sql wilt uitvoeren
$repo = PeristentStorage::getInstance()->getRepository("User");
$user = $repo->fetchUser(1);


In jouw voorbeeld zou de Menu klasse dus wel van én PersistentStorage moeten weten én van wat voor repo je ook maar implementeert, maar desnoods kun je het nog zo maken dat je een Menu repo meeleverd en die aan je Menu meegeeft (dat je voor de Menu instantatie eerst de repo opvraagt)

[ Voor 4% gewijzigd door Verwijderd op 13-08-2012 22:24 ]

Pagina: 1