[PHP][MVC] Validatie

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Bv202
  • Registratie: Oktober 2006
  • Laatst online: 14-11-2021
Hey,

Ik probeer mijn PHP-code volgens het MVC-model te schrijven. Op dit moment stelt het niet veel voor; het controller-gedeelte is zelfs nog volledig functioneel ipv OO. Maar veel ervaring met MVC heb ik niet, laat staan in PHP. Maar voor een huidig project is deze aanpak nodig.

Ik probeer dus nu simpelweg een view, een model en een controller te scheiden. Op zich geen probleem, tot het op inputvalidatie aankomt. Blijkbaar is de beste methode deze validatie in de model uit te voeren. Na een beetje zoeken kwam ik op de volgende tutorial uit, welke me wel een goede aanpak lijkt:
https://www.firehed.net/mvc-model-data-validation

Dit lijkt me een eenvoudige manier om deze controles uit te voeren. Echter heb ik hier 2 bedenkingen:
1) In de controller gebruikt hij de $_POST-variabelen. Maar mag je er vanuitgaan dat deze bestaan? Indien het formulier correct is verzonden zullen deze gewoon bestaan (al dan niet leeg, maar dat is buiten de kwestie), maar wat als er iets misgaat met het verzenden? Of iemand probeert z'n eigen POST-data te versturen zonder dit formulier te gebruiken en $_POST['username'] bestaat niet?

Moet hier Überhaupt rekening mee gehouden worden? Zo ja, wat is hier de beste manier voor?

2) Laten we even stellen dat die save()-methode bij mij addUser() noemt en een INSERT-query uitvoert. Zijn validatieaanpak werkt hier wel, maar natuurlijk moet je eerst kijken of alle benodigde velden wel bestaan. Aangezien er magic methods gebruikt worden, is dit redelijk lastig, want je weet niet welke namen er in de controller gekozen zijn.

Een oplossing is in de addUser()-methode een array bij te houden met benodigde velden en te gaan controleren of deze bestaan:

PHP:
1
2
3
4
5
6
7
8
9
10
function addUser() {
  $fields = array("username", "password", "firstname");

  foreach ($fields as $field) {
    if (!isset($this->data[$field]))
      return false;
  }

  return  $this->db->execute("INSERT into blablabla");
}


Maar nu ben je ook verplicht in de controller deze veldnamen te gebruiken, waardoor de afhankelijkheid tussen de model en de controller te groot wordt. Volgens mij is zoiets een van de slechtere dingen die je kunt doen in een MVC-design.

Mijn eerste idee (en wat ik op dit moment ook doe) is gebruik maken van een aparte validatieklasse welke methodes als validateUsername(), validatePassword(), etc. bevat. Deze bevat ook een validateAdd()-methode welke er zo uiziet:
PHP:
1
2
3
4
5
6
7
8
9
public function validateAdd($username, $firstname, $password, $pass_confirm) {
    $this->message = "";
    
      if (!$this->validateName($firstname))                                      $this->message .= "- Ongeldige voornaam.\n";
      if (!$this->validateUsername($username))                                   $this->message .= "- Deze gebruikersnaam is ongeldig.\n";
      if (!$this->validatePasswords($password, $pass_confirm))  $this->message .= "- Ongeldig wachtwoord.\n";
    
    return empty($this->message);
  }

(De methode bevat nog meer methode-calls, maar dit is even een simpel voorbeeld)

Ook hier is volgens mij veel mis mee. Een methode dat een 10-tal andere methodes gaat oproepen op deze manier lijkt me geen goed design. Het grote probleem doet zich echter voor bij de validateEdit()-methode (je krijgt een formuliertje te zien waarin je gebruikersgegevens kunt wjzigen) omdat:

- De methode dat controleert of de username al in gebruik is, werkt niet meer aangezien je kan besluiten de naam niet te wijzigen. Bij zo'n controle zal de username inderdaad al bestaan, terwijl je deze helemaal niet wilt wijzigen. Oplossing: de originele username vergelijken met de nieuw ingevulde. Indien deze hetzelfde zijn, is deze controle niet nodig.

- Wachtwoorden: Deze moet je bij het wijzigen niet verplicht invullen (bij het toevoegen wel). Als ze wel zijn ingevuld, is controle nodig. MAAR als de validatie faalt moeten de ingevulde wachtwoorden wel terug naar de view gestuurd worden zodat de gebruiker deze niet opnieuw moet invullen.

- Gebruikerniveau's: Deze zijn enkel te wijzigen in specifieke omstandigheden.

Mijn huidige validateEdit()-methode werkt, maar deze durf ik niet posten. Enerzijds omdat ik dan uitgelachen wordt, anderzijds omdat ik de code zelf nog nauwelijks snap 8)7


