Create A Form

Create a new form instance and add fields into it.

use Windwalker\Form\Field\TextareaField;
use Windwalker\Form\Form;
use Windwalker\Form\Field\TextField;
use Windwalker\Form\Field\PasswordField;

$form = new Form;

$form->addField(new TextField('username', 'Username'));
$form->addField(new PasswordField('password', 'Password'));

// Another way

$form->add('email',       new TextField)->label('Email');
$form->add('description', new TextareaField)->label('Description'));

echo $form->renderFields();

Render all fields, and we get this HTML output.

<div id="username-control" class="text-field ">
    <label id="username-label" for="username">Username</label>
    <input type="text" name="username" id="username" />
</div>
<div id="password-control" class="password-field ">
    <label id="password-label" for="password">Password</label>
    <input type="password" name="password" id="password" />
</div>
<div id="email-control" class="text-field ">
    <label id="email-label" for="email">Email</label>
    <input type="text" name="email" id="email" />
</div>
<div id="description-control" class="textarea-field ">
    <label id="description-label" for="description">Description</label>
    <textarea name="description" id="description"></textarea>
</div>

img

Using XML as Configuration

<form>
    <field
        name="username"
        type="text"
        label="Username"
        />

    <field
        name="password"
        type="password"
        label="Password"
        />
</form>
$form = new Form;

$form->loadFile('form.xml');

Field Definition Interface

FieldDefinitionInterface is a simple interface to help us create an empty space to define form fields.

use Windwalker\Form\FieldDefinitionInterface;

class MyFieldDefinition implements FieldDefinitionInterface
{
    public function define(Form $form)
    {
        $form->add('name', new TextField)->label('Name');
    }
}

Then add this definition to Form object.

$form = new Form;

$form->defineFormField(new MyFieldDefinition);

Form Control

$control = 'user';

$form = new Form($control);

$form->addField(new TextField('username', 'Username'));

echo $form->renderFields();

The result will make name as an array.

<div id="user-username-control" class="text-field ">
    <label id="user-username-label" for="user-username">Username</label>
    <input type="text" name="user[username]" id="user-username" />
</div>

Organize Fields

Fieldset

Fieldset is a category of fields, we can filter our fields by fieldset:

$form = new Form;

$form->addField(new TextField('flower', 'Flower'), 'plant');
$form->addField(new TextField('tree', 'Tree'), 'plant');
$form->addField(new TextField('dog', 'Dog'), 'animal');
$form->addField(new TextField('cat', 'Cat'), 'animal');

// Only render Flower & Tree
echo $form->renderFields('plant');

// Only render Dog & Cat
echo $form->renderFields('animal');

// Will render all
echo $form->renderFields();

Using XML

<form>
    <fieldset name="plant">
        <field name="flower" label="Flower"/>
        <field name="tree" label="Tree"/>
    </fieldset>

    <fieldset name="animal">
        <field name="dog" label="Dog"/>
        <field name="cat" label="Cat"/>
    </fieldset>
</form>

Group

Group is like fieldset as a category of fields, but it will make name of fields to be array:

$form = new Form;

$form->addField(new TextField('flower', 'Flower'), null, 'earth');
$form->addField(new TextField('bird', 'Bird'), null, 'sky');

// The name will be name="earth[flower]"
echo $form->renderFields(null, 'plant');

// The name will be name="sky[bird]"
echo $form->renderFields(null, 'animal');
<input type="text" name="earth[flower]" />
<input type="text" name="sky[bird]" />

Now we can use fieldset and group to organize our fields.

$form = new Form;

$form->addField(new TextField('flower', 'Flower'), 'plant', 'earth');
$form->addField(new TextField('tree', 'Tree'), 'plant', 'earth');
$form->addField(new TextField('dog', 'Dog'), 'animal', 'home');
$form->addField(new TextField('cat', 'Cat'), 'animal', 'home');
$form->addField(new TextField('bird', 'Bird'), 'animal', 'sky');

// Only render Bird
echo $form->renderFields('animal', 'sky');

// Only render Dog & Cat & Bird
echo $form->renderFields('animal');

// Only render Flower & Tree
echo $form->renderFields(null, 'earth');

// Will render all
echo $form->renderFields();

Using XML

<form>
    <group name="earth">
        <fieldset name="plant">
            <field name="flower" label="Flower"/>
            <field name="tree" label="Tree"/>
        </fieldset>
    </group>

    <fieldset name="animal">
        <group name="home">
            <field name="dog" label="Dog"/>
            <field name="cat" label="Cat"/>
        </group>

        <group name="home">
            <field name="bird" label="Bird"/>
        </group>
    </fieldset>
</form>

Use Wrapper Methods

Windwalker 2.1 supports wrapper method to help us organize form fieldset and group.

$form->wrap('fieldset', null, function(Form $form)
{
    $form->add('name', new TextField)
        ->label('Name')
        ->required();

    $form->add('email', new TextField)
        ->label('Email')
        ->required();

    $form->add('password', new PasswordField)
        ->label('Password')
        ->set('autocomplete', 'off');

    $form->add('password2', new PasswordField)
        ->label('Confirm Password')
        ->set('autocomplete', 'off');
});

// Use group
use Windwalker\Html\Option;

$form->wrap('fieldset', 'group', function(Form $form)
{
    // State
    $form->add('state', new Field\RadioField)
        ->label('State')
        ->set('class', 'btn-group')
        ->set('default', 1)
        ->addOption(new Option('On', '1'))
        ->addOption(new Option('Off', '0'));
});

