Getting started

As challenged by a tweet, this library extracts validation rules and filters from a html form and validates submitted user data against it.

It’s pretty crazy what you have to do to get a form build in frameworks. Create a lot of php classes for elements, validation, etc. So why not build a html form and use the standard element attributes to extract the validation rules and filters. Together with some powerful html compliant data attributes you can create forms, customize validation rules and filters in one place.

Installation

1
$ composer require xtreamwayz/html-form-validator

How does it work?

1. Build the form

Create the form from html. Nothing fancy here. Create the form with the form factory from a html form and optionally default values. Only the first <form></form> element is processed. Following form elements or html code outside the first form element is ignored.

1
$form = (new FormFactory())->fromHtml($htmlForm, $defaultValues);

2. Validate the form

The easiest way is to use a framework that uses PSR-7 requests.

1
2
3
// Validate PSR-7 request and return a ValidationResponseInterface
// It should only start validation if it was a post and if there are submitted values
$validationResult = $form->validateRequest($request);

Under the hood it uses laminas-inputfilter which makes all its validators and filters available to you.

If you use a framework that doesn’t handle PSR-7 requests, you can still reduce boilerplate code by passing the request method yourself:

1
$validationResult = $form->validate($submittedData, $_SERVER['REQUEST_METHOD']);

You can also leave the method validation out. It won’t check for a valid POST method.

1
$validationResult = $form->validate($submittedData);

3. Process validation result

Submitted data should be valid if it was a post and there are no validation messages set.

1
2
3
4
5
6
7
8
9
// It should be valid if it was a post and if there are no validation messages
if ($validationResult->isValid()) {
    // Get filter submitted values
    $data = $validationResult->getValues();

    // Process data

    return new RedirectResponse('/');
}

If PSR-7 request methods are not available, you might can check for a valid post method yourself.

1
2
3
if ($_SERVER['REQUEST_METHOD'] == 'POST' && $validationResult->isValid()) {
    // ...
}

4. Render the form

Last step is rendering the form and injecting the submitted values and validation messages.

1
2
3
4
// Render the form
return new HtmlResponse($this->template->render('app::edit', [
    'form' => $form->asString($validationResult),
]));

If you don’t want the values and messages injected for you, just leave out the validation result.

1
echo $form->asString();

Before rendering, the FormFactory removes any data validation attributes used to instantiate custom validation (e.g. data-validators, data-filters). This also removes possible sensitive data that was used to setup the validators.

The $validationResult is optional and triggers the following tasks:

Submit button detection

Who doesn’t want to know which button is clicked? For this to work the submit button must have a name attribute set.

1
2
3
4
<form>
  <input type="submit" name="confirm" value="Confirm" />
  <button type="submit" name="cancel">Cancel</button>
</form>

You can check by the button name attribute if a specific button is clicked.

1
2
// Returns a boolean
$validationResult->isClicked('confirm');

Or get the name of the clicked button.

1
2
// Returns the name of the clicked button or null if no named was clicked
$validationResult->getClicked();

Example

 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
59
60
61
62
63
64
65
// Basic contact form

$htmlForm = <<<'HTML'
<form action="{{ path() }}" method="post">
    <div class="row">
        <div class="col-md-6">
            <div class="form-group">
                <label class="form-control-label" for="name">Name</label>
                <input type="text" id="name" name="name" placeholder="Your name" required
                       data-reuse-submitted-value="true" data-filters="striptags|stringtrim"
                       class="form-control" />
            </div>
        </div>
        <div class="col-md-6">
            <div class="form-group">
                <label class="form-control-label" for="email">Email address</label>
                <input type="email" id="email" name="email" placeholder="Your email address" required
                       data-reuse-submitted-value="true" data-filters="striptags|stringtrim"
                       class="form-control" />
            </div>
        </div>
    </div>

    <div class="form-group">
        <label class="form-control-label" for="subject">Subject</label>
        <input type="text" id="subject" name="subject" placeholder="Subject" required
               data-reuse-submitted-value="true" data-filters="striptags|stringtrim"
               class="form-control" />
    </div>

    <div class="form-group">
        <label class="form-control-label" for="body">Message</label>
        <textarea id="body" name="body" rows="5" required
                  data-reuse-submitted-value="true" data-filters="stringtrim"
                  class="form-control" placeholder="Message"></textarea>
    </div>

    <input type="hidden" name="token" value="{{ csrf-token }}"
           data-validators="identical{token:{{ csrf-token }}}" required />

    <button type="submit" class="btn btn-primary">Submit</button>
</form>
HTML;

// Create form validator from a twig rendered form template
$form = (new FormFactory())->fromHtml($template->render($htmlForm, [
    'csrf-token' => '123456'
]));

$_POST['name'] = 'Barney Stinsons';
$_POST['email'] = 'barney.stinsons@example.com';
$_POST['subject'] = 'Hi';
$_POST['body'] = 'It is going to be Legen-Wait For It... DARY! LEGENDARY!';

// Validate form and return form validation result object
$result = $form->validate($_POST);

// Check validation result
if ($result->isValid()) {
    $data = $result->getValues();
    // Process data ...
} else {
    // Inject error messages and filtered values from the result object
    echo $form->asString($result);
}