Deze validator zou ik dan toevoegen aan de model en deze bij addUser() eerst gaan gebruiken. Opnieuw heb ik geen idee hoe ik dit het best aanpak (om de foutmeldingen te kunnen opvangen).

Dan zit ik nog met een tweede, kleiner probleempje, maar volgens mij moet ik me maar eerst focussen op het valideren van de user input.

Hoe pak je dit best aan? Volgens mij ben ik helemaal verkeerd bezig hiermee. Alle advies om dit te verbeteren is welkom.

Alvast bedankt!

Acties:
  • 0 Henk 'm!

  • RedHat
  • Registratie: Augustus 2000
  • Laatst online: 09-09 17:16
Je mag toch best wel validatiecode in je model hebben?

Het formulier wordt verzonden en je controleert gewoon je input, dat hoeven volgens mij niet persé methods te zijn. Je validatieklasse wordt toch op elke pagina aangeroepen waarin wordt gekeken of er sessies/cookies zijn, rechtenset etc.

Om voor elke 'controle' een method te maken vind ik persoonlijk wat ver gaan. Mocht het 'vaker' voorkomen (Maar goed; je email veld kun je niet controleren met een Username-method bij wijze van) dan kun je er een method van maken maar veelal doe ik actie -> validatie -> return (Gewoon simpel).

Wat ga je doen als het éne username veld net wat andere criteria heeft ? Method aanmaken 'username2'? :+

Misschien kun je ook eens kijken naar Exceptions :)

[ Voor 33% gewijzigd door RedHat op 11-04-2011 17:36 ]


Acties:
  • 0 Henk 'm!

  • Bv202
  • Registratie: Oktober 2006
  • Laatst online: 14-11-2021
Bedankt voor je reactie :)
Je mag toch best wel validatiecode in je model hebben?
Dat is nu net hetgene wat ik niet snap. Sommigen beweren dat je de controller zo klein mogelijk moet houden en zoveel mogelijk validatie voor de model bestemd is.

Als ik even kijk naar PHP frameworks, lijkt het erop dat CakePHP validatie inderdaad uitvoert in de controller, maar CodeIgniter dit in de model doet (correct me if wrong). Redelijk verwarrend dus.

Het probleem is dus enerzijds dat ik niet snap wat precies waar hoort en anderzijds dat m'n validateEdit()-method zo ingewikkeld is dat ik er zelf niets meer van snap. Wat je zegt over het niet gebruiken van een methode zou het enkel ingewikkelder maken (sowieso komt bijna alles wel 2x voor; 1x voor het toevoegen van een user, anderzijds voor het wijzigen van een user).

Acties:
  • 0 Henk 'm!

  • telefoontoestel
  • Registratie: Oktober 2002
  • Laatst online: 29-06-2024

telefoontoestel

Maak me gelukkig....Bel!!

wat mii betreft kun je de validatie het beste doen in de model. daar ben je al op de hoogte welke informatie nodig is en aan welke voorwaarden deze moet voldoen. je kan dan een aparte validatie functie schrijven die je de variabele en enkele parameters in een array kan meegeven waaraan voldaan zou moeten worden. iets als

validate($username, array(minlength=>2, maxlength=>255, chars=>[a-zA-Z0-9]));

verder is het goed als je je database tabellen ook goed instelt dus bij username unique bijv. dan start je een transaction voor je database invoer en als de username dan dubbel is krijg je een foutmelding. die kan je afvangen en een eigen foutafhandeling voor gebruiken.

telefoontoestel


Acties:
  • 0 Henk 'm!

  • RedHat
  • Registratie: Augustus 2000
  • Laatst online: 09-09 17:16
telefoontoestel schreef op maandag 11 april 2011 @ 18:27:
wat mii betreft kun je de validatie het beste doen in de model. daar ben je al op de hoogte welke informatie nodig is en aan welke voorwaarden deze moet voldoen. je kan dan een aparte validatie functie schrijven die je de variabele en enkele parameters in een array kan meegeven waaraan voldaan zou moeten worden. iets als

validate($username, array(minlength=>2, maxlength=>255, chars=>[a-zA-Z0-9]));

verder is het goed als je je database tabellen ook goed instelt dus bij username unique bijv. dan start je een transaction voor je database invoer en als de username dan dubbel is krijg je een foutmelding. die kan je afvangen en een eigen foutafhandeling voor gebruiken.
Dit dus.

Een aparte method voor elk 'soort' validatie is gewoon te veel van het goede.

Acties:
  • 0 Henk 'm!

Verwijderd

Generieke code stop je in je model, code voor een specifieke view stop je in een specifieke controller.
Je hoort sowieso maar één model te hebben voor een entiteit, meerdere models voor hetzelfde is uit den boze, terwijl je voor verschillende views best verschillende controllers kunt hebben.

