Say you have a textarea field on an input form where the user inputs a list of items (like email addresses) and you intend to regularly use this list (for instance to send emails to each email address). Several things must be accomplished here:
- Change the text into an array of items
- Sanitize the items (unlike standard input, this list will be displayed and used much differently, so it is more convenient to store it in a ready-to-use state)
- Validate the items (No point storing invalid email addresses)
- Store the finished list in the database
If any step fails, the form should maintain the original input, so we also won’t touch the data on the controller side. We’ll utilize model hooks and and a custom validation method to do everything we need.
Let’s start by formatting and sanitizing the data so we can validate it. I define beforeValidate() as follows:
function beforeValidate
() {
// I allow this field to be empty
if(empty($this->data['Survey']['recipient_list'])) {
return true;
}
// try splitting by new lines first
$list = preg_split("[\n|\r]", $this->data['Survey']['recipient_list'], -1, PREG_SPLIT_NO_EMPTY
);
$final_list = array();
// check for comma splits
$L = count($list);
for($i=0; $i<$L; $i++) {
$rows = explode(',', $list[$i]);
if(count($rows) > 1) {
foreach($rows as $row) {
if(!empty($row)) {
$final_list[] = $row;
}
}
}
else {
$final_list[] = $list[$i];
}
}
// sanitize the final list
App
::import('Sanitize');
foreach($final_list as &$addr) {
$addr = Sanitize
::paranoid($addr, array('@', '.', '-', '_'));
}
// Update our soon-to-be-validated data
$this->data['Survey']['recipient_list'] = $final_list;
// Save will stop if we don't return true
return true;
}
This is obviously customized for my use, but it handles some pretty standard needs. It splits the input into items based on new lines and commas (any mix of the two). Then sanitizes each item, ensuring to preserve standard email address characters.
We then add our own validation method and define it as follows:
function validateRecipientList
($check) {
if(empty($check['recipient_list'])) {
return true;
}
// Using cake's built in validation class
$Validation = Validation
::getInstance();
foreach($check['recipient_list'] as $addr) {
if(!$Validation->email($addr)) {
return false;
}
}
// Needed to continue
return true;
}
I’m simply ensuring each item is an email address (note the use of Cake’s built in validation class).
Lastly, we utilize beforeSave() to serialize the list and prepare it for storage:
function beforeSave
() {
// serialize recipient list
$this->data['Survey']['recipient_list'] = serialize($this->data['Survey']['recipient_list']);
// Again, needed to continue
return true;
}
All of this will now happen automagically whenever this model’s save() is called. We can even handle unserializing the list via afterFind(), but that’ll be left to the reader.