apility / form-builder
Form builder base library for the Netflex SDK
Requires
- netflex/sdk: ^4
- netflex/structures: ^4
README
Installation
Add the form builder to your composer file
$ composer require apility/form-builder
Create a new Service provider that extends the Netflex\FormBuilder\Providers\FormBuilderServiceProvider
$ php artisan form-builder:install
Will generate a new service provider remember to register it.
Basic example.
<?php namespace App\Providers; use Netflex\FormBuilder\Fields\TextInput; use Netflex\FormBuilder\Interfaces\FormFieldRepository; use \Netflex\FormBuilder\Providers\FormBuilderServiceProvider as FormBuilderBaseServiceProvider; class FormBuilderServiceProvider extends FormBuilderBaseServiceProvider { function boot(FormFieldRepository $repo) { $repo->registerField('text', TextInput::class); } }
The registerField
method registers a question/field type that can be rendered by the form builder. You can implement
your own (see the section below for instructions). The class that is provided must implement
the Netflex\FormBuilder\Interfaces\FormField
class.
We do have a basic implementation that comes with prefilled working bookkeeping functionality such as formModel and name injection, as well as hoisting values from the matrix field data into the model as attributes.
It might be quicker to use this than implementing similar functionality manually.
Check out the Netflex\FormBuilder\Fields\BaseField
class.
Overrides
It is possible to override data coming from Netflex using an optional 3rd parameter to the registerField
function.
You can supply an optional associative array to the function like this
$repo->registerField('text', TextInput::class); $repo->registerField('always-required-text', TextInput::class, ['required' => true]);
This is merely substituting any values from Netflex with these, that way you can omit fields that are configurable in the class, but not something the user should be able to change. Or lets you create reusable field types that can be configured when added.
It is possible to add the same class more than once.
Add the service providers to your bootstrap/app.php
$app->register(\App\Providers\FormBuilderServiceProvider::class);
One of these is the one you made yourself, the other provides views.
Have your form model implement the FormModel interface
The getFormAttributeKey
should be the same as the alias of the Matrix that contains the questions for the form.
The getErrorBagName
function decides where the validation errors for FormBuilderRequests go, this should often be
something other than default
.
<?php namespace App\Models; use Illuminate\Routing\Router; use Netflex\FormBuilder\Interfaces\FormModel; use Netflex\FormBuilder\Traits\DefaultFormFieldName; use Netflex\Structure\Model; class Form extends Model implements FormModel { use DefaultFormFieldName; /** * The directory_id associated with the model. * * @var int */ protected $relationId = 12345; // public function getFormAttributeKey(): string { return "fields"; } public function getErrorBagName(): string { return "form"; } }
DefaultFormFieldName
trait
The model must decare some values which are considered to be menial bookkeeping and not something the developer really should need to care about except for very specific cases. This trait implements this functionality so the developer does not have to.
What it does is default the named prefix for all fields to be "questions". If you want to rename this field, implement the relevant methods manually.
Implementing custom field types
The Class
In order to create your own field type, create a new class that extends the Netflex\FormBuilder\Fields\BaseField
class.
This is both a Laravel view and a special field class that requires you to extends certain variables.
The BaseField class, represents a question in your form and is instantiated once for each
QOL tips
- As long as you don't override the constructor of the BaseField class, all the matrix content will be put on this object
automatically. In other words; If you have a "question" field in your matrix block in Netflex, then you will have access to it using
$this->question
<?php namespace Netflex\FormBuilder\Fields; class TextInput extends BaseField { public $required; public $question; public $description; public function formQuestion(): string { return $this->question ?? "Question is missing"; } public function formDescription(): ?string { return $this->description; } function formValidators(): array { return $this->required ? ['required'] : []; } function formValidationMessages(): array { return [ 'required' => "Du må fylle inn '{$this->question}' feltet", ]; } public function render() { return view("form-builder::form-fields.text-input", [ 'question' => $this->question, 'description' => $this->description ]); } }
The View
They are basically normal components
It is recommended that you use the view()
function when rendering these components.
- If your render method returns a Laravel view object (for example using the
view()
function) then the errors for the form will be hoisted to the default viewBag so you dont have to manually get the correct view bag when resolving errors for a particular field. - If your render method returns a Laravel view object, then you will also get only error messages related to your current field from the variable
$fieldErrors
Usage
This package is intended to transform Netflex Structure entries into forms using a matrix field. It provides some simple components for rendering forms components and validating them.
You must supply your own <form>
tags and backend functionality after the form request has been
verified. The indented ide here is that we provide functionality that handles all form specific things, but does still
let's you use laravel for other non form related things in an organic way.
To get started first create a form using any structure that has a model that implements the FormModel
interface.
/// Find some entry based on whatever criteria you want $form = MyFormModel::find(12345);
Rendering forms
You can render a simple form like this.
You wrap a <x-form-builder::form>
component in a form tag.
That is it.
It is still possible to add your own input fields that are not part of the form. You will need to manually parse them, but it is still possible.
<form action="{{ route('some.route-that-accepts-forms', $form) }}" method="post">
<input type="text" name="optional_hard_coded_field">
<x-form-builder::form :form="$form" />
<button class="submit-button">Send this form</button>
</form>
Show all errors for the form
Sometimes you want a big alert that gives you a list of all failing validation errors when a user submitted a form and it
failed to pass validation.
Use the <x-form-builder::validation-errors>
component for that.
<x-form-builder::validation-errors :form="$form" />
Slot
The <x-form-builder::validation-errors>
component has a slot that lets you input text and similar right above the list
of validation errors, inside the alert.
<x-form-builder::validation-errors :form="$form"> Oops, something went wrong. Check the contents of your form and try again </x-form-builder::validation-errors>
Built in Form fields
The library comes with some built in Bootstrap 5 compatible fields for quick and dirty forms.
Text Input (Netflex\FormBuilder\Fields\TextInput
)
All arguments can be controlled from Netflex if added to the matrix block definition
Arguments
Internationalization
The following internationalization keys are used by this field
form-builder.question.text-input.required
This field determines the message the user sees when the field is empty. (Should not happend as it will be validated by the browser)
Following variables can be used in the form:name
Returns the question
Numeric Text Field (Netflex\FormBuilder\Fields\NumericTextInput
)
This text field is forced to be a number field. It also validates number ranges on the backend Placeholder is set to an i18n value that indicates potential top/bottom bounds of valid options.
Arguments
Internationalization
The following internationalization keys are used by this field
-
form-builder.question.numeric-text-input.required
This field determines the message the user sees when the field is empty. (Should not happend as it will be validated by the browser)
Following variables can be used in the form:name
Returns the question
-
form-builder.question.numeric-text-input.min
Message shown to user when the number they selected is too low
Following variables can be used in the form:name
Returns the question:count
Returns minimum value
-
form-builder.question.numeric-text-input.max
Message shown to user when the number they selected is too high
Following variables can be used in the form:name
Returns the question:count
Returns maximum value
-
form-builder.question.numeric-text-input.numeric
Message shown to user if the number presented is not numeric
Following variables can be used in the form():name
Returns the question
TextArea (Netflex\FormBuilder\Fields\TextArea
)
A multiline text area
Arguments
Accepts all the same fields as TextInput (except from formType
), but also
Internationalization
The following internationalization keys are used by this field
form-builder.question.numeric-text-area.required
This field determines the message the user sees when the field is empty. (Should not happend as it will be validated by the browser)
Following variables can be used in the form:name
Returns the question
Checkbox (Netflex\FormBuilder\Fields\Checkbox
)
Displays a single checkbox. If required, the box must be checked to proceed. This is mostly used for confirmation boxes.
Arguments
Internationalization
The following internationalization keys are used by this field
form-builder.question.checkbox.required
This field determines the message the user sees when the field is not checked, if it is required
Following variables can be used in the form:name
Returns the question
Select (Netflex\FormBuilder\Fields\Select
)
Shows a dropdown of preselected values
Arguments
Internationalization
The following internationalization keys are used by this field
form-builder.question.select.required
This field determines the message the user sees when no value has been selected (if required is set)
Following variables can be used in the form:name
Returns the question
Form Validation
The package comes with a juiced up Laravel HTTP class that can automatically validate form fields. This allows you to specify validation logic per component and not have to deal with it, as long as you use laravel form requests.
$ php artisan make:form-builder
works similarilly to make:request
and will create a standard request.
Basic example
<?php namespace App\Http\Requests\FormBuilder; use Illuminate\Foundation\Http\FormRequest; use Netflex\FormBuilder\Interfaces\Form; use Netflex\FormBuilder\Interfaces\FormModel; use Netflex\FormBuilder\Requests\FormBuilderRequest; class NormalFormRequest extends FormBuilderRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } public function rules() { return [ ]; } /** * * Resolve the same form that you used to render the submitted form * * @return FormModel|null */ function getForm(): ?FormModel { // TODO: Resolve form } }
The getForm
method must return the same form that was used to render the submitted form.
Personally, I like using url parameters for this. That way we can use the Route parameter injection in