Milk Admin

Form Containers with FormBuilder

This guide shows how to organize form fields using Bootstrap grid layouts with the addContainer() method in FormBuilder. Containers allow you to create professional, responsive multi-column layouts for better form organization.

Note: The addContainer() method is implemented in FormContainerManagementTrait and automatically integrated into FormBuilder.

Container Overview

The addContainer() method provides:

  • Bootstrap Grid Layout: Uses Bootstrap's responsive col-md-X classes
  • Equal or Custom Columns: Specify number of columns or custom column sizes
  • Automatic Wrapping: Extra fields automatically wrap to new rows
  • Custom Styling: Add class, style, id, or any HTML attributes
  • Positioning Control: Insert before specific fields or append at the end
  • Optional Titles: Add descriptive titles to each container

Method Signature

public function addContainer(
    string $id,              // Unique container ID
    array $fields,           // Array of field names to include
    int|array $cols,         // Number of columns OR array of column sizes
    string $position_before, // Field name before which to insert (empty = append)
    string $title,           // Optional container title
    array $attributes        // Additional HTML attributes (class, style, etc.)
): self

Basic Usage

Example 1: Equal Columns

Create a container with 3 equal columns:

$form = \Builders\FormBuilder::create($this->model, $this->page)

    // Add 3 fields in 3 equal columns (col-md-4 each)
    ->addContainer(
        'contact_info',                           // Container ID
        ['name', 'email', 'phone'],              // Fields to include
        3,                                        // 3 equal columns
        'status',                                 // Insert before 'status' field
        'Contact Information',                    // Container title
        ['class' => 'border rounded p-3 mb-4']   // Custom styling
    )

    ->addStandardActions()
    ->render();
✓ Result: Creates a Bootstrap grid with 3 equal columns (col-md-4), each containing one field. The container appears before the 'status' field.

Example 2: Custom Column Sizes

Create a container with custom column sizes using Bootstrap grid (total 12):

$form = \Builders\FormBuilder::create($this->model, $this->page)
    // Custom column sizes: [4, 5, 3] = col-md-4, col-md-5, col-md-3
    ->addContainer(
        'address_info',
        ['address', 'city', 'zip_code'],
        [4, 5, 3],                              // Custom sizes (must sum to 12)
        '',                                      // Empty = append at end
        'Address Information',
        ['class' => 'border rounded p-3', 'style' => 'background-color: #f8f9fa;']
    )

    ->render();
✓ Result: Creates a container with three columns of different widths. Address takes 4/12, city takes 5/12, and zip_code takes 3/12 of the row.

Example 3: Automatic Wrapping

When you have more fields than columns, they automatically wrap to new rows:

$form = \Builders\FormBuilder::create($this->model, $this->page)

    // 5 fields in 3 columns = 2 rows (3 fields + 2 fields)
    ->addContainer(
        'user_details',
        ['first_name', 'last_name', 'email', 'phone', 'birthdate'],
        3,                                      // 3 columns per row
        'password',
        'User Details (Auto-wrapping)',
        ['class' => 'border p-3 mb-4', 'style' => 'background-color: #e7f3ff;']
    )

    ->render();
✓ Result: Creates 2 rows. First row has 3 fields (first_name, last_name, email), second row has 2 fields (phone, birthdate).

Complete Module Example

This example demonstrates a complete working module with multiple containers:

Step 1: Create the Module PHP File

Create milkadmin/Modules/TestFormContainerModule.php:

<?php
namespace Modules;

use App\Abstracts\{AbstractModule, AbstractModel};
use App\Attributes\RequestAction;
use App\Response;

class TestFormContainerModule extends AbstractModule {

    protected function configure($rule): void
    {
        $rule->page('testFormContainer')
             ->title('Test Form Container')
             ->menu('Test Form Container')
             ->access('public');
    }

