So I have this problem. I'm doing a server side validation and a jquery validation.
In server side validation what I do is to use codeigniter's form_validation library, more specifically:
$this->form_validation->set_rules('documentn', 'Passport number', 'required|min_length[7]|max_length[20]|is_natural|callback_checkDocAndUser');
which needs a return true or return false.
I have this user edit form, to change user data. But there are some restrictions... user when stored in database has a unique passport number. I need to be able to change this passport number if it's wrong... but passport numbers should not be repeated on the database.
This is the php function that is called from callback_checkDocAndUser :
public function checkDocAndUser(){
if ((isset($_POST['documentn'])) && (isset($_POST['id']))) {
$dn = UserManager::getInstance()->checkUserDocument($_POST['documentn'],$_POST['id']);
if ($dn) {
//passport belongs to the user
echo "true";
// return true;
}else{
//does the passport entered belong to another user?
$exists = UserManager::getInstance()->getByDocument($_POST['documentn']);
if (!$exists) {
//passport belongs to another user
echo "true";
// return true;
}else{
//passport number is free to use
echo "false";
// return false;
}
}
}
}
As you can see I put some "echo" in the functions. This is because I want to use the same function for a jQuery validation (which needs echo, doesn't work with "return").
documentn: {
required: true,
minlength: 7,
maxlength: 20,
remote: {
url: '/admin/checkDocAndUser',
type: 'POST',
data: {
id: function(){
return $('#id').val();
}
}
}
},
So how can I use the same function for both kind of validations...? is there a way to make jquery function receive a return..or codeigniter's function to receive an echo?
I do the same thing in my CodeIgniter projects and the solution is quite simple. The following answer is generically named where you only need to add your validation logic.
This answer follows the DRY principle where your validation code is not repeated, as well as CodeIgniter structure.
This MODEL does the actual validation for both CodeIgniter (server-side) validation and jQuery Validate (client-side) remote...
// file name 'models/demo_model.php'
class Demo_model extends CI_Model {
public function check_demo($params)
{
// insert your validation logic here...
// the entirety of your validation logic, check the DB, etc.
// if it passes validation
return TRUE;
// if it fails validation
return FALSE;
}
}
This CONTROLLER is only called by client-side remote for jQuery Validation...
// file name 'controllers/demo.php'
class Demo extends CI_Controller {
public function remote_demo($params = FALSE)
{
// call the Model to do the actual validation
$valid = $this->demo_model->check_demo($params);
if ($valid)
{
echo 'true'; // passes validation
}
else
{
echo 'false'; // fails validation
}
}
}
This LIBRARY is only used for server-side validation by CodeIgniter...
// file name 'libraries/MY_Form_validation.php'
class MY_Form_validation extends CI_Form_validation {
public function __construct()
{
parent::__construct();
$this->ci =& get_instance();
}
public function demo_check($params)
{
$this->ci->form_validation->set_message('demo_check', 'The %s is incorrect.');
// call the Model to do the actual validation
return $this->ci->demo_model->check_demo($params);
}
}
The way it's organized, you'll have ONE centrally located validation function residing with your CodeIgniter Models. I chose Models as the central location because the code is primarily interacting with the database.
The following uses the same Model function for both kinds of validation.
Server-side CodeIgniter validation: Calls the Model from MY_Form_validation and the Model will return TRUE or FALSE back to your other CodeIgniter Controllers as per your CodeIgniter validation rules.
Client-side jQuery Validate remote: Calls the Model from the Controller and the Model will return TRUE or FALSE back to the Controller. Then the Controller function will echo true or false based on this boolean response from the Model.
Validation functions always need to return a boolean value.
In your controller, try to retrieve the return value of the validation methods and echo "true" or "false" there.
Put an exit at the end of your function.
EDIT:
If you want to use the same set of validations for both client and server side validation, divide your call to two functions, one which handles client and the another which handles server. check the following code:
In you jquery function call url - admin/validate_form_client
function validate_form_client(){
$op = $this->checkDocAndUser($_POST['documentn'],$_POST['id']);
echo $op;
exit;
}
function validate_form_server(){
if ((isset($_POST['documentn'])) && (isset($_POST['id']))) {
return $this->checkDocAndUser($_POST['documentn'],$_POST['id']);
}
}
public function checkDocAndUser($documentn,$id){
$dn = UserManager::getInstance()->checkUserDocument($documentn,$id);
if ($dn) {
//id belongs to the user
return true;
}else{
//does the id entered belong to another user?
$exists = UserManager::getInstance()->getByDocument($documentn);
if (!$exists) {
// id number belongs to another user
return "true";
}else{
//id number is free to use
return "false";
}
}
}
}
Note: the given function names are just for example. please follow standard practice in the variable and function naming conventions.
Related
I am building a small class to handle api requests and I am running into an issue with error handling (I am also a novice at OOP so please with me) I need to restrict or throw an error with any methods in my class that require the user parameter to be set and I also need to samething if token has not been retreived and I can't seem to wrap my head around it.
This is what I have so far...
$user array is set in a config file outside of class like so (empty by default):
$user = array(
'user_email' = '',
'user_pass' = ''
);
Class for handling API (simplified for question)
class eventAPI {
private $user
private $token
public function __construct($user) {
$this->user = $user;
// if possible assign token when instantiated
$this->retrieve_token($user);
}
private function retreive_token($user) {
// Check if user parameter has been set
if($this->validate_user_parameter()) {
// use credentials to make HTTP request for token
$token = 'somerandomtoken';
// assign token property retreived value
$this->token = $token;
} else {
echo 'User parameter has not been set.' // Not real message just for testing
return FALSE;
}
}
public function show_all_events() {
// Check if token has been retreived
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
} else {
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE
}
}
// reduntant code... Can't wrap my head around another way for checking for token.
public function show_single_event() {
// Check if token has been retreived
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
} else {
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE
}
}
// This is mostly where I am confused how to solve.
private function validate_user_parameter() {
foreach($this->user as $key => $value) {
// Remove whitespace from value
$value = trim($value);
if(empty($value)) {
echo 'User credentials have not been set'; // for testing purposes
return FALSE;
}
}
}
private function validate_token_retreived() {
$result = FALSE;
// Bool value not sure if this is the best way to do this
if(isset($this->$token)) {
$result = TRUE;
}
return $result;
}
}
First issue: I need to validate the user parameter which is in an array so I can use with a private method to retrieve the token. I chose to use a foreach loop to check each value but it seems a little archaic.
Second Issue: I have a redundant check in place for each public method to check if token is valid. Is there a better way to do this with OOP? I have many methods that require the token.
In short, how can I make sure that once the class is instantiated a public method that will be used by end user will not fire if any validation fails. The user info only needs to be valid once when instantiated and then the token needs to be valid for most remaining methods.
You don't need to pass $user parameter to retreive_token function. You got it in class scope. Just use $this->user in the function to access it. Also you didn't use it in that function, so why u passed it?
You didn't send true in any function.
There's nothing wrong with for-each but you want to check array_map too. Atleast you're applying a function to every item in array. It can be usefull. ps: seems for-each still faster then array_map
Also you would want to check empty function on which cases it returns false
You can use multiple returns in a function. You dont need to set a variable to do that.
Example
private function validate_token_retreived()
{
if(isset($this->token))
return true;
return false;
}
You couldn't use else in most cases.
Example
public function show_all_events()
{
if($this->validate_token_retreived()) {
// Use token to retreive events list via HTTP request
// and return here
}
echo 'API not active. No valid token detected'; // for testing purposes
return FALSE; // do you really return here? seems you are just generating an output
}
I'm currently validating input and returning errors in a "fat controller" as follows:
class SomeController
{
public function register()
{
// validate input
$username = isset($_POST['username']) && strlen($_POST['username']) <= 20 ? $_POST['username'] : null;
// proceed if validation passed
if (isset($username)) {
$user = $this->model->build('user');
if ($user->insert($username)) {
$_SESSION['success'] = 'User created!';
} else {
$_SESSION['error'] = 'Could not register user.';
}
} else {
$_SESSION['failed']['username'] = 'Your username cannot be greater than 20 characters.';
}
// load appropriate view here
}
}
class SomeModel
{
public function insert($username)
{
// sql for insertion
// ...
return $result;
}
}
While this works and is easy enough for me to implement, I understand that this is incorrect because the validation belongs in the model, which I'm attempting to correct using a "fat model" as follows:
class SomeController
{
public function register()
{
$user = $this->model->build('user');
$user->insert($_POST['username']);
// load appropriate view here
// ...
}
}
class SomeModel
{
public function insert($username)
{
// validate input
$error = false;
$username = trim($username) != '' && strlen($username) <= 20 ? $username : null;
// proceed if validation passed
if (isset($username)) {
// sql for insertion
// ...
$_SESSION['success'] = 'User created!';
} else {
// store error in session
$error = true;
$_SESSION['error']['username'] = 'Your username cannot be greater than 20 characters ';
}
return $error ? false : true;
}
}
The problem I see here is that the model is supposed to be portable, in that it should never need to change. But if the requirement for the length of $username changes, then obviously I'll have to alter my model.
I feel like this may be a really common question but I've yet to find a straight-forward answer. Without implementing any extra "layers", "mappers" or whatever other confusing terms are out there, how could the example pseudo-code provided be modified to correctly handle this transaction? (eg, validate input, return error if validation fails)?
Without implementing any extra "layers", "mappers" or whatever
You should consider the "model" to be a application layer rather than a single class. The term "layer" could be thought of as a simple way to reference the M slice of MVC sandwich. So to accomplish the flexibility you desire you will need to create it.
A number of clear seperations can be made. I would consider having three abstractions: services, data mappers and entities.
A service would be exposed to the controller and perform the service being requested.
// some controller
function register() {
$service = $this->getUserService();
$user = $service->register($_POST['first_name'], $_POST['last_name']);
if ($user instanceof \My\Entity\User) {
// set user in view
} else {
// redirect to error
}
}
So task one complete, the controller is now dumb to whatever happens within register, all it wants to know is how to resolve the appropriate result. If there is a user object, success, otherwise false something went wrong.
The service class itself would encapsulate the services being offered:
// class UserService.php
function register($firstname, $lastname) {
// validate arguments
if ($this->isValidUsername(....
$userMapper = $this->getUserMapper();
$user = new My\Entity\User();
$user->setFirstName($firstname);
$user->setLastName($lastname);
return $userMapper->save($user);
}
return false;
}
We handle the validation of the arguments and also create the new user, passing it to the data mapper which will perform the "actual save" abstracting the database operations.
// UserMapper
function save($user) {
// save $user to db
$sql = 'INSERT INTO ....
return true;
}
I'm not sure what you would consider to be an undesirable "layer" or "mapper". This is an interesting question, and my first though was that you could just include a configuration file that defined a constant for your username length. My second though was that you could have someModel extend a class or implement an interface, wherein you values would be set as properties or constants. I suspect that you have thought of these, and are avoiding them; that this is what you mean by avoiding "layers" and "mappers" It seems that you are being guided by these principals in this code:
Avoid "magic numbers"
KISS
Composition over inheritance
skinny controller/fat model
So, are you running php5.4+ ? Maybe define a trait which could be used in this and other models that defines the username length and other changeable values in the application. Or maybe that too is to much of a "layer"?
I want to make a class in OOP PHP to validate forms. However, I've having trouble structuring this.
Initially I thought of creating individual functions for each type of validation (check length of submitted data, check whether it's a number or not, etc), then another function to check whether data passed the validation tests and pass errors into an array.
I'm getting stuck though as my code is becoming very long and difficult to manage- I'm pretty new, so how would you approach this problem?
As i was reading through your post, a question came into my mind about what you write:
Why, instead of validating a form, dont you validte your model's objects?
I mean, in an OOP way of looking things your model´s object (or domain objects) are the ones who knows what data is valid or not for each of their attributes.
Not doint that, and pushing that logic into the UI makes your design fragile, UI dependant and harder to maintain. If you add a new attribute to one of your model's object, you'll have to modify the form validator as well.
If you go with Objects Validation, the idea is that an object cannot be created in an invalid state. If you try to modify it with invalid data, an exception will be thrown.
This makes easy to work with forms. The only think you have to do is populate your objects and watch for exceptions thrown in that process.
This is only an idea to get you started and see another way of solving this problem.
Regarding your question about Forms Validation, as the other guys said, it is always better not to reinvent the wheel and go for an existing, proven, validation framework.
However, if you are curious about it, here is one of the many ways you can do it:
Let's go through the things you need: you are talking about a form that needs to be validated with one or more validation functions. Then you talk about a function that tells you whether the form passed the validation or not, and as a result you got the list of errors found during the validation phase.
As you talk about OOP, the way to go is to give each concept or idea of your problem domain (the domain of form validation) entity via a class that represents it that model the behavior they have.
So, it is natural to think about a FormValidator class with a list of ValidationRule instances, where each one colaborates in the validation process. This validation process is done by calling the validate function of the FormValidator. Also, each ValidationRule will give, as result of calling it´s own validate method an instance of the ValidationRuleResult class, that tells whether the validation was successful or not, along with an error message and additional data (if needed) about the validation. Once all the validation rules were evaluated, the validate method of the FormValidator class will return an instance of ValidationResult class, that summarizes all the validation results of the rules evaluated providing the list of errors found.
To get this down to earth, here is the sample model we're talking about:
A sample implementation
Disclaimer: please bear in mind that, as any design, it may contains flaws. The following is intended to help you to solve your problem, not to be a complete solution.
class FormValidator {
private $_validationRules;
function __construct() {
$this->_validationRules = array();
}
// Registers a new validation rule
function addRule($aValidationRule) { $this->validationRules[] = $aValidationRule; }
// Validates $aForm, evaluating each of the $_validationRules defined
function validate($aForm) {
$aValidationResult = new ValidationResult();
foreach($this->_validationRules as $aValidationRule) {
$aValidationRuleResult = $aValidationRule->validate($aForm);
$aValidationResult->addResult($aValidationRuleResult);
}
return $aValidationResult;
}
}
abstract class ValidationRule {
private $_fieldName;
// The form's field name to be validated
function __construct($aFieldName) {
$this->_fieldName = $aFieldName;
}
function fieldName() { return $this->_fieldName; }
// Returns an instance of ValidationResult describing the result of evaluating the ValidationRule in $aForm.
abstract public function validate($aForm);
}
class ValidationResult {
private $_validationRuleResults;
function __construct() {
$this->_validationRuleResults = array();
}
// Registers a validation rule result
function addResult($aValidationRuleResult) {
$this->_validationRuleResults[] = $aValidationRuleResult;
}
// Returns the list of the error messages of the validation rule results that did't passed
function errorsFound() {
$errors = array();
foreach($this->validationRuleResults as $aValidationResult) {
if ($aValidationResult->passed()) continue;
$errors[] = $aValidationResult->errorMessage();
}
return $errors;
}
// Tells whether all the validation rule results passed or not
function validationPassed() {
foreach($this->validationRuleResults as $validationResult) {
if ($validationResult->passed() == false) return false;
}
return true;
}
}
class ValidationRuleResult {
private $_passed, $_error_message;
function __construct($passed) {
$this->_passed = $passed;
$this->_error_message = '';
}
// Tells whether the form passed this validation rule or not
public function passed() { return $this->_passed; }
public function
// The error message should be empty if passed to avoid confusion
public function errorMessage { return $this->passed() ? '' : $this->_error_message; }
public function setErrorMessage($anErrorMessage) { $this->_error_message = $anErrorMessage; }
}
You can create a validation rule this way:
class NotEmptyValidationRule extends ValidationRule {
public function validate($aForm) {
$fieldName = $this->fieldName();
$fieldValue = $aForm[$fieldName];
$passed = !empty($fieldValue);
$result = new ValidationRuleResult($passed);
if (!$passed) {
$result->setErrorMessage("$fieldName cannot be empty");
}
return $result;
}
}
Some things to note:
Im assuming that $aForm is an associative array of field name / value
You can note that if a validation rule passes, the result is not used (as the ValidationResult class works only on those results that didn't pass). Remember that this is a sample only for the purpose of helping you, is not a complete solution.
Usage
$rule = new NotEmptyValidationRule('name');
$validator = new FormValidator();
$validator->addRule($rule);
$aForm = $__POST['myForm'];
$validationResult = $validator->validate($aForm);
if ($validationResult->validationPassed()) {
$errorsFound = $validationResult->errorsFound();
// do something with the $errorMessage
$errorMessage = array_join('<br/>', $errorsFound);
}
I'm having this problem about direct access to functions: for example I have this code:
controller users
function index(){
//this is my users index view, user can add,edit,delete cars
}
function details($id){
//a function where 1 car can be viewed in detail..
function add(){
//function to add car
}
Now if I go to address bar and type. localhost/myapp/users/detail it will go to the url and echo an error since $id is null. What I want is only the index is directly accessible if a user would type in the address bar. I don't want the users to go directly to myapp/users/add, etc..
CI Controller functions always must be able to handle user input (i.e. url segments), which means anyone can type in whatever they wish and make a request. You can't stop that. The best practice is to either:
Always provide default arguments
Use the URI class to get your parameters, or func_get_args()
Always validate the presence of and integrity of arguments passed to the controller, as you would with any other user input
Since it's much more common, accepted, and easier to read - just make sure to always provide defaults and validate them.
An example with your controller:
function index() {
//this is my users index view
//user can add,edit,delete cars
}
function details($id = NULL) {
if ( ! $id) {
// No ID present, maybe redirect without message
redirect('users');
}
$user = $this->user_model->get($id);
if ( ! $user) {
// ID present but no user found, redirect with error message
$this->session->set_flashdata('error_message', 'User not found');
redirect('users');
}
// We found a user, load view here etc.
}
function add() {
// Check for the presence of a $_POST value
// You could also use the Form_validation lib here
if ( ! $this->input->post('add_car')
{
$this->session->set_flashdata('error_message', 'Invalid request');
redirect('users');
}
// Try to add the car here and always redirect from here
}
The only other way is to make the method private or use CI's _underscore() naming as suggested (making it inaccessible from the url). You can still call the function in other methods if you wish, as in:
function index() {
if ($this->input->post('add_car')
{
// Call the private "_add" method
$this->_add();
}
// Load index view
}
So to make a long story short: You can't stop the requests from being made, you can only decide what to do when the request is invalid.
Add an underscore before the names of functions you want to hide:
function _details($id){
//a function where 1 car can be viewed in detail..
}
function add(){
//function to add car
}
I'm trying to create a universal header for a website built on CodeIgniter, and I'm having trouble figuring out the code that will switch the 'Login' link for the user's name (with a link to the profile page) after the user logs in.
In the controller functions, I've tried the following code:
if(!$this->session->userdata($userSessionVar))
{
$data['header_output'] = "<li><a href='" . base_url() . "index.php/main/login'>Login</a></li>";
} else
{
$data['header_output'] = $this->session->data('userFirstName');
}
(I realize this is incomplete, based on my designs, but it's just to test.) $userSessionVar holds the value "logged in" once logged in. Probably not the best way to do that. And that doesn't seem to work (and I pass the $data to the view). I've also tried making a custom function:
function check_login()
{
$CI =& get_instance();
$userSessionVar = 'logged_in';
if( ! $CI->session->userdata($userSessionVar))
{
return false;
} return true;
}
And then use the true/false return to structure the $header_output variable. None of these seem to work. I'm new to CodeIgniter and have some intermediate level of PHP/HTML/CSS, etc. I'm sure I'm missing something obvious and would appreciate any help, as well as a heads-up on how to avoid including the code in every controller function.
The variable $userSessionVar is only available within the function check_login(), so when you try to use it outside of the function, it will be blank (and therefore useless).
I recommend that you simply use $this->session->userdata('logged_in') and $CI->session->userdata('logged_in') rather than using the variable $userSessionVar to store what appears to be a constant value.
Also, you have an error in your code. You need to replace $this->session->data('userFirstName') with $this->session->userdata('userFirstName')
Here's how I typically deal with user data. First, add auth.php to the models folder:
<?php
class Auth extends Model {
private $user_data = false;
function Auth() {
parent::Model();
if ($this->input->post('action') == 'login') $this->login();
else if ($auth_id = $this->session->userdata('auth_id')) {
$user = // load user data from the database into the variable $user
if ($user) {
$this->user_data = $user;
} else $this->session->unset_userdata('auth_id');
}
}
function login() {
// process POST, check with database, and then store user_id using
// $this->session->set_userdata('auth_id', $user_id_here)
}
function me() {
return $this->user_data? (object)$this->user_data : false;
}
}
?>
Then, auto-load the model. To do this, edit config/autoload.php like so:
$autoload['model'] = array('auth');
Now your IF statement could look like this:
if ($me = $this->me()) $data['header_output'] = $me->userFirstName;
else $data['header_output'] = '<li>Login</li>';
in your model auth.php you've got the statements
class Auth extends Model
and
parent::Model();
With CodeIgniter, should these not be "CI_Model"...?