[OOP] Opzet formulier classes
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)
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
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.
$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
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 -
Het is ook wel de bedoeling om de element aan het formulier te laten hangen, maar ik weet niet hoe je dat tekent :P
Wat Jelmer bedoelt is hetvolgende: Jij doet in je code nu dit:
Code (php)
1
2
3
4
5
6
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)
?>
$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)
1
2
3
4
5
6
7
8
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);
?>
// 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
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?
@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
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 :).
1. en 2.: Ik heb zelf even snel een klassendiagram gemaakt hoe ik het zou doen: 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 :).
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 -
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.
Ferluci's diagram voor hoe de pijljes e.d. moeten[/url])
Ik krijg nu moeite om je te volgen. Kan je dat nog eens compleet samenvatten, eventueel met een diagram? (Zie 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)
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
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;
}
?>
$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..
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
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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();
}
?>
$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 -
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.