Orde in PHP
Probleem
Ik heb hier een mooi voorbeeld van waarop OO ooit is bedacht. In bijlage vind je de code uit een bachelor cursus PHP tweede jaar. Wat is het probleem in deze code?
- functies, proceduriaal, klassen worden allemaal door elkaar gebruikt;
- een klasse wordt hier gebruikt als een functie, enfin wat je normaal gezien in een functie doet;
Design
Functionele vereisten
Het aanmelden opdelen in op zichzelf staande onderdelen ook wel primitieven genoemd. Een primitief is een zelfstandig benoembare handeling die op zichzelf getest kan worden. Dat ligt in de richting van SOC (speration of concerns), het SRP (single responsabilty principle), één van de SOLID design patronen en het DRY (don't repeat your self) principe. Enfin dit laatse vloeit automatisch voort uit de twee voorgaande.
We beginnen met het opsommen van de primitieven:
- een definitie van een gebruiker
- gegevens van een gebruiker persistent maken:
- registreren, definitief in een database;
- tijdelijk in een sessie, cookie;
- gegevens van een gebruiker opzoeken in:
- database;
- session/cookie;
- intypen van gebruikersgegevens
- intypen van logingegevens
- paswoord encrypteren
- paswoord verifiëren
- email verifiëren
Domein
We leggen eerst ons domeinmodel vast. Met welke gegevens uit het domein moeten we werken?
Naam | Gegevenstype | National | Beschrijving |
name | text | YES | gebruikersnaam |
text | NO | email adres van de gebruiker | |
password | text | NO | geëncrypteerd paswoord |
lastActivity | datetime | wanneer heeft de gebruiker zich het laatst aangemeld | |
createdOn | datetime | wanneer heeft de gebruiker voor de eerste keer zich aangemeld | |
createdBy | text | YES | wie heeft de gebruiker aangemaakt |
updatedOn | datetime | wanneer zijn de gebruikersgegevens voor het laatst gewijzigd | |
updatedBy | text | YES | wie heeft de gebruikersgegevens het laatst gewijzigd |
De User
klasse beschikt over getters en setters voor alle velden. Daarnaast hebben we nog enkele specifieke methoden nodig.
Naam | Parameters | Return type | Beschrijving | |
create | geen | int | insert de gegevens die al in de velden staan in de User tabel en retourneert true als de insert gelukt is, anders false | |
readByEmail | int | zoekt de rij met de opgegeven email op en retourneert true als rij gevonden, anders false | ||
readByName | int | zoekt de rij met de opgegeven name op en retourneert true als rij gevonden, anders false | ||
__construct | we maken een verbinding in de constructor | |||
__destruct | we verbreken de verbinding |
De velden zijn niet moeijlijk te maken:
De getters en setters zijn voorlopig uiterst eenvoudig. De validatie doen we later.
In de constructor maken we een verbinding met de MySQL server. We gebruiken hiervoor de PDO klasse. Het leggen van de verbinding staat in een try catch blok om:
- het programma niet laten vastlopen als er iets niet in orde is met SQL server
- meer info te krijgen over wat er eventueel niet in orde is; daarvoor gebruiken we de
PDOException
klasse.
In de create
methode staat de execute
methode in een try catch
blok.
Maar dat vangt de eventuele foutmeldingen, die van MySQL komen, niet op. Maar dat is heel moeilijk te debuggen. Als er fout in het SQL statement staat (bijvoorbeeld een verkeerde kolomnaam) loopt de code gewoon door. Execute retourneert wel false maar we weten bij god niet waarom.
Daarom is het nuttig de retourwaarde van de errorInfo methode van het prepared statement object te bekijken.
En dan stoot je gelijk op een ander probleem. Hoe ga je die info aan de view doorgeven? Je kan dit gauw gauw met een var_dump doen maar veel netter met een feedback systeem. Kijk bijvoorbeeld eens naar het feedback systeem van Dialog.
Nog een verbeterpunt is de manier wordt het SQL statement aan MySQL wordt doorgegeven. Het is al goed dat de waarden voor de insert en de select statements als parameters worden doorgegeven. Maar ik zou nog een stap verder gaan en geen SQL statement vanuit PHP doorsturen maar een de naam van een op de server uit te voeren stored procedure.
Interactie
Knoppen staan op formulieren, al een geluk. Maar de code, die de interactie met de gebruiker afhandelt, staat werkelijk overal versnipperd. Heel wat redirects waarmee je van her naar der springt. Redirects raad ik af want moeilijk te debuggen. De afhandeling van de interactie staat waarlijk overal en nergens. Hoe kan je in godsnaam de workflow volgen? Is het geen idee om alle interactie met de gebruiker op één plaats te centraliseren in wat in het MVC patroon, de controller wordt genoemd? We maken één enkele switch die alle interactie met de gebruiker afhandelt.
Een eerste stap op de goede weg bestaat er in het html button element te gebruiken i.p.v. het input type submit html element. Immers voor elk input type submit element moet je een andere waarde toekennen aan het name attribuut. Om te weten als er op een submit input knop geglikt werd moet je isset() gebruiken en als parameter de waarde meegeven van het name attribuut van het submit input element. Als je het button element gebruikt kan je dezelfde techniek gebruiken als voor radio buttons en toch nog een andere tekst laten zien aan de gebruiker.
Een tweede stap, is het opstellen van use cases. Je gaat zien dat met elke use case een knop overeenstemt. Ik heb het niet over systeem use cases maar over gebruikers use cases.