Versio

[OOP] Opzet formulier classes

Overzicht Reageren

Citroen Anoniem Graag

Citroen Anoniem Graag

29/03/2008 21:20:00
Quote Anchor link
Goede avond,

Nadat ik me de afgelopen tijd een beetje verdiept heb in het OO programeren en heel veel artikelen heb doorgelezen, besloot ik dat het tijd was maar eens iets te gaan proberen, met het idee dat ik de problemen wel zou tegen komen.

Wat wil ik:
Ik wil een klasse maken voor formulieren; de (valid) xHTML output en de verwerking van het formulier.

Wat heb ik:
Ik heb nu een klassemodel (is dat een woord?) gemaakt, deze is hier te bewonderen.

Dit is hoe het zou gaan moeten werken:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?php

$oForm
= new Formulier (id, actie, method, classe)
$oGebruikersnaam = new text($oForm, id, naam, maxlenght, class, regex, show_verplicht)
$oGeslacht = new radio($oForm, id, naam, array(naam => label, ...), class, show_verplicht)
$oVoorwaarden = new checkbox($oForm, naam, array(array(naam, label, checked), ...), class, show_verplicht)

if($_SERVER['request_method'] == method)
{

    $oForm->valideer();
}

if($oForm->verwerkt != false)
{

    echo $oForm->mededelingen();

    echo $oForm->geef_html(start);
    echo $oGeslacht->geef_html();
    echo $oGebruikersnaam->geef_html();
    echo $oForm->geef_html(eind);
}

else
{
    echo 'Formulier verwerkt';
    $oForm->AllesOpruimen();
        $oForm = NULL;
}


?>


NB: Dit is uiteraard geen goede php-code, maar het dient om mijn bedoeling duidelijker te maken.


Even de uitleg:
Eerst wordt er een formulier instantie/object (?) gemaakt, dit is de basis van alles. Nu we die hebben kunnen er elementen worden toegevoegd.
Elk type element heeft zijn eigen klasse die de abstracte klasse element implementeert.
Elk type element z'n klasse heeft een constructor (foutje in het plaatje, daar mist het), de functie valideer_geldige_gegevens() en de functie nieuw_element(). Deze staan niet in de klasse element omdat inhoud van de functies per type-klas verschilt. Er moeten bij een tekst field natuurlijk andere controles worden uitgevoerd dan bij een radio group.
De functie ruim alles op dient om alle element klasses te vernietigen, de formulier klasse kan zich zelf niet ten gronde richten dus dat moet handmatig gebeuren.

Als er een element wordt aangemaakt dan wordt het object doorgegeven aan de formulier klasse, zodat deze weet welke elementen er in zijn formulier zitten. Bij het valideren check hij alle regexen, ed, is alles goed dan wordt het action bestand geincluded. En zal de variable verwerkt op true springen.

Wat is mijn vraag?
Ik wil weten of dit klassemodel goed is, wat de (mogeljke) voor en nadelen zijn, en of dit de moeite waard is om uit te programeren.

Groetjes Freek

Nog een paar opmerkingen:
* Op het plaatje staan de parameters van functies nog niet aangegeven.
* Ik heb nog nooit een klassemodel getekend m.b.v de computer, ik het dus hoogst waarschijnlijk de verkeerde soort lijntjes gebruik, tekentjes gebruikt,...
* de propertie show_verplicht betekend of er een sterretje (*) moet worden laten zien
* de functie valideer() dient om te kijken of de user input voldoet aan de gestelde eisen, de functie valideer_geldige_gegevens(), kijkt of er een element kan worden aangemaakt met de gegeven gegevens (een radiogroup waar alle opties moeten worden geselecteerd kan natuurlijk niet :P)
* Dit is niet de definitieve naamgeving, want deze is nogal verwarrent
* Het type password en tekst hebben dezelfde klasse omdat deze praktisch gelijk aan elkaar zijn, zal ik deze toch scheiden voor de volledigheid
* Ik zou nog een interface kunnen maken voor elk specifiek element klasse met daarin de functies valideer_geldige_gegevens() en nieuw_element(), voegt niet veel toe, maar zal ik dat doen?

Nu ik dit allemaal typ ontdek ik zelf al 1 nadeel, ben benieuwt of jullie die ook zien.
Gewijzigd op 01/01/1970 01:00:00 door Citroen Anoniem Graag
 
PHP hulp

