Overview
Data validation allows you to control what users can enter into cells. You can create dropdown lists, restrict input to specific ranges, require specific data types, and provide helpful messages to guide users.
Basic Data Validation
Here’s a simple example with different validation types:
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Add some labels and initial values
$sheet->setCellValue('A1', 'Cell B3 and B5 contain data validation...')
->setCellValue('A3', 'Number:')
->setCellValue('B3', '10')
->setCellValue('A5', 'List:')
->setCellValue('B5', 'Item A');
Numeric Validation
Restrict input to numbers within a specific range:
// Validate that B3 contains a whole number between 10 and 20
$validation = $sheet->getCell('B3')->getDataValidation();
$validation->setType(DataValidation::TYPE_WHOLE);
$validation->setErrorStyle(DataValidation::STYLE_STOP);
$validation->setAllowBlank(true);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setErrorTitle('Input error');
$validation->setError('Only numbers between 10 and 20 are allowed!');
$validation->setPromptTitle('Allowed input');
$validation->setPrompt('Only numbers between 10 and 20 are allowed.');
$validation->setFormula1('10'); // Minimum value
$validation->setFormula2('20'); // Maximum value
Validation Types
Available validation types:
TYPE_NONE - No validation
TYPE_WHOLE - Whole number
TYPE_DECIMAL - Decimal number
TYPE_LIST - List of values (dropdown)
TYPE_DATE - Date
TYPE_TIME - Time
TYPE_TEXTLENGTH - Text length
TYPE_CUSTOM - Custom formula
Dropdown Lists
Create dropdown lists for users to select from:
Static List (Comma-Separated)
$validation = $sheet->getCell('B5')->getDataValidation();
$validation->setType(DataValidation::TYPE_LIST);
$validation->setErrorStyle(DataValidation::STYLE_INFORMATION);
$validation->setAllowBlank(false);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setShowDropDown(true);
$validation->setErrorTitle('Input error');
$validation->setError('Value is not in list.');
$validation->setPromptTitle('Pick from list');
$validation->setPrompt('Please pick a value from the drop-down list.');
$validation->setFormula1('"Item A,Item B,Item C"'); // Note the double quotes!
When using a comma-separated list, you must enclose the entire list in double quotes: '"Item A,Item B,Item C"'
Dynamic List (Cell Range)
// First, populate the source cells
$sheet->setCellValue('A7', 'List #2:')
->setCellValue('B7', 'Item #2')
->setCellValue('D2', 'Item #1')
->setCellValue('D3', 'Item #2')
->setCellValue('D4', 'Item #3')
->setCellValue('D5', 'Item #4')
->setCellValue('D6', 'Item #5');
// Create validation referencing the cell range
$validation = $sheet->getCell('B7')->getDataValidation();
$validation->setType(DataValidation::TYPE_LIST);
$validation->setErrorStyle(DataValidation::STYLE_INFORMATION);
$validation->setAllowBlank(false);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setShowDropDown(true);
$validation->setErrorTitle('Input error');
$validation->setError('Value is not in list.');
$validation->setPromptTitle('Pick from list');
$validation->setPrompt('Please pick a value from the drop-down list.');
$validation->setFormula1('$D$2:$D$6'); // Note: NO quotes for ranges
When referencing a cell range, do NOT enclose it in quotes. Use absolute references (e.g., $D$2:$D$6) for best results.
Date Validation
Restrict input to dates within a specific range:
$validation = $sheet->getCell('B9')->getDataValidation();
$validation->setType(DataValidation::TYPE_DATE);
$validation->setErrorStyle(DataValidation::STYLE_STOP);
$validation->setOperator(DataValidation::OPERATOR_BETWEEN);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setErrorTitle('Invalid Date');
$validation->setError('Date must be between 2024-01-01 and 2024-12-31');
$validation->setPromptTitle('Enter Date');
$validation->setPrompt('Please enter a date in 2024.');
$validation->setFormula1('2024-01-01');
$validation->setFormula2('2024-12-31');
Validation Operators
For numeric, date, and time validations:
OPERATOR_BETWEEN - Between two values
OPERATOR_NOTBETWEEN - Not between two values
OPERATOR_EQUAL - Equal to
OPERATOR_NOTEQUAL - Not equal to
OPERATOR_GREATERTHAN - Greater than
OPERATOR_GREATERTHANOREQUAL - Greater than or equal to
OPERATOR_LESSTHAN - Less than
OPERATOR_LESSTHANOREQUAL - Less than or equal to
Text Length Validation
Restrict the length of text input:
$validation = $sheet->getCell('B11')->getDataValidation();
$validation->setType(DataValidation::TYPE_TEXTLENGTH);
$validation->setErrorStyle(DataValidation::STYLE_STOP);
$validation->setOperator(DataValidation::OPERATOR_LESSTHANOREQUAL);
$validation->setAllowBlank(false);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setErrorTitle('Text too long');
$validation->setError('Text must be 50 characters or less.');
$validation->setPromptTitle('Enter text');
$validation->setPrompt('Maximum 50 characters allowed.');
$validation->setFormula1('50'); // Maximum length
Use a custom formula for complex validation:
// Validate that B13 is greater than B3
$validation = $sheet->getCell('B13')->getDataValidation();
$validation->setType(DataValidation::TYPE_CUSTOM);
$validation->setErrorStyle(DataValidation::STYLE_STOP);
$validation->setAllowBlank(false);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setErrorTitle('Invalid value');
$validation->setError('Value must be greater than the number in B3.');
$validation->setPromptTitle('Enter value');
$validation->setPrompt('Enter a number greater than B3.');
$validation->setFormula1('=B13>$B$3');
Error Styles
Control how validation errors are handled:
// STYLE_STOP - Prevents invalid input (default)
$validation->setErrorStyle(DataValidation::STYLE_STOP);
// STYLE_WARNING - Shows warning but allows invalid input
$validation->setErrorStyle(DataValidation::STYLE_WARNING);
// STYLE_INFORMATION - Shows info message but allows invalid input
$validation->setErrorStyle(DataValidation::STYLE_INFORMATION);
Provide helpful guidance to users:
$validation->setShowInputMessage(true);
$validation->setPromptTitle('Instructions');
$validation->setPrompt('Enter a value between 1 and 100. Decimals are allowed.');
Decimal Number Validation
$validation = $sheet->getCell('B15')->getDataValidation();
$validation->setType(DataValidation::TYPE_DECIMAL);
$validation->setErrorStyle(DataValidation::STYLE_STOP);
$validation->setOperator(DataValidation::OPERATOR_BETWEEN);
$validation->setAllowBlank(true);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setErrorTitle('Invalid decimal');
$validation->setError('Decimal must be between 0.0 and 1.0');
$validation->setPromptTitle('Enter decimal');
$validation->setPrompt('Enter a decimal between 0.0 and 1.0');
$validation->setFormula1('0.0');
$validation->setFormula2('1.0');
Time Validation
$validation = $sheet->getCell('B17')->getDataValidation();
$validation->setType(DataValidation::TYPE_TIME);
$validation->setErrorStyle(DataValidation::STYLE_STOP);
$validation->setOperator(DataValidation::OPERATOR_BETWEEN);
$validation->setAllowBlank(false);
$validation->setShowInputMessage(true);
$validation->setShowErrorMessage(true);
$validation->setErrorTitle('Invalid time');
$validation->setError('Time must be between 09:00 and 17:00');
$validation->setPromptTitle('Enter time');
$validation->setPrompt('Enter a time during business hours (9 AM - 5 PM)');
$validation->setFormula1('09:00');
$validation->setFormula2('17:00');
Copying Validation to Multiple Cells
Apply the same validation to multiple cells:
// Create validation for one cell
$validation = $sheet->getCell('B3')->getDataValidation();
$validation->setType(DataValidation::TYPE_WHOLE);
$validation->setFormula1('1');
$validation->setFormula2('100');
// Copy to other cells
for ($row = 4; $row <= 10; $row++) {
$sheet->getCell("B{$row}")->setDataValidation(clone $validation);
}
Always use clone when copying validation to prevent all cells from sharing the same validation object.
Complete Example
Here’s a complete example with multiple validation types:
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\IOFactory;
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Add form labels
$sheet->setCellValue('A1', 'Employee Data Entry Form');
$sheet->getStyle('A1')->getFont()->setBold(true)->setSize(14);
$sheet->setCellValue('A3', 'Employee ID:')
->setCellValue('A4', 'Name:')
->setCellValue('A5', 'Department:')
->setCellValue('A6', 'Hire Date:')
->setCellValue('A7', 'Salary:')
->setCellValue('A8', 'Comments:');
// Employee ID: Whole number 1-9999
$validation = $sheet->getCell('B3')->getDataValidation();
$validation->setType(DataValidation::TYPE_WHOLE)
->setOperator(DataValidation::OPERATOR_BETWEEN)
->setErrorStyle(DataValidation::STYLE_STOP)
->setAllowBlank(false)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setErrorTitle('Invalid ID')
->setError('Employee ID must be a number between 1 and 9999')
->setPromptTitle('Employee ID')
->setPrompt('Enter employee ID (1-9999)')
->setFormula1('1')
->setFormula2('9999');
// Name: Text length 1-50 characters
$validation = $sheet->getCell('B4')->getDataValidation();
$validation->setType(DataValidation::TYPE_TEXTLENGTH)
->setOperator(DataValidation::OPERATOR_BETWEEN)
->setErrorStyle(DataValidation::STYLE_STOP)
->setAllowBlank(false)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setErrorTitle('Invalid Name')
->setError('Name must be between 1 and 50 characters')
->setPromptTitle('Employee Name')
->setPrompt('Enter full name (max 50 characters)')
->setFormula1('1')
->setFormula2('50');
// Department: Dropdown list
$validation = $sheet->getCell('B5')->getDataValidation();
$validation->setType(DataValidation::TYPE_LIST)
->setErrorStyle(DataValidation::STYLE_STOP)
->setAllowBlank(false)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setShowDropDown(true)
->setErrorTitle('Invalid Department')
->setError('Please select a department from the list')
->setPromptTitle('Department')
->setPrompt('Select employee department')
->setFormula1('"Sales,Marketing,Engineering,HR,Finance"');
// Hire Date: Date between 2020-01-01 and today
$validation = $sheet->getCell('B6')->getDataValidation();
$validation->setType(DataValidation::TYPE_DATE)
->setOperator(DataValidation::OPERATOR_BETWEEN)
->setErrorStyle(DataValidation::STYLE_STOP)
->setAllowBlank(false)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setErrorTitle('Invalid Date')
->setError('Hire date must be between 2020-01-01 and today')
->setPromptTitle('Hire Date')
->setPrompt('Enter employee hire date')
->setFormula1('2020-01-01')
->setFormula2('=TODAY()');
// Salary: Decimal between 30000 and 200000
$validation = $sheet->getCell('B7')->getDataValidation();
$validation->setType(DataValidation::TYPE_DECIMAL)
->setOperator(DataValidation::OPERATOR_BETWEEN)
->setErrorStyle(DataValidation::STYLE_STOP)
->setAllowBlank(false)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setErrorTitle('Invalid Salary')
->setError('Salary must be between $30,000 and $200,000')
->setPromptTitle('Annual Salary')
->setPrompt('Enter annual salary ($30,000 - $200,000)')
->setFormula1('30000')
->setFormula2('200000');
// Comments: Optional text up to 200 characters
$validation = $sheet->getCell('B8')->getDataValidation();
$validation->setType(DataValidation::TYPE_TEXTLENGTH)
->setOperator(DataValidation::OPERATOR_LESSTHANOREQUAL)
->setErrorStyle(DataValidation::STYLE_WARNING)
->setAllowBlank(true)
->setShowInputMessage(true)
->setShowErrorMessage(true)
->setErrorTitle('Text too long')
->setError('Comments should be 200 characters or less')
->setPromptTitle('Comments')
->setPrompt('Enter any additional comments (optional, max 200 characters)')
->setFormula1('200');
// Format the sheet
$sheet->getColumnDimension('A')->setWidth(15);
$sheet->getColumnDimension('B')->setWidth(30);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('employee_form.xlsx');
Expected Behavior
When users open the Excel file:
- Cells with validation show an input message when selected
- Dropdown arrows appear for list validations
- Invalid input triggers error messages
- Error style determines whether invalid input is prevented or allowed