Ik ben bezig met een wordpress plugin voor een simpel contactformulier. Aangezien ik graag wat meer ervaring op wil doen met OOP probeer ik deze helemaal in classes in te delen.

Voor het afhandelen van het formulier moet de input worden opgehaald, gevalideerd en daarna moet een mail gestuurd worden. Het leek mij logisch om aparte classes to maken voor de validatie en de afhandeling, maar voor beide heb ik de post variabelen nodig.
Zodoende heb ik een parent class gemaakt die alle post variabelen ophaalt en dan twee child classes voor de validatie en de afhandeling, die dus beide toegang hebben tot alle variabelen.

Is dat een logische methode? Of had ik beter alles in 1 class kunnen zetten? Of het misschien nog op een heel andere manier moeten doen.
Dit voert misschien wat ver voor een "simpel" contactformulier?

Indien je dit enkel wilt opbouwen uit herbruikbare componenten (en wanneer dit enige meerwaarde heeft, bijvoorbeeld omdat je dit vaak doet, zodat deze handeling zich na een eenmalige investering snel terugverdient) zou je eens kunnen kijken naar wat er nu eigenlijk gebeurt:
- het tonen van een formulier
- het verwerken van een formulier
- eventueel met terugkoppeling tijdens invullen (terugkoppeling over fouten) of na verzenden (bedankpagina)

Je bent voornamelijk met formulieren bezig, dus als je herbruikbare code hebt waarmee je formulieren snel kunt opbouwen en formulierinvoer kunt valideren dan kun je daar veel tijdswinst mee pakken. Maar dit wordt pas interessant zodra je dit soort functionaliteit (formulieren + afhandeling) vaker bouwt.

Mogelijke indeling voor classes:
Form
FormField (abstract)
concrete FormField typen (text, checkbox, select, of zelfs custom, samengestelde formulierelementen)

FormFields hang je op aan het formulier. Wanneer je het formulier valideert doorloop je de velden en roep je validatieregels van de specifieke velden aan.

Door alle bouwstenen voor te bakken kun je formulieren snel in elkaar zetten. Daarna zou je eens na kunnen gaan denken over een gebruiksvriendelijke interface zodat je formulieren (letterlijk) in elkaar kunt klikken (en dus zelfs geen code meer hoeft te schrijven, of een minimale hoeveelheid). En dit is volgens mij precies wat sommige plugins (al) doen.
Hoi Thomas,

Bedankt voor je reactie.

Ik besef nu dat mijn formulier classes echt compleet niet reusable zijn en ik dus eigenlijk de hele middag bezig ben geweest en nu een contactformuliertje heb (wat ik al had maar dan niet OOP) en wat ik niet opnieuw kan gebruiken. Nou ja, van je fouten leer je zeggen ze.

Wat jij zegt zou dan eigenlijk zoiets worden?


	
<?php

public function create_text_field( $name ) {
		
		$this->text_name = $name;
		
		$this->text_value = $_POST[ $name ];
		
		echo ?><input name="<?php echo $this->text_name; ?>" type="text" id="<?php echo $this->text_name; ?>" value="<?php echo $this->value; ?>" /><?php ;
	
	}

?>


waarbij je als volgt tekstfields aan kunt maken:
$form = new Formfields();
$form->create_text_field( $adres );

? (en dan moet er natuurlijk nog een validatie in maar dit heb ik nu even snel getypt)


Je moet altijd goed onthouden dat je binnen een functie NIKS weergeeft. Je gebruikt dus niet 'echo' binnen de functie maar in plaats daarvan 'return'.

Waar je het formulier opbouwt krijg je dan zoiets als:

<?php

$form = new Form();
echo $form->addTextField('name');

?>
In de praktijk zou je nog meer variabelen kunnen meegeven, bijvoorbeeld de max. lengte van de invoer (maxlength).
Goed dat je het zegt! Ik had dat al wel eens gelezen maar het zit er nog niet helemaal in...

Maar hoe doe je dat dan bij deze regel?

  <?php
echo ?><input name="<?php echo $this->text_name; ?>" type="text" id="<?php echo $this->text_name; ?>" value="<?php echo $this->value; ?>" /><?php ;

?>


De eerste echo kan gewoon een return worden maar wat dan met die echo's in de html?

Dat worden dan geen echo's.

Even een verkort voorbeeldje voor het idee.

<?php

$field = '<input name="' . $this->text_name . '" type="text">';
return $field;

?>
Omdat het hier om 'patronen' gaat, zou je ook met [php]sprintf[/php] kunnen werken.
De magische methode __toString() is bedoeld voor de afhandeling van een object als een string. Je zou het daarmee bijvoorbeeld zo kunnen aanpakken:


<?php
abstract class AbstractInput
{
    /**
     * @var string $Type
     *   The type of form input control.  The default type is `text`.
     */
    protected $Type = 'text';
    
    /**
     * @var array $Attributes
     *   Other form control attributes.
     */
    protected $Attributes = array();
    
    /**
     * @param string|null $name
     * @param string|null $value
     * @return self
     */
    public function __construct($name = null, $value = null)
    {
        if ($name !== null) {
            $this->setName($name);
        }
        if ($value !== null) {
            $this->setValue($value);
        }
    }
    
    /**
     * @param void
     * @return string
     */
    public function __toString()
    {
        $str = '<input type="' . $this->Type . '"';
        foreach ($this->Attributes as $attribute => $value) {
            $str .= ' ' . $attribute . '="' . htmlspecialchars($value) . '"';
        }
        $str .= '>';
        return $str;
    }

    /**
     * @param string $name
     * @return void
     * @throws \InvalidArgumentException
     */
    public function setName($name)
    {
        if (!is_string($name) || empty($name)) {
            throw new \InvalidArgumentException();
        }
        $this->Attributes['name'] = $name;
    }

    /**
     * @param mixed $value
     * @return void
     */
    public function setValue($value)
    {
        $this->Attributes['value'] = $value;
    }
}
?>


Met deze basisopzet kun je elk type formulierinput maken, bijvoorbeeld:


<?php
class Checkbox extends AbstractInput
{
    /**
     * @inheritDoc
     */
    public function __construct($name = null, $value = null)
    {
        parent::__construct($name, $value);
        $this->Type = 'checkbox';
    }
}
?>
Wauw Ward, die is mooi! Dank je wel voor deze uitleg, en bovendien kende ik die magic method nog niet, is ook wel handig om die te gebruiken!!

Ik ga er mee aan de slag, als ik er niet uit kom mag ik dan hier weer komen vragen? :)



[size=xsmall]Toevoeging op 07/01/2017 14:23:36:[/size]

Als aanvulling op hierboven, hiermee kan alleen geen textarea gemaakt worden omdat die anders in elkaar zit.

Als ik een class Textarea extends AbstractInput maak waar ook weer de method __toString in gebruikt wordt met variabele $str, en ik maak die zo dat hij geschikt is voor een textarea, gebruikt hij deze dan als ik een instance van Textarea aanmaak, terwijl hij de method uit de abstract class blijft gebruiken bij alle andere classes?

En (uit nieuwsgierigheid) waarom heb je hier gekozen voor een abstract class en niet een parent class? Wat ik ervan begrijp is dat een abstract class altijd minstens één abstract method moet bevatten, wat hier niet het geval is.

Hm. In mijn opzet heb je een abstracte class "Formfield", wat eigenlijk van alles kan zijn. Elke class afgeleid van een FormField vormt een logische eenheid in het formulier. Zo'n eenheid kan best opgebouwd zijn uit meerdere elementaire formulier elementen (selects, inputs en buttons) die samen een nieuw formulier element vormt (met functionaliteit die verder voert dan de delen waar ze uit opgebouwd zijn), bijvoorbeeld zoiets:


Dat veld toevoegen aan het formulier verloopt via iets als:
<?php
// addField(<field type>, <field name>, <options>)
$form->addField('moveselect', 'rights', array('label' => 'rights', 'options' => $this->getRights()));
?>

Je zou zelfs een formulier-element kunnen maken voor het opbouwen van een rechten-boom.
Thomas, ik had de class zoals jij voorstelde inmiddels al deels uitgewerkt (alleen de formfields zelf, nog geen validatie erbij) en dat werkt ook prachtig.

Ik merk iig dat mijn vraag 'wat is de beste manier om classes in te delen' misschien niet zo makkelijk is als ik dacht, omdat het van veel dingen af hangt en dan nog er meerdere manier zijn.

We kunnen in ieder geval stellen dat mijn aanvankelijke class helemaal fout was, en dat ik bij deze twee betere manier heb om mee aan het werk te kunnen!