PHP hulp

25/05/2012 12:57:55
Gesponsorde koppelingen:
 
Jelmer rrrr

Jelmer rrrr

29/03/2008 21:26:00
Quote Anchor link
Je hangt nu het formulier aan de formulier-elementen. Is het niet slimmer om het andersom te doen? Het formulier is niet een onderdeel van een formulierelement, maar andersom klopt het wel.

Doe je dat, dan kan je ook meteen profiteren van een interface. Ieder element dat die interface implementeert mag zich aan het formulier koppelen.
Gewijzigd op 01/01/1970 01:00:00 door Jelmer rrrr
 
Citroen Anoniem Graag

Citroen Anoniem Graag

29/03/2008 21:30:00
Quote Anchor link
Het is ook wel de bedoeling om de element aan het formulier te laten hangen, maar ik weet niet hoe je dat tekent :P
 
Ferluci

Ferluci

30/03/2008 00:14:00
Quote Anchor link
Je klassendiagram is niet verkeerd. Ik zie dat je nu wel in de klasse element de methode geef_html() hebt staan. Deze zou ik ook per subklasse zetten, want een textbox geeft een andere html code dan een textarea.

Wat Jelmer bedoelt is hetvolgende: Jij doet in je code nu dit:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
<?php
$oForm
= new Formulier (id, actie, method, classe)
$oGebruikersnaam = new text($oForm, id, naam, maxlenght, class, regex, show_verplicht)
$oGeslacht = new radio($oForm, id, naam, array(naam => label, ...), class, show_verplicht)
$oVoorwaarden = new checkbox($oForm, naam, array(array(naam, label, checked), ...), class, show_verplicht)
?>


Je maakt een nieuw form aan en je maakt een aantal elementen aan waaraan je dit form meegeeft. Het zou andersom moeten zijn:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
<?php
// Form starten
$oForm = new Formulier($parameters);
// Element aanmaken
$oElement = new Radio($parameters);
// Element aan het form hangen
$oForm->AddElement($oElement);
?>

Je form heeft namelijk een aantal elementen. In de klasse Formulier heb je dan een array met elementen.

De lijntjes in je klassendiagram zijn goed, ik zou wel de relaties tussen de klassen erbij zetten, dat is een stuk makkelijker als je dadelijk gaat programmeren.
Gewijzigd op 01/01/1970 01:00:00 door Ferluci
 
Citroen Anoniem Graag

Citroen Anoniem Graag

30/03/2008 00:32:00
Quote Anchor link
Edit:

@PersoonBovenMij: Ik dacht dat er een stippellijntje oid moest als je iets implementeerde.. Maar heb een veel te complex programma, weet iemand iets simpels?


Op die manier, I've got it. Maar je hebt weer een regeltje extra, en dus minder automatisch. Ik kan dus 3 dingen doen.
1. Houden zoals het is, verkeerd met werkt wel.
2. Het aanmaken van een element gaat via de formulier klasse (Weet niet of dit handig is)
3. Een hele andere oplossing verzinnen en alles omgooien

Wat zal ik doen.

Wat betreft de geef_html functie heb je helemaal gelijk, dit heb ik over het hoofd gezien.

Vier hele korte vraagjes waar ik het antwoord/advies graag op zou krijgen:
* Alle type element klasse (bv Radio) aan een interface linken? De klasse Radio zou dan de abstrace klasse element implementeren en de interface InterfaceElement emplementeren. In die interface zouden dat alleen de functies valideer_geldige_gegevens, nieuw_element en geef_html moeten staan. (Lijkt me zelf wel een goed idee, want de klasse moet hier tenslotte altijd aan voldoen)
* De klasse Text opsplitsen in een Text classe en een Password classe?
* Hoe doen met de foutenafhandeling? Exeptions gebruiken met een eigen exeption handler, het nadeel is dat hij na de eerste fout stopt, dus je kan geen lijstje krijgen met dit is fout, dit is fout en dit is fout. Alle (formuliervalidatie)fouten opsparen in een tijdelijke array en met de functie geef_mededelingen() retourneren. LIjkt mij zelf wel handig. Eventueel nog een andere classe maken waarin die fouten worden gestopt (Zoals Jan Koehoorn dat met zijn form class doet)
* Iemand het nadeel al ontdekt, waar ik in mn eerste post iets over zeg?
Gewijzigd op 01/01/1970 01:00:00 door Citroen Anoniem Graag
 
