Wenprise Forms
Extend Nette Forms for WordPress use.
by WordPress 智库 · github.com/iwillhappy1314/wenprise-forms · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/iwillhappy1314/wenprise-forms/archive/refs/heads/master.zipReadme
wenprise-forms
Extend Nette Forms for WordPress use.
Basic usage: Nette Form
Features
- Supports both Composer library mode and WordPress plugin mode
- Extends Nette Forms with WordPress-ready fields and UI widgets
- Includes form validation, conditional display, datastore integration, and AJAX-friendly inputs
Requirements
- PHP
>=8.3 ext-jsonnette/forms ^3.2nette/utils ^4.1- WordPress
>=6.0(recommended)
Installation
Option A: Install as a Composer library
composer require wenprise/forms
Option B: Install as a WordPress plugin
- Clone or download this repository into
wp-content/plugins/wenprise-forms. - Run
composer installinside the plugin directory. - Activate
Wenprise Formsin WordPress admin.
Runtime Modes
Composer library mode
- Your project is responsible for loading Composer autoload.
- Your code decides when and where to bootstrap the form objects.
WordPress plugin mode
- Plugin entry file
wenprise-forms.phploadsvendor/autoload.phpautomatically. - Plugin bootstraps with
new \Wenprise\Forms\Init();.
Quick Start (Minimal Working Example)
Forms has its own HTTP request handling flow. Instantiate it before head_sent, usually on init.
<?php
use Wenprise\Forms\Form;
use Wenprise\Forms\Helpers;
use Wenprise\Forms\Renders\DefaultFormRender;
add_action('init', function () {
global $demo_form;
$demo_form = Helpers::get_form();
$demo_form->setRenderer(new DefaultFormRender('horizontal'));
$demo_form->setMethod('POST');
$demo_form->setAction(home_url('/'));
$demo_form->addText('first_name', 'First Name')->setRequired();
$demo_form->addSubmit('send', 'Save');
});
add_shortcode('wenprise_form_demo', function () {
global $demo_form;
if ($demo_form->isSuccess()) {
$values = $demo_form->getValues();
$first_name = $values->first_name;
// Save or process your value here.
}
return (string) $demo_form;
});
Usage
Quick start
Forms includes its own HTTP request handling flow, so instantiate it before head_sent, usually in the init action.
add_action('init', function ()
{
global $form;
// Get forms object
$form = Helpers::get_form();
});
Create a Form instance
use Wenprise\Forms\Form;
$form = new Form;
$form->setRenderer(new \Wenprise\Forms\Renders\DefaultFormRender('horizontal'));
// Set form method
$form->setMethod( 'POST' );
$form->setAction('https://www.example.com');
// Set form field
$form->addText('first_name', 'First Name');
// Set submit button
$form->addSubmit( 'send', 'Save' );
// Validate form and get form data
if ( $form->isSuccess() ) {
$values = $form->getValues();
$first_name = $values->first_name;
}
Frontend Assets
Some fields depend on JavaScript/CSS widgets (for example: Slider, Chosen, Date Picker, Signature, Star Rating, Autocomplete, Chained Select).
- Ensure assets are properly enqueued in your theme/plugin runtime.
- Prefer testing one advanced field at a time when integrating into existing themes.
Security Notes
- Always include CSRF protection for public forms (
$form->addCsrf(...)). - For AJAX endpoints, validate nonce and capability before processing input.
- Sanitize incoming data and escape output in templates.
- Do not trust client-side validation alone; keep validation rules on the server side.
Set Required
$form->addText('first_name', 'First Name')
->setRequired();
Add rule
$form->addPassword('re_password', 'Password again:')
->addRule($form::EQUAL, 'Password mismatch', $form['password']);
Add field description
$form->addText('first_name', 'First Name')
->setOption('description', 'This is your first name.');
With Html
$form->addTextArea('description', Html::el('p')
->setHtml('This number remains hidden. <a href="...">Terms of service.</a>')
);
Set condition display
$form->addColorPicker('first_name3', 'First Name')
->setHtmlAttribute('data-cond', '[name=first_name2] == 2');
Control width
setWidth() supports both Bootstrap-based renderers and TailwindGridFormRender.
$form->addText('first_name', 'First Name')
->setWidth(4);
You can also define responsive widths with breakpoint names:
$form->addText('first_name', 'First Name')
->setWidth(6, 'base')
->setWidth(3, 'md')
->setWidth(4, 'lg');
Or use the shorthand form:
$form->addText('first_name', 'First Name')
->setWidth(6, 3, 4);
The shorthand above means:
base = 6md = 3lg = 4
With DefaultFormRender('vertical') and InlineFormRender(), setWidth() is mapped to Bootstrap-style classes such as rs-col-md-4.
With TailwindGridFormRender('vertical'), setWidth() is mapped to grid span classes such as rs-grid-form__span-md-4.
DefaultFormRender('horizontal') keeps each field full width by design, so width settings are ignored there.
Repeater
Use addRepeater() to build repeatable field groups.
use Wenprise\Forms\Renders\GridFormRender;
$form = new \Wenprise\Forms\Form('contacts_form');
$form->setRenderer(new GridFormRender('vertical'));
$form->addRepeater('contacts', 'Contacts', function (\Wenprise\Forms\Container $row): void {
$row->addText('name', 'Name')->setWidth(6, 3, 4);
$row->addText('phone', 'Phone')->setWidth(6, 3, 4);
$row->addText('email', 'Email')->setWidth(12, 6, 4);
}, 1, 5);
$form->addSubmit('send', 'Save');
addRepeater() arguments:
$name: repeater field name$label: repeater label$factory: callback used to define each row container$copy_count: initial row count$max_copies: optional maximum row count
Submitted values are returned as nested arrays and can be stored by the built-in datastore layer.
Multiple Submit Button
$form->addSubmit('save', 'Save');
$form->addSubmit('delete', 'Delete');
if ($form->isSuccess()) {
if ($form['save']->isSubmittedBy()) {
....
}
if ($form['delete']->isSubmittedBy()) {
....
}
}
Use html in label or description
$confirm = Html::el('span')->setHtml('I agree the <a href="#">Terms of service.</a>');
$form->addCheckbox('confirm', $confirm)->setOption('description', $confirm);
Set Datastore
$form->setDatastore(new \Wenprise\Forms\Datastores\PostMetaDatastore(1));
$form->save();
Custom Settings Page Example
Use OptionsDatastore to build a WordPress admin settings page quickly.
<?php
use Wenprise\Forms\Form;
use Wenprise\Forms\Datastores\OptionsDatastore;
use Wenprise\Forms\Renders\AdminFormRender;
add_action('admin_menu', function () {
add_menu_page(
'Wenprise Form Settings',
'Form Settings',
'manage_options',
'wenprise-form-settings',
'render_wenprise_form_settings_page'
);
});
function render_wenprise_form_settings_page() {
$form = new Form('wenprise_form_settings');
$form->setRenderer(new AdminFormRender('vertical'));
$form->setMethod('POST');
$form->addText('wprs_company_name', 'Company Name')
->setDefaultValue(get_option('wprs_company_name', ''));
$form->addText('wprs_support_email', 'Support Email')
->setRequired()
->addRule($form::EMAIL, 'Please enter a valid email address.')
->setDefaultValue(get_option('wprs_support_email', ''));
$form->addCheckbox('wprs_enable_notifications', 'Enable Email Notifications')
->setDefaultValue((bool) get_option('wprs_enable_notifications', false));
$form->addSubmit('save', 'Save Settings');
$form->setDatastore(new OptionsDatastore());
$form->save();
echo '<div class="wrap"><h1>Wenprise Form Settings</h1>';
$form->render();
echo '</div>';
}
Tab Groups
Use tab groups to split long forms into multiple panels.
$form = new \Wenprise\Forms\Form();
$form->addTab('basic', 'Basic', true);
$form->addText('first_name', 'First Name');
$form->addText('email', 'Email');
$form->addTab('advanced', 'Advanced');
$form->addTextArea('notes', 'Notes');
$form->endTab();
// This submit button is outside tab groups.
$form->addSubmit('send', 'Save');
// Template context:
echo $form;
Tabs are rendered automatically through the existing render flow ($form->render() / echo $form), so no extra render method is required.
Submit buttons created inside tab groups are rendered outside tab panes automatically, so the save action stays visible.
You can also use underscore naming method:
$form->add_tab('basic', 'Basic', true);
echo $form;
Stepper Groups
Use stepper groups to render a progress bar with previous and next step actions.
$form = new \Wenprise\Forms\Form();
$form->addStep('campaign', 'Select master blaster campaign settings', true);
$form->addText('campaign_name', 'Campaign Name');
$form->endStep();
$form->addStep('ad_group', 'Create an ad group');
$form->addText('group_name', 'Group Name');
$form->endStep();
$form->addStep('ad', 'Create an ad');
$form->addTextArea('ad_content', 'Ad Content');
$form->endStep();
$form->addSubmit('send', 'Save');
echo $form;
You can also use underscore naming method:
$form->add_step('campaign', 'Campaign', true);
If you render inside a shortcode callback, return the form HTML instead of echoing:
add_shortcode('wenprise_form_demo', function () use ($form) {
return (string) $form;
});
Fields
nonce field
$form->addCsrf('postform', 'Nonce invalid');
WordPress Tinymce editor
$form->addEditor('post_extra', 'Extra content', []);
Ajax uploader
$form->addAjaxUpload('photos', 'Photos', true, )
->setUrl( admin_url( 'admin-ajax.php?action=upload' ) );
Uploader backend sample.
add_action('wp_ajax_upload', 'ajax_uploader');
add_action('wp_ajax_nopriv_upload', 'ajax_uploader');
function ajax_uploader()
{
require_once(ABSPATH . 'wp-admin/includes/image.php');
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/media.php');
$attachment_id = media_handle_upload('file', 0);
$thumb_url = wp_get_attachment_thumb_url($attachment_id);
$origin_url = wp_get_attachment_url($attachment_id);
$thumb = get_post($attachment_id);
$file_data = [
'id' => $attachment_id,
'original' => $thumb->post_title,
'size' => $thumb->size,
'state' => 'SUCCESS',
'title' => $thumb->post_title,
'type' => $thumb->post_mime_type,
'thumb' => $thumb_url,
'url' => $origin_url,
];
wp_send_json($file_data, 200);
return false;
}
Slider input
$form->addSlider('price', 'Price', []);
Data Picker
$form->AddBirthdaypicker('_birthday', 'Date of Birth', [
'format' => 'YYYY-MM-DD',
'template' => 'YYYY-MM-DD',
'minYear' => '1900',
'maxYear' => date("Y")
]);
Color Picker
$form->addColorPicker('color', 'Color', []);
Chosen
$choices = [
'php' => 'PHP',
'javascript' => 'JavaScript',
'css' => 'CSS',
'java' => 'Java',
];
$form->addChosen('category', 'Category', $choices);
Multi Chosen
$form->addMultiChosen('post_tags', 'Tags', $choices);
Signature Field
$options = [
'width' => '500',
'height' => '250',
'border' => '#999',
'background' => '#f3f3f3',
];
$form->addSignature('first_name9', 'First Name', $options);
Star rating input
$options = [
'displayOnly' => false,
'showClear' => false,
'theme' => 'krajee-svg',
'step' => 1,
'min' => 1,
'max' => 5,
];
$form->addStarRating('rating', 'Rating', $options);
Image Select
$options = [
'light' => 'https://via.placeholder.com/64/EEEEEE/000000/?text=Light',
'dark' => 'https://via.placeholder.com/64/000000/FFFFFF/?text=Dark',
];
$form->addImagePicker('theme', 'Theme', $options);
Autocomplete Input
Source is an array or a url returns an array.
$form->addAutocomplete('name', 'Name')->setSource([
[
'value' => 'aaa',
'data' => 'AAA',
],
[
'value' => 'bbb',
'data' => 'BBB',
],
[
'value' => 'ccc',
'data' => 'CCC',
],
]);
If Source is an ajax url, the backend need return data as bellow.
suggestions: [
{ "value": "United Arab Emirates", "data": "AE" },
{ "value": "United Kingdom", "data": "UK" },
{ "value": "United States", "data": "US" }
]
Table input
$fields = [
[
'name' => 'name',
'display' => 'Product Name',
'type' => 'text',
],
[
'name' => 'quantity',
'display' => 'Quantity',
'type' => 'text',
],
[
'name' => 'price',
'display' => 'Unit Price',
'type' => 'text',
],
];
$values = [
[
'name' => 'Macbook Pro',
'quantity' => '1',
'price' => '8500',
],
[
'name' => 'Pixel XL',
'quantity' => '2',
'price' => '8500',
],
];
$form->addTableInput('table', 'Table', [], $fields)
->setDefaultValue($values);Read the full README on GitHub →
Releases
| Tag | Published |
|---|---|
| v3.1.3 | May 19, 2026 |
| v3.1.2 | May 19, 2026 |
| v3.1.1 | May 19, 2026 |
| v3.1.0 | May 18, 2026 |
| 3.0.4 | May 14, 2026 |
| 3.0.3 | May 14, 2026 |
| 3.0.2 | May 14, 2026 |
| 3.0.1 | May 14, 2026 |
| 3.0.0 | May 14, 2026 |
| v2.3.5 | Aug 27, 2025 |
| v2.3.4 | Aug 27, 2025 |
| v2.3.3 | Dec 24, 2024 |
| v2.3.2 | Dec 16, 2024 |
| v2.3.1 | Dec 16, 2024 |
| 2.3.0 | Sep 19, 2024 |
| 2.2.1 | Aug 15, 2023 |
| 2.2.0 | Aug 15, 2023 |
| 2.1.3 | Aug 11, 2023 |
| 2.1.2 | Aug 6, 2023 |
| 2.1.1 | Aug 5, 2023 |
| 2.1.0 | May 27, 2023 |
| 2.0.2 | Feb 15, 2023 |
| 2.0.1 | Feb 14, 2023 |
| 2.0.0 | Feb 14, 2023 |
| 1.8.9 | Dec 16, 2022 |
These releases are tags only. The author does not attach a packaged zip, so there are no download counts to report.