Maar even ervanuit gaande dat het voor een model niet boeit welke velden er in een formulier staan en hoe die gevalideerd moeten worden, kan een model natuurlijk wel nog een waarde die je set controleren en eventueel een exception gooien die de controller dan oppikt en naar de gebruiker stuurt in de vorm van feedback.

Die exception hoort uiteraard niet te komen, want de controller moet zelf eerst de waarde valideren voordat er iets naar de model gaat.

Acties:
  • 0 Henk 'm!

  • Noork
  • Registratie: Juni 2001
  • Niet online
RedHat schreef op maandag 11 april 2011 @ 18:33:
[...]

Dit dus.

Een aparte method voor elk 'soort' validatie is gewoon te veel van het goede.
Idd, wellicht een goede tutorial voor de TS:
http://www.phpro.org/tutorials/Validating-User-Input.html
http://www.phpro.org/classes/Validation-Class.html

Zoiets gebruik ik zelf, werkt perfect naar mijn mening.

Daarbij, voor het afvangen van POST/GET waarden, zou je ook een soort aparte classe kunnen maken. B.v. iets dat NULL teruggeeft wanneer een POST/GET niet bestaat. (Zoiets wordt bij Codeignitor gebruikt; de input class) Dan krijg je geen Unhandled exception in je model, maar kun je de waarde gewoon als een soort validatie exception behandelen.

Acties:
  • 0 Henk 'm!

  • Bv202
  • Registratie: Oktober 2006
  • Laatst online: 14-11-2021
Noork schreef op maandag 11 april 2011 @ 18:39:
[...]

Idd, wellicht een goede tutorial voor de TS:
http://www.phpro.org/tutorials/Validating-User-Input.html
http://www.phpro.org/classes/Validation-Class.html

Zoiets gebruik ik zelf, werkt perfect naar mijn mening.