Ferluci

Ferluci

30/03/2008 00:46:00
Quote Anchor link
1. en 2.: Ik heb zelf even snel een klassendiagram gemaakt hoe ik het zou doen: klik. Ik zeg niet dat dit het beste is, maar het is wel een manier. Ik zou een abstracte klasse element maken, die drie subklassen heeft. Input, textarea en select, waarvan input ook weer een abstracte klasse kan zijn (ben ik vergeten abstract te tekenen in het plaatje). En input heeft dan weer een aantal subklasse. Zou houdt je alles mooi gescheiden, want iedere input heeft ook weer zo zijn eigen eigenschappen. Bij Text en Password kun je in principe ook gewoon een veld toevoegen wat aangeeft of het een text of password veld is, omdat dit het enige verschil is tussen deze twee elementen. Dat is een ontwerpkeuze en het is allebei goed.

Een Interface lijkt mij in dit geval een goed idee. Zo heb je altijd de juiste methodes in je klasse staan.

3. Ik zou zeker gebruik maken van exceptions, dat is immers de foutafhandeling van OOP. Dit doe je alleen bij het samenstellen van het formulier (dus de html code maken) en als je dan gaat valideren of de ingevulde waardes goed zijn kun je gewoon een lijst teruggeven met gevonden fouten voor het gebruikersgemak (alle fouten in een keer laten zien).

4. Nog niet gevonden, nu ben ik wel benieuwd :).
 
Jelmer rrrr

Jelmer rrrr

30/03/2008 00:54:00
Quote Anchor link
1) De elementen kennen je form, maar je form de elementen niet. Beetje vreemde opbouw. Als je het andersom doet, kan je het zo maken dat je get_html() alleen op het form-object aanroept, welke dan zelf de aan hem gekoppelde elementen afgaat. Scheelt je weer herhaalde code buiten je klasse, en je voorkomt dat je per ongeluk elementen buiten de form-tag echo't.

2) Zou ik niet doen. Nu hoeft de formulier-klasse geen kennis te hebben alle elementen die er zijn. Want zolang de element-klassen maar valideren kunnen en html kunnen opleveren, is het formulier tevreden. Als je elementen via het formulier gaat maken, moet je ergens in je formulier-klasse de mogelijke elementen opsommen, en nu kan je niet meer even snel een element toevoegen.

Je kan je password-klasse ook afleiden van de text-klasse. Het enige wat anders is is de get_html method. Die overschrijf je, de rest erf je over. Password is ook tekst ;)

Waarom geef je alle eigenschappen, ook de triviale, mee in de constructor? Ik zou de belangrijke eigenschappen, zoals een naam en een label van het element via de constructor laten doen en de rest via methods, setters. Op die manier komen je constructors van de elementen meer met elkaar overeen, is het makkelijker te onthouden. Daarbij wordt je code veel leesbaarder. Nu moet je aan de hand van de volgorde afleiden wat voor waarde erin moet. Gebruik je methods, dan kan je aan de naam van de method al zien waar de waarde voor dient.

Foutenafhandeling tenslotte nog, je hebt een method 'validate' op ieder element. Het lijkt mij niet meer dan logisch dat die true of false teruggeeft. Een echte 'fout' is het niet, et is een 2e mogelijke situatie waar je je prima op hebt voorbereid. Eventueel hang je aan ieder element nog een melding. In je form-klasse roep je op ieder element validate() aan, en voldoen alle velden, dan geeft ook form->validate() true terug, en kan je verder in je PHP code met de waarden. Geeft form->validate() echter false terug, dan geef je het formulier opnieuw weer, en het formulier weet dan welke velden niet goed waren, en kan de foutmeldingen weergeven. Ook daarom is het handig wanneer je formulier kennis van zijn elementen heeft - het element zal daarentegen geen kennis van het form-object hoeven hebben, ik kan mij zo snel geen noodzaak voor vinden.
Gewijzigd op 01/01/1970 01:00:00 door Jelmer rrrr
 
Citroen Anoniem Graag

Citroen Anoniem Graag

30/03/2008 13:32:00
Quote Anchor link
met die foutenafhandeling gaat het wel lukken.

Kort samengevat: Als ik verander dat het formelement een element van het form wordt en de functie geef_html uit de abstracte klasse element haal en in de specifieke element klasse zet is alles goed?
Verder maak ik ook nog een Interface voor die specifieke element klasses.
 
