rickselby/laravel-request-field-types

Allow definitions of common input field types for requests

3.0.1 2018-05-19 09:12 UTC

README

PHP 7.0+ Build Status SensioLabs Insight Code Coverage Software License

A way of defining common input field types in a central location and use them among all requests in your app.

NB: 3.x passes tests but I haven't tried it 'for real' yet; I will do so at some point and may write better tests...

Laravel Request Field Types Laravel PHP
3.x 5.6 7.1.3+
2.x 5.4 – 5.5 7.0+

Installing

$ composer require rickselby/laravel-request-field-types

Under Laravel >=5.5, the service provider will be automatically registered.

Terminology

'Field' is used as two different terms here; I hope I've been clear throughout the documentation:

  • Field Type - e.g. date, name, email...
  • Input Field - the fields posted as part of a request

Defining field types

Each field must implement RickSelby\LaravelRequestFieldTypes\InterfacesFieldTypeInterface. RickSelby\LaravelRequestFieldTypes\BaseFieldType implements the interface and sets up common functions, and is a good starting place to implementing your own fields.

Two things need implementing from the BaseFieldType:

  • rules() - the default rules for an input field
  • setMessagesFor($inputField) - define any custom messages for the input field

An example field is included (DateFieldType).

Using field types in requests

Start by extending RickSelby\LaravelRequestFieldTypes\FieldTypesRequest instead of Illuminate\Foundation\Http\FormRequest.

Then, two functions need defining:

  • defineRules()
  • defineMessages()

There is no need to define rules() and messages(); these are managed within the class.

defineRules()

Instead of adding rules to an array in rules(), we can define them using functions here.

For a defined field types, use setInputsFor():

$this->setInputsFor(DateFieldType::class, ['start_date', 'end_date']);

// Passing a key => value pair allows extra rules to be added to an input field;
$this->setInputsFor(DateFieldType::class, ['start_date' => 'required']);

// Keyed and non-keyed field names can be mixed as required
$this->setInputsFor(DateFieldType::class, ['start_date' => 'required', 'end_date']);

For other fields, rules can be set directly with setRules():

$this->setRules('otherfield', ['required', 'numeric']);

Ordering

The request keeps track of the order rules are set, and returns the rules in the given order, so the validation messages are returned in the desired order. It is possible to override the field order, if preferred:

$this->setFieldOrder(['field1', 'field2'...]);

defineMessages()

Custom messages for defined field types' default rules can be set in the field type. Other messages can be set for rules using setMessage()

$this->setMessage('start_date.required', 'A start date must be provided.');

Modifying the request data

(This is probably a contentious way of modifying input, but it makes sense to me...)

Say we have a date field. The input field knows what format will be generated, and the request will know what format to validate. Where does it get converted for use in the rest of the app? Do we need to define the date format elsewhere as well, or can this be handled in the request?

Since the request knows about the expected input formats, it seems the right place to modify (valid) data for use in the rest of the app.

The supplied DateFieldType does this; the mapAfterValidationFunction will be run on all input fields set for this field type once validation has suceeded but before the validation returns.

If you need to do more complex alterations to the request data, the modifyInputAfterValidation function can be overridden directly.

...but why?

This was mostly driven by the desire to modify the request data.

Starting with a date field - must I define the date format in every request? Can I have a single base request that others extend from, and I define it there? Can I convert the input to a carbon instance before it is returned to the app?

Then, other fields fell into the same pattern - other inputs that could be modified to a better format for use in the app. The base request for the app became large and unwieldy, and thus this class was born.

Perhaps it is overkill, even for defining common field types within an app.