Daarbij, voor het afvangen van POST/GET waarden, zou je ook een soort aparte classe kunnen maken. B.v. iets dat NULL teruggeeft wanneer een POST/GET niet bestaat. (Zoiets wordt bij Codeignitor gebruikt; de input class) Dan krijg je geen Unhandled exception in je model, maar kun je de waarde gewoon als een soort validatie exception behandelen.
Dat ziet er mij een goede manier uit, maar hoe ga je dan controleren of 2 ingevulde wachtwoorden wel hetzelfde zijn? Dat kan niet in die validatieklasse. Hetzelfde geldt om te controleren of een gebruikersnaam al in gebruik is of niet (als je een gebruiker aanpast zal zo'n functie (alreadyExists())true als resultaat geven terwijl de naam gewoon niet aangepast is). Deze dingen worden dan apart gedaan?

EDIT:
Ik heb het over link #2.

[ Voor 3% gewijzigd door Bv202 op 11-04-2011 19:52 ]


Acties:
  • 0 Henk 'm!

  • Noork
  • Registratie: Juni 2001
  • Niet online
Class uitbreiden met een eigen validatie?

Acties:
  • 0 Henk 'm!

  • telefoontoestel
  • Registratie: Oktober 2002
  • Laatst online: 29-06-2024

telefoontoestel

Maak me gelukkig....Bel!!

zoals ik al aangaf in mijn eerdere post. als je je database tabel goed instelt waarbij de username unique is zal hij een foutmelding geven bij invoer van een dubbele naam. zelfde voor bijv de email. deze kun je prima afvangen.

wat betreft je password vraag kan je als variabele best een array accepteren met een compare parameter. bijv ietz als

validate(array($password, $passcheck), array(compare=>true, minlength=>8, chars=>[a-zA-Z0-9@#$%&\?]))

waarbij dus compare true is als de array items gelijk moeten zijn en false als de juist niet gelijk moeten zijn.

telefoontoestel


Acties:
  • 0 Henk 'm!

  • Bv202
  • Registratie: Oktober 2006
  • Laatst online: 14-11-2021
Het lukt me niet...

De validator wordt er wel netjes door, maar de editUser()-methode krijgt al die messy code nu...
De parameters die worden meegegeven komen uit de $_POST-array.

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
  public function editUser($login, $firstname, $lastname, $password, $password_confirm, $email, $level, $id) {
    $result = false;

    // userinfo verkrijgen van de user die we willen aanpassen
    $user = $this->getUser($id);
    $max = 0;
    


    if (empty($user)) {
      // ongeldig userid opgegeven
      $this->setErrors('Er is iets misgegaan..');
      return false;
    }
    


    if ($user['user_login'] == $login) {
      // Als de ingevulde username dezelfde is als de username die al in de db staat (niet verandert is dus), mag hij 1x voorkomen
      $max = 1;
    }
    

   // enkele regeltjes..
    $this->validator->addRule('login', array($login), 'string', true, true, 1, 128, $max, 'users', 'user_login');
    $this->validator->addRule('voornaam', array($firstname), 'name');
    $this->validator->addRule('familienaam', array($lastname), 'name');
    $this->validator->addRule('wachtwoorden', array($password, $password_confirm), 'pass', false);
    $this->validator->addRule('e-mailadres', array($email), 'email');
    


    if ($user['user_level'] == 1)
      $this->validator->addRule('gebruikersgroep', array($level), 'int', true, 1, 1);
    } // hier moeten nog meer condities over dat user level komen. Voorbeeld: Als het user_level 2 is, kan dit niet aangepast worden.
    


    if ($validator->validate()) {
      if (!empty($password)) {
        $hash = calculateHash($password);
        $result = $this->db->execute("UPDATE `users` SET user_login = ?, user_firstname = ?, user_lastname = ?, user_password = ?, 
                                      user_email = ?, user_level = ? WHERE user_id = ?", $login, $firstname, $lastname, $hash, $email, $level, $id);
      } else {
        // Geen wachtwoord ingevuld, dus pw moet niet geupdate worden
        $result = $this->db->execute("UPDATE `users` SET user_login = ?, user_firstname = ?, user_lastname = ?, 
                                      user_email = ?, user_level = ? WHERE user_id = ?", $login, $firstname, $lastname, $email, $level, $id);
      }
      
      if (!$result)
        $this->setErrors('Er is iets misgegaan.');
        
    } else {
      $this->setErrors($this->validator->getErrors());
    }
    
    return $result;
  }


En dit is nog niet compleet; er moeten nog meer "uitzonderingen" in verwerkt worden (zoals die user_level waar nog extra condities aan moeten toegevoegd worden).

De validator is tevens nog niet geschreven, maar zo zou hij dan gaan werken.

Is er een manier om zoiets netjes te schrijven?

Acties:
  • 0 Henk 'm!

Verwijderd

Mijn ervaring is dat frameworks of standaardoplossingen voor validatie zoals Spring annotaties (Java) of het verpakken van validatieregels in een array eigenlijk nooit specifiek genoeg in te richten zijn om de realiteit van invoervalidatie mee te dekken. Of het is misschien wel mogelijk, maar kost veel uitzoekwerk en gedoe om het precies te krijgen zoals je het wil hebben.

Neem bijvoorbeeld de volgende situaties:

- Als ik een user ga toevoegen moet die een niet bestaand emailadres en een niet bestaande gebruikersnaam hebben.
- Als ik een user ga updaten en het emailadres is veranderd mag dit alleen veranderen in een emailadres dat nog niet bestaat voor een andere user. Of misschien mag het emailadres wel helemaal niet veranderen.
- Als ik een user ga updaten mag de gebruikersnaam niet veranderd zijn.
- Als ik een user ga toevoegen moeten de ingevoerde wachtwoorden overeen komen.

De zaken die je wil checken beslaan soms meerdere velden en zijn afhankelijk van de operatie die je uitvoert (update, insert, delete).

Ik maak in mijn applicaties daarom altijd een validatie package met voor elk formulier een klasse.

Zo heb ik bijvoorbeeld een NewUserFormValidator klasse met 1 validate() methode die een ValidationResult object teruggeeft.

In dit ValidationResult zit een boolean isValid en een Map (Java) of een tweedimensionale array (PHP) met per veldnaam een eventuele foutmelding.

Zo kan ik in de controller met 1 call mijn form valideren en ik krijg daar alle informatie uit terug die ik nodig heb. Ik weet of het formulier valide is en als dit niet het geval is heb ik foutmeldingen die ik aan de gebruik kan laten zien. De controller blijft hiermee in elk geval schoon.

Die isValid methodes hergebruiken in de achtergrond veel standaardfuncties, zoals emailExists(), usernameExists(), etc.

[ Voor 3% gewijzigd door Verwijderd op 13-04-2011 02:02 ]


Acties:
  • 0 Henk 'm!

  • wha
  • Registratie: April 2011
  • Laatst online: 03-09 09:35

wha

Codeigniter doet de validatie van uit de controller zelf.

Momenteel gebruik ik Codeigniter zelf, dit topic vind ik wel intressant. _/-\o_

*Bookmark this topic.*

Acties:
  • 0 Henk 'm!

  • Cartman!
  • Registratie: April 2000
  • Niet online
Ik gebruik Zend Framework en maak voor validatie eigenlijk altijd een losse validator aan, als die het allemaal goed vind dan sla ik alles op.

PHP:
1
2
3
4
5
$validator = new Utility_Validate_Signup();
if(!$validator->isValid($_POST)) {
    // return error or something
}
// save data
Pagina: 1