Ik ben weer eens lekker aan het programmeren, en dit keer ook weer OOP-stijl. Ik was daarom van plan om een heel gebruikerssysteem te maken met OOP. Ik heb veel gehad aan de uitleg van Wouter J, en dan bedoel ik deze post: klik
Het probleem waar ik op vastloop is het volgende. Wanneer je een gebruikerssysteem hebt, dan is het natuurlijk logisch dat gebruikers weer inloggen en dat ze dan een object hebben met hun gegevens daarin.
Nu is de vraag, wát moet daar precies in, en welke methods gebruik je hiervoor? Moet je bijvoorbeeld ook gegevens als een registratiedatum, laatst actief e.d. in het object bewaren (zelfs als je ze niet gebruikt)?
M'n tweede punt is hoe je zo'n bestaande gebruiker weer terug omzet in object. Als je bij het voorbeeld van Wouter J de constructor gebruikt met een paar argumenten zoals de gebruikersnaam en het wachtwoord, dan kun je deze niet meer gebruiken om een nieuw object aan te maken waarin we gelijk alle gegevens (zoals registratiedatum) meesturen. Hoe los ik dat op? Uiteraard gebruiken we een mapper voor de communicatie tussen het gebruikersobject en de database.
Dit leek me wel een leuk discussiepunt en een leerzaam topic. Althans, voor mij :-)
@roel, nee. Een singleton is een klasse die maar 1 instance heeft per aanroep. Je zal dus nooit 2 instances hebben.
Sommige mensen vinden dat leuk en handig, alleen ik vind het alleen maar rot. Een singleton en TDD (test driven development) gaan namelijk totaal niet samen...
@Bert, naast het feit dat je eigenlijk in elke if === moet gebruiken en == gewoon helemaal moet vergeten (behalve bij objecten want daar werkt een == als een ===...), hoeft het natuurlijk niet verplicht.
[size=xsmall]Toevoeging op 16/06/2012 18:14:42:[/size]
Even de code wat beter bekeken. En hier mijn puntjes:
Gebruik DI voor je Mappers. Je moet niet een PDO object meegeven aan Gastenboek en in Gastenboek::__construct() dit object omzetten in een ReactieMapper, maar je moet juist de ReactieMapper al maken en die meegeven aan Gastenboek::__construct():
<?php
$reactionMapper = new ReactionMapper(new PDO('...'));
$guestbook = new Guestbook($reactionMapper);
?>
[hr]
Tevens twijfel ik over het gebruik van de reactionMapper op deze manier. Misschien is dit wel handiger:
<?php
$reactionMapper->create(new Reaction('...'), $guestbook);
?>
En dan in ReactionMapper::create() de reactie in de DB stoppen, bij het id van het guestbook (die je ophaalt met Gastenboek::getId()), en dan een method Gastenboek::toevoegen() maken die slechts alleen dat reactie object toevoegt aan een array reacties.
[hr]
Elke keer een query uitvoeren als je Gastenboek::getReacties() gebruikt lijkt mij wat traag... Ik zou een aparte, protected/private, method maken die de reacties eenmaal ophaalt en die in een property plaats:
<?php
class Guestbook
{
protected $reactions = array();
public function getReactions()
{
if (count($this->reactions) === 0) {
$this->reactions = $this->getReactionsFromDb();
}
return $this->reactions;
}
protected function getReactionsFromDb()
{
return #reacties;
}
}
?>
Maar met mijn verhaal hierboven over een andere manier van de mapper gaat dit helemaal niet op, want dan gebeurt het maar 1 keer.
[hr]
Tevens, script je nou in het engels of in het nederlands? (mijn voorkeur gaat uit naar engels) Want in index.php heb je op regel 10 $resultaat en op regel 15 $result waarbij je in de body alleen gebruik maakt van $result...
[hr]
Bij default waardes, ookal zijn die leeg, is het alsnog verstandig om je te houden aan het type. Dus:
<?php
class Gastenboek
{
public function __construct(PDO $pdo, $id = 0)
{
$this->_reactieMapper = new ReactieMapper($pdo, $id);
$this->_id = (int) $id;
}
}
?>
[hr]
Nog even wat commentaar over je PHPdoc.
De PHPdocs die je nu op file niveau (helemaal bovenin je code) hebt staan zijn eigenlijk PHPdocs op class niveau:
<?php
/**
* Class voor gastenboek
*/
class Gastenboek
{
// ...
}
?>
[hr]
Bij sommige dingen is het heel heel heel logisch wat ze betekenen, je hoeft dan geen PHPdoc te hebben, dat gaat dan eerder storend werken. Bijv:
<?php
class Gastenboek
{
/**
* Het ID van het Gastenboek object
*
* @var integer $_id
*/
private $_id;
}
?>
Zou ik maken:
<?php
class Gastenboek
{
/**
* @var integer
*/
private $_id;
}
?>
Ook zijn die variabelenamen op property niveau, zoals in het voorbeeld hierboven, totaal overbodig.
[hr]
Gebruik voor het type van een parameter oid die een object is niet het type resource maar de naam van het object:
<?php
class Gastenboek
{
/**
* Constructor van een nieuw Gastenboek object
*
* @param PDO $pdo Het PDO databaseobject
* @param integer $id Het ID van het Gastenboek - niet verplicht
*/
public function __construct(PDO $pdo, $id = '')
{
$this->_reactieMapper = new ReactieMapper($pdo, $id);
$this->_id = (int) $id;
}
}
?>
(je had hier ook een typo, kijk daar nog even na).
[hr]
Als laatst vind ik die underscore voor private properties niet heel handig, alle properties die je hebt zijn private of protected, dus eigenlijk is het totaal overbodig. Maar dit is natuurlijk persoonlijk.
@VeeWee, ik zou geen gebruik gaan maken met singletons, nergens voor nodig en soms heb je meerdere database connecties nodig, als je gaat wisselen van db bijv.
Tevens vroeg ik me af wat er erg is aan meerdere properties meegeven aan een constructor? Ik gebruik dat namelijk altijd.
Wel zou ik een populate method maken in de DataMapper klassen. Deze maakt dan van een array een object.
@Bert, naast het feit dat je eigenlijk in elke if === moet gebruiken en == gewoon helemaal moet vergeten (behalve bij objecten want daar werkt een == als een ===...), hoeft het natuurlijk niet verplicht.
Wouter, graag een toelichting. Waarom zou je in elke if === moeten gebruiken? Dat kost meer performance dan == en het is niet altijd nodig.
Ozzie, omdat == niet kijkt op type en daarom hele vreemde dingen gaat doen:
<?php
var_dump(0 == false); // true
var_dump(0 == 'foo'); // true
var_dump(true == 'foo'); // true
// dus:
var_dump(true == false);
Ik zou altijd strictly type aanraden, daarom gebruik ik altijd type casting en ook vaak ===.
Even in het kort voor beginners: == kijkt of een waarde dezelfde waarde heeft, bijv. 0 == '0', === kijkt ook naar het type, bijv. 0 !== '0', maar 0 === 0.
Behalve bij objecten, daarbij kijkt == naar het type en de waardes en === of het van hetzelfde instance is.
Thanks Wouter, ik snap dat verhaal van de hashes en het wachtwoord niet... hij vergelijkt strings en de helft van die strings komt niet overeen maar er komt toch true uit? Huh??? :-s
Even terugkomend op dit voorbeeldje:
if ($_SERVER['REQUEST_METHOD'] == 'POST')
Dit kan toch nooit fout gaan? Hier is dan toch geen === benodigd?
Ik vraag dit omdat === trager is dan ==. Als je overal === gebruikt wordt je site trager.
Ozzie, nee bij die request_method maakt het niet heel veel uit. Maar om je script een beetje constant te houden zou ik ook hier gewoon === gebruiken. En trager, tja dat noemen we microoptimalisatie en dat is iets wat overbodig is...
En die hashes, tja, dat is weer 1 van die kromme dingen in PHP. Ik vind die uitleg in de tutorial geweldig. PHP is te vergelijken met een gereedschapskist waarin allemaal tools zitten die het net niet zijn, net niet optimaal voor het werkt, maar met een beetje creativiteit kun je er wel mee werken.
En mocht je het met hashes niet geloven, probeer zelf maar uit!
@wouter
Singletons hebben inderdaad zijn voor- en nadelen. Als je ze niet wilt gebruiken, dan doe je dat gewoon niet... Toch zijn er ook wel handige uitwerkingen van dit patroon. Zelf vind ik het niet zo plezant om overal een pdo object mee te gaan geven. Ik stop die liever op 1 plaats, waar ik het object steeds kan terugvinden. Misschien minder handig voor TDD. Maar dat gebruik je dikwijls kleine projecten toch niet, omdat dat meer tijd in neemt.
Ik vind het persoonlijk onhandig om alle mogelijke properties mee te geven met de constructer. Op die manier moet je weten dat het eerste argument bijvoorbeeld id is etc. Dan maak ik liever een leeg object aan en vervolgens setId() etc.
Die populate-functie (of fromArray die ik dan persoonlijk dikwijls gebruik) is natuurlijk ook super handig!
Dit zijn natuurlijk persoonlijke meningen. Het is een kwestie van hoe je gewoon bent om alles te schrijven. Als jij het op een andere manier aanpakt, voor mij even goed hoor :)
@Wouter: ik geloof dat het zal werken met die hashes... maar ik begrijp het totaal niet.
Volgens die uitleg, als ik het goed begrijp, als je "123450000" vergelijkt met "1234567890" geeft ie true? Kun jij het even in je eigen woorden uitleggen misschien want ik mis even iets geloof ik.
Het verschil tussen == en === zal inderdaad niet veel zijn, maaaaaaar... waarom zou je overal === gebruiken terwijl dat niet overal nodig is. If-statements gebruik je echt heeeel vaak. Stel dat je per pagina request 300 if-statements hebt (ik noem maar even wat) en je gebruikt overal === terwijl dat maar bij 50 van de 300 if-statements nodig is, dan doe je dus 250x een if-statement te veel. Dat zou zomaar 0,1 tot 0,2 seconden kunnen schelen (let wel, ik heb dit nooit gemeten... maar het is ook meer even een voorbeeldje).