popphp/pop-form

Pop Form Component for Pop PHP Framework

3.0.0 2017-03-23 22:00 UTC

README

Build Status Coverage Status

OVERVIEW

pop-form is a robust component for managing, rendering and validating HTML forms. With it, you can have complete control over how a form looks and functions as well as granular control over field validation. Features include:

  • Field element creation and configuration
  • Validation
    • Use any callable validation object, such as pop-validator or custom validators
  • Filtering
  • Dynamic field generation based on the fields of a database table

pop-formis a component of the Pop PHP Framework.

INSTALL

Install pop-form using Composer.

composer require popphp/pop-form

BASIC USAGE

Using field element objects

use Pop\Form\Form;
use Pop\Form\Element\Input;
use Pop\Validator;

$form = new Form();
$form->setAttribute('id', 'my-form');

$username = new Input\Text('username');
$username->setLabel('Username:')
         ->setRequired(true)
         ->setAttribute('size', 40)
         ->addValidator(new Validator\AlphaNumeric());

$email = new Input\Email('email');
$email->setLabel('Email:')
      ->setRequired(true)
      ->setAttribute('size', 40);

$submit = new Input\Submit('submit', 'SUBMIT');

$form->addElements([$username, $email, $submit]);

if ($_POST) {
    $form->setFieldValues($_POST);
    if (!$form->isValid()) {
        echo $form; // Has errors
    } else {
        echo 'Valid!';
    }
} else {
    echo $form;
}

So a few things are going on in the above example:

  1. We created the form object and gave it an 'id' attribute.
  2. We created the individual field elements setting their name, label, attributes, validators, etc.
  3. We added the field elements to the form object.
  4. We checked for a $_POST submission. If not detected, we just render the form for the first time.
  5. If a $_POST submission is detected:
    1. Set the field values with the values in the $_POST array (a bad idea without any filtering)
    2. Check if the form object passes validation. If not, re-render the form with the errors. If it does pass, then you're good to go.

Just as a note, the form object will default to 'post' as the method and the current request URI as the action otherwise changed by the user.

On the first pass, the form will render like this:

<form action="/" method="post" id="my-form">
    <fieldset d="my-form-fieldset-1" class="my-form-fieldset"></fieldset>
        <dl >
            <dt>
                <label for="username" class="required">Username:</label>
            </dt>
            <dd>
                <input type="text" name="username" id="username" value="" required="required" size="40" />
            </dd>
            <dt>
                <label for="email" class="required">Email:</label>
            </dt>
            <dd>
                <input type="email" name="email" id="email" value="" required="required" size="40" />
            </dd>
            <dd>
                <input type="submit" name="submit" id="submit" value="SUBMIT" />
            </dd>
        </dl>
    </fieldset>
</form>

If it fails validation, it will render with the errors. In this case, the username was not alphanumeric:

<form action="/" method="post" id="my-form">
    <fieldset d="my-form-fieldset-1" class="my-form-fieldset"></fieldset>
        <dl >
            <dt>
                <label for="username" class="required">Username:</label>
            </dt>
            <dd>
                <input type="text" name="username" id="username" value="" required="required" size="40" />
                <div class="error">The value must only contain alphanumeric characters.</div>
            </dd>
            <dt>
                <label for="email" class="required">Email:</label>
            </dt>
            <dd>
                <input type="email" name="email" id="email" value="" required="required" size="40" />
            </dd>
            <dd>
                <input type="submit" name="submit" id="submit" value="SUBMIT" />
            </dd>
        </dl>
    </fieldset>
</form>

Top

Using field configurations

We can do the same thing as above with a field configuration array, which helps streamline the process:

use Pop\Form\Form;
use Pop\Validator;

$fields = [
    'username' => [
        'type'       => 'text',
        'label'      => 'Username:',
        'required'   => true,
        'attributes' => [
            'size' => 40
        ],
        'validators' => [
            new Validator\AlphaNumeric()
        ]
    ],
    'email' => [
        'type'       => 'email',
        'label'      => 'Email:',
        'required'   => true,
        'attributes' => [
            'size' => 40
        ]
    ],
    'submit' => [
        'type'  => 'submit',
        'value' => 'SUBMIT'
    ]
];

