Have you seen our video tutorials? Check 'em out!

Form Builder - Custom layout

emergingdzns - 3 weeks ago

I'm trying to build a module that has a stream in it called "Customers". In that stream are the following fields:

'email' => [
    'type' => 'anomaly.field_type.email'
],
'billing_first_name' => [
    'type' => 'anomaly.field_type.text'
],
'billing_last_name' => [
    'type' => 'anomaly.field_type.text'
],
'billing_phone' => [
    'type' => 'anomaly.field_type.text'
],
'billing_address' => [
    'type' => 'anomaly.field_type.textarea'
],
'billing_city' => [
    'type' => 'anomaly.field_type.text'
],
'billing_state' => [
    "type"   => "anomaly.field_type.state",
    "config" => [
        "default_value" => null,
        "top_options"   => null,
        "countries"     => ['US','CA'],
        "mode"          => "text",
        "handler"       => "Anomaly\StateFieldType\Handler\[email protected]"
    ]
],
'billing_zip' => [
    'type' => 'anomaly.field_type.text'
],
"billing_country" => [
    "type"   => "anomaly.field_type.country",
    "config" => [
        "default_value" => 'US',
        "top_options"   => ["US", "CA", "MX"],
        "mode"          => "text",
        "handler"       => "Anomaly\CountryFieldType\Handler\[email protected]"
    ]
],
'shipping_first_name' => [
    'type' => 'anomaly.field_type.text'
],
'shipping_last_name' => [
    'type' => 'anomaly.field_type.text'
],
'shipping_phone' => [
    'type' => 'anomaly.field_type.text'
],
'shipping_address' => [
    'type' => 'anomaly.field_type.textarea'
],
'shipping_city' => [
    'type' => 'anomaly.field_type.text'
],
'shipping_state' => [
    "type"   => "anomaly.field_type.state",
    "config" => [
        "default_value" => null,
        "top_options"   => null,
        "countries"     => ['US','CA'],
        "mode"          => "text",
        "handler"       => "Anomaly\StateFieldType\Handler\[email protected]"
    ]
],
'shipping_zip' => [
    'type' => 'anomaly.field_type.text'
],
"billing_country" => [
    "type"   => "anomaly.field_type.country",
    "config" => [
        "default_value" => 'US',
        "top_options"   => ["US", "CA", "MX"],
        "mode"          => "text",
        "handler"       => "Anomaly\CountryFieldType\Handler\[email protected]"
    ]
]

Now, if I use the CustomerFormBuilder out of the box, I end up with one seriously loooong form in a single column.

What I want is a two column layout, with the billing fields (email plus all billing_* fields) in one column and then all of the shipping fields (shipping_* fields) in the second column.

I also want to add a single checkbox field before the shipping fields that the customer can check to say that they are using the same info for shipping as for billing. But I didn't want that as a field in the stream.

So it should look something like this:

https://www.evernote.com/l/AE3_1czw02NKqpngVlMSuEgpJVTkyDNBtlY

I'm unclear as to how to make this work with the builder. I tried messing with sections before but coudn't make heads or tails of it at the time.

Thanks!

Answer

ryanthompson - 3 weeks ago

Since Pyro does not yet support columns / rows just yet the easiest way to do this is with sections and in particular sections using views.

You can define sections in your builder like protected $sections = [];

Try something like this:

protected $sections = [
    'billing' => [
        'view' => 'example.module.foo::the/form/billing_col',
    ],
    'shipping' => [
        'view' => 'example.module.foo::the/form/shipping_col',
    ],
];

Now inside of those views just include your HTML markup and include the fields like {{ form.fields.billing_first_name|raw }}.

ryanthompson - 3 weeks ago

Since Pyro does not yet support columns / rows just yet the easiest way to do this is with sections and in particular sections using views.

You can define sections in your builder like protected $sections = [];

Try something like this:

protected $sections = [
    'billing' => [
        'view' => 'example.module.foo::the/form/billing_col',
    ],
    'shipping' => [
        'view' => 'example.module.foo::the/form/shipping_col',
    ],
];

Now inside of those views just include your HTML markup and include the fields like {{ form.fields.billing_first_name|raw }}.

emergingdzns - 3 weeks ago

Ahhhhhh! That's rad. I had no idea we could define views inside of the sections. That's great!

Thanks!

ryanthompson - 3 weeks ago

You are quite welcome! Check this out: http://pyrocms.com/documentation/streams-platform/v1.1#ui/control-panel/the-section-definition