Attributes of Fields

Name & Label

$form->addField(new TextField('name', 'Label'));

Set Attributes

$form->addField(new TextField('name', 'Label'))
    ->set('id', 'my-name')
    ->set('class', 'col-md-8 form-input')
    ->set('onclick', 'return false;');

Required, Disabled and Readonly

$form->addField(new TextField('name', 'Label'))
    ->set('id', 'my-name')
    ->required()
    ->disabled();

Set to false.

$form->addField(new TextField('name', 'Label'))
    ->set('id', 'my-name')
    ->required(false)
    ->disabled(false);

XML

<field
    name="name"
    label="Label"
    id="my-name"
    required="true"
    disabled="false"
/>

Filter

use Windwalker\Filter\InputFilter;

$form->addField(new TextField('id', 'ID'))
    ->setFilter(InputFilter::INTEGER);

// Prepare data
$data['id'] = '123abc';

// Bind data into form
$form->bind($data);

// Do filter
$form->filter();

$values = $form->getValues(); // Array(id = 123)

Validate

$form->addField(new TextField('name', 'Name'))
    ->required();

$form->addField(new TextField('email', 'Email'))
    ->setValidator(new EmailValidator);

// Prepare data
$data['name'] = null;
$data['email'] = 'foo';

// Bind data into form
$form->bind($data);

// Do validate
$r = $form->validate();

$results = $form->getErrors();

var_dump($r); // bool(false)

$results[0]->getMessage(); // Field Email validate fail.
$results[1]->getMessage(); // Field Name value not allow empty.

Field Types

Select List

use Windwalker\Form\Field\ListField;
use Windwalker\Html\Option;

$selectField = new ListField(
    'flower',
    'Flower',
    array(
        new Option('', ''),
        new Option('Yes', 1),
        new Option('No', 0),
    )
);

echo $selectField->render();

The output is:

<div id="flower-control" class="list-field ">
    <label id="flower-label" for="flower">Flower</label>
    <select name="flower" id="flower" class="stub-flower">
        <option selected="selected"></option>
        <option value="1">Yes</option>
        <option value="0">No</option>
    </select>
</div>

Multiple List

$selectField->set('multiple', true);

RadioList

$form->addField(new RadioList('flower', 'Flower'))
    ->addOption(new Option('Yes', 1))
    ->addOption(new Option('No', 0));

We can also add options in constructor:

$field = new RadioList(
    'flower',
    'Flower',
    array(
        new Option('Yes', 1),
        new Option('No', 0),
    )
);

Available Fields

Name HTML Description
TextField <input type="text"> The text input field.
TextareaField <textarea></textarea> Textarea field.
EmailField <input type="email"> The email text field.
HiddenField <input type="hidden"> Hidden input.
PasswordField <input type="password"> Password field.
SpacerField <hr> The spacer to separate fields and fields.
ListField <select> Select list.
CheckboxField <input type="checkbox"> Single checkbox.
CheckboxesField <input type="checkbox"> Checkbox list.
RadioField <input type="radio"> Radio list.
TimezoneField <select> A timezone select list.

Custom Fields

Custom TextField.

namespace MyCode\Fields;

class FooField extends TextField
{
    protected $type = 'foo';

    public function prepare(&$attrs)
    {
        parent::prepare($attrs);

        $attrs['bar'] = $this->getAttribute('bar');
    }
}

For XML

\Windwalker\Form\FieldHelper::addNamespace('MyCode\Filter');
<field
    name="foo"
    type="foo"
/>

Custom List field

namespace MyCode\Fields;

class UsersField extends ListField
{
    protected function prepareOptions()
    {
        $users = Database::getList('SELECT * FROM users');

        $options = array();

        foreach ($users as $user)
        {
            $options[] = new Option($user->name, $user->id);
        }

        return $options;
    }
}

Custom Filter

namespace MyCode\Filter;

class MyFilter implements FilterInterface
{
    public function clean($text)
    {
        return my_filter_function($text);
    }
}

(new TextField('foo', 'Foo'))
    ->setFilter(new MyFilter);

For XML

\Windwalker\Form\FilterHelper::addNamespace('MyCode\Filter');
<field
    name="foo"
    type="text"
    filter="my"
/>

Custom Validator

namespace MyCode\Validator;

use Windwalker\Validator\AbstractValidator;

class MyValidator extends AbstractValidator
{
    protected function test($value)
    {
        return (bool) $value;
    }
}

(new TextField('foo', 'Foo'))
    ->setFilter(new MyValidator);

For XML

\Windwalker\Form\ValidatorHelper::addNamespace('MyCode\Validator');
<field
    name="foo"
    type="text"
    filter="my"
/>

Renderer

We can add our own custom renderer to render form fields. This is a renderer example.

class MyFormRenderer
{
    public static function render(AbstractField $field, Form $form)
    {
        switch($field->getType())
        {
            case 'text':
                return static::renderText($field, $form);
                break;

            case 'list':
            case 'radio':
            case 'checkboxes':
                return static::renderList($field, $form);
                break;

            // More...

            default:
                return static::renderInput($field, $form);
        }
    }

    // more ...
}

Add renderer callback to Form:

$form->setFieldRenderHandler(array('MyFormRenderer', 'render'));

Or use invokable object.

class MyFormRenderer
{
    public function __invoke(AbstractField $field, Form $form)
    {
        // ...
    }

    // more ...
}
$form->setFieldRenderHandler(new MyFormRenderer);

If you found a typo or error, please help us improve this document.