Repository navigation
Releases
v0.5.0
Documentation site with tested examples, themes that extend other themes, and genuine PUT,
PATCH and DELETE request bodies. No breaking changes.
Added
- Documentation site, built from
docs/with Zensical (readingmkdocs.yml) and published
to GitHub Pages on release tags by a new Docs workflow that builds in strict mode on every
push and pull request. Code examples are real files indocs/examples/app/, included into
the pages and run by a newtests/Docssuite, so published examples always work. CONTRIBUTING.mdwith instructions for running the tests and previewing the documentation.- Getting started tutorial: appointment booking with a two-column layout, a business rule
and editing, built from tested example files run over HTTP against a database. - Guides for layout, multiple choices, file uploads, custom field types and customising views,
each with tested examples (a realapp/Config/Forms.phpexample is tested in separate
processes so it cannot affect other tests). - Themes can extend another theme (
'extends' => 'bootstrap5'): templates missing from a
theme's views folder are taken from the theme it extends, so a custom theme only needs the
templates it changes. The parent must be a registered theme; loops are guarded against. - Guides for option providers, forms written by admins, showing submissions, testing your
forms, and JavaScript and APIs, with tested example apps (an admin form editor and public
form pages, a submissions screen, a JSON API) and example tests that use CodeIgniter's
standard$migrate = true. tests/bootstrap.phptreatsdocs/examples/appas part of the test app, so example
migrations and views work as they would in a real app.- Reference section: Form, Fields, Schemas, Configuration, and Helpers and commands, written
by hand and checked byApiReferenceTest(every public method, setting, theme class key,
helper and command option must be documented, and everything documented must exist), plus
a Schema keys page generated fromFormSchema::describe()withcomposer docs:reference
and checked bySchemaReferenceTest. - Concepts section explaining values, validation, security and architecture, with every
statement checked byConceptsClaimsTest(each test is named after the page section that
makes the claim); Upgrading and Changelog pages (the changelog page includes this file).
Fixed
validate()ignored the data of genuinePUT,PATCHandDELETErequests (from
JavaScript or API clients), whose URL-encoded body PHP does not put in$_POST. It now
reads the raw body for those methods when$_POSTis empty. Browser forms, which send
POSTwith a hidden_methodfield, were not affected.
v0.4.0
Showing stored data, a forms helper, and FormSchema::describe() as groundwork for visual
builders. No breaking changes.
Added
Form::present($data)turns stored data, such as a saved submission, into readable rows:
field labels, option labels instead of values (including groups and provider options),
Yes/No for checkboxes and lists for multi-value fields. Values no longer among the options
are shown as stored. Passwords, buttons and uploads are never included.
Form::renderPresented($data)renders them with the newpresentedtheme view.Contracts\PresentableFieldInterface(isPresentable(),presentValue()), implemented by
every field; custom fields can overridepresentValue(), for example to format amounts.- Config
$presentYes,$presentNo,$presentEmptyand theme classespresented,
presented_label,presented_value,presented_empty. - A
formshelper (helper('forms')) withforms_load(),forms_check()and
forms_present(). Functions are prefixedforms_so they never clash with CodeIgniter's
formhelper. FormSchema::describe($policy)describes what a schema may contain under a policy, as
JSON-ready data: every allowed field type with its label, accepted keys, the kind of value
each key takes and a one-line explanation, which keys are required (requires) or cannot be
combined (exclusive), plus the allowed rules with their parameter kinds, attributes,
option providers, upload types, size limits and forbidden names. Groundwork for visual
builders and admin help pages; a test loads every described key through the loader under
each policy, so the description and the loader cannot disagree.SchemaLoader::keysFor()(the loader's own key lists, shared withdescribe()) and
SchemaPolicy::getRules(),getAttributes(),getForbiddenNames(),getLimits().
v0.3.0
Forms from schemas: load forms from arrays or JSON written by developers or admins, with a
restricted policy by default, option providers, admin checks and export. No breaking changes.
Added
- Schema loading:
Form::fromSchema(),Form::fromJson()and$form->loadSchema()build
forms from arrays or JSON (schema version 1): every built-in field type and option, rows and
fieldsets, form settings, and registered custom types. Loading is all or nothing: the
schema is first built on a scratch form and every error is collected with its location
(fields[2].children[0].max_size: ...), including typo suggestions and hints for HTML
habits such asmaxlength. Keys are applied in schema order. Schema\SchemaExceptionwithgetErrors()andgetMessages();Schema\SchemaLoader::errors()
checks a schema without loading it.Contracts\SchemaConfigurableInterfacelets custom field types accept extra schema keys.Schema\SchemaPolicy: every schema is checked against a policy, restricted by default
(Form::fromSchema(),fromJson()andloadSchema()all take an optionalpolicy).
SchemaPolicy::restricted(), for schemas written by admins or clients, blocks event-handler
and behaviour attributes (on*,formaction,style,data-*), rules outside an
allow-list with checked parameters (nois_unique,regex_matchor custom rules), a
schema-set form action, uploads withoutmax_size(capped at 10 MB) or without an
explicitmimeslist from safe types (no SVG or HTML), protected field names such as
roleandis_admin,passwordandhiddenfields, unlisted custom types, and oversized
schemas.SchemaPolicy::trusted()is for developer-written schemas. Policies are immutable
and can be customised (allowTypes(),allowRules(),allowAttributes(),allowMimes(),
forbidNames(),allowNames(),withLimits(),withMaxUploadSize(),allowAction());
unsafe allowances such ason*attributes or SVG uploads are refused.- Option providers: named sources of options registered in
$optionProviders
([ClassName::class, 'method']for static methods, CodeIgniter models or plain classes,
'ClassName::method', or a Closure). Schemas use"options_from": "services"; code uses
->optionsFrom('services'). Providers run lazily, once per field, and never while a schema
is loaded or checked; strict option checking uses their options.SchemaPolicycan narrow
which providers admin schemas may use (onlyProviders(),disallowProviders()).
Support\OptionProviderschecks registrations;getOptionProvider()on choice fields. Form::toSchema()andForm::toJson()export any form, including form classes, as a
version 1 schema that loads back into an identical form (same schema, same HTML, same
validation). Provider options are exported asoptions_from; attributes with their own key
(autocomplete,rows,step,min, ...) are exported under it. Closure rules and
unregistered custom field classes make the export fail with their location.Schema\FormSchema::check()for admin screens: checks array or JSON input without throwing
and returns aSchemaCheck(isValid(),getErrors(),getMessages(),getErrorsFor(),
getSchema(),toArray()for JSON responses); restricted by default.
FormSchema::load()andFormSchema::export()shortcuts.php spark forms:schema check <file> [--trusted]and
php spark forms:schema export <FormClass> [--output <file>].SchemaConfigurableInterface::schemaValues()so custom fields can export their extra keys;
Field::getExplicitId(),Field::getOwnAttributes(),Form::getExplicitId(),
File::isUploadRequired(),File::getExtraRules().
Fixed
- Forms could not be created on the command line (spark commands, queue workers, seeders):
checking reserved field names calledcsrf_token(), which starts the Security service and
needs a web request. The CSRF token name is now read from the Security config. - A source-integrity test that fails when a docblock missing its closing
*/hides class
members.
v0.2.0
Multi-value fields, strict option checking, file uploads, layout, new inputs and custom field
types. Read "Upgrading from 0.1" in the README before upgrading.
Added
- Multi-value fields:
select()->multiple()(withsize()) and a newcheckboxes()group
field. Both submit asname[]andvalidated()returns a list, empty when nothing is
chosen. eachRules()for rules checked against every item of a multi-value field, using
CodeIgniter'sname.*wildcard.permit_emptyis added automatically so an empty list
passes; userules('required')to require at least one item.- New
field_checkboxesview; views receive$inputName,$valuesand$multiple. - Strict option checking: submitted values for selects, multi-selects, radios, checkbox
groups and single checkboxes must be among the field's options (for a checkbox, its checked
value). Controlled by$strictOptionsand$strictOptionsMessagein config, per field with
->strictOptions(bool)and->messages(['options' => '...']). Errors survive
redirect()->withInput(). Contracts\ChoiceFieldInterface(getAllowedValues(),getStrictOptions()) for custom
fields with a fixed set of values;Form::usesStrictOptions().- File uploads:
file()field withrequired(),multiple(),maxSize()(kilobytes),
extensions(),mimes(),image()andmaxDimensions(), mapped to CodeIgniter's file
rules, plus a generatedacceptattribute. Optional file fields are only validated when a
file is uploaded. Uploads are never part ofvalidated(); get them with
Form::getFile(),Form::getFiles()andForm::hasUploadedFile(). - Forms with a file field are sent as multipart automatically.
- After a failed submission, file fields show a "choose the file again" hint
($fileReselectMessage, theme classreselect), since browsers cannot refill them. Contracts\FileFieldInterfacefor custom upload fields; newfield_fileview.- Theme templates missing from a theme's views folder fall back to the package's bundled copy
of that theme, so views published before an upgrade keep working with new field types.
php spark forms:publishadds the missing templates without touching customised ones. - Layout:
row()places fields side by side andfieldset()groups them under a titled
section (description(),id()); both nest and takeaddClass(). Fields and fieldsets get
width(1-12)for their column inside a row; fields without a width share the row. Layout is
defined in the form class and rendered by<?= $form ?>andfields(); fields themselves
stay a flat list, so values, validation andfield()are unaffected. Form::getLayout(),Layout\Row,Layout\Fieldset; newlayout_rowand
layout_fieldsetviews and theme classesrow,column({width}placeholder),
column_auto,section,section_legend,section_description.- New inputs:
time(),datetimeLocal(),color()(values lower-cased) andsearch().
date(),time()anddatetimeLocal()sharemin(),max()andstep()through the new
Fields\DateTimeInputFieldbase class. - Custom field type registry:
$fieldTypes = ['money' => MoneyField::class]in config makes
$this->money('price')a builder method, including inline chaining and inside groups.
Registrations are checked: camelCase names, no clashes with built-in types orForm
methods, and classes must implementFieldInterface. Form::addField($type, $name, $label)creates any built-in or registered field by type key;
Form::hasFieldType();Support\FieldTypes::BUILT_INlists the built-in keys.- Custom fields can render their own view: a
VIEWcontaining a path (fields/moneyor
Vendor\Package\Views\money) is loaded as given instead of from the theme. Field views can
ask$class()for any theme class key.
Changed
- Behaviour change: strict option checking is on by default. A form that accepted values
outside a field's options in 0.1 now rejects them. Set$strictOptions = falsein
app/Config/Forms.phpto restore the 0.1 behaviour, or opt single fields out with
->strictOptions(false). Existingin_listrules keep working; they are now optional for
option fields. FieldInterfacehas three new methods:getEachRules(),isMultiple()andgetWidth(). Custom fields
extendingFields\Fieldget both automatically; classes implementing the interface
directly must add them.getOptionId()moved fromRadiointo the sharedHasOptionstrait.fields()with no arguments now renders the form's layout (rows and fieldsets) instead of
a flat list.fields('a', 'b')with names is unchanged. Publishedform.phpviews get
layouts automatically.- The
field_selectview uses$inputNameand$values. Re-publish the views
(php spark forms:publish --force) or apply the same change if you customised it.
Fixed
- An array posted to a single-value field (for example
nickname[]=x) could pass validation
on fields without rules and reachvalidated()as an array. Single-value fields now treat
a posted array as no value, and an unticked checkbox's array counts as unticked.
v0.1.0
First release of ephraitech/ci4-forms: a form builder for CodeIgniter 4.7+.
Define a form once in PHP and the package renders it with Bootstrap 5, validates it
with CodeIgniter's Validation service, repopulates input after errors, and returns
only clean data for the fields you defined.
Highlights
- Form classes (
define()) and inline forms (Form::make()) - 13 field types: text, email, password, number, tel, url, date, hidden, textarea,
select (with option groups), radio, checkbox, submit validate()from POST, GET or JSON requests;validated()returns only defined fields,
trimmed, with empty strings as null and unticked checkboxes as 0- Works with both re-rendering and
redirect()->withInput(), including manual errors
fromsetError() fill()for edit screens from arrays or Entities; PUT/PATCH/DELETE spoofing- Escaped, accessible Bootstrap 5 markup; custom layouts with
field()andcontrol() php spark forms:publishto customise the views
Requirements
PHP 8.2+ and CodeIgniter 4.7+.
See the README for full documentation.