ryanthompson - 3 weeks ago

Er.. shit. It's not listed! Ill correct them.

emergingdzns - 3 weeks ago

Ok, so I've revised the code and added the form builder php as follows:

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

use Anomaly\Streams\Platform\Ui\Form\FormBuilder;
use MyFolder\StoreModule\Customer\CustomerModel;

class CustomerFormBuilder extends FormBuilder
{

    protected $model = CustomerModel::class;

    /**
     * The form fields.
     *
     * @var array|string
     */
    protected $fields = [
        'email',
        'billing_first_name',
        'billing_last_name',
        'billing_address',
        'billing_city',
        'billing_state',
        'billing_zip',
        'billing_country',
        'billing_phone',
        'shipping_first_name',
        'shipping_last_name',
        'shipping_address',
        'shipping_city',
        'shipping_state',
        'shipping_zip',
        'shipping_country',
        'shipping_phone'
    ];

    /**
     * Fields to skip.
     *
     * @var array|string
     */
    protected $skips = [];

    /**
     * The form actions.
     *
     * @var array|string
     */
    protected $actions = [];

    /**
     * The form buttons.
     *
     * @var array|string
     */
    protected $buttons = [];

    /**
     * The form options.
     *
     * @var array
     */
    protected $options = [];

    /**
     * The form sections.
     *
     * @var array
     */
    protected $sections = [
        'billing' => [
            'view' => 'myfolder.module.store::billing_columns',
        ],
        'shipping' => [
            'view' => 'myfolder.module.store::shipping_columns',
        ],
    ];

    /**
     * The form assets.
     *
     * @var array
     */
    protected $assets = [];

}

In the view where I'm trying to load the form, I tried adding the following: {{ form('myfolder.module.store:customer')|raw }} {{ form('myfolder.module.store::customer')|raw }} {{ form('customer')|raw }}

But in all cases, I get an error that the Class customer does not Exist.

What does I need to do?

Thanks!!

emergingdzns - 3 weeks ago

Oh wait. I figured that part out. I was missing the binding in the service provider. Oops.

That being said, I'm getting a different error. I'll keep plugging away at it, but figured I'd mention it here now just in case you know right off what the issue is...

Where I put the form, it's now showing this:

Whoops, looks like something went wrong.

FatalErrorException in 8acf457473d34a610bd7984941106c9c5fad63ee7b39a0d809f5c4e40ab4956a.php line 0:
Method Anomaly\Streams\Platform\Ui\Form\FormCriteria::__toString() must not throw an exception, caught ErrorException: Undefined index: type
in 8acf457473d34a610bd7984941106c9c5fad63ee7b39a0d809f5c4e40ab4956a.php line 0

Not a lot of info to go on there...

emergingdzns - 3 weeks ago

I still haven't figured this out. I tried adding some debugging to the FormCriteria file to see if I can figure out where things are going wrong, but didn't get anywhere. Here's the breakdown of how things are put together.

In my module, I have a controller that is doing this in the checkout process:

return view('myfolder.module.store::checkout-step2');

Here is the view file mentioned above:

{% extends layout('default') %}

{% block content %}
<div class="row">
    <div class="col-xs-12 col-sm-12">
        {{ form('customer')|raw }}
    </div>
</div>
{% endblock %}

The CustomerFormBuilder is above. The CustomerFormFields file is here:

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

use Illuminate\Contracts\Config\Repository;

/**
 * Class CustomerFormFields
 *
 */
class CustomerFormFields
{

    /**
     * Handle the fields.
     *
     * @param CustomerFormBuilder $builder
     * @param Repository       $config
     */
    public function handle(CustomerFormBuilder $builder, Repository $config)
    {
        $builder->setFields(
            [
                'email',
                'billing_phone',
                'billing_first_name',
                'billing_last_name',
                'billing_address',
                'billing_city',
                'billing_state',
                'billing_zip',
                'billing_country',
                'shipping_first_name',
                'shipping_last_name',
                'shipping_address',
                'shipping_city',
                'shipping_state',
                'shipping_zip',
                'shipping_country',
                'shipping_phone'
            ]
        );
    }
}

Here is the CustomerFormHandler:

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

use Illuminate\Routing\Redirector;
use Symfony\Component\HttpFoundation\Response;
use Clarityhealth\StoreModule\Customer\CustomerModel;
use Input;
use Session;
use Request;

/**
 * Class CustomerFormHandler
 *
 */
