Hallo,

Mag een set method valideren?

Stel we hebben een product en we willen aan dat product een productnummer toevoegen.

<?php

$product = new Product();
$product->setProdNr('ABC1234');

?>
De vraag is of deze setProdNr method (of iedere set method in het algemeen) mag valideren of de input klopt.

In het voorbeeld gaat het nu om een productnummer, maar in de praktijk heeft een product nog meer eigenschappen die gavalideerd moeten worden. Denk bijvoorbeeld aan de productnaam. Is het een string, en bestaat die string uit minimaal 3 tekens? Nou ja, zo zijn er dus een heleboel dingen die gevalideerd moeten worden.

Mijn simpele vraag is, mag een setter eigenlijk wel iets valideren?

Stel dat het product gevalideerd is, en hiervoor hebben 10 controles plaatsgevonden. Vervolgens slaan we de gegevens op in de database. De volgende keer halen we het product op uit de database en via de mapper worden alle gegevens keurig weer geset. MAAR... opnieuw vinden er nu 10 controles plaats, want die controles zijn onderdeel van de set methods!

Mijn stelling is dan ook: een set method mag geen input valideren en alle input moet gevalideerd worden door een validator object. Dit validator object maakt vervolgens een product aan.

Dan krijg je dus zoiets:

<?php
$pv = new ProductValidator();
$pv->setProdNr('ABC1234');

$product = $pv->createProduct();

// opslaan in database
$pm = new ProductMapper();
$pm->save($product);

// ophalen uit database
$pm->load('ABC1234'); // hier vindt dus geen validatie meer plaats

?>
Graag jullie reacties. Ik ben erg benieuwd!!!
>> Volgens mij is ophalen get!

Dat kan, maar ik gebruik load als iets uit een file of database komt.

Afgezien daarvan. De vraag is dus of het de bedoeling is dat een set method gaat valideren. Daar ben ik erg benieuwd naar.
Hoe zie jij dan als je een product uit de database (of een bestand) ophaalt het verband met de setters.
Of anders gezegd als je mapper een product uit de database haalt hoe weet ie dan welke setter ie aan moet roepen?
Ik ging er vanuit dat een ProductMapper de methods van de Product class kent. Dus dat de ProductMapper de method setProdNr van de Product class kent. Maar je zou de gegevens ook als een object kunnen fetchen.

Maar dan nog steeds blijft de vraag... moet een (Product) class zelf valideren, of moet een andere (validator) class dat doen?
Nee, in de product class niet, maar in de mapper bv wel.
En dan nog zal je in de mapper gaan moeten regelen welke database velden wij welke product eigenschapen horen.
>> Nee, in de product class niet, maar in de mapper bv wel.

Heb je het nu over het valideren?

Ik probeer even duidelijk te krijgen waar nu eigenlijk de verantwoordelijkheden liggen.

Ik heb bijv. User classes gezien met een setName method die controleert of het een niet lege string is van minimaal 3 tekens. Zijn dat zaken die in zo'n method thuishoren? Of gebruik je daar een userValidator class voor?
Simpele validaties kun je best in de User class zelf doen. Ik zou zelfs zeggen dat dat het beste is. Je wilt namelijk dat zo'n entity class "atlijd" valid is (Dit principe komt van DDD). Dit dwing je ten eerste af door de properties die geset moeten worden mee te geven als params aan de constructor van zo'n class. Maar daarnaast valideer je die properties ook via de hun setters.

Dit geldt dan overigens niet meer wanneer je meer dan alleen een regex vergelijking doet, of een strlen() oid. Als je moet controleren of een email adres bijvoorbeeld al bestaat in je DB, dan hoort deze validatie logica ergens anders.
Thanks voor je reactie.

>> Dit principe komt van DDD

Kun je dit toelichten?

>> ... Maar daarnaast valideer je die properties ook via de hun setters.

Spreek jij dan vanuit de constructor een setter aan? Ook als er geen controle hoeft plaats te vinden?

>> Dit geldt dan overigens niet meer wanneer je meer dan alleen een regex vergelijking doet, of een strlen() oid.

Maar waar leg je dan de grens vraag ik me af. Ik kan me voorstellen dat je de 1e keer dat een User wordt aangemaakt je de benodigde controles uitvoert. Maar als je de User eenmaal (in de database) hebt opgeslagen, dan wil je toch niet iedere keer als je de User uit de database haalt de gegevens weer controleren? Want dat heb je al gedaan voordat je de gegevens ging opslaan.
DDD staat voor Domain Driven Design. Dat is een onderwerp wat weer geheel op zich zelf staat. Is niet iets wat zomaar even te uitleggen is in een paar zinnen. Maar het principe dat een Entity altijd valid moet zijn (een entity moet dus geen "invalid" state kennen) komt daar vandaan. Als je hier meer over wilt weten zou ik gewoon even heel veel Googlen. Je hebt namelijk wel een aantal verschillende artikelen nodig om DDD in zijn geheel te begrijpen.

Maar heb je een voorbeeld artikel over dit onderwerp: http://www.blogcoward.com/archive/2009/02/20/DDD-Lite-Mama-donrsquot-let-your-entities-grow-up-to-be.aspx

>> Spreek jij dan vanuit de constructor een setter aan? Ook als er geen controle hoeft plaats te vinden?
Ja, vanuit de constructor roep ik dan de setters aan. Anders moet je validaties in de constructor coden en ook nog eens in de setter. Is niet nodig natuurlijk.

>> Maar waar leg je dan de grens vraag ik me af.
Een class moet altijd maar 1 ding doen. En niets meer (SOLID principle, heb je waarschijnlijk al meer voorbij zien komen). Dit geldt zeker voor een Entity class. Wanneer je een andere class aanroept vanuit je User class, dan kun je in principe zeggen dat het niet goed is.

Overigens hoef je je ook niet blind te staren op die zogenaamde "regels". Als jij vanuit een setEmail($email) bijvoorbeeld een Util::isValidEmail($email); aanroept gaat er echt niemand dood :)
Ozzie PHP op 06/03/2014 13:54:47

Maar als je de User eenmaal (in de database) hebt opgeslagen, dan wil je toch niet iedere keer als je de User uit de database haalt de gegevens weer controleren? Want dat heb je al gedaan voordat je de gegevens ging opslaan.

Als jij
- op database niveau met CHECK CONSTRAINTs (MySQL is dus geen keuze) de zelfde checks hebt als in je code.
- in je applicatie verbind met een account dat geen ALTER en andere overbodige rechten heeft.
- via een beveiligde verbinding met de database communiceert.

Dan is validatie van waarschijnlijk niet nodig voor data die uit de database komt aangezien deze dan via SQL injectie niet naar illegale waardes/formaten gezet zou kunnen worden. (betekend niet dat de ruwe data niet meer gesanitized hoeft te worden.)

Het hangt er vanaf hoe paranoïde je bent.

Je zou het zo kunnen doen dat wanneer je een User maakt met data uit een database je de validatie fouten logt en verder negeert. Zo weet je dan dat er ergens iets niet helemaal lekker gaat, misschien in een hele andere applicatie, maar dan kun je het tenminste terugvinden.

Of je besluit dat een keer controleren veilig genoeg is en je je energie richt op bestaande functionaliteit verbeteren/nieuwe functionaliteiten.

Reageren