Try Live
Add Docs
Rankings
Pricing
Docs
Install
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
Nette Forms
https://github.com/nette/forms
Admin
Nette Forms is a PHP library that greatly facilitates creating and processing secure web forms,
...
Tokens:
10,226
Snippets:
27
Trust Score:
7.7
Update:
4 months ago
Context
Skills
Chat
Benchmark
88.6
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Nette Forms v2.2 Nette Forms is a comprehensive PHP library for creating, validating, and rendering secure web forms. It provides an object-oriented API for building forms with built-in protection against common web vulnerabilities including Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF). The library handles both server-side validation in PHP and client-side validation in JavaScript, eliminating the need to write validation logic twice. The framework follows a component-based architecture where forms are containers that hold controls (input fields, buttons, etc.), with support for nested containers, validation rules, conditional logic, and flexible rendering. It integrates seamlessly with the Nette ecosystem including Latte templating and dependency injection, while also working standalone. All form inputs are automatically checked for UTF-8 validity, and multiple-choice controls are verified against forged values during validation. ## Creating a Basic Form Create a simple registration form with text inputs, validation, and submission handling. ```php <?php use Nette\Forms\Form; // Create a new form instance $form = new Form; // Add text input with required validation $form->addText('name', 'Name:') ->setRequired('Please fill your name.'); // Add email input with validation rule $form->addText('email', 'Email:') ->setRequired('Please enter your email.') ->addRule($form::EMAIL, 'Invalid email address'); // Add password input with length validation $form->addPassword('password', 'Password:') ->setRequired('Choose your password') ->addRule($form::MIN_LENGTH, 'The password is too short: it must be at least %d characters', 8); // Add submit button $form->addSubmit('send', 'Register'); // Process form submission if ($form->isSuccess()) { $values = $form->getValues(); // $values->name, $values->email, $values->password // Process registration... echo "Registration successful!"; } // Render the form ?> <!DOCTYPE html> <html> <head> <script src="https://nette.github.io/resources/js/netteForms.js"></script> </head> <body> <?php echo $form; ?> </body> </html> ``` ## Adding Form Controls Add various input types including text, numbers, dates, selections, and file uploads. ```php <?php use Nette\Forms\Form; $form = new Form; // Text inputs $form->addText('username', 'Username:'); $form->addPassword('password', 'Password:'); $form->addTextArea('bio', 'Biography:'); $form->addText('email', 'Email:') ->addRule($form::EMAIL, 'Invalid email address'); $form->addText('age', 'Age:') ->addRule($form::INTEGER, 'Age must be a number'); $form->addHidden('userid'); // Checkbox and radio buttons $form->addCheckbox('agree', 'I agree to terms'); $form->addCheckboxList('colors', 'Favorite colors:', [ 'r' => 'red', 'g' => 'green', 'b' => 'blue', ]); $form->addRadioList('gender', 'Gender:', [ 'm' => 'male', 'f' => 'female', ]); // Select dropdown with prompt $countries = [ 'us' => 'United States', 'ca' => 'Canada', 'uk' => 'United Kingdom', ]; $form->addSelect('country', 'Country:', $countries) ->setPrompt('Select your country'); // Multi-select dropdown $form->addMultiSelect('interests', 'Interests:', [ 'sports' => 'Sports', 'music' => 'Music', 'tech' => 'Technology', ]); // File upload with validation $form->addUpload('avatar', 'Profile Picture:') ->addRule($form::IMAGE, 'Uploaded file must be an image') ->addRule($form::MAX_FILE_SIZE, 'Maximum file size is 2 MB', 2 * 1024 * 1024); // Multiple file upload $form->addMultiUpload('photos', 'Photos:'); // Buttons $form->addSubmit('submit', 'Submit'); $form->addButton('cancel', 'Cancel'); // Set default values $form->setDefaults([ 'username' => 'john_doe', 'userid' => 231, 'agree' => true, ]); if ($form->isSuccess()) { $values = $form->getValues(); var_dump($values); } echo $form; ``` ## Validation Rules Apply server-side and client-side validation rules with error messages. ```php <?php use Nette\Forms\Form; $form = new Form; // Required field $form->addText('name', 'Name:') ->setRequired('Name is required'); // Email validation $form->addText('email', 'Email:') ->addRule($form::EMAIL, 'Invalid email address'); // String length validation $form->addText('username', 'Username:') ->addRule($form::MIN_LENGTH, 'Username must be at least %d characters', 3) ->addRule($form::MAX_LENGTH, 'Username must be at most %d characters', 20); // Range validation for length $form->addText('code', 'Code:') ->addRule($form::LENGTH, 'Code must be between %d and %d characters', [5, 15]); // Numeric validation $form->addText('age', 'Age:') ->addRule($form::INTEGER, 'Age must be a number') ->addRule($form::RANGE, 'Age must be between %d and %d', [18, 100]); $form->addText('price', 'Price:') ->addRule($form::FLOAT, 'Price must be a decimal number') ->addRule($form::MIN, 'Price must be at least %d', 0) ->addRule($form::MAX, 'Price must be at most %d', 999999); // Pattern matching (regex) $form->addText('phone', 'Phone:') ->addRule($form::PATTERN, 'Phone must be in format XXX-XXX-XXXX', '[0-9]{3}-[0-9]{3}-[0-9]{4}'); // URL validation $form->addText('website', 'Website:') ->addRule($form::URL, 'Invalid URL'); // Password confirmation $password = $form->addPassword('password', 'Password:') ->setRequired('Choose your password') ->addRule($form::MIN_LENGTH, 'Password must be at least %d characters', 8); $form->addPassword('password2', 'Confirm Password:') ->setRequired('Reenter your password') ->addRule($form::EQUAL, 'Passwords do not match', $password); // File upload validation $form->addUpload('document', 'Document:') ->addRule($form::MAX_FILE_SIZE, 'Maximum file size is 5 MB', 5 * 1024 * 1024) ->addRule($form::MIME_TYPE, 'Document must be PDF or Word', ['application/pdf', 'application/msword']); // Multiple file upload validation $form->addMultiUpload('photos', 'Photos:') ->addRule($form::COUNT, 'Upload between %d and %d photos', [2, 5]) ->addRule($form::IMAGE, 'All files must be images'); $form->addSubmit('send', 'Submit'); if ($form->isSuccess()) { $values = $form->getValues(); echo "Form validated successfully!"; } else { // Display all errors foreach ($form->getErrors() as $error) { echo "<div class='error'>$error</div>"; } } echo $form; ``` ## Conditional Validation Apply validation rules conditionally based on other field values. ```php <?php use Nette\Forms\Form; $form = new Form; // Simple conditional: toggle visibility $form->addCheckbox('send', 'Ship to address') ->addCondition($form::FILLED) // if checkbox is checked ->toggle('shipping-box'); // toggle element with id="shipping-box" // Conditional validation: required if checkbox is checked $form->addText('street', 'Street:') ->addConditionOn($form['send'], $form::FILLED) ->setRequired('Enter your shipping address'); $form->addText('city', 'City:') ->addConditionOn($form['send'], $form::FILLED) ->setRequired('Enter your city'); // Conditional with specific value $form->addSelect('payment', 'Payment Method:', [ 'card' => 'Credit Card', 'paypal' => 'PayPal', 'bank' => 'Bank Transfer', ]); $form->addText('card_number', 'Card Number:') ->addConditionOn($form['payment'], $form::EQUAL, 'card') ->setRequired('Enter card number') ->addRule($form::PATTERN, 'Invalid card number', '[0-9]{16}'); $form->addText('paypal_email', 'PayPal Email:') ->addConditionOn($form['payment'], $form::EQUAL, 'paypal') ->setRequired('Enter PayPal email') ->addRule($form::EMAIL, 'Invalid email address'); // Chained conditions: if field is filled AND meets criteria $form->addText('discount_code', 'Discount Code:') ->addCondition($form::FILLED) // if filled ->addRule($form::MIN_LENGTH, 'Code must be at least %d characters', 5); // Complex condition: if one field is filled, another is required $form->addText('phone', 'Phone:'); $form->addText('email', 'Email:'); $form->addCondition($form::BLANK, $form['phone']) ->addCondition($form::BLANK, $form['email']) ->addRule(function() { return false; }, 'Fill either phone or email'); $form->addSubmit('submit', 'Submit'); if ($form->isSuccess()) { $values = $form->getValues(); var_dump($values); } ?> <!DOCTYPE html> <html> <head> <script src="https://nette.github.io/resources/js/netteForms.js"></script> </head> <body> <div id="shipping-box" style="display:none"> <p>Shipping information will appear here</p> </div> <?php echo $form; ?> </body> </html> ``` ## Form Groups and Organization Organize form controls into logical groups with fieldsets. ```php <?php use Nette\Forms\Form; use Nette\Utils\Html; $form = new Form; // Create first group with description $form->addGroup('Personal Information') ->setOption('description', 'Please provide your personal details.'); $form->addText('name', 'Full Name:') ->setRequired('Enter your name'); $form->addText('birthdate', 'Birth Date:'); $form->addRadioList('gender', 'Gender:', [ 'm' => 'Male', 'f' => 'Female', ]); // Create shipping address group $form->addGroup('Shipping Address') ->setOption('embedNext', true); // embed next group inside this one $form->addCheckbox('send', 'Ship to address') ->addCondition($form::FILLED) ->toggle('shipping-fields'); // Subgroup with custom container $form->addGroup() ->setOption('container', Html::el('div')->id('shipping-fields')); $form->addText('street', 'Street:'); $form->addText('city', 'City:') ->addConditionOn($form['send'], $form::FILLED) ->setRequired('Enter city'); $form->addSelect('country', 'Country:', [ 'us' => 'United States', 'ca' => 'Canada', 'uk' => 'United Kingdom', ])->setPrompt('Select country'); // Account settings group $form->addGroup('Account Settings'); $form->addPassword('password', 'Password:') ->setRequired('Choose password') ->addRule($form::MIN_LENGTH, 'Password must be at least %d characters', 8); $form->addPassword('password2', 'Confirm Password:') ->setRequired('Reenter password') ->addRule($form::EQUAL, 'Passwords do not match', $form['password']); $form->addTextArea('bio', 'Biography:') ->setOption('description', 'Tell us about yourself (optional)'); // Group for buttons (no label) $form->addGroup(); $form->addSubmit('submit', 'Create Account'); if ($form->isSuccess()) { $values = $form->getValues(); echo "<h2>Account created successfully!</h2>"; var_dump($values); } echo $form; ``` ## Nested Containers Use containers to organize form data hierarchically and create reusable form sections. ```php <?php use Nette\Forms\Form; $form = new Form; // Create container for billing address $billing = $form->addContainer('billing'); $billing->addText('name', 'Name:')->setRequired(); $billing->addText('street', 'Street:')->setRequired(); $billing->addText('city', 'City:')->setRequired(); $billing->addText('zip', 'ZIP:')->setRequired(); // Create container for shipping address $shipping = $form->addContainer('shipping'); $shipping->addText('name', 'Name:')->setRequired(); $shipping->addText('street', 'Street:')->setRequired(); $shipping->addText('city', 'City:')->setRequired(); $shipping->addText('zip', 'ZIP:')->setRequired(); // Container for contact information $contact = $form->addContainer('contact'); $contact->addText('email', 'Email:') ->setRequired() ->addRule($form::EMAIL, 'Invalid email address'); $contact->addText('phone', 'Phone:'); $form->addSubmit('submit', 'Submit Order'); // Set default values for containers $form->setDefaults([ 'billing' => [ 'name' => 'John Doe', 'city' => 'New York', ], 'contact' => [ 'email' => 'john@example.com', ], ]); if ($form->isSuccess()) { $values = $form->getValues(); // Access nested values echo "Billing Name: " . $values->billing->name . "\n"; echo "Billing City: " . $values->billing->city . "\n"; echo "Shipping Name: " . $values->shipping->name . "\n"; echo "Contact Email: " . $values->contact->email . "\n"; // Values are organized hierarchically var_dump($values); /* object(stdClass) { billing => object(stdClass) { name => "John Doe" street => "123 Main St" city => "New York" zip => "10001" } shipping => object(stdClass) { name => "Jane Doe" street => "456 Oak Ave" city => "Boston" zip => "02101" } contact => object(stdClass) { email => "john@example.com" phone => "555-1234" } } */ } echo $form; ``` ## Custom Validators Create custom validation functions for specialized validation logic. ```php <?php use Nette\Forms\Form; use Nette\Forms\Controls\BaseControl; // Define custom validator class class MyValidators { public static function divisibilityValidator(BaseControl $control, $divisor) { return $control->getValue() % $divisor === 0; } public static function uniqueUsername(BaseControl $control) { $username = $control->getValue(); // Check database for existing username // This is a simplified example $existingUsers = ['admin', 'test', 'user']; return !in_array($username, $existingUsers); } public static function strongPassword(BaseControl $control) { $password = $control->getValue(); // Check for uppercase, lowercase, number, and special character return preg_match('/[A-Z]/', $password) && preg_match('/[a-z]/', $password) && preg_match('/[0-9]/', $password) && preg_match('/[^A-Za-z0-9]/', $password); } } $form = new Form; // Use custom validator with parameter $form->addText('number', 'Multiple of 8:') ->setRequired() ->addRule('MyValidators::divisibilityValidator', 'Number must be divisible by %d', 8); // Use custom validator without parameter $form->addText('username', 'Username:') ->setRequired() ->addRule('MyValidators::uniqueUsername', 'Username is already taken'); // Use custom validator for complex password rules $form->addPassword('password', 'Password:') ->setRequired() ->addRule($form::MIN_LENGTH, 'Password must be at least %d characters', 8) ->addRule('MyValidators::strongPassword', 'Password must contain uppercase, lowercase, number, and special character'); // Custom validator using closure $form->addText('promo_code', 'Promo Code:') ->addCondition($form::FILLED) ->addRule(function(BaseControl $control) { $validCodes = ['SAVE10', 'SAVE20', 'FREESHIP']; return in_array(strtoupper($control->getValue()), $validCodes); }, 'Invalid promo code'); $form->addSubmit('submit', 'Submit'); if ($form->isSuccess()) { $values = $form->getValues(); echo "Validation passed!"; var_dump($values); } ?> <!DOCTYPE html> <html> <head> <script src="https://nette.github.io/resources/js/netteForms.js"></script> <script> // Client-side implementation of custom validators Nette.validators.MyValidators_divisibilityValidator = function(elem, args, val) { return val % args === 0; }; Nette.validators.MyValidators_strongPassword = function(elem, args, val) { return /[A-Z]/.test(val) && /[a-z]/.test(val) && /[0-9]/.test(val) && /[^A-Za-z0-9]/.test(val); }; </script> </head> <body> <?php echo $form; ?> </body> </html> ``` ## Manual Form Rendering Manually render form elements with full control over HTML structure. ```php <?php use Nette\Forms\Form; $form = new Form; $form->addText('name', 'Name:')->setRequired('Enter your name'); $form->addText('age', 'Age:') ->setRequired('Enter your age') ->addRule($form::INTEGER, 'Age must be a number'); $form->addRadioList('gender', 'Gender:', ['m' => 'male', 'f' => 'female']); $form->addText('email', 'Email:') ->addRule($form::EMAIL, 'Invalid email address'); $form->addSubmit('submit', 'Send'); if ($form->isSuccess()) { echo '<h2>Form submitted successfully</h2>'; var_dump($form->getValues()); exit; } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://nette.github.io/resources/js/netteForms.js"></script> <style> .required { color: #c00; } .error { color: #c00; background: #fcc; padding: 5px; margin: 5px 0; } </style> </head> <body> <!-- Render form opening tag --> <?php $form->render('begin') ?> <!-- Display global form errors --> <?php if ($form->getErrors()): ?> <ul class="error"> <?php foreach ($form->getErrors() as $error): ?> <li><?php echo htmlspecialchars($error) ?></li> <?php endforeach ?> </ul> <?php endif ?> <fieldset> <legend>Personal Data</legend> <table> <!-- Manual rendering of name field --> <tr class="<?php echo $form['name']->isRequired() ? 'required' : '' ?>"> <th><?php echo $form['name']->getLabel() ?></th> <td> <?php echo $form['name']->getControl() ?> <?php if ($form['name']->getError()): ?> <span class="error"><?php echo $form['name']->getError() ?></span> <?php endif ?> </td> </tr> <!-- Manual rendering with modified attributes --> <tr class="<?php echo $form['age']->isRequired() ? 'required' : '' ?>"> <th><?php echo $form['age']->getLabel() ?></th> <td> <?php echo $form['age']->getControl()->size(5) ?> <?php echo $form['age']->getError() ?> </td> </tr> <!-- Render gender radio list --> <tr> <th><?php echo $form['gender']->getLabel() ?></th> <td> <?php echo $form['gender']->getControl() ?> <?php echo $form['gender']->getError() ?> </td> </tr> <!-- Render email with custom size --> <tr> <th><?php echo $form['email']->getLabel() ?></th> <td> <?php echo $form['email']->getControl()->size(35) ?> <?php echo $form['email']->getError() ?> </td> </tr> </table> </fieldset> <!-- Render submit button with custom caption --> <div> <?php echo $form['submit']->getControl('Send Form') ?> </div> <!-- Render form closing tag --> <?php $form->render('end') ?> </body> </html> ``` ## Bootstrap 3 Rendering Customize form rendering for Bootstrap 3 framework with CSS classes and structure. ```php <?php use Nette\Forms\Form; use Nette\Forms\Controls\Checkbox; function makeBootstrap3(Form $form) { $renderer = $form->getRenderer(); // Configure wrapper structure for Bootstrap 3 $renderer->wrappers['controls']['container'] = null; $renderer->wrappers['pair']['container'] = 'div class=form-group'; $renderer->wrappers['pair']['.error'] = 'has-error'; $renderer->wrappers['control']['container'] = 'div class=col-sm-9'; $renderer->wrappers['label']['container'] = 'div class="col-sm-3 control-label"'; $renderer->wrappers['control']['description'] = 'span class=help-block'; $renderer->wrappers['control']['errorcontainer'] = 'span class=help-block'; $renderer->wrappers['error']['container'] = 'div class="alert alert-danger"'; // Add Bootstrap classes to controls foreach ($form->getControls() as $control) { $type = $control->getOption('type'); if ($type === 'button') { $control->getControlPrototype()->addClass('btn btn-primary'); } elseif (in_array($type, ['text', 'textarea', 'select'], true)) { $control->getControlPrototype()->addClass('form-control'); } elseif (in_array($type, ['checkbox', 'radio'], true)) { if ($control instanceof Checkbox) { $control->getLabelPrototype()->addClass('checkbox'); } else { $control->getItemLabelPrototype()->addClass('radio'); } } } } $form = new Form; $form->onRender[] = 'makeBootstrap3'; $form->addGroup('Personal Data'); $form->addText('name', 'Full Name') ->setRequired('Enter your name') ->setOption('description', 'First and last name'); $form->addText('birth', 'Date of Birth'); $form->addRadioList('gender', 'Gender', ['male', 'female']); $form->addCheckboxList('colors', 'Favorite Colors', ['red', 'green', 'blue']); $form->addSelect('country', 'Country', [ 'us' => 'United States', 'ca' => 'Canada', 'uk' => 'United Kingdom', ]); $form->addGroup('Additional'); $form->addPassword('password', 'Password'); $form->addUpload('avatar', 'Profile Picture'); $form->addTextArea('note', 'Comments'); $form->addGroup(); $form->addSubmit('submit', 'Submit'); $form->addSubmit('cancel', 'Cancel'); if ($form->isSuccess()) { echo '<div class="alert alert-success">Form submitted successfully!</div>'; var_dump($form->getValues()); exit; } ?> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Bootstrap 3 Form</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <script src="https://nette.github.io/resources/js/netteForms.js"></script> </head> <body> <div class="container"> <h1>Bootstrap 3 Form</h1> <?php $form->render() ?> </div> </body> </html> ``` ## CSRF Protection Add Cross-Site Request Forgery protection to forms with automatic token generation. ```php <?php use Nette\Forms\Form; $form = new Form; // Add CSRF protection (creates hidden token field) $form->addProtection('Security token has expired, please submit the form again'); // Add form fields $form->addText('username', 'Username:') ->setRequired(); $form->addPassword('password', 'Password:') ->setRequired(); $form->addSubmit('login', 'Login'); if ($form->isSuccess()) { $values = $form->getValues(); // Token was validated automatically echo "Login successful! Token validated."; // Process login... } elseif ($form->isSubmitted()) { // Form submitted but validation failed (possibly invalid token) echo "Validation failed. Please check for errors."; } // The form will automatically include a hidden CSRF token field echo $form; /* Rendered HTML will include: <input type="hidden" name="_token_" value="abc123...token...xyz"> The token is automatically validated on form submission. If the token is missing, expired, or invalid, the form will not pass validation. */ ``` ## Event Handlers Attach callbacks to form events for processing successful submissions and handling errors. ```php <?php use Nette\Forms\Form; $form = new Form; $form->addText('email', 'Email:') ->setRequired() ->addRule($form::EMAIL, 'Invalid email address'); $form->addPassword('password', 'Password:') ->setRequired(); $form->addSubmit('login', 'Login'); // onSuccess: Called when form is submitted and validated successfully $form->onSuccess[] = function(Form $form) { $values = $form->getValues(); // Process valid form data echo "Login successful!\n"; echo "Email: {$values->email}\n"; // Multiple handlers can be attached // They are called in the order they were added }; // onSuccess can have multiple handlers $form->onSuccess[] = function(Form $form) { $values = $form->getValues(); // Log the successful login error_log("User {$values->email} logged in"); // Redirect after successful login // header('Location: /dashboard'); // exit; }; // onError: Called when form is submitted but validation fails $form->onError[] = function(Form $form) { echo "Form has errors:\n"; foreach ($form->getErrors() as $error) { echo "- $error\n"; } }; // onSubmit: Called on every form submission (before validation) $form->onSubmit[] = function(Form $form) { echo "Form was submitted\n"; // Useful for logging or analytics }; // Button-specific handlers $loginButton = $form['login']; $loginButton->onClick[] = function($button) { $form = $button->getForm(); $values = $form->getValues(); echo "Login button clicked\n"; }; // Add a second submit button with different handler $form->addSubmit('register', 'Register Instead'); $form['register']->onClick[] = function($button) { echo "Register button clicked\n"; // Redirect to registration // header('Location: /register'); // exit; }; // Process the form if ($form->isSubmitted() && $form->isValid()) { // Handlers are called automatically // No need to manually call them } echo $form; ``` ## Working with Form Data Set default values, retrieve submitted data, and reset forms. ```php <?php use Nette\Forms\Form; $form = new Form; $form->addText('name', 'Name:'); $form->addText('email', 'Email:') ->addRule($form::EMAIL, 'Invalid email address'); $form->addText('age', 'Age:') ->addRule($form::INTEGER, 'Age must be a number'); $form->addCheckbox('subscribe', 'Subscribe to newsletter'); $billing = $form->addContainer('billing'); $billing->addText('street', 'Street:'); $billing->addText('city', 'City:'); $form->addSubmit('submit', 'Submit'); // Set default values for the entire form $form->setDefaults([ 'name' => 'John Doe', 'email' => 'john@example.com', 'age' => 30, 'subscribe' => true, 'billing' => [ 'street' => '123 Main St', 'city' => 'New York', ], ]); // Set values from database or other source $userData = [ 'name' => 'Jane Smith', 'email' => 'jane@example.com', 'age' => 25, ]; $form->setValues($userData, true); // second param: erase fields not in $userData // Get submitted values (after validation) if ($form->isSuccess()) { // Get values as ArrayHash object $values = $form->getValues(); echo $values->name; // 'Jane Smith' echo $values->email; // 'jane@example.com' echo $values->age; // 25 echo $values->subscribe; // true/false echo $values->billing->city; // 'New York' // Get values as associative array $valuesArray = $form->getValues(true); echo $valuesArray['name']; // 'Jane Smith' echo $valuesArray['billing']['city']; // 'New York' // Access individual control values $name = $form['name']->getValue(); $email = $form['email']->getValue(); // Modify values programmatically $form['name']->setValue('Modified Name'); } // Check if form was submitted if ($form->isSubmitted()) { echo "Form was submitted\n"; // Check if validation passed if ($form->isValid()) { echo "Form is valid\n"; } else { echo "Form has errors\n"; } } // Reset form to defaults $form->reset(); // Clear all values $form->setValues([], true); // Check form errors if (!$form->isValid()) { $errors = $form->getErrors(); foreach ($errors as $error) { echo "Error: $error\n"; } // Get errors for specific control $nameErrors = $form['name']->getErrors(); } // Add custom error to form if ($form->isSuccess()) { $values = $form->getValues(); // Check business logic $existingEmails = ['existing@example.com', 'test@example.com']; if (in_array($values->email, $existingEmails)) { $form['email']->addError('Email already registered'); } if (!$form->hasErrors()) { // Process form... } } echo $form; ``` ## Latte Template Integration Render forms using Latte templating engine for maximum flexibility. ```php <?php // In your PHP file (e.g., presenter or controller) use Nette\Forms\Form; use Latte\Engine; $form = new Form('myForm'); $form->addGroup('Personal Information'); $form->addText('name', 'Name:')->setRequired(); $form->addText('email', 'Email:') ->setRequired() ->addRule($form::EMAIL, 'Invalid email address'); $form->addGroup('Address'); $form->addText('street', 'Street:'); $form->addText('city', 'City:'); $form->addGroup(); $form->addSubmit('submit', 'Submit'); if ($form->isSuccess()) { $values = $form->getValues(); echo "Form submitted successfully!"; } // Render using Latte template $latte = new Engine; $latte->render('form-template.latte', ['myForm' => $form]); ``` ```latte {* form-template.latte *} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Form with Latte</title> <script src="https://nette.github.io/resources/js/netteForms.js"></script> <style> .error { color: red; } .required label { font-weight: bold; } </style> </head> <body> <h1>Registration Form</h1> {* Render entire form automatically *} {form myForm} <div class="form-content"> {* Iterate through groups *} {foreach $myForm->getGroups() as $group} {if $group->getOption('label')} <fieldset> <legend>{$group->getOption('label')}</legend> {/if} {* Iterate through controls in group *} {foreach $group->getControls() as $control} <div class="{if $control->isRequired()}required{/if}"> {label $control /} {input $control} {inputError $control} </div> {/foreach} {if $group->getOption('label')} </fieldset> {/if} {/foreach} </div> {/form} {* Alternative: Manual rendering with Latte *} {form myForm} <table> <tr> <th>{label name /}</th> <td> {input name} {inputError name} </td> </tr> <tr> <th>{label email /}</th> <td> {input email} {inputError email} </td> </tr> <tr> <th>{label street /}</th> <td>{input street}</td> </tr> <tr> <th>{label city /}</th> <td>{input city}</td> </tr> <tr> <td colspan="2">{input submit}</td> </tr> </table> {/form} {* Render specific parts *} {form myForm} {* Form errors *} {if $myForm->hasErrors()} <ul class="error"> {foreach $myForm->getErrors() as $error} <li>{$error}</li> {/foreach} </ul> {/if} {* Individual controls *} <div> {label name}Full Name:{/label} {input name, class => 'form-control', placeholder => 'Enter your name'} {inputError name} </div> {input submit, class => 'btn btn-primary'} {/form} </body> </html> ``` ## Client-Side JavaScript Validation Enable automatic client-side validation using the Nette Forms JavaScript library. ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Client-Side Validation</title> <!-- Include Nette Forms JavaScript library --> <script src="https://nette.github.io/resources/js/netteForms.js"></script> <style> .error { color: red; } input.error { border-color: red; } .required label::after { content: " *"; color: red; } </style> </head> <body> <?php use Nette\Forms\Form; $form = new Form; // All validation rules are automatically exported to JavaScript $form->addText('username', 'Username:') ->setRequired('Username is required') ->addRule($form::MIN_LENGTH, 'Username must be at least %d characters', 3) ->addRule($form::MAX_LENGTH, 'Username must be at most %d characters', 20); $form->addText('email', 'Email:') ->setRequired('Email is required') ->addRule($form::EMAIL, 'Invalid email format'); $form->addText('age', 'Age:') ->setRequired() ->addRule($form::INTEGER, 'Age must be a number') ->addRule($form::RANGE, 'Age must be between %d and %d', [18, 100]); $form->addPassword('password', 'Password:') ->setRequired() ->addRule($form::MIN_LENGTH, 'Password must be at least %d characters', 8) ->addRule($form::PATTERN, 'Password must contain letters and numbers', '.*[0-9].*[a-zA-Z]|.*[a-zA-Z].*[0-9].*'); // Conditional validation also works on client-side $form->addCheckbox('shipping', 'Ship to different address') ->addCondition($form::FILLED) ->toggle('shipping-address'); // Shows/hides element with id="shipping-address" $form->addText('shipping_address', 'Shipping Address:') ->addConditionOn($form['shipping'], $form::FILLED) ->setRequired('Enter shipping address'); $form->addSubmit('submit', 'Submit'); // The form will be validated on submit before being sent to server // Invalid fields will be highlighted and error messages displayed echo $form; ?> <div id="shipping-address" style="display:none;"> Shipping address field appears here </div> <script> // Custom client-side validation Nette.validators.myCustomValidator = function(elem, arg, val) { // Return true if valid, false if invalid return val.length > 5; }; // Access form validation programmatically document.querySelector('form').addEventListener('submit', function(e) { // Nette.validateForm is automatically called // You can add additional logic here console.log('Form is being validated'); }); // Live validation on blur Nette.initOnLoad(); </script> </body> </html> ``` --- ## Summary Nette Forms v2.2 provides a complete solution for web form handling in PHP applications, combining security, usability, and flexibility. The library is commonly used in Nette Framework applications but works standalone with any PHP project. Primary use cases include user registration and authentication forms, data collection and surveys, e-commerce checkout processes, admin panels and content management interfaces, and any scenario requiring secure form validation. The dual validation system ensures data integrity while providing immediate user feedback through client-side validation. Integration patterns include standalone usage by simply instantiating the Form class and rendering it, Nette Application integration through presenters with automatic request handling, Latte template integration for advanced rendering control with template macros, and Bootstrap/CSS framework integration through renderer customization. The library's architecture allows for extending controls with custom input types, creating reusable form factories, building dynamic forms based on database schemas, and implementing multi-step forms with session persistence. With built-in CSRF protection, XSS prevention, and comprehensive validation, Nette Forms handles security concerns automatically while remaining highly customizable for specific application needs. Version 2.2 requires PHP 5.3.1+ and uses `addText()` with validation rules for specialized inputs like email and integers, rather than the convenience methods introduced in later versions.