$form = Form::createFromConfig($fields);
$form->setAttribute('id', 'my-form');

if ($_POST) {
    $form->setFieldValues($_POST);
    if (!$form->isValid()) {
        echo $form; // Has errors
    } else {
        echo 'Valid!';
    }
} else {
    echo $form;
}

Top

Filtering

As mentioned above, when dealing user-submitted values, it's a bad idea to use them or display them back on the screen without filtering them. A common set a filters to employ would be strip_tags and htmlentities. So in the first example, we would add filters to the $_POST block:

if ($_POST) {
    $form->addFilter('strip_tags')
         ->addFilter('htmlentities', [ENT_QUOTES, 'UTF-8']);
    $form->setFieldValues($_POST);
    if (!$form->isValid()) {
        echo $form; // Has errors
    } else {
        $form->clearFilters();
        $form->addFilter('html_entity_decode', [ENT_QUOTES, 'UTF-8']);
        echo 'Valid!';
    }
} else {
    echo $form;
}

Of course, the strip_tags filter will strip out any possible malicious tags. The htmlentities filter is useful if the form has to render with the values in it again:

<input type="text" name="username" id="username" value="Hello&quot;World&quot;" required="required" size="40" />

Without the htmlentities filter, the quotes within the value would break the HTML of the input field. Of course, if you want to use the values after the form is validated, then you have to call clearFilters() and filter the values with html_entity_decode.

Top

Validation

Of course, one of the main reasons for using a form component such as this one is the leverage the validation aspect of it. You've already seen the use of a basic validator from the pop-validator component and those are easy enough to use. But, you can create your own custom validators by either extending the pop-validator component with your own or just writing your own custom callable validators. The only real rule that needs to be followed is that the custom validator must return null on success or a string message on failure that is then used in error display. Here are some examples:

Using a closure
$username = new Input\Text('username');
$username->addValidator(function ($value) {
    if (strlen($value) < 6) {
        return 'The username value must be greater than or equal to 6.';
    }
});
Using a custom class
class MyValidator
{
    public function validate($value)
    {
        if (strlen($value) < 6) {
            return 'The username value must be greater than or equal to 6';
        }
    }
}

$username = new Input\Text('username');
$username->addValidator([new MyValidator(), 'validate']);

Top

Dynamic fields from a database table

The pop-form comes with the functionality to very quickly wire up form fields that are mapped to the columns in a database. It does require the installation of the pop-db component to work. Consider that there is a database table class called Users that is mapped to the users table in the database. It has three fields: id, username and password.

(For more information on using pop-db click here.)

use Pop\Form\Form;
use Pop\Form\Fields;
use MyApp\Table\Users;

$fields = new Fields(Users::getTableInfo(), null, null, 'id');
$fields->submit = [
    'type'  => 'submit',
    'value' => 'SUBMIT'
];

$form = new Form($fields->getFields());
echo $form;

This will render like:

<form action="/fields2.php" method="post">
    <fieldset id="pop-form-fieldset-1" class="pop-form-fieldset">
        <dl>
            <dt>
                <label for="username" class="required">Username:</label>
            </dt>
            <dd>
                <input type="text" name="username" id="username" value="" required="required" />
            </dd>
            <dt>
                <label for="password" class="required">Password:</label>
            </dt>
            <dd>
                <input type="password" name="password" id="password" value="" required="required" />
            </dd>
            <dd>
                <input type="submit" name="submit" id="submit" value="SUBMIT" />
            </dd>
        </dl>
    </fieldset>
</form>

You can set element-specific attributes and values, as well as set fields to omit, like the 'id' parameter in the above examples. Any TEXT column type in the database is created as textarea objects and then the rest are created as input text objects.

Top