Voor de grap dan hier nog even mijn originele 2 classes. Behalve dus dat ze niet reusable zijn en helemaal verkeerd ingedeeld, heb ik hier nog meer dingen gedaan die niet goed zijn/niet zouden moeten of iets dergelijks? (voor de duidelijkheid helemaal onderaan nog de partial met de html


<?php
class Topsecreet_Contactformulier_Verwerk {
	
	private $naam;
	private $email;
	private $telefoon;
	private $bericht;
	private $verzonden;
	
	public function __construct() {
		
		$this->naam = $_POST['naam'];
		$this->email = $_POST['email'];
		$this->telefoon = $_POST['telefoon'];
		$this->bericht = $_POST['bericht'];
		$this->verzonden = $_GET['verzonden'];
		
	}
	
	//de method om de variabele waar de naam in is gepost op te vragen
	public function get_naam() {
		
		return $this->naam;
		
	}
	//de method om de variabele waar het emailadres in is gepost op te vragen
	public function get_email() {
		
		return $this->email;
		
	}	
	//de method om de variabele waar het telefoonnummer in is gepost op te vragen
	public function get_telefoon() {
		
		return $this->telefoon;
		
	}	
	//de method om de variabele waar het bericht in is gepost op te vragen
	public function get_bericht() {
		
		return $this->bericht;
		
	}
	//de method om te contoleren of het formulier verzonden is of niet
	public function get_verzonden() {
		
		return $this->verzonden;
		
	}
	

}

class Topsecreet_Contactformulier_Validate extends Topsecreet_Contactformulier_Verwerk {
	
	//hier slaan we de foutmeldingen in op	
	private $errors;
	
	
	public function __construct() {
		parent::__construct();		
		 $this->errors = $errors;
	}
		
	//valideer invoerveld naam en sanitize uitvoer	
	public function validate_naam() {
		
		if( $this->get_naam() == "" ){
			
			return $this->errors = 1;
		
		} else {
			
			return $this->naam = filter_var( $this->get_naam(), FILTER_SANITIZE_STRING );
			
		}

	}
	//valideer invoerveld email en sanitize uitvoer	
	public function validate_email() {
				
		if( $this->get_email() == "" || filter_var( $this->get_email(), FILTER_VALIDATE_EMAIL ) === false ) {
			
			return $this->errors = 1;
				
		} else {
			
			return $this->email = filter_var( $this->get_email(), FILTER_SANITIZE_EMAIL );
		}
				
	}	
	//sanitize telefoon uitvoer
	public function sanitize_telefoon() {
		
		return $this->telefoon = filter_var( $this->get_telefoon(), FILTER_SANITIZE_STRING );
		
	}
	//valideer invoerveld bericht en sanitze uitvoer
	public function validate_bericht() {
		
		if( $this->get_bericht() == "" ){
			
			return $this->errors = 1;
			
		} else {
			
			return $this->bericht = filter_var($this->get_bericht(), FILTER_SANITIZE_STRING);
			
		}

	}
	
	//controleer of er fouten zijn en zo ja, geef de 1 terug. Dit is nodig om te bepalen of het formulier getoond moet worden of
	//dat we verder gaan met het aanmaken en verzenden van de email
	public function check_errors() {
		
		$this->validate_bericht();
		$this->validate_email();
		$this->validate_naam();	
		return $this->errors;
		
	}	
}
?>




<?php
if( "ja" == $this->validate_form->get_verzonden() &&  1 === $this->validate_form->check_errors() ) { 

	echo "er zijn fouten gevonden in het formulier";   
				
} elseif ( "ja" != $this->validate_form->get_verzonden() ) { ?>

	<form id="form1" name="form1" method="post" action="<?php echo esc_url( $_SERVER['SELF'] ) . '?verzonden=ja'; ?>">

	<?php if( "ja" == $this->validate_form->get_verzonden() && 0 === $this->validate_form->validate_naam() ){ echo "<i>* Naam:</i>"; } else { echo "<b>Naam:</b>"; } ?><br />
	<input name="naam" type="text" class="formulier" id="naam" size="40" value="<?php echo $this->validate_form->get_naam(); ?>" /><br /><br />


	<?php if( "ja" == $this->validate_form->get_verzonden() && 0 === $this->validate_form->validate_email() ) { echo "<i>* Email:</i>"; } else { echo "<b>Email:</b>"; } ?><br />
	<input name="email" type="text" class="formulier" id="email" size="40" value="<?php echo $this->validate_form->get_email(); ?>" /><br /><br />

	<b>Telefoon:</b><br />
	<input name="telefoon" type="text" class="formulier" id="telefoon" size="40" value="<?php echo $this->validate_form->get_telefoon(); ?>" /><br /><br />

	<?php if( "ja" == $this->validate_form->get_verzonden() && 0 === $this->validate_form->validate_bericht() ){ echo "<i>* Bericht:</i>"; } else { echo "<b>Bericht:</b>"; } ?><br />
	<textarea name="bericht" cols="40" rows="10" class="formulier" id="bericht"><?php echo $this->validate_form->get_bericht(); ?></textarea><br /><br />


	<input type="image" name="verzeden" src="afbeeldingen/verzenden.jpg" />
    
    <?php } else {
		
		//verstuur de email
		
}?>



[size=xsmall]Toevoeging op 07/01/2017 18:05:00:[/size]

oh, ik zie net de de html niet klopt, omdat ik dit uit 2 bestanden heb samengevoegd voordat ik het hier plakte. Als er fouten zijn moet het formulier natuurlijk ook getoond worden! ;)

Reageren