I have a quick question that's killing my head.
I'm trying to make a Form Validation System with Method Chaining in PHP
What I want to do is to be able to call for example (please check the code comments):
$firstname = $OBJECT->Forms->Field("First Name", "firstname"); //This one doesn't validate, but just puts what's on firstname field on to $firstname. But this way doesn't work for me, because I have to return the object so it can be chainable and not the variable of the POST. How can I do this?
$firstname = $OBJECT->Forms->Field("First Name", "firstname")->Validate(); //this one validates if the field is not empty and if it's empty it'll insert the first parameter ("First Name") onto an array to display the errors.
$email = $OBJECT->Forms->Field("Email", "email")->Validate()->Email(); //This one does the same as above but validates Email and inserts the value of the email field onto $email
but I prefer the next one...
$email = $OBJECT->Forms->Field("Email", "email")->Validate->Email(); //I'd rather prefer this method but I don't know how to do it without using the parenthesis on the Validate method.
I can only make it work like this
$firstname = $OBJECT->Forms->Field("First Name", "firstname")->Validate();
and
$firstname = $OBJECT->Forms->Field("First Name", "firstname")->Validate()->Email();
Without ->Validate(); I can't seem to make it work (Like this: $firstname = $OBJECT->Forms->Field("First Name", "firstname");)
The code is kinda mess to share. But the code is simple... I have a forms.class.php and a validate.class.php.
The forms.class.php creates an instance of Validate class from validate.class.php and the Forms Object is passed through the Validate class on the constructor.
I want to be able to do:
$OBJECT->Forms->Field();
$OBJECT->Forms->Field()->Validate();
$OBJECT->Forms->Field()->Validate()->Email;
$OBJECT->Forms->Field()->Validate()->Telephone;
or this preferebly:
$OBJECT->Forms->Field();
$OBJECT->Forms->Field()->Validate;
$OBJECT->Forms->Field()->Validate->Email;
$OBJECT->Forms->Field()->Validate->Telephone;
Only figured out:
$OBJECT->Forms->Field()->Validate();
$OBJECT->Forms->Field()->Validate()->Email();
$OBJECT->Forms->Field()->Validate()->Telephone();
But any form is OK
Thank you.
See if this is what you are trying to do:
<?php
class FormValidate
{
protected $args;
public $valid;
public function Forms()
{
// Don't know what this function is supposed to do....
return $this;
}
public function Validate()
{
$numargs = func_num_args();
$this->args = array();
if($numargs == 2) {
$vals = func_get_args();
$this->args[$vals[1]] = $vals[0];
$this->valid = true;
}
else
$this->valid = false;
if(isset($this->args['firstname']) && !empty($this->args['firstname']))
return true;
return $this;
}
public function Email()
{
if(isset($this->args['email'])) {
if(filter_var($this->args['email'],FILTER_VALIDATE_EMAIL))
return $this->valid = $this->args['email'];
}
return $this->valid = false;
}
public function Telephone()
{
if(isset($this->args['telephone'])) {
if(preg_match('/^[0-9]{3}-[0-9]{3}-[0-9]{4}$/',$this->args['telephone']))
return $this->valid = $this->args['telephone'];
}
return $this->valid = false;
}
}
$test = new FormValidate();
// These will throw a fatal error on the base Validate('First Name','firstname')
// if you add another method to the chain like so: ->Validate('First Name','firstname')->Email();
echo $test->Forms()->Validate('123-876-0987','telephone')->Telephone();
?>
Related
I am writing a method that uses POST variables posted by AJAX to add a user to a certain course in the database, but I can't get the callback to work correctly:
public function enroll()
{
$package = array();
$this->load->library('form_validation');
$this->form_validation->set_rules('course', 'Vak', 'required|callback_not_enrolled');
$fields = array("course");
if ($this->form_validation->run($this) === FALSE) {
$errors = array();
$success = array();
foreach ($fields as $field) {
$error = form_error($field);
if ($error !== "") {
$errors[$field] = $error;
} else {
$success[$field] = True;
}
}
$package["field_errors"] = $errors;
$package["field_success"] = $success;
$package["success"] = False;
} else {
$package["database"] = $this->course_model->enroll_user($this->data["user"], $this->input->post("course"));
$package["success"] = True;
}
echo json_encode($package);
}
I wrote the callback not_enrolled to check if the user is not already enrolled to the database. Note that I can't use is_unique because I have to test the combined uniqueness of two fields (so just one or two separate ones don't do the trick) and the id of the user is not included in the form (because it's part of the Code Igniter session).
The callback function:
public function _not_enrolled($course)
{
$exists = ($this->user->is_enrolled($course, $this->data["user_id"]) != False);
if ($exists != False) {
$this->form_validation->set_message("not_enrolled", "Already enrolled");
return False;
} else {
return True;
}
}
And finally the method is_enrolled from the model:
public function is_enrolled($course, $user=False) {
if($user==False){
$user = $this->data["user_id"];
}
$this->db->select()->from("course_participant")->where("user_id", $user)->where("course_id", $course);
$query = $this->db->get();
return($query->num_rows()>0);
}
Through a call to var_dump($this->_not_enrolled($existing_course_id)); I know that both the callback function and the method from the model work, as it correctly returned true.
When I var_dump the $package array or validation_errors() I don't get any validation errors except that it says Unable to access an error message corresponding to your field name Vak(not_enrolled).
I tried removing the initial _ from the function name but that gives me a Server Status 500 error.
I have another setup exactly like this, albeit other database calls, with a callback using the same syntax. This method works perfectly.
I'm trying to learn about Object Oriented Programming and I want to turn this code into such. I've got some knowledge so far from google and here and in particular http://code.tutsplus.com/tutorials/object-oriented-php-for-beginners--net-12762.
The way I understand it is I need classes that contain a certain set of instructions that can be used with universal objects outside of those classes.
My idea so far has been to set up a User class where names are stored (coming from a HTML/PHP form).
class User {
public $nameStore, $fName, $lName, $email;
public function __construct ($fName, $lName, $email) {
$this->$fN = $fName;
$this->$lN = $lName;
$this->$eN = $email;
}
Like the above^. But I'm still confused about where other instructions of my code should go. That's where I need the most help. From what I've read, it hasn't helped me get the full grasp of what I need to do. If someone could help get me started in the right direction on how to make my code into an OOP type I would greatly appreciate it. Thanks!
Below is my procedural code that I want to convert to OOP.
<?php
session_start();
$formNames = $_POST['names'];
$active = (isset($_POST['activate'])) ? $_POST['activate'] : false;
//checks if activate checkbox is being used
$email = '#grantle.com';
$fullnames = explode(", ", $_POST['names']);
if ($active == true) {
$active = '1';
//sets activate checkbox to '1' if it has been selected
}
/*----------------------Function to Insert User-------------------------*/
function newUser($firstName,$lastName,$emailUser,$active,$conn){
//a function to insert a user into a database is here
}
//newUser function enters names in database
/*-------------------------End Function to Insert User--------------------*/
/*-----------------------Function for Errors------------------------------*/
function errorCheck($formNames, $nameSplit, $fullname){
$isValid = false;
if (empty($fullname)) {
$_SESSION['error'][] = '<br><br> Error: Name Missing Here: '.$fullname.'<br><br>';
} elseif (empty($nameSplit[0])) {
$_SESSION['error'][] = '<br><br> Error: First Name Missing Here: '.$fullname.'<br><br>';
} elseif (empty($nameSplit[1])) {
$_SESSION['error'][] = '<br><br> Error: Last Name Missing Here: '.$fullname.'<br><br>';
} elseif (preg_match('/[^A-Za-z, ]/', $fullname)) {
$_SESSION['error'][] = '<br><br> Error: Illegal Character Found in: '.$fullname.'<br><br>';
} else {
$isValid = true;
}
return $isValid;
}
//errorCheck function tests for errors in names and stops them from being entered in the
//database if there are errors in the name. Allows good names to go through
/*-----------------------------End Function for Errors---------------------*/
/*--------------------------Function for Redirect--------------------------*/
function redirect($url){
$string = '<script type="text/javascript">';
$string .= 'window.location = "' .$url. '"';
$string .= '</script>';
echo $string;
}
//redirect function uses a javascript script to redirect user because headers have already been sent.
/*-----------------------------End Function for Redirect-----------------------*/
// Connect to database
I connect to the database here//
// Initialize empty error array
$_SESSION['error'] = array();
foreach ($fullnames as $fullname) {
$nameSplit = explode(" ", $fullname);
//I open the database here
//opens the database
if (errorCheck($formNames, $nameSplit, $fullname)) {
$firstName = $nameSplit[0];//sets first part of name to first name
$lastName = $nameSplit[1];//sets second part of name to last name
$emailUser = $nameSplit[0].$email;//sets first part and adds email extension
newUser($firstName,$lastName,$emailUser,$active,$conn);//do this BELOW only for names that have no errors
}//ends if of errorCheck
}//ends fullnames foreach
if (count($_SESSION['error']) == 0) {
redirect('viewAll.php');
} else {
redirect('form.php');
}
/*Redirects to viewAll page only once and as long as no errors have been found*/
Your
class User {
public $nameStore, $fName, $lName, $email;
public function __construct ($fName, $lName, $email) {
$this->$fN = $fName;
$this->$lN = $lName;
$this->$eN = $email;
}
I would break this up into more specific parts such as GET and SET for each value you are trying to store in the Class:
class User {
private $fName, $lName, $email;
public function set_firstname($fname){
$this->fName = $fname;
}
public function set_surname($lName){
$this->lName = $lName;
}
public function set_email($email){
$this->email = $email;
}
public function get_email(){
return $this->email;
}
public function get_fname(){
return $this->fName;
}
public function get_surname(){
return $this->lName;
}
Then when you create the class, you can add and return each value individually, rather than forcing yourself to do them all at once. This is more flexible. But you can also add the values at the creation of the class as well if you wish, using the __construct similar to what you had already:
public function __construct ($fName = null, $lName = null, $email = null) {
if(!empty($fName)){
$this->set_firstname($fName);
}
if(!empty($lName)){
$this->set_surname($lName);
}
if(filter_var($email, FILTER_VALIDATE_EMAIL) !== false){
$this->set_email($email);
}
}
What this does is for each non-empty value it runs the corresponding SET method. Also checking that the email value is valid before saving it. If no values are passed to the class then it doesn't save anything.
Your setting up of the class is incorrect, firstly you need to include the class file into the working PHP so at the top of your page add:
include "path/to/users.class.php";
And then initiate the class correctly:
$userClassInstance = new User($firstName,$lastName,$emailUser);
When the above line runs, you will then have a User object containing three variables referenced as $userClassInstance. you can do var_dump($userClassInstance);
Be careful as your code has newUser as one line and also has an incorrect number of variables in the construct statement. Generally all the functions in a page should be placed inside an appropriate class, so all your string management functions such as errorCheck() could be put into the Users class to check the values given before assigning them to the variables in the class.
Finally, to view the stored variables you would then do:
print $userClassInstance->get_fname(); //will outout the value of the class $fName
I have a php file(register.php) with a public function register($data) where errors are validated.Then errors are counted and if no errors are found, validation is passed.
register.php:
class ARegister {
public function register($data) {
$user = $data['userData'];
//validate provided data
$errors = $this->validateUser($data);
if(count($errors) == 0) {
//first validation
}
}
public function validateUser($data, $botProtection = true) {
$id = $data['fieldId'];
$user = $data['userData'];
$errors = array();
$validator = new AValidator();
if( $validator->isEmpty($user['password']) )
$errors[] = array(
"id" => $id['password'],
"msg" => Lang::get('password_required')
);
return $errors;
}
The problem is, that I need to get this confirmation of validated data to my other php file (othervalidation.php) where I've made another validation:
othervalidation.php:
<?php
require 'register.php';
if ( !empty($action) ) {
switch ( $action ) {
case 'process_payment':
try {
$instance = new ARegister();
if($instance->validateUser($data, $errors)) {
throw new Exception('Validation error');
}
} catch (Exception $e) {
$status = false;
$message = $e->getMessage();
}
}
How can I send the result of $errors variable to my other validation (othervalidation.php)?
I looked at your new code design and here's the new problems I found.
First, in your register function, you use the errors variable as an integer while your validate function returns an array. You got two possibilities here.
You can change your register method to check out if your error array is empty like this:
if(empty($errors)) {
//first validation
}
Count is also valid, but I still prefer empty since it's syntactically clearer. Furthermore, the count function returns 1 if the parameter is not an array or a countable object or 0 if the parameter is NULL. As I said, it is a functional solution in your current case but, in some other contexts, it might cause you unexpected results.
Here in your method declaration, I see that you are expecting a boolean (botProtection).
public function validateUser($data, $botProtection = true) {
But you are supplying an errors parameter
if($instance->validateUser($data, $errors)) {
You don't provide me the declaration of the errors variable, but it is probably not matching the bot protection parameter your function is expecting. PHP is using lose typing, it is useful but, once again, you got to be careful for bugs hard to find. For public function, you should always make sure a way or another that the supplied parameter won't lead to code crash.
In your code, the data parameter seems to be an array. You can use parameter hinting to force the use of array like this:
public function register(array $data) {
public function validateUser(array $data, $botProtection = true) {
And even specific class (as if you where using "instance of" in a condition)
public function register(MyDataClass $data) {
public function validateUser(MyDataClass $data, $botProtection = true) {
Also, you're not even using the botProtection parameter in your validateUser method.
On the same function call:
if($instance->validateUser($data, $errors)) {
you are expecting a Boolean (true or false), but the method returns an array. If you want to use the code the way it is currently designed, you must use it like this
if(!empty($instance->validateUser($data, $errors)) {
Here, I'm not so sure it is necessary to use exception. Ain't it be easier to design your code like this?
if(!empty($instance->validateUser($data, $errors)) {
$message = 'Validation error';
}
In your validate function, is the "isEmpty" function also validating if the client provided a password?
If that's the case you could validate it like this:
if(!in_array($user['password']) or empty($user['password']))
With those corrections, your code should be functional.
Here's a sample of how I would had design your code (considering the code sample provided):
class ARegister {
public function register($data) {
$user = $data['userData']; //don't declare it here, all the user validations must be done in validateUser($data, &$errors)
$errors = array();
if($this->validateUser($data, $errors)) {
//first validation
}
}
/**
* Note: If you are not returing more than one error at the time, $errors should be a string instead of an array.
*/
public function validateUser($data, array &$errors) {
$isValid = false;
if (in_array($data['fieldId']) and in_array($data['fieldId']['password']) and in_array($data['userData'])){
if(!in_array($data['userData']['password']) or empty($data['userData']['password'])){
$errors[$data['fieldId']['password']] = Lang::get('password_required');
}
else{
$isValid = true;
}
}
else{
//an invalid data array had been provided
}
return $isValid;
}
For the next part, if the code is executed directly in the view and you are a beginner, create a procedural external controller file (all functions will be public...). If you are a professional, you MUST create a class to encapsulate the treatment.
You must not do treatment directly in the view. The view is a dumb placeholder for data presentation and collecting client's input. The sole action it must do is display the data sent by the controller and send back the client's input to the controller.
The treatment on data is the controller responsibility.
if (!empty($action) ) {
$errors =array();
switch ( $action ) {
case 'process_payment':
$instance = new ARegister();
if($instance->validateUser($data, $errors)) {
//the user is valid, do the treatment
}
else
PageManager::dispayError($errors);
}
unset($instance);
}
}
Here's an example how you can centralize your error display
/**
* Can be more complexe than that, but I'm at my father's home at four hundred kms away from Montreal right now..
*/
public static function dispayError($errors, $size = 4){
if (is_numeric($size)){
if ($size < 0){
$size = 1;
}
elseif($size > 5){
$size = 5;
}
}
else{
$size = 4;
}
if (is_scalar($errors)){
echo '<h' . $size . 'class="ERROR_MESSAGE">' . $errors . '</h' . $size . '><br>';
}
elseif (is_array($errors)){
foreach ($errors as $error){
if (is_scalar($error)){
echo '<h' . $size . 'class="ERROR_MESSAGE">' . $error . '</h' . $size . '><br>';
}
}
}
}
Of course, you can also support many kind of message:
public static function dispayError($errors, $size = 4){
self::displayMessage("ERROR_MESSAGE", $errors, $size=4);
}
private static displayMessage($class, $messages, $size=4)
Well, took me two hours to write that. I hope you have now enough material to build an efficient, reusable and, no less important, safe code design.
Good success,
Jonathan Parent-Lévesque from Montreal
You can try something like this:
class ARegister {
private $error = 0;
public function register($data) {
if (!$this->validateUser($data)){
$this->error++;
}
}
public function getErrorCount(){
return $this->error;
}
public resetErrorCount(){
$this->error = 0;
}
Or pass the error by reference:
public function register(&$error, $data) {
if (!$this->validateUser($data)){
$error++;
}
}
Personally, I would do all the validation in the same method (in the class for encapsulation), use an error message parameter (passed by reference) to return why the validation failed and use the return statement to return true or false.
class MyClass{
public function validation(&$errorMessage, $firstParameter, $secondParameter){
$success = false;
if (!$this->firstValidation($firstParameter)){
$errorMessage = "this is not working pal.";
}
elseif (!this->secondeValidation($firstParameter)){
$errorMessage = "Still not working buddy...";
}
else{
$success = true;
}
return $success;
}
private function firstValidation($firstParameter){
$success = false;
return $success;
}
private function secondeValidation($secondParameter){
$success = false;
return $success;
}
}
In your other file:
<?php
$instance = new MyClass();
$errorMessage = "";
if ($instance->validation($errorMessage, $firstParameter, $secondParameter)){
echo "Woot, it's working!!!";
}
else{
echo $errorMessage;
}
?>
Is one of these code solutions fit your needs?
Jonathan Parent-Lévesque from Montreal
I'm trying to create small functions to validate each of my form elements. But I'm having some difficulties. I'm fairly new to PHP functions in general.
Currently I'm trying to create a validation function for the 'surname' entry in a form. This is what I have:
//Call surname validation function
If (validSurname($surname) === false) {
$mistakes[] = 'Your surname is either empty or Enter only ALPHABET characters.';
}
function validSurname($surname) {
$surname = trim($surname);
if (empty($surname) || (!ctype_alpha(str_replace(' ', '', $surname)))) {
$isValid = false;
} else {
//accept surname entry and sanitize it
$surname = mysql_real_escape_string(stripslashes($surname));
}
return $isValid;
}
So currently I use something similar for validating emails, and it works correctly. But now I want to actually pass the surname through certain stages such as:
$surname = trim($surname);
For this new value to be accessed outside of the function and then entered into the database, do I need to return it at the end of the function? e.g. Something like return $surname; at the end of the function?
Put simply - how do I use this new value of $surname (rather than the initial one entered in the form) outside of the function?
You should consider what you function is trying to do. In my mind you would probably need two functions isValidSurname which would return a boolean either true or false and formatSurname would take the valid surname and return it correctly formatted.
You either have to use a global variable, or pass $surname by reference if you want to do it the way you first wrote it.
Alternatively, you could pass back the modified string, or no string at all if it failed.
$surname = validSurname($surname);
if (strlen($surname) == 0) {
$mistakes[] = 'Your surname is either empty or Enter only ALPHABET characters.';
}
function validSurname($surname) {
$surname = trim($surname);
if (empty($surname) || (!ctype_alpha(str_replace(' ', '', $surname)))) {
$surname = '';
} else {
//accept surname entry and sanitize it
$surname = mysql_real_escape_string(stripslashes($surname));
}
return $surname;
}
You should set it to global within the function. I wouldn't use the same variable name as what you're using as a parameter to your function though. If you want to mess with $surnameVar, do this:
function validSurname($surname) {
global $surnameVar;
$surnameVar = trim($surname);
if (empty($surname) || (!ctype_alpha(str_replace(' ', '', $surname)))) {
$isValid = false;
} else {
//accept surname entry and sanitize it
$surname = mysql_real_escape_string(stripslashes($surname));
}
return $isValid;
}
You be more explicit with your function names and return the new value.
For example:
function isSurnameValid($surname) {
return (empty($surname) || (!ctype_alpha(str_replace(' ', '', $surname)))) ? false : true;
}
function cleanSurname($surname) {
$surname = mysql_real_escape_string(stripslashes(trim($surname)));
return $surname;
}
This way you have clear, descriptive function names and every function has a single responsibility.
I'm having some problems when I try upload multiples files in multiples instances of my model (tabular way).
I have a model called Files and a view that generate a form for multiple instances of that model. When I save the form without "multpart/form-data", everything works, but if I put this parameter on form and submit it, the validation shows the message that "File cannot be blank."
See my controller code bellow:
public function actionRegistration() {
$company = new Company;
$contacts = $this->getModelItens('Contact', 3);
$banks = $this->getModelItens('Bank' , 2);
$files = $this->getModelItens('File' , 2);
$company->scenario = 'create';
if($_POST['Company']) {
$company->attributes = $_POST['Company'];
$valid = $company->validate();
$valid = $this->validateModels($_POST['Contact'], $contacts) && $valid;
$valid = $this->validateModels($_POST['Bank'], $banks) && $valid;
$valid = $this->validateModels($_POST['File'], $files) && $valid;
if($valid) {
if($company->save()) {
$this->saveModels($contacts, $company->id);
$this->saveModels($banks, $company->id);
$this->saveModels($files, $company->id);
$this->redirect('/obrigado');
}
}
}
$this->render('registration', array('company' => $company, 'contacts' => $contacts, 'banks' => $banks, 'files' => $files));
}
private function getModelItens($model_name, $times, $scenario = 'create') {
$models = array();
for($i = 0; $i < $times; $i++) {
$models[$i] = new $model_name;
$models[$i]->scenario = $scenario;
}
return $models;
}
private function validateModels($forms, $models) {
$valid = true;
foreach($forms as $k => $form) {
$models[$k]->attributes = $form;
$models[$k]->position = $k;
$valid = $models[$k]->validate() && $valid;
}
return $valid;
}
private function saveModels($models, $company_id) {
foreach($models as $k => $model) {
$model->company_id = $company_id;
if($model instanceOf File) {
if($model->save()) $this->upload_file($model, "file");
} else $model->save();
}
}
private function upload_file($model, $field, $k) {
$path = Yii::app()->basePath . "/../assets/files/companies/{$model->company_id}/";
$file = CUploadedFile::getInstance($model, $field);
if($file instanceof CUploadedFile) $model->$field = $file;
if($model->$field instanceof CUploadedFile) {
if(!file_exists($path)) exec("mkdir -p {$path}");
$model->$field->saveAs($path . $model->$field);
}
}
I've tried everything but I can't fix it, any suggestion?
Thanks.
I am not sure if this assignment $models[$k]->attributes = $form; will do the $_FILES as well as the $_POST for your model. Looking at this example, I think you need to do that CUploadedFile stuff before you validate() or save() (which also validates), otherwise your file field will be blank.
I think you need this order of events:
$model->$field = CUploadedFile::getInstance($model, $field); // set file field
$model->save() OR $model->validate() // THEN validate
$model->$field->saveAs($path . $model->$field); // then save the file
Instead, you have this order of events currently:
$model->save() AND $model->validate()
$model->$field = CUploadedFile::getInstance($model, $field);
$model->$field->saveAs($path . $model->$field);
I could be way off though, I'm just looking at your code and not actually testing this. Also, I have no idea why it only throws an error when you set the multpart/form-data attribute on the form; it should not work at all without that! Maybe it was skipping validation on the file fields without that? Or attributes() was assigning something from $_POST besides the file? /shrug
UPDATE:
Another thing that might be happening, is that with "tabular" input you need to reference the input field with the '$key' as well (i.e. Model[0][imageFile]). So here, where you are calling:
$this->upload_file($model, "file");`
You probably need to call something like this, so that getInstanceByName() is getting the right field name:
$this->upload_file($model, "[".$k."]file");`
I think you will still need to re-arrange your code as I mention above too.
Good luck!
More reading:
Yii forum post about this subject
Your same question in the Yii forum