Skip to content

Multiple choices

Use a checkbox group or a multiple select when people may choose several options. The value is a list, such as ['suits', 'kanzu'].

Example

app/Forms/PreferencesForm.php
<?php

declare(strict_types=1);

namespace App\Forms;

use Ephraitech\Forms\Form;

class PreferencesForm extends Form
{
    protected function define(): void
    {
        $this->checkboxes('interests', 'What are you interested in?', [
            'suits'       => 'Suits',
            'shirts'      => 'Shirts',
            'kanzu'       => 'Kanzu',
            'accessories' => 'Accessories',
        ])
            ->rules('required')
            ->messages(['required' => 'Choose at least one.'])
            ->inline();

        $this->select('branches', 'Branches you visit', [
            'kampala' => 'Kampala',
            'entebbe' => 'Entebbe',
            'jinja'   => 'Jinja',
        ])
            ->multiple()
            ->size(3)
            ->help('Hold Ctrl (Cmd on a Mac) to choose several.');

        $this->submit('Save preferences');
    }
}

validated() returns lists:

['interests' => ['suits', 'kanzu'], 'branches' => []]

How it works

Checkbox groups and multiple selects
checkboxes() shows one box per option; select()->multiple() shows a list where several items can be selected, with size() setting how many rows are visible. Both are submitted as name[].
Always a list
validated() returns a list, and an empty one ([]) when nothing is chosen, never null or a missing key. A single value sent instead of a list is wrapped in one.
Rules
rules() applies to the list as a whole: required means at least one item. eachRules() applies to every item, for example eachRules('max_length[20]').
Options are checked
Every chosen value must be one of the options. Anything else, such as a value added with the browser's developer tools, is rejected with "Please choose a valid option for ...". You do not need an in_list rule.
Editing
fill() accepts a list, and the matching boxes are ticked. After a failed submission, exactly the boxes the visitor ticked stay ticked.

Storing lists

A list can be stored as JSON in a text column. CodeIgniter models convert it for you with casts:

app/Models/CustomerPreferenceModel.php
<?php

declare(strict_types=1);

namespace App\Models;

use CodeIgniter\Model;

class CustomerPreferenceModel extends Model
{
    protected $table         = 'customer_preferences';
    protected $returnType    = 'array';
    protected $allowedFields = ['customer_id', 'interests', 'branches'];

    // Lists are stored as JSON text and come back as arrays.
    protected array $casts = [
        'interests' => 'json-array',
        'branches'  => 'json-array',
    ];
}
app/Database/Migrations/2026-10-29-000200_CreateCustomerPreferences.php
<?php

declare(strict_types=1);

namespace App\Database\Migrations;

use CodeIgniter\Database\Migration;

class CreateCustomerPreferences extends Migration
{
    public function up(): void
    {
        $this->forge->addField([
            'id'          => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
            'customer_id' => ['type' => 'INT', 'unsigned' => true],
            'interests'   => ['type' => 'TEXT'],
            'branches'    => ['type' => 'TEXT'],
        ]);
        $this->forge->addPrimaryKey('id');
        $this->forge->createTable('customer_preferences');
    }

    public function down(): void
    {
        $this->forge->dropTable('customer_preferences');
    }
}

With the casts, $model->insert($form->validated()) stores ["suits","kanzu"], find() returns the list again, and fill() puts it back into the form. When you need to search or join on the choices, store them in a separate table with one row per choice instead.

Common mistakes

Saving the list straight into a plain column. Without a cast (or json_encode()), saving an array fails. Use the casts above.

Using rules('in_list[...]') for the items. It checks the list as a whole, not each item. The options are checked automatically; for other item rules use eachRules().

Expecting a placeholder on a multiple select. A multiple select has no "Choose..." option, because it would itself be selectable. Use help() instead.