Durch eine Forenanfrage bin ich auf die Frage gekommen, wie bei Contact Form 7 eine eigene Regel für einzelne Felder definiert werden kann. In dem konkreten Beispiel ging es um die Frage nach der Postleitzahl.
Natürlich kann hier auch mit Number gearbeitet werden. Aber die Definition [number* plz min:0 max:99999]
hat mehrere Probleme. Zum einen ist die Fehlermeldung unspezifisch („Zahl ist zu hoch“) und dann fügen die Browser ein für diesen Fall unnötiges UI ein, um die Zahl schrittweise zu verkleinern/vergrößern, was aber theoretisch per CSS ausgeblendet werden kann:
.wpcf7 input[type='number'] { -moz-appearance:textfield; } .wpcf7 input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; }
Alternativ könnte man ein Textfeld nehmen und die Mindestlänge wie Maximallänge auf 5 Zeichen setzen: [[text* plz minlength:5 maxlength:5]]
. Das Problem ist dann natürlich, dass hier auch Buchstaben eingetragen werden könnten. Auch nicht das was man will …
Am besten ist eine eine eigene Feld-Validierung. So eine Custom Validation bietet die meisten Möglichkeiten, ist sehr flexibel und lässt eine eigene Fehlermeldung zu. So könnte eine fertige Lösung aussehen:
function plz_custom_number_confirmation_validation_filter( $result, $tag ) { // bail early if this is not a postleitzahl if ( 'plz' != $tag->name ) return $result; // get content from field $plz = isset( $_POST['plz'] ) ? trim( $_POST['plz'] ) : ''; // check with regex $value = preg_match( '/^\d{5}$/', $plz); // set error message if regex is not matching (no postleitzahl) if ( !$value ) { $result->invalidate( $tag, "Bitte geben Sie eine korrekte Postleitzahl ein." ); } return $result; } add_filter( 'wpcf7_validate_text', 'plz_custom_number_confirmation_validation_filter', 10, 2 ); add_filter( 'wpcf7_validate_text*', 'plz_custom_number_confirmation_validation_filter', 10, 2 );
Ich gehe den Code mal Zeile für Zeile durch.
function plz_custom_number_confirmation_validation_filter( $result, $tag ) {
Wir definieren eine Funktion mit den Parametern $result
für das Ergebnis der Validierung und $tag
für das Feld an dem wir uns befinden.
if ( 'plz' != $tag->name ) return $result;
Wenn das Feld nicht den Namen „plz“ hat, dann muss nichts getan werden, also verlassen wir die Funktion direkt wieder. Damit es weitergeht muss der Shortcode für das Feld (in der Minimalversion) so aussehen: [[text plz]]
. Das kann natürlich noch mit anderen Parametern kombiniert werden oder auch ein erforderliches Feld sein: [[text* plz]]
. Es reicht, dass der Name „plz“ lautet.
$plz = isset( $_POST['plz'] ) ? trim( $_POST['plz'] ) : '';
Um den Inhalt zu testen wird das Feld ausgelesen. Wenn es befüllt ist, werden Leerzeichen vorne und hinten entfernt. Ist das Feld nicht befüllt, wird die Variable auf einen leeren String gesetzt.
$value = preg_match( '/^\d{5}$/', $plz);
Hier findet die eigentliche Magie statt. Mit preg_match
wird geprüft, ob die regular expression zutrifft. Wobei diese wie folgt aussieht: ^ steht für den Start, dann kommen exakt 5 Zahlen und dann soll der Block enden ($).
if ( !$value ) { $result->invalidate( $tag, "Bitte geben Sie eine korrekte Postleitzahl ein." ); }
Ist der Test aus der letzten Zeile nicht true
, so liegt keine Postleitzahl (5 Zahlen) vor und es wird als Resultat eine Fehlermeldung gespeichert.
return $result;
Das Resultat wird zurückgegeben.
add_filter( 'wpcf7_validate_text', 'plz_custom_number_confirmation_validation_filter', 10, 2 ); add_filter( 'wpcf7_validate_text*', 'plz_custom_number_confirmation_validation_filter', 10, 2 );
Zuletzt wird die Funktion an zwei Filter gehängt. Und zwar an die Validierung für das Textfeld und das Textfeld als Pflichtfeld (mit Sternchen).
Bei jedem Textfeld wird nun beim Validieren diese Funktion aufgerufen. Lautet der Feldname „plz“ wird geprüft ob die regular expression greift und ggf. eine Fehlermeldung gesetzt.
Ein großartiges Tool zum Testen eigener RegEx ist die Website regex101.com.
Du hast auch eine custom validation gebaut oder eine Idee dafür? Dann schicke mir gerne Link oder Idee als Kommentar.
Abgesehen davon, dass die Validierung „nur“ mit .de PLZs funktioniert (bzw. dort wo 5-stellige numerische PLZs verwendet werden), was spräche dagegen gleich gegen eine Liste gültiger PLZs zu validieren: https://www.datendieter.de/item/Postleitzahlen_Liste_Deutschland ?
Das spricht natürlich gar nichts dagegen. Es ging mir aber mehr um die custom validation als Technik und nicht um die beste Art Postleitzahlen zu verifizieren. Aber da die prinzipielle Technik jetzt ja vorgestellt ist, freue ich mich eine Umsetzung deiner Idee in einem Artikel in deinem Blog. Der kommt doch jetzt, oder? 😉
Die RegEx kann übrigens international angepasst werden. Es existieren diverse RegEx für alle möglichen Länder. Das ist ja das spannende an dieser Technik:
https://stackoverflow.com/questions/578406/what-is-the-ultimate-postal-code-and-zip-regex
Was mir bei der Liste etwas Sorge bereitet ist die Aktualität der Daten.
Die obige Lösung ist vielleicht nicht perfekt. Muss aber dafür wohl erstmal nicht gewartet werden …
Die Postleitzahlendaten von 2013 kann man übrigens auf der Website für 89 EUR dann kaufen. Ja, hmm. Nein.
Moin!
Noch besser als eine Validierung der Eingabe erst auf dem Server fände ich ja eine Überprüfung bereits im Browser des Benutzer.
Dafür gibt es seit HTML5 die pattern Eigenschaft für Input-Felder.
Nutzung dieser Eigenschaft lässt Benutzer nicht erst das Formular abschicken und dann doch wieder nacharbeiten.
Schöne UX Ergänzung 😉
Leider unterstützt Contact Form 7 dieses Attribut nicht. Wenn es das tun würde, wäre das natürlich schöner, da die gleiche RegEx nun im Browser arbeiten wurde und dazu nicht erst das Formular abgesendet werden müsste.
Wenn jemand eine Variante kennt eigene Attribute bei CF7 einzubauen, dann freue ich mich über einen Hinweis.
Hallo,
welches Formular-Plugin unterstützt denn die browserseitige Feld- zu Feldbearbeitung, so dass man nicht erst das Abschicken des Formulars („submit“) abwarten muss?
Besten Dank im voraus!
Caldera Forms kann das zum Beispiel, nennt sich dort „Mask Input“:
https://calderaforms.com/doc/single-line-text-fields/
Auch NinjaForms 3 hat mit „Input Mask“ so ein Feature. Leider schlecht dokumentiert.
Wenn ich mich nicht sehr irre, wird der Filter optionale PLZ-Felder immer invalid markieren. Ich würde aus isset( $_POST[‚plz‘] ) eine Bedingung machen, besser noch mit !empty und für leere Eingaben $result ohne weitere Prüfung durchreichen. Den Fall arbeitet nämlich schon die Basisprüfung von CF7 ab.