SQL en OOP
Ik ben om te leren OOP te programmeren met php een forum aan het maken.
Wat is nu de beste methode om je objecten te vullen met de data uit de database? Moet je een constructor (of aparte functie) maken met daarin de SQL om je hele object te van zijn data te voorzien door bijvoorbeeld een id mee te geven? Of is het de bedoeling dat je buiten het object een query uitvoert en d.m.v. je setters de data in je object pompt?
Na lang denken en wikken en wegen ben ik hier nog steeds uit.
Groeten,
Jens V
indien je sql in een class opneemt dan ben je dus afhankelijk van .... en dat is nu net niet de bedoeling.
Ik ben gewend programma's te schrijven in C++ en Java, en dat allemaal OOP. Het voordeel daar is dat je objecten over het hele programma blijven bestaan, tenzij je de destruct. Hoe moet je dat oplossen in php?
Jens
- Active record. Je Domain Object (User) extend een bepaalde klasse. Je vindt dit oa in Doctrine 1.
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
$user = User::find($id);
$user->setEmail('bla');
$group = Group::find($id);
$user->setGroup($group);
$user->save();
?>
$user = User::find($id);
$user->setEmail('bla');
$group = Group::find($id);
$user->setGroup($group);
$user->save();
?>
Dit heeft het nadeel dat je Domain Objects afhankelijk worden van je Active Record mechanisme. Mooier is methode twee:
- Data mapper. Een andere klasse regelt de 'persistence' (opslag) van je objecten. Je vindt oa in Doctrine 2. De andere klasse heet daar de Entity Manager.
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?
$user = $em->find('User', $id);
$user->setEmail('bla');
$group = $em->find('Group', $id);
$user->setGroup($group);
$em->persist($user); // Stel het object in als 'persistent'
$em->flush(); // Doe alle wijzigingen in 1x
?>
$user = $em->find('User', $id);
$user->setEmail('bla');
$group = $em->find('Group', $id);
$user->setGroup($group);
$em->persist($user); // Stel het object in als 'persistent'
$em->flush(); // Doe alle wijzigingen in 1x
?>
Handmatig kan natuurlijk ook:
Code (php)
Maar dit kost veel werk en associaties zijn lastig beheersbaar.
Gewijzigd op 09/01/2011 21:33:15 door Pim -
Wat me ook nog steeds niet duidelijk is, is de algemene structuur van een opgeroepen pagina. Stel, ik roep de pagina op die gewoon het overzicht van forums ('categorien') laat zien. Hoe ziet dat er dan uit?
Ik heb als 'model' gewoon een paar klassen die Forum, Topic en Post voorstellen. Maar deze klassen hebben tot nu toe nog geen enkele functionaliteit. Ik neem aan dat iets zoals het volgende niet de bedoeling is?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Dat lijkt mij eigenlijk gewoon dubbel werk...
Hoe dus wel? :)
Gewijzigd op 09/01/2011 21:35:01 door Jens V
De beste manier om objecten te vullen vanuit een query vindt ik dan ook:
Met __set() kan je dan nog eventueel gedrag beïnvloeden.
Gewijzigd op 09/01/2011 22:58:36 door Mark PHP
Bekijk het eens van de andere kant.
Vergeet even de class op zich en denk aan het gebruik er van, buiten de class.
Schrijf je logica en doe alsof de methodes van je class al precies doen wat je wil.
Als je dat hebt, vul dan je class in en zorg dat het doet wat je vraagt.
Een voorbeeld: een gastenboek:
Wat is dat? Een formulier, een lijst berichten en de mogelijkheid om een nieuw bericht te posten.
Voor mij zijn dat dus drie publieke methodes (+ een constructor uiteraard).
Je zou er zo-iets van kunnen maken:
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('guestbook.php');
$m_guestbook = new guestbook();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$m_guestbook->postMessage($_POST);
header('location ...');
}
else {
$guestbook = $m_guestbook->form() . $m_guestbook->messages();
echo '<html>...<body>... '. $guestbook .'</body></html>';
}
?>
require_once('guestbook.php');
$m_guestbook = new guestbook();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$m_guestbook->postMessage($_POST);
header('location ...');
}
else {
$guestbook = $m_guestbook->form() . $m_guestbook->messages();
echo '<html>...<body>... '. $guestbook .'</body></html>';
}
?>
Als je dit hebt, dan kan je beginnen nadenken over hoe je guestbook.php precies aanpakt.
Gewijzigd op 10/01/2011 10:25:51 door Kris Peeters
In je voorbeeldje van je guestbook, waar haal je nu je gegevens uit je db? In je guestbook-klasse neem ik aan? En waar dan?
Een $db object en een $user object.
$db is bv. een pdo object. Eventueel gewrapped in een eigen geschreven class.
Overal waar je een query moet uitvoeren, doe je bv. dit:
Code (php)
Zo'n $user object kan dan dingen bijhouden als ingelogd / ip / e-mail ... van de huidige client.
Mijn punt is vooral: je moet je algemene logica van je class niet omgooien omdat "men" zegt dat je beter geen query's laat uitvoeren binnen classes.
Gewijzigd op 10/01/2011 13:27:24 door Kris Peeters
Nog iets, jij hebt functies om een heel form of de rest uit te printen? Is dat wel zo goed?
En als ik dit nu met Smarty ofzo wil doen? Kan ik dan via een assign een heel object meegeven en dat object 'ondervragen' (data uithalen) in de tpl-files?
edit:
Dat van Smarty heb ik gevonden, assign_by_ref() :)
Gewijzigd op 10/01/2011 15:50:20 door Jens V
Sorry, ik weet veel te weinig van Smarty om hier verder iets zinnigs te zeggen.
Globals zijn uit de boze! Beter is om DI te gebruiken.
Voorbeeld:
Code (php)
Offtopic:
Het is een beetje een late reactie maar kon het niet laten om het te zeggen.
Toch bedankt ;) Heb ook altijd gehoord dat globals niet zo geweldig zijn, maar ja:)
Nota bene, drupal doet het ook zo. Het $user object staat global (Bij databases gaat het met wrapper functies).
Gewijzigd op 14/01/2011 22:02:36 door Kris Peeters
Gebruik jij 1 user-object in je applicatie (en dat ligt nogal voor de hand binnen de context van een webrequest), dan is een global volstrekt legitiem. Om te lezen wel te verstaan. Wordt er op diverse plekken in je toepassing een nieuwe user in gezet, dan klopt er weer iets niet (iets wijzigen aan het *user-object* waarnaar de global verwijst is weer hartstikke OK).
Wanneer je dus een nieuw User object koppelt en het emailadres verandert (de volgorde maakt niet uit), blijft dat emailadres veranderd in de daarop volgende requests.
In de EM kan een object 2 statussen hebben (+ nog 2, die er nu niet toe doen).
- Nieuw, er wordt dus een insert uitgevoed
- Attached, een object is met de EM opgehaald en gewijzigd. De EM vergelijkt het origineel met de gewijzigde versie en updated de wijzigingen naar de DB.
Wanneer een object wordt opgehaald, gebruikt de EM reflection om de properties te vullen. Zo heb je minder getters/setters nodig en kan je de constructor gebruiken voor het maken van nieuwe objecten.
Hoe een object in de DB weergegeven moet worden, stel je in in ofwel phpDoc blocks, ofwel een XML of YAML bestand.
Om objecten op te halen maak je gebruik van Repositories. Hierin kan je ingewikkelde SELECT DQL (een SQL variant, maar dan nuttig voor gebruik in een ORM) queries opslaan.