Jelmer rrrr

Jelmer rrrr

30/03/2008 14:52:00
Quote Anchor link
Ik krijg nu moeite om je te volgen. Kan je dat nog eens compleet samenvatten, eventueel met een diagram? (Zie Ferluci's diagram voor hoe de pijljes e.d. moeten[/url])
 
Citroen Anoniem Graag

Citroen Anoniem Graag

30/03/2008 20:04:00
Quote Anchor link
Ik heb alles weer eens even op een rijtje gezet:

Plaatje!

Weet niet of ik alles goed heb getekend, maar het komt er op neer dat elke type element een eigen klasse heeft (radio, text, select, etc.) die de interface InterfaceElement implementeerd, en ook de abstracte klasse element.

Het moet ik praktijk zo gaan werken:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?php

$oForm
= new Formulier (params)
$oGebruikersnaam = new text(params)
$oGeslacht = new radio(params)
$oVoorwaarden = new checkbox(params)
$oForm->voeg_element_toe($oGebruikersnaam)
$oForm->voeg_element_toe($oGeslacht)
$oForm->voeg_element_toe($oVoorwaarden)
//Maak ook nog een funtie voeg_elementen_toe() Dan kan je een array met objecten geven, dat is wel makkelijker..

if($_SERVER['request_method'] == method)
{

    $oForm->valideer();
}

if($oForm->verwerkt != false)
{

    echo $oForm->mededelingen();

    echo $oForm->geef_html(start);
    echo $oGeslacht->geef_html();
    echo $oGebruikersnaam->geef_html();
    echo $oForm->geef_html(eind);
}

else
{
    echo 'Formulier verwerkt';
    $oForm->AllesOpruimen();
    $oForm = NULL;
}


?>


Is dit goed?

Edit:

Ik bedenk met net de de abstracte class element kan worden samegevoeg met de interface InterfaceElement, want nu staan er geen standaard functies meer in de abstrace klasse, aleen nog maar properties die verplicht zijn..
Gewijzigd op 01/01/1970 01:00:00 door Citroen Anoniem Graag
 
Jelmer rrrr

Jelmer rrrr

30/03/2008 21:05:00
Quote Anchor link
Ik zou het zo doen: (let even niet op de namen, ik gebruik zelf graag Engelse, en ben Nederlandse niet echt gewend)
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$formulier
= new Formulier('index.php');
$veldGebruikersnaam = new TextField('gebruikersnaam', 'Gebruikersnaam:');
$veldGebruikersnaam->pattern = '^[a-z]+$';
$formulier->voegVeldToe($veld);

if($formulier->isValid()) {
   echo 'Je naam is ' . $veldGebruikersnaam->waarde();
}
else {
   if($_SERVER['REQUEST_METHOD'] == 'POST') {  
      echo $formulier->meldingen();
   }

  
   echo $formulier->html();
}

?>


De method nieuw_element op je interface vat ik niet helemaal. Wat moet die doen?

edit: Ik zou sowieso geen properties verplicht stellen, omdat dat simpelweg niet gaat. Daarbij kan je niet 'zien' wanneer een property wordt uitgelezen (tenzij je lastig gaat doen met __get) en ben je dus verplicht bij __construct al alle waarden in de properties te zetten. Methods zijn handiger, omdat je die kan afdwingen via een interface, en de waarde pas hoeft te berekenen wanneer hij wordt aangeroepen. Wanneer de method nooit wordt aangeroepen, hoeft hij ook geen (overbodige) moeite te doen de waarde te berekenen.
Gewijzigd op 01/01/1970 01:00:00 door Jelmer rrrr
 
Citroen Anoniem Graag

Citroen Anoniem Graag

30/03/2008 21:54:00
Quote Anchor link
Ik ben in mijn vorige post vergeten te vertellen dat ik inderdaad alleen de verplichte parameters via de contructor mee geef en voor de rest getters en seters maak.

De method nieuw_element dient (de naam zegt het al een beetje) een nieuw element toe te voegen ;-)
Deze functie ben ik vergeten eruit te halen, maar in mijn vorige opstelling diende die functie ervoor om het object van het element aan de formulier klasse door te geven. Nu gebeurd dat via een method van de formulierklasse, en is die functie idd overbodig/fout.
 



Overzicht Reageren

Get Adobe Flash player