class CustomerFormHandler
{

    /**
     * Handle the form.
     *
     * @param CustomerFormBuilder  $builder
     * @param UserAuthenticator $authenticator
     * @param UserSecurity      $security
     * @param Redirector        $redirect
     */
    public function handle(CustomerFormBuilder $builder, Redirector $redirect) {
        // I haven't built the action yet...
    }
}

So now here are the billing and shipping column twigs:

<div class="row">
    <div class="col-xs-12 col-sm-6"><h4>Billing Information</h4>
        <div class="form-group">
            {{ customer.fields.email|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_phone|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_first_name|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_last_name|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_address|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_city|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_state|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_zip|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.billing_country|raw }}
        </div>
    </div>
    <div class="col-xs-12 col-sm-6">
        <h4>Shipping Information</h4>
        <div class="form-group">
            <label>Use the billing information for shipping?</label>
            <div class="checkbox" style="margin-bottom: 4px;">
                <label><input type="checkbox" name="use_billing" value="1"> Yes</label>
            </div>
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_phone|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_first_name|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_last_name|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_address|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_city|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_state|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_zip|raw }}
        </div>
        <div class="form-group">
            {{ customer.fields.shipping_country|raw }}
        </div>
    </div>
</div>

FYI, I tried "form.fields.xxxx" in the above as well just as suggested but still get the error. So I then removed all of the twig field lines so each view is straight up html and still get the same error.

Could really use some guidance here. Thanks!

ryanthompson - 3 weeks ago

Try using {{ form('customer').get().getContent()|raw }} to get more output information about that error.

emergingdzns - 3 weeks ago

Thanks. That provided the following:

An exception has been thrown during the rendering of a template ("Undefined index: type") 

Line 28 is the form line you suggested. I'm perplexed as to what "type" index it's referring to.

emergingdzns - 3 weeks ago

It just occurred to me that maybe I needed to add {% block content %} and {% endblock %} to the two column view files. I tried that and still no luck. However, I scrolled down and now see two additional messages:

1/3
ErrorException in FieldTypeBuilder.php line 95:
Undefined index: type

and

2/3
Twig_Error_Runtime in Template.php line 182:
An exception has been thrown during the rendering of a template ("Undefined index: type")

In the 1/3 above (FieldTypeBuilder.php), I'm seeing more info in the stacktrace:

in FieldTypeBuilder.php line 95
at HandleExceptions->handleError('8', 'Undefined index: type', '/path/to/site/vendor/anomaly/streams-platform/src/Addon/FieldType/FieldTypeBuilder.php', '95', array('parameters' => array('field' => 'shipping_country', 'prefix' => null, 'rules' => array('nullable'), 'translatable' => false), 'type' => null)) in FieldTypeBuilder.php line 95
at FieldTypeBuilder->build(array('field' => 'shipping_country', 'prefix' => null, 'rules' => array('nullable'), 'translatable' => false)) in FieldFactory.php line 87
at FieldFactory->make(array('field' => 'shipping_country', 'prefix' => null, 'rules' => array('nullable'), 'translatable' => false), object(StreamModel), object(CustomerModel)) in FieldBuilder.php line 86
at FieldBuilder->build(object(CustomerFormBuilder)) in BuildFields.php line 33

I tried removing the shipping_country and billing_country fields from the two twig columns but still get the error.

I'm going to keep looking for the problem.

emergingdzns - 3 weeks ago

ok so more progress. I discovered I had a field defined that I had forgotten to add to the stream! I removed that one. Now I'm getting a different error. I feel like I'm getting close but I just can't break through this.

FatalErrorException in Environment.php(403) : eval()'d code line 0:
Method Illuminate\View\View::__toString() must not throw an exception, caught ErrorException: An exception has been thrown during the rendering of a template ("An exception has been thrown during the rendering of a template ("An exception has been thrown during the rendering of a template ("View [text] not found.") in "/path/to/site/vendor/anomaly/streams-platform/src/View/Command/../../../resources/views/form/partials/wrapper.twig" at line 41.") in "/path/to/site/vendor/anomaly/streams-platform/src/View/Command/../../../resources/views/form/partials/fields.twig" at line 2.") in "/path/to/site/vendor/anomaly/streams-platform/src/View/Command/../../../resources/views/form/standard.twig" at line 11.

ryanthompson - 3 weeks ago

Ok that one means that there is a syntax error I believe. Double check your views / post them here if you like. Try the deletion of your section views and add them back to find the culprit.