Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Zend Form Laravel Package

zendframework/zend-form

Zend Form provides form building, elements, fieldsets, collections, input filters, validation integration, and view helpers for Zend Framework applications. This package is abandoned and superseded by laminas/laminas-form.

Deep Wiki
Context7

Getting Started

This package is archived. Treat zendframework/zend-form as a legacy dependency: install it only to maintain an existing app, and target laminas/laminas-form for anything new. The API is identical except for the Zend\ → Laminas\ namespace.

composer require zendframework/zend-form:^2.14
# new projects:
composer require laminas/laminas-form

Where to look first:

  • src/Form.php — the Form container and add()/bind()/isValid() lifecycle
  • src/Element/ — built-in element types (Text, Email, Select, Csrf, Collection, …)
  • src/Factory.php and src/FormElementManager* — how array specs become objects
  • src/View/Helper/ — form(), formRow(), formElement(), formCollection()

A minimal, framework-agnostic form:

use Zend\Form\Form;
use Zend\InputFilter\InputFilter;

$form = new Form('login');
$form->add(['name' => 'email',    'type' => 'Email',    'options' => ['label' => 'Email']]);
$form->add(['name' => 'password', 'type' => 'Password', 'options' => ['label' => 'Password']]);
$form->add(['name' => 'submit',   'type' => 'Submit',   'attributes' => ['value' => 'Sign in']]);

$form->setInputFilter((new InputFilter())->add([
    'name'     => 'email',
    'required' => true,
    'validators' => [['name' => 'EmailAddress']],
]));

if ($request->isPost()) {
    $form->setData($request->all());
    if ($form->isValid()) {
        $data = $form->getData(); // filtered + validated
    }
}

First real use case: a settings/profile form with server-side filtering and re-rendering on error. That is where the component earns its keep — declarative element definitions plus a parallel InputFilter.

Implementation Patterns

Compose with Fieldsets, not one giant Form. A Fieldset is a reusable group of elements with its own InputFilter. Build AddressFieldset, ContactFieldset, then attach them to the parent Form. This is the single biggest reason to use this package over hand-rolled HTML: fieldsets are portable across forms and easy to unit test.

class AddressFieldset extends Fieldset {
    public function init() {
        $this->add(['name' => 'street', 'type' => 'Text']);
        $this->add(['name' => 'country', 'type' => 'Select', 'options' => ['value_options' => $countries]]);
    }
}
$form->add(new AddressFieldset('billing'));
$form->add(new AddressFieldset('shipping'));

Bind objects with a hydrator. This is the day-to-day workflow for CRUD forms:

$form->setHydrator(new ClassMethodsHydrator(false));
$form->setObject(new User());
$form->bind($user);          // populates elements from $user
// ... on POST:
$form->setData($request->all());
if ($form->isValid()) {
    $user = $form->getData(); // now a hydrated User
}

Use setBindOnDispatch(false) if you want to control hydration timing (e.g., after CSRF check).

Use the Factory for config-driven forms. Instead of instantiating element classes, define array specs (or config arrays) and let Zend\Form\Factory build them. This pairs well with a Laravel service provider that registers a singleton FormElementManager and Factory.

Rendering in Laravel. The built-in view helpers depend on zend-view, which you will not want to wire into Blade. Practical options, in order of preference:

  1. Render manually in Blade ({{ $form->get('email')->getLabel() }}, {{ $form->get('email')->getValue() }}) and let Blade handle escaping.
  2. Use the helpers standalone with a PhpRenderer and expose a thin Blade directive/component:
$renderer = new \Zend\View\Renderer\PhpRenderer();
$renderer->getHelperPluginManager()->setFactory('formRow', fn() => new \Zend\Form\View\Helper\FormRow());
// in a Blade component: echo $renderer->formRow($form->get('email'));
  1. Keep a small FormRenderer service and call it from Blade — this keeps zend-view out of your HTTP stack.

CSRF. Zend\Form\Element\Csrf validates against Zend\Session. In Laravel you either bridge Laravel's session into a Zend\Session\Storage\StorageInterface, or skip the element and use Laravel's @csrf + VerifyCsrfToken middleware. Do not run both — double CSRF is a common source of "form always invalid" bugs.

Collections for repeatable rows. Zend\Form\Element\Collection with target_element (an element spec or fieldset) and count/allow_add handles line items, phone numbers, etc. Its InputFilter is generated from the target element's filter, so define the target's filter once.

Laravel integration tip: if you are already on Laravel, resist mixing Laravel's Validator with this package's InputFilter for the same form. Pick one source of truth. This package is most defensible when you need (a) nested fieldset trees, (b) hydration into domain objects, or (c) a form definition shared between an API and a web controller.

Gotchas and Tips

  • Archived package. No security patches, no PHP 8.x guarantees beyond what the community forks. Run composer require laminas/laminas-migration and migrate; the namespace swap is mechanical (Zend\Form → Laminas\Form). Also swap zendframework/zend-inputfilter, zend-view, etc.
  • isValid() requires setData() first. Calling isValid() before setting data silently validates an empty array and will mark required fields invalid. Always prepare() before rendering (it finalizes elements and populates values) and setData() before validating.
  • InputFilter is not automatic. Elements do not self-validate. You must attach a filter per form/fieldset, or each element spec needs input_filter config. Forgetting this means isValid() returns true with garbage.
  • Form::add('someName') with a string resolves through the FormElementManager. If the element type isn't registered, you get a ServiceNotFoundException at render time, not construction time — easy to miss until production.
  • Element name collisions across fieldsets. Two fieldsets both containing email will collide in getData() unless namespaced by their fieldset name (billing[email]). Use $form->get('billing')->get('email') to disambiguate.
  • CSRF element name is random by default; if you set it explicitly, ensure the HTML and the validator agree. Sessions must be started before rendering.
  • Escaping. View helpers escape values and attributes by default. If you disable escaping (setEscapeHtml(false)), you own XSS. With Blade, do not double-escape ({!! !!} around already-escaped helper output).
  • File uploads need enctype="multipart/form-data" on the <form> and the File element's InputFilter must include Zend\Filter\File\RenameUpload if you want to persist. Otherwise the upload data sits in the value array unprocessed.
  • Hydration gotchas. ClassMethodsHydrator(false) disables __set; ReflectionHydrator needs accessible properties or setters. If getData() returns the original object unchanged after isValid(), check bindOnDispatch and whether you called bind() before or after setData().
  • Performance. FormElementManager is a service manager, so each element may be a shared instance. Clone or use distinct names for repeated elements; otherwise attributes from one fieldset leak into another.
  • Debugging. $form->getMessages() returns a nested array keyed by element name; $form->getInputFilter()->getMessages() shows filter-level errors. When a form "just doesn't validate," dump both — the discrepancy usually points to a missing filter or a mismatched element name.
  • Extension points worth knowing: custom Element subclasses, custom View\Helper classes, and Zend\Form\ElementInterface decorators. Register custom types in the FormElementManager (setInvokableClass / setFactory) rather than new-ing them inline, or config-driven forms will break.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource