Combining Form Builders


Introduction

Form builders are based on the concept of a single model and therefor represent a single stream data structure. However you can leverage multiple form builders to combine multiple form builders into one.

Basic Usage

Let's assume for this example that we want to combine forms for a user and customer information that is tied to a given user.

To begin first create a form builder that extends \Anomaly\Streams\Platform\Ui\Form\Multiple\MultipleFormBuilder like this:

<?php namespace Anomaly\StoreModule\Customer\Form;

use Anomaly\Streams\Platform\Ui\Form\Multiple\MultipleFormBuilder;

class RegisteredCustomerFormBuilder extends MultipleFormBuilder
{
    ...
}

Since the a form builder for the user stream already exists in core we will only need a form builder for the customer stream. Let's assume that the CustomerFormBuilder has already been created for us with the make:stream command.

Combining Forms

The only required step in using a multiple form builder is adding the child forms to it. To add a form to the multiple form builder use the addForm method. The first argument is a key that is used as the prefix. If the key is customer then the form being added will be prefixed customer_. The second argument is the form builder instance:

$multiple->addForm('user', $userBuilder);
$multiple->addForm('customer', $customerBuilder);

All of the fields, skips, and validation are all inherited by the form that is added and will be run all the same.

Important: Forms are validated and saved in the order in which they are added.

Multiple Form Builder Sections

Just like with normal forms you may want to customize the layout and organization of fields within a multiple form builder. The process is the exact same in that you define sections on the multiple form builder's $sections property. There is only one exception; You must refer to field including their form's prefix.

So a field with the slug of name in the $customerBuilder referenced above the field would be referenced in the multiple form builder's sections as customer_name.

Tying Entries Together

Needing to combine multiple form builders often indicates that the entries need to force a relation between each other. To do this we will keep in mind our saving order and use callbacks to set information on the entries of the other forms.

When posting and saving the multiple form builders fire a special callback for each form builder specifically:

posting_{form_key}
posted_{form_key}
saving_{form_key}
saved_{form_key}

Let's use the saved callback to pass the saved user entry to our customer entry being created which has a required relationship field with the slug of user:

$multiple->on('saved_user', function() use ($multiple) {

    $user = $multiple->getChildFormEntry('user');
    $customer = $multiple->getChildFormEntry('customer');

    $customer->user = $user;
});

We can also use bind to register the callback. Using bind will allow us to use $this within the closure as if it were the multiple form builder. Similar to a native class function.

$multiple->bind('saved_user', function() {

    $user = $this->getChildFormEntry('user');
    $customer = $this->getChildFormEntry('customer');

    $customer->user = $user;
});

Putting It Together

Let's put this all together in our controller method for creating a new registered customer:

public function create(RegisteredCustomerFormBuilder $form, CustomerFormBuilder $customer, UserFormBuilder $user)
{
    $form->addForm('user', $user);
    $form->addForm('customer', $customer);

    $form->on('saved_user', function() use ($form) {

        $user = $form->getChildFormEntry('user');
        $customer = $form->getChildFormEntry('customer');

        $customer->user = $user;
    });

    return $form->render();
}