This library gives you the tools to create your own custom fields. By extending the base Field
class, your field can be easily used in any form you build. And the best part is that you still have complete control over how it looks and feels.
To learn how to create a field, let's work together on a new type of field that allows the user to submit their age in years. This is a nice, simple example that will require some validation.
When we finish, usage of this new field will be as easy as:
$form->age('age')->label('Age')->required();
Let's get started!
In the same namespace (or folder) that you created your form in Step 1, create a folder called Fields
. For example path/to/my/app/forms/Fields
. Next, inside the folder we just created, create another folder called Age
that will contain everything for our new field - like so path/to/my/app/forms/Fields/Age
.
Create a class named Age
that extends Helmut\Forms\Field
.
// File: path/to/my/app/Forms/Fields/Age/Age.php
namespace App\Forms\Fields\Age;
use \Helmut\Forms\Field;
class Age extends Field {
}
Because you are extending Helmut\Forms\Field
there will be a few abstract methods that you will need to implement. Let's walk through each one below
-
The
getValue
method returns the current value of the field (fancy that eh?). It is up to you to define how the field stores it's current value. In this case we'll add a property to the class to hold the current value and return it using thegetValue
method.protected $value = ''; public function getValue() { return $this->value; }
-
The
getButtonName
method returns the name of any keys that are associated with buttons that can submit the form. As this field does not contain any buttons we don't have to do anything other than implement an empty method.public function getButtonName() { }
-
The
renderWith
method should return an array of properties to be passed to the template when rendering. The only property we have at the moment is value so let's just return that. Other properties such asid
,form_id
,name
,type
,label
,required
,valid
andinvalid
are included automatically.public function renderWith() { return ['value' => $this->value]; }
-
The
setValueFromDefault
method is used to set the current value of the field using any defaults that have been set. By extendingHelmut\Forms\Field
we have access to adefault
property that contains specified default values. Defaults can be set when your field is created - for example$form->age('age')->default(18)
. This method is only called if a default has actually been set, so you can be sure that$this->default
contains a value.public function setValueFromDefault() { $this->value = $this->default; }
-
The
setValueFromModel
method is used to set the current value of the field using a model. So to implement this method we really just want to check if the passed in model has the same name property as the field and then set the value accordingly. The name of our field is a property of the parent class and can be retrieved using$this->name
.public function setValueFromModel($model) { if (isset($model->{$this->name})) $this->value = $model->{$this->name}; }
-
The
setValueFromRequest
method is used to set the current value of the field using the request object. Fetch the value for this field out of the request (using the name again) and set it. For details about the request object check out the Request interface.public function setValueFromRequest($request) { $this->value = $request->get($this->name); }
-
The
fillModelWithValue
method is the opposite of thesetValueFromModel
. Take this field's value and fill the matching property of the model with it.public function fillModelWithValue($model) { if (isset($model->{$this->name})) $model->{$this->name} = $this->value; }
-
The
validate
method is where you can add any validation methods that need to run by default. For many fields there are no default validations needed so you can simply ignore. However in this case we want make sure the response is always numeric and no less than zero. Here you can see we added two new validation methods and called them from thevalidate
method.public function validate() { $this->numeric()->min(0); } public function validateNumeric() { return is_numeric($this->value); } public function validateMin($number) { return $this->value > 0; }
-
The
validateRequired
method is the only validation that you are forced to implement. We can just check that the value property is not empty.public function validateRequired() { return ! empty($this->value); }
So your full Age
class should look like this:
namespace App\Forms\Fields\Age;
use \Helmut\Forms\Field;
class Age extends Field {
protected $value = '';
public function getValue()
{
return $this->value;
}
public function getButtonName()
{
}
public function renderWith()
{
return ['value' => $this->value];
}
public function setValueFromDefault()
{
$this->value = $this->default;
}
public function setValueFromModel($model)
{
if (isset($model->{$this->name})) $this->value = $model->{$this->name};
}
public function setValueFromRequest($request)
{
$this->value = $request->get($this->name);
}
public function fillModelWithValue($model)
{
if (isset($model->{$this->name})) $model->{$this->name} = $this->value;
}
public function validate()
{
$this->numeric();
}
public function validateNumeric()
{
return is_numeric($this->value);
}
public function validateRequired()
{
return ! empty($this->value);
}
}
Create a folder called templates
for your age field - path/to/my/app/forms/Fields/Age/templates
. Then create a subfolder with the same name as your theme. So if we are using the default theme, the folder will be called bootstrap
- path/to/my/app/forms/Fields/Age/templates/bootstrap
.
Add a new template file named age.mustache.php
. Templates can be rendered by specifying the engine in the filename extension. For example age.mustache.php
will use the Mustache engine. Twig and Blade are also available.
// File: path/to/my/app/Forms/Fields/Age/templates/bootstrap/age.mustache.php
<div class="row">
<div class="col-sm-12">
<div class="form-group" style="margin-bottom:10px">
<label>{{ label }}{{# required }} <span style="font-weight:normal" title="{{ lang.required }}">*</span>{{/ required}}</label>
<input name="{{ name }}" value="{{ value }}" type="text" class="form-control" style="width:25%">
</div>
</div>
</div>
As you can see, you have full control over how the field is rendered.
We're almost finished! But let's try out this new age field before we continue.
// Build the form
$form = new App\Forms\Form;
// Define the new field
$form->age('age')->label('Age')->required();
// Render
echo $form->render();
Not bad hey!
Don't forget that we also needed to be able to have the option of setting a minimum. For example $form->age('age')->label('Age')->min(21)
. To create a min(21)
validation we need to add a validateMin
method to our Age
class. All validation methods must be prefixed with the word validate.
public function validateMin($min)
{
return $this->value >= $min;
}
Format the error message within a language file. Create a folder called lang
for your age field - path/to/my/app/forms/Fields/Age/lang
. Then create a file for english called en.php
- path/to/my/app/forms/Fields/Age/lang/en.php
. The error message will be picked up automatically.
return [
'validate_min' => 'The [field] field must be a minimum of [min].',
];
As you can see, it works!