Kotchasan Framework Documentation
Form and HTML Generation
Form and HTML Generation
Kotchasan Framework provides a comprehensive set of classes for creating forms and managing HTML, from basic input fields and table management to complex layout creation.
Table of Contents
- Form Class - Creating Form Fields
- Html Class - Managing HTML Elements
- HtmlTable Class - Creating Tables
- Grid Class - Managing Grid Layout
- Real-world Usage
- Best Practices
Form Class - Creating Form Fields
Basic Input Fields
use Kotchasan\Form;
// Text input
$textInput = Form::text([
'id' => 'username',
'name' => 'username',
'class' => 'g-input',
'placeholder' => 'Enter username',
'maxlength' => 50,
'required' => true
]);
// Password input
$passwordInput = Form::password([
'id' => 'password',
'name' => 'password',
'class' => 'g-input',
'placeholder' => 'Enter password',
'required' => true
]);
// Email input with validation
$emailInput = Form::email([
'id' => 'email',
'name' => 'email',
'class' => 'g-input',
'placeholder' => 'name@example.com',
'required' => true,
'title' => 'Please enter a valid email address'
]);
// Number input
$ageInput = Form::number([
'id' => 'age',
'name' => 'age',
'class' => 'g-input',
'min' => 1,
'max' => 120,
'placeholder' => 'Age'
]);Select and Options
// Simple select
$countrySelect = Form::select([
'id' => 'country',
'name' => 'country',
'class' => 'g-select',
'options' => [
'TH' => 'Thailand',
'US' => 'United States',
'JP' => 'Japan',
'UK' => 'United Kingdom'
],
'value' => 'TH' // Default value
]);
// Select with grouped options
$productSelect = Form::select([
'id' => 'product',
'name' => 'product',
'class' => 'g-select',
'options' => [
'Electronics' => [
'laptop' => 'Laptop',
'smartphone' => 'Smartphone',
'tablet' => 'Tablet'
],
'Clothing' => [
'shirt' => 'Shirt',
'pants' => 'Pants',
'shoes' => 'Shoes'
]
],
'required' => true
]);
// Multi-select
$skillsSelect = Form::select([
'id' => 'skills',
'name' => 'skills[]',
'class' => 'g-select',
'multiple' => true,
'size' => 5,
'options' => [
'php' => 'PHP',
'javascript' => 'JavaScript',
'python' => 'Python',
'java' => 'Java',
'css' => 'CSS/HTML'
]
]);Checkbox and Radio
// Single checkbox
$agreeCheckbox = Form::checkbox([
'id' => 'agree',
'name' => 'agree',
'value' => '1',
'checked' => false
]);
// Radio group
$genderOptions = [
'M' => 'Male',
'F' => 'Female',
'O' => 'Other'
];
foreach ($genderOptions as $value => $label) {
$genderRadio = Form::radio([
'id' => 'gender_' . $value,
'name' => 'gender',
'value' => $value,
'checked' => ($value === 'M') // Set default
]);
}
// Checkbox group
$hobbies = [
'reading' => 'Reading',
'music' => 'Music',
'sports' => 'Sports',
'travel' => 'Travel',
'cooking' => 'Cooking'
];
foreach ($hobbies as $value => $label) {
$hobbyCheckbox = Form::checkbox([
'id' => 'hobby_' . $value,
'name' => 'hobbies[]',
'value' => $value
]);
}Special Fields
// File upload
$avatarUpload = Form::file([
'id' => 'avatar',
'name' => 'avatar',
'accept' => 'image/*',
'class' => 'g-file'
]);
// Multiple file upload
$documentsUpload = Form::file([
'id' => 'documents',
'name' => 'documents[]',
'multiple' => true,
'accept' => '.pdf,.doc,.docx,.txt'
]);
// Date input
$birthdateInput = Form::date([
'id' => 'birthdate',
'name' => 'birthdate',
'class' => 'g-input',
'max' => date('Y-m-d'), // Not later than today
'required' => true
]);
// DateTime-local input
$appointmentInput = Form::datetime([
'id' => 'appointment',
'name' => 'appointment',
'class' => 'g-input',
'min' => date('Y-m-d\TH:i'), // Not earlier than now
'step' => 1800 // Every 30 minutes
]);
// Color picker
$colorInput = Form::color([
'id' => 'theme_color',
'name' => 'theme_color',
'value' => '#3498db'
]);
// Range slider
$volumeRange = Form::range([
'id' => 'volume',
'name' => 'volume',
'min' => 0,
'max' => 100,
'value' => 50,
'step' => 1
]);
*/Currency and Number Format
// Currency input (Thai Baht)
$priceInput = Form::currency([
'id' => 'price',
'name' => 'price',
'class' => 'g-input',
'placeholder' => '0.00',
'min' => 0,
'step' => 0.01,
'currency' => 'THB'
]);
// Integer input
$quantityInput = Form::integer([
'id' => 'quantity',
'name' => 'quantity',
'class' => 'g-input',
'min' => 1,
'max' => 999,
'value' => 1
]);Buttons
// Submit button
$submitButton = Form::submit([
'id' => 'submit_btn',
'class' => 'button save large',
'value' => 'Save Data'
]);
// Reset button
$resetButton = Form::reset([
'id' => 'reset_btn',
'class' => 'button cancel',
'value' => 'Clear Data'
]);
// Custom button
$customButton = Form::button([
'id' => 'custom_btn',
'class' => 'button action',
'value' => 'Execute',
'onclick' => 'handleCustomAction()'
]);Html Class - Managing HTML Elements
Creating Complete Forms
use Kotchasan\Html;
// Create main form
$form = Html::create('form', [
'id' => 'user_form',
'class' => 'setup_frm',
'method' => 'post',
'action' => 'index.php/demo/save',
'autocomplete' => 'off',
'ajax' => true,
'token' => true
]);
// Add fieldset
$personalInfo = $form->add('fieldset', [
'title' => 'Personal Information',
'titleClass' => 'icon-user'
]);
// Add field groups
$nameGroup = $personalInfo->add('groups');
// First name
$nameGroup->add('label', [
'for' => 'first_name',
'innerHTML' => 'First Name <span class="required"></span>'
]);
$nameGroup->add('input', [
'type' => 'text',
'id' => 'first_name',
'name' => 'first_name',
'class' => 'g-input',
'maxlength' => 50,
'required' => true
]);
// Last name
$nameGroup->add('label', [
'for' => 'last_name',
'innerHTML' => 'Last Name <span class="required"></span>'
]);
$nameGroup->add('input', [
'type' => 'text',
'id' => 'last_name',
'name' => 'last_name',
'class' => 'g-input',
'maxlength' => 50,
'required' => true
]);Input Groups and Row Groups
// Input groups for field grouping
$contactGroup = $personalInfo->add('inputgroups');
// Email
$emailRow = $contactGroup->add('groups');
$emailRow->add('label', [
'for' => 'email',
'innerHTML' => 'Email <span class="required">*</span>'
]);
$emailRow->add('input', [
'type' => 'email',
'id' => 'email',
'name' => 'email',
'class' => 'g-input',
'required' => true,
'pattern' => '[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'
]);
// Phone number
$phoneRow = $contactGroup->add('groups');
$phoneRow->add('label', [
'for' => 'phone',
'innerHTML' => 'Phone Number'
]);
$phoneRow->add('input', [
'type' => 'tel',
'id' => 'phone',
'name' => 'phone',
'class' => 'g-input',
'pattern' => '[0-9]{9,10}'
]);Radio Groups and Checkbox Groups
// Radio groups
$genderGroup = $personalInfo->add('radiogroups', [
'label' => 'Gender <span class="required">*</span>',
'labelClass' => 'g-input',
'name' => 'gender',
'options' => [
'M' => 'Male',
'F' => 'Female',
'O' => 'Not specified'
],
'value' => 'M' // Default value
]);
// Checkbox groups
$skillsGroup = $personalInfo->add('checkboxgroups', [
'label' => 'Skills',
'labelClass' => 'g-input',
'name' => 'skills[]',
'options' => [
'php' => 'PHP',
'javascript' => 'JavaScript',
'mysql' => 'MySQL',
'css' => 'CSS/HTML',
'python' => 'Python'
],
'value' => ['php', 'mysql'] // Default selected
]);Menu Button and Dropdown
// Menu button with dropdown
$actionMenu = $form->add('menubutton', [
'class' => 'button action',
'text' => 'Select Action',
'options' => [
[
'text' => 'Save and Send',
'onclick' => 'submitForm("send")'
],
[
'text' => 'Save Draft',
'onclick' => 'submitForm("draft")'
],
'-', // separator
[
'text' => 'Export PDF',
'onclick' => 'exportPDF()'
],
[
'text' => 'Export Excel',
'onclick' => 'exportExcel()'
]
]
]);Using CKEditor
// Rich text editor
$descriptionEditor = $form->add('ckeditor', [
'id' => 'description',
'name' => 'description',
'height' => 300,
'language' => 'en',
'toolbar' => [
['Bold', 'Italic', 'Underline'],
['NumberedList', 'BulletedList'],
['Link', 'Unlink'],
['Image', 'Table'],
['Source']
],
'value' => '<p>Initial content</p>'
]);JavaScript Management
// Add JavaScript to form
$form->script('
// Basic validation
document.getElementById("user_form").addEventListener("submit", function(e) {
var firstName = document.getElementById("first_name").value;
var lastName = document.getElementById("last_name").value;
if (!firstName.trim() || !lastName.trim()) {
alert("Please enter first and last name");
e.preventDefault();
return false;
}
// Email format validation
var email = document.getElementById("email").value;
var emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (email && !emailPattern.test(email)) {
alert("Invalid email format");
e.preventDefault();
return false;
}
return true;
});
');HtmlTable Class - Creating Tables
Basic Table Creation
use Kotchasan\HtmlTable;
// Create new table
$table = new HtmlTable([
'id' => 'users_table',
'class' => 'data fullwidth',
'border' => 0
]);
// Add caption
$table->addCaption('System Users List');
// Add header
$table->addHeader([
'ID',
'Full Name',
'Email',
'Status',
'Join Date',
'Actions'
]);
// Add data rows
$users = [
[
'id' => 1,
'name' => 'John Doe',
'email' => 'john@example.com',
'status' => 'active',
'created' => '2024-01-15',
'actions' => ['edit', 'delete']
],
[
'id' => 2,
'name' => 'Jane Smith',
'email' => 'jane@example.com',
'status' => 'inactive',
'created' => '2024-01-20',
'actions' => ['edit', 'delete']
]
];
foreach ($users as $user) {
// Create action buttons
$actions = [];
if (in_array('edit', $user['actions'])) {
$actions[] = '<a href="edit.php?id=' . $user['id'] . '" class="icon-edit" title="Edit"></a>';
}
if (in_array('delete', $user['actions'])) {
$actions[] = '<a href="javascript:void(0)" onclick="confirmDelete(' . $user['id'] . ')" class="icon-delete" title="Delete"></a>';
}
// Status badge
$statusBadge = $user['status'] === 'active'
? '<span class="badge green">Active</span>'
: '<span class="badge red">Inactive</span>';
$table->addRow([
$user['id'],
$user['name'],
$user['email'],
$statusBadge,
date('m/d/Y', strtotime($user['created'])),
implode(' ', $actions)
]);
}
// Add footer (if needed)
$footerRow = \Kotchasan\TableRow::create();
$footerRow->addCell('Total ' . count($users) . ' records', [
'colspan' => 6,
'class' => 'center'
]);
$table->addFooter($footerRow);Complex Data Table
// Sales report table
$salesTable = new HtmlTable([
'id' => 'sales_report',
'class' => 'data responsive fullwidth'
]);
$salesTable->addCaption('Monthly Sales Report');
// Multi-level header
$salesTable->addHeader([
['text' => 'Month', 'rowspan' => 2],
['text' => 'Sales', 'colspan' => 3],
['text' => 'Comparison', 'rowspan' => 2]
]);
$salesTable->addHeader([
'Target',
'Actual',
'Percentage'
]);
// Sales data
$salesData = [
['January', 100000, 95000, 95.0, '+5%'],
['February', 120000, 135000, 112.5, '+12%'],
['March', 110000, 108000, 98.2, '-2%']
];
foreach ($salesData as $data) {
$row = [];
$row[] = $data[0]; // Month
$row[] = number_format($data[1]); // Target
$row[] = number_format($data[2]); // Actual
// Percentage with color based on performance
$percentage = $data[3];
$percentageClass = $percentage >= 100 ? 'text-success' : 'text-warning';
$row[] = '<span class="' . $percentageClass . '">' . $percentage . '%</span>';
// Comparison
$comparison = $data[4];
$comparisonClass = str_starts_with($comparison, '+') ? 'text-success' : 'text-danger';
$row[] = '<span class="' . $comparisonClass . '">' . $comparison . '</span>';
$salesTable->addRow($row);
}
// Add summary row
$totalRow = \Kotchasan\TableRow::create(['class' => 'summary']);
$totalRow->addCell('Total', ['class' => 'bold']);
$totalRow->addCell(number_format(330000), ['class' => 'bold']);
$totalRow->addCell(number_format(338000), ['class' => 'bold']);
$totalRow->addCell('<span class="text-success bold">102.4%</span>');
$totalRow->addCell('<span class="text-success bold">+2.4%</span>');
$salesTable->addFooter($totalRow);Grid Class - Managing Grid Layout
Using Grid System
use Kotchasan\Grid;
// Create grid container
$grid = new Grid();
$grid->setCols(12); // 12-column system
// Add content to grid
$content = '
<div class="ggrid">
<div class="block4 tablet6 mobile12">
<div class="card">
<h3>User Statistics</h3>
<div class="stat-number">1,234</div>
<div class="stat-text">Total Users</div>
</div>
</div>
<div class="block4 tablet6 mobile12">
<div class="card">
<h3>Today\'s Sales</h3>
<div class="stat-number">$45,680</div>
<div class="stat-text">Up 12%</div>
</div>
</div>
<div class="block4 tablet12 mobile12">
<div class="card">
<h3>New Orders</h3>
<div class="stat-number">28</div>
<div class="stat-text">Pending Confirmation</div>
</div>
</div>
</div>';
// Responsive grid layout
$responsiveLayout = '
<div class="ggrid">
<!-- Main content -->
<div class="block8 tablet12 mobile12">
<div class="content-area">
<h2>Main Content</h2>
<p>Website content goes here...</p>
</div>
</div>
<!-- Sidebar -->
<div class="block4 tablet12 mobile12">
<div class="sidebar">
<h3>Sidebar Menu</h3>
<ul>
<li><a href="#section1">Topic 1</a></li>
<li><a href="#section2">Topic 2</a></li>
<li><a href="#section3">Topic 3</a></li>
</ul>
</div>
</div>
</div>';Real-world Usage
User Registration Form
use Kotchasan\Html;
use Kotchasan\Form;
class RegistrationForm
{
public static function create()
{
// Create main form
$form = Html::create('form', [
'id' => 'registration_form',
'class' => 'setup_frm',
'method' => 'post',
'action' => 'index.php/register/save',
'autocomplete' => 'off',
'ajax' => true,
'token' => true
]);
// Personal information
$personalSection = $form->add('fieldset', [
'title' => 'Personal Information',
'titleClass' => 'icon-user'
]);
// Name fields
$nameGroup = $personalSection->add('groups');
$nameGroup->add('label', [
'for' => 'first_name',
'innerHTML' => 'First Name <span class="required"></span>'
]);
$nameGroup->add('input', [
'type' => 'text',
'id' => 'first_name',
'name' => 'first_name',
'class' => 'g-input',
'maxlength' => 50,
'required' => true,
'autofocus' => true
]);
$nameGroup->add('label', [
'for' => 'last_name',
'innerHTML' => 'Last Name <span class="required"></span>'
]);
$nameGroup->add('input', [
'type' => 'text',
'id' => 'last_name',
'name' => 'last_name',
'class' => 'g-input',
'maxlength' => 50,
'required' => true
]);
// Email and password
$credentialGroup = $personalSection->add('groups');
$credentialGroup->add('label', [
'for' => 'email',
'innerHTML' => 'Email <span class="required"></span>'
]);
$credentialGroup->add('input', [
'type' => 'email',
'id' => 'email',
'name' => 'email',
'class' => 'g-input',
'required' => true
]);
$credentialGroup->add('label', [
'for' => 'password',
'innerHTML' => 'Password <span class="required"></span>'
]);
$credentialGroup->add('input', [
'type' => 'password',
'id' => 'password',
'name' => 'password',
'class' => 'g-input',
'minlength' => 8,
'required' => true
]);
// Gender
$genderGroup = $personalSection->add('radiogroups', [
'label' => 'Gender <span class="required"></span>',
'labelClass' => 'g-input',
'name' => 'gender',
'options' => [
'M' => 'Male',
'F' => 'Female',
'O' => 'Not specified'
]
]);
// Birth date
$birthdateGroup = $personalSection->add('groups');
$birthdateGroup->add('label', [
'for' => 'birthdate',
'innerHTML' => 'Birth Date'
]);
$birthdateGroup->add('input', [
'type' => 'date',
'id' => 'birthdate',
'name' => 'birthdate',
'class' => 'g-input',
'max' => date('Y-m-d')
]);
// Contact information
$contactSection = $form->add('fieldset', [
'title' => 'Contact Information',
'titleClass' => 'icon-phone'
]);
// Phone
$phoneGroup = $contactSection->add('groups');
$phoneGroup->add('label', [
'for' => 'phone',
'innerHTML' => 'Phone Number'
]);
$phoneGroup->add('input', [
'type' => 'tel',
'id' => 'phone',
'name' => 'phone',
'class' => 'g-input',
'pattern' => '[0-9]{9,10}',
'placeholder' => '0812345678'
]);
// Address
$addressGroup = $contactSection->add('groups');
$addressGroup->add('label', [
'for' => 'address',
'innerHTML' => 'Address'
]);
$addressGroup->add('textarea', [
'id' => 'address',
'name' => 'address',
'class' => 'g-input',
'rows' => 3,
'placeholder' => 'Current address'
]);
// Province
$provinceGroup = $contactSection->add('groups');
$provinceGroup->add('label', [
'for' => 'province',
'innerHTML' => 'Province'
]);
$provinceGroup->add('select', [
'id' => 'province',
'name' => 'province',
'class' => 'g-select',
'options' => self::getProvinceOptions()
]);
// Terms and conditions
$termsSection = $form->add('fieldset', [
'title' => 'Terms and Conditions',
'titleClass' => 'icon-verfied'
]);
$termsGroup = $termsSection->add('groups');
$termsGroup->add('label', [
'for' => 'accept_terms',
'innerHTML' => 'Accept <a href="terms.php" target="_blank">Terms and Conditions</a> <span class="required"></span>'
]);
$termsGroup->add('input', [
'type' => 'checkbox',
'id' => 'accept_terms',
'name' => 'accept_terms',
'value' => '1',
'required' => true
]);
$newsletterGroup = $termsSection->add('groups');
$newsletterGroup->add('label', [
'for' => 'newsletter',
'innerHTML' => 'Receive Newsletter'
]);
$newsletterGroup->add('input', [
'type' => 'checkbox',
'id' => 'newsletter',
'name' => 'newsletter',
'value' => '1',
'checked' => true
]);
// Submit buttons
$submitGroup = $form->add('groups', ['class' => 'submit']);
$submitGroup->add('input', [
'type' => 'submit',
'class' => 'button save large',
'value' => 'Register'
]);
$submitGroup->add('input', [
'type' => 'reset',
'class' => 'button cancel',
'value' => 'Clear'
]);
// Add JavaScript validation
$form->script('
// Real-time validation
document.getElementById("email").addEventListener("blur", function() {
checkEmailAvailability(this.value);
});
document.getElementById("password").addEventListener("input", function() {
checkPasswordStrength(this.value);
});
// Email availability check
function checkEmailAvailability(email) {
if (email.length > 0) {
// Ajax request to check email
fetch("index.php/register/checkEmail", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({email: email})
})
.then(response => response.json())
.then(data => {
const emailField = document.getElementById("email");
if (data.available) {
emailField.setCustomValidity("");
} else {
emailField.setCustomValidity("This email is already taken");
}
});
}
}
// Password strength check
function checkPasswordStrength(password) {
const strengthMeter = document.getElementById("password_strength");
let strength = 0;
let message = "";
if (password.length >= 8) strength++;
if (/[a-z]/.test(password)) strength++;
if (/[A-Z]/.test(password)) strength++;
if (/[0-9]/.test(password)) strength++;
if (/[^A-Za-z0-9]/.test(password)) strength++;
switch(strength) {
case 0:
case 1:
message = "Weak";
break;
case 2:
case 3:
message = "Medium";
break;
case 4:
case 5:
message = "Strong";
break;
}
if (strengthMeter) {
strengthMeter.innerHTML = message;
strengthMeter.className = "password-strength " + message.toLowerCase();
}
}
');
return $form->render();
}
private static function getProvinceOptions()
{
// Example province list
return [
'' => 'Select Province',
'Bangkok' => 'Bangkok',
'Chiang Mai' => 'Chiang Mai',
'Chiang Rai' => 'Chiang Rai',
'Khon Kaen' => 'Khon Kaen',
'Nakhon Ratchasima' => 'Nakhon Ratchasima',
'Phuket' => 'Phuket',
'Surat Thani' => 'Surat Thani'
];
}
}Best Practices
1. Naming and Organization
// ✅ Use meaningful names
$userRegistrationForm = Html::create('form', [
'id' => 'user_registration_form',
'class' => 'setup_frm user-form'
]);
// ❌ Avoid meaningless names
$form1 = Html::create('form', ['id' => 'f1']);
// ✅ Group related fields
$personalInfo = $form->add('fieldset', ['title' => 'Personal Information']);
$contactInfo = $form->add('fieldset', ['title' => 'Contact Information']);
$preferences = $form->add('fieldset', ['title' => 'Preferences']);2. Accessibility and UX
// ✅ Use appropriate labels
$emailGroup->add('label', [
'for' => 'email',
'innerHTML' => 'Email <span class="required" aria-label="Required">*</span>'
]);
// ✅ Add ARIA attributes
$form->add('input', [
'type' => 'password',
'id' => 'password',
'name' => 'password',
'aria-describedby' => 'password_help',
'aria-required' => 'true'
]);
// ✅ Helpful placeholders
$form->add('input', [
'type' => 'tel',
'placeholder' => '0XX-XXX-XXXX',
'pattern' => '[0-9]{3}-[0-9]{3}-[0-9]{4}'
]);3. Error Handling
// ✅ Client-side validation
$form->script('
function validateForm() {
const email = document.getElementById("email");
const password = document.getElementById("password");
// Custom validation
if (!isValidEmail(email.value)) {
showError(email, "Invalid email format");
return false;
}
if (password.value.length < 8) {
showError(password, "Password must be at least 8 characters");
return false;
}
return true;
}
function showError(field, message) {
field.setCustomValidity(message);
field.reportValidity();
}
');
// ✅ Server-side validation feedback
if (isset($errors['email'])) {
$emailInput->setAttribute('class', 'g-input error');
$emailInput->setAttribute('title', $errors['email']);
}4. Performance
// ✅ Load JavaScript when needed
$form->script('
// Load library when needed
if (typeof DatePicker === "undefined") {
loadScript("/js/datepicker.js", function() {
initDatePicker("#birthdate");
});
}
');
// ✅ Use lazy loading for tables
$table->setAttribute('data-lazy', 'true');
$table->setAttribute('data-source', 'index.php/api/users');5. Security
// ✅ Add CSRF token
$form = Html::create('form', [
'token' => true, // Automatically add CSRF token
'method' => 'post'
]);
// ✅ Escape user input
$userInput = htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// ✅ Validate file uploads
$fileUpload = Form::file([
'accept' => 'image/jpeg,image/png,image/gif',
'max-size' => '2MB'
]);Using Kotchasan's Form, Html, HtmlTable, and Grid classes helps create forms and tables efficiently, quickly, and securely, with full support for cross-device compatibility.