    #[RequestAction('home')]
    public function home() {
        $id = _absint($_REQUEST['id'] ?? 0);
        $data = $this->model->getByIdForEdit($id);

        $form = \Builders\FormBuilder::create($this->model, $this->page)
            // Add extra fields for testing
            ->addField('email', 'email', ['label' => 'Email Address'])
            ->addField('phone', 'tel', ['label' => 'Phone Number'])
            ->addField('address', 'string', ['label' => 'Address'])
            ->addField('city', 'string', ['label' => 'City'])
            ->addField('zip_code', 'string', ['label' => 'ZIP Code'])

            // Demo 1: Equal columns with wrapping
            ->addContainer('container1',
                ['name', 'email', 'phone', 'city', 'zip_code'],
                3,                                      // 3 equal columns
                'status',
                'Contact Information (3 cols, 5 fields = 2 rows)',
                ['class' => 'border rounded p-3 mb-4', 'style' => 'background-color: #f8f9fa;']
            )

            // Demo 2: Custom column sizes
            ->addContainer('container2',
                ['address', 'status', 'password'],
                [4, 5, 3],                             // Custom sizes
                '',                                     // Append at end
                'Address & Status (custom sizes: 4, 5, 3)',
                ['class' => 'border rounded p-3 mb-4', 'style' => 'background-color: #e7f3ff;']
            )

            ->addStandardActions()
            ->render();

        Response::render($form);
    }
}

class TestFormContainerModel extends AbstractModel {
    protected function configure($rule): void
    {
        $rule
        ->table('test_form_container')
        ->id()
        ->string('name', 100)
        ->string('status', 50)->options([
            'pending' => 'Pending',
            'active' => 'Active',
            'inactive' => 'Inactive',
            'archived' => 'Archived'
        ])->formType('list')
        ->string('password', 100, false);
    }
}

Container Features in Detail

1. Container ID

The container ID is used as the HTML id attribute for the container div:

->addContainer('my_container', [...], 3, '', '', [])

// Generates: <div id="my_container" class="...">...</div>

2. Field Names Array

Specify which fields to include in the container. All fields must exist in the form. You can also include inline HTML snippets in the array to render custom content inside a column.

// ✓ Valid - fields exist
->addContainer('container1', ['name', 'email', 'phone'], 3, '', '', [])

// ✗ Invalid - 'nonexistent_field' doesn't exist
->addContainer('container2', ['name', 'nonexistent_field'], 2, '', '', [])
// Throws: InvalidArgumentException: Field 'nonexistent_field' does not exist in the form

3. Column Configuration

Integer (Equal Columns):

->addContainer('container1', ['field1', 'field2', 'field3'], 3, '', '', [])
// Creates: col-md-4, col-md-4, col-md-4 (12/3 = 4)

Array (Custom Sizes):

->addContainer('container2', ['field1', 'field2', 'field3'], [4, 5, 3], '', '', [])
// Creates: col-md-4, col-md-5, col-md-3 (total = 12)
Note: Bootstrap uses a 12-column grid system. Column sizes should ideally sum to 12 per row for best results.

4. Position Control

Control where the container appears in the form:

// Insert before 'status' field
->addContainer('container1', [...], 3, 'status', '', [])

// Append at the end (empty string or omit)
->addContainer('container2', [...], 3, '', '', [])

5. Container Title

Add a descriptive title to the container:

->addContainer('container1', [...], 3, '', 'Contact Information', [])
// Generates: <h4 class="mb-3">Contact Information</h4>

6. Custom Attributes

Add any HTML attributes to the container div:

->addContainer('container1', [...], 3, '', 'Title', [
    'class' => 'border rounded p-3 mb-4',
    'style' => 'background-color: #f8f9fa;',
    'data-section' => 'contact',
    'id' => 'custom-id'                    // Note: 'id' will be overridden by container ID
])

Generated HTML Structure

The addContainer() method generates a Bootstrap grid structure:

<!-- Container with 3 equal columns -->
<div class="border rounded p-3 mb-4" id="contact_info" style="background-color: #f8f9fa;">
    <h4 class="mb-3">Contact Information</h4>

    <!-- Row 1 (first 3 fields) -->
    <div class="row g-3 milk-row-1 mb-3">
        <div class="col-md-4">
            <!-- name field HTML -->
        </div>
        <div class="col-md-4">
            <!-- email field HTML -->
        </div>
        <div class="col-md-4">
            <!-- phone field HTML -->
        </div>
    </div>

    <!-- Row 2 (remaining fields, if wrapping) -->
    <div class="row g-3 milk-row-2">
        <div class="col-md-4">
            <!-- additional field -->
        </div>
    </div>
</div>

Related Documentation

Loading...