I have a table with a unique key (date + userid) in my web application database. When I try to insert record with existing date and userid I receive the following error:
dupicate key in table
I turned on the database debugger in application config, because I needed to use MySQL error numbers and messages. Now I need to process this error. I can use hard coded constants in the controller, but I think it's not good idea. I need database error handling in many places, and I dont like CodeIgniter's error handling. What is a best practice concerning the processing of database errors.
We use in our project constructions like these:
$this->db->_error_number();
$this->db->_error_message();
but this undocumented functions and in next releases this could change.
Of course you can simply use standard php for mysql error handle function:
mysql_errno()
mysql_error()
internally CI use these functions.
As for me the best practice is use in custom Model's base class (BaseModel)
$this->db->_error_number();
and determine error, next throw exception with information error message taken from
$this->db->_error_message();
All your Model derived from BaseModel, and call method to check last request for db error and your Model must handle exception, and process it (may be additionally log), of course you can implement check result as result value and avoid of throwing exception.
I have a different suggestion for this i will recommend this
$this->form_validation->set_rules('email', 'Email', 'required|max_length[32]|valid_email|callback_email_available');
while submitting the form you need to define rules. always use callbacks to interact with database
Controller callback method
public function email_available($str)
{
// You can access $_POST variable
$this->load->model('mymodel');
$result = $this->mymodel->emailAvailability($_POST['email']);
if ($result)
{
$this->form_validation->set_message('email_available', 'The %s already exists');
return FALSE;
}else{
return TRUE;
}
}
And model method
public function emailAvailability($email)
{
$this->db->where('email',$email);
$query = $this->db->get('tablename');
return $query->row();
}
this way you will always avoid database errors on the front and can get user see things in a better way. you dont have to handle db errors because form validation is handling everything for you.
Related
I have Php Login system using MVC structure. For database data validation I create LoginModel. I need to print failure error to view like: User Not Exist Now Which way is right and better:
1- Add error data validation in Login Model and get in Controller and Print to View Like This:
class LoginModel extends \App\Core\Model
{
public function login($user_name, $user_password, $set_remember_me_cookie = null)
{
$returnError = array();
// checks if user exists, if login is not blocked (due to failed logins) and if password fits the hash
$result = $this->validateAndGetUser($user_name, $user_password);
// check if that user exists.
if (!$result) {
$returnError['isMessage'] = false;
$returnError['name'] = "User Not Found";
}
return $returnError;
}
private function validateAndGetUser($user_name, $user_password){
//Check User Data Validation
}
}
2- Add Only True Or False in LoginModel and Get in Controller And Set Error Name and Print to View Like This:
class LoginModel extends \App\Core\Model
{
public function login($user_name, $user_password, $set_remember_me_cookie = null)
{
// checks if user exists, if login is not blocked (due to failed logins) and if password fits the hash
$result = $this->validateAndGetUser($user_name, $user_password);
// check if that user exists.
if (!$result) {
return false;
}
return true;
}
private function validateAndGetUser($user_name, $user_password){
//Check User Data Validation
}
}
In action my really question is: Can I add error message in Model and Get in Controller?! Which way is right and true?
There are many ways to do this.
For me the best way is the first way you have suggested, but better declare bug reports in a single file eg errors.php and make the model return you array with 2 keys, the first key is always TRUE or FALSE and then if your model returns the first key FALSE reads the error number from the second key.
Then, in the controller you can replace the error number with the corresponding key in the array you declared in errors.php
If the first key is TRUE, then the second key will be your user information.
Suggestion:
Split the login functionality into two (main) steps:
Check if the posted user already exists. If not, throw an exception.
Match the posted password against the stored one. If they don't match, throw an exception. I suggest the use of password_hash for storing passwords, and of password_verify for matching a password with the stored one.
Then - based on your current architecture, in your controller, use a try-catch block to catch the exception thrown by the login steps and proceed as you wish to display the exception message to the user.
Just as a note: In case of an ajax request, you would send a specific response header (with status code 500, for example) or a custom response header (with status code 420, for example) back to the client, e.g. browser, in order to activate the error function of the ajax request.
Notes:
The domain model is a layer. And it must have no knowledge about the outside world.
A controller should only (!) update the domain model (through services).
A view should be a class - not a template file - responsible with fetching data from the model layer (through services), preparing/formatting it for presentation, and passing it to a response object, in order for this to be returned and printed.
The controller and the view (mostly 1:1 relation) should be created separately. The controller should not have any knowledge about the view. This creation step would take place in the front-controller class or file.
As for error reporting, I would recommend to read this.
Background:
Inside large controllers I will have a number of different functions being called, to create a user, to update their order, to schedule in an event. Each of these operations are handled by a function in the model layer... here is an example of one of those functions:
$user_id = 1;
$data = array('name' => 'Billy');
if (updateUser($user_id, $data) === false) {
// handle error?
}
// continue with rest of controller
Problem:
I finally took a reality check today and realised that I have no good reason for coding like this...
If updateUser() returns false then something has seriously gone wrong with my Database Abstraction Layer that has prevented me from updating data in my database. This should never happen and therefore there are no practical errors to show my users anyway (that would allow them to take appropriate actions).
Basically my app is fundamentally broken at that point.
Question:
Should I bother to check functions that should never return false? If so how? Or should I just call them like this without any checks?
updateUser($foo)
createBooking($bar)
scheduleEvent($qux)
When something happens inside a function that should never happen, throw an exception.
And then you can handle (catch) all exceptions where you want to do that. For example by showing a friendly message to the user and logging all details for yourself so that you know what went wrong and where.
Then you can get rid of the if statements and only use these when there are valid / normal options.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I am developing a new application using object oriented approach with some REST involved, I am not using any frameworks.
The question I have is where is the best place to validate a user’s input in a setter like below:
public function setSalary($salary)
{
if (Validator::money($salary))
$this->salary = $salary;
else
return 'Error that is an invalid number';
}
Or in the controller?
public function updateSalary()
{
$errors = array();
if (Validator::money($_POST['salary']))
$salary = $_POST['salary'];
else
$errors ['salary'] = 'Error that is an invalid number';
if(count($errors))
return $errors;
$employee = new Employee($_POST['e_Id']);
$employee->setSalary($salary);
$employee->save();
}
If I was to put in the setter how should my controller look, and return validation errors?
I have seen most people do validation in the controller, however I think should be the models responsibility for validation as it going to be using the data, and we can reuse that model without repeating ourselves.
However there can be times when validation rules may need to be different in some special cases like different validation for a different view or different validation for a supper admin.
Which one would you say is in accordance with best practices?
First of all, since you seem to aspire to implement MVC-like structure, lets start by some general mistakes, that are not related to validation directly.
Only part of your code, containing PHP superglobals, should be the bootstrap stage. Having superglobals sprinkled all over your code makes it really hard to test. And your code also becomes tightly couple to your HTML, via the <input> names.
Even if your for or if statement contains a single line, you should always use curly brackets. Well, in general your code should follow the PSR-1 and PSR-2 guidelines.
Controllers should not have any logic, or be dealing with saving of data. Read this post, maybe it clears some things up.
Ok .. now back to the original subject.
In general there are two schools of thought:
You do the validation in the domain entity
Your domain entity (in your case Employee) contains all the business roles, that pertain to it. And it can use those rules to assess, if it is in a valid state.
The code would go something like this:
$employee = new Entity\Employee;
$employee->setID($id);
$employee->setSalary($money);
if ($employee->isValid()) {
$mapper = new Mapper\Employee($dbConn);
$mapper->store($emplyee);
}
You never create invalid domain entity
This approach comes from DDD, where you domain entity is created by some other class and it can only be changes from one valid state to another valid state. Essentially, if you want to explore this approach, you will have to read this book (probably several times).
Also, there is one other validation form, which is note covered by the previous two: data integrity checks. This is type of validation, that is actually done my RDBMS. For example, the UNIQUE constraints.
When you encounter ans integrity violation, it usually would throw an exception, that you handle in service layer.
Validation must be called every time you write data to the database. So in this case from the controller. The actual validation happens in the model. The model is the object, that knows which rules it's fields obey and it can check whether the data is valid or not. Also, the model is the border between the rest of the world and the database. So, I would do something like this:
public function updateSalary()
{
$employee = new Employee($_POST['e_Id']);
$employee->setSalary($_POST['salary']));
if ($employee->validate()) {
$employee->save();
} else {
return $employee->getErrors();
}
}
Why I offer you this way:
because you keep the validation at one place. Later, if you want to validate another field, you will call the validate() method again. You won't write another validation for each field or class;
You can create a base class and put the validate() method there - all clients will call the validate() method, and wouldn't care about the specifics of the fields. The validate method will care only of what to validate - which fields and what the rules are. This information will be set in the specific (child) classes, like the Employee class.
If you want to validate only one field (like in your case), in the validate() method you can make a simple check of which fields are changed and do validation only of these fields.
Depends of you, if the validation rules are "global", in other words if they are the same every time you update that DB table/Object propriety, place them in the Model, otherwise validate user input in the Controller if in different situations you need different validation rules for the same Entity.
Firstly, I am not a geek below is just what I think.
It should be done in controller, just because right now you are only validating number, which just simple check and I think you just have to apply regex for this.
What actually I understand is that, model is where you keep your business logic, but if your field value is all together wrong than you will never process business logic and you don't want your model to come in play.
I would suggest to apply validation in the Model where possible. It has the advantage that the Model can be tested directly in a more complete way, and that the Model is guaranteed to only persist valid data.
Of course, the Controller needs to handle validation, and might be the first layer that calls on validation when it concerns complex validation on distributed items. But in the example you give there is no such complexity.
Note that anyway some validation will even be performed by the database engine (such as NOT NULL and primary key requirements).
I would also suggest to use exceptions in the Model, as this guarantees the interruption of the running function, and lets you process all (validation) errors in a similar way within the Controller. I would advise to configure your database access layer to also trigger exceptions. In case of PDO you would do that as follows:
$dbh = new PDO($dsn, $user, $password);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
In the Model you would throw an exception when validation fails:
public function setSalary($salary) {
if (!Validator::money($salary)) {
throw new Exception('Invalid value provided as salary.');
}
$this->salary = $salary;
}
In the Controller you would catch errors and log them: as you did in $errors, but I would keep them in the Model as well for later access by the View. This illustrates how the Model detects the validation error, but the Controller deals with it.
I would also suggest to not create an Employee instance directly, but to let the Model do that for you:
public function updateSalary($emp_id, $salary) {
try {
// Note that any of the following statements could trigger exceptions:
$employee = $this->$model->getEmployee($emp_id);
$employee->setSalary($salary);
$employee->save();
} catch(Exception $e) {
$this->$model->logError('salary', $e->getMessage());
}
}
Call the latter function with the posted arguments, as this gives a better indication what the method is using as input. The top-level PHP code would look like this:
$model = new Model();
$controller = new Controller($model);
$view = new View($controller, $model);
$controller->updateSalary($_POST['e_Id'], $_POST['salary']);
echo $view->output();
The View would access to the logged errors to report them to the client.
I realise that the debate as to where to detect validation errors, where handle them, when to trigger exceptions (and when not), ... etc, will never end. But this works for me.
i am starting to put validation/sanitization in my codeigniter models, and before diving in too deep I am looking for some suggestions on best practices. the form validation library for controllers is great, but obviously i don't want to rely on the controllers to send me good data.
currently I return bool values, TRUE (or data) on success, FALSE on failure, which makes it really hard to pass error messages back to the caller. I would like to get away from the FALSE on failure.
while definitely not an expert, i have started reading quite a bit on Exceptions and have come across them quite a bit with external libraries, and they seem like a good candidate for this. my question is, is this appropriate use of exceptions? are model errors exceptional errors?
a possible example:
<?php
class person_model extends CI_Model{
public function getPersonById($personId){
//check for int
if(!is_int($personId) OR $personId < 0){
throw new Exception('Invalid person ID');
}
//setup query
$this->db->select('*')
->where('personId', $personId);
//run query
$result = $this->db->get('person');
//failed to get
if(!$result){
throw new Exception('DB query failed');
//should i also return false?
return FALSE;
}
//got info
else{
return $result;
}
}
}
?>
thanks for the help!
EDIT:
I have to say I am quite surprised by the responses suggesting that data validation should only be done in the controller. Models are the last barrier to your data storage. The model is the data and the rules applying to that data, your application logic. Data validation seems like application logic to me. Also you may have many controllers accessing the same model method. Do you want to rely on both controllers implementing the same validation? That seems silly to me.
Also, not all data is coming from user input, some of it could be hardcoded into the script by your programmer writing the controller. What if they pass a string when your model is expecting an integer? Or pass an incorrectly formatted date? shouldn't the model say something about that.
I'm open to a discussion, but I feel data validation DEFINITELY belongs in the model. (in addition to the controller, and even the view (html5/javascript for convenience))
the easiest way i've found to deal with this is to always check for the negative condition first with an if check. this also makes it easy to check multiple steps.
always return something back from your model methods whenever possible. Like even in this example - we need to validate the form - i would use codeigniters form validation to validate that its an integer etc. if it passes validation then we need the $personId for the database search. So instead of just returning true/false from the validation - if validation passes then return the $personId :
function getperson() {
// Validate the form
// AND if its valid, return the validated $personId
// Note the separate private method if the validation failed
if ( ! $personId = $this->person->_validateGetPersonForm() ) {
$this->error_msg = 'Error in validating the form. Please use a number.';
$this->_showGetPersonFailed() ; }
elseif ( ! $person = $this->person->getBy($personId) ) {
$this->error_msg = 'There is no person in our records with the number:'. $personId;
$this->_showGetPersonFailed() ; }
else { $this->_showResultsFor($person) ; }
}
$this->error_msg is automatically available for any of your views and because we have broken out validation and the database search - the error message is easily appropriate for the exact condition. if the search failed then there is a private method _showGetPersonFailed() to show the form again. the last if / else is usually success - there is a separate private method to handle showing the results with the appropriate views.
also suggest not using the word "model" in the file name like "person_model". it just clutters up the overall naming and forces you to type the word model over and over again :-) think of it this way: if you are calling something from a controller and getting back results - its almost always going to be a model. and all your model files will always be in a folder named models.
Go to:
application/config/database.php
and search for db_debug to be TRUE. For example:
...
$db['default']['dbprefix'] = '';
$db['default']['pconnect'] = TRUE;
$db['default']['db_debug'] = TRUE; //<-- Have this to true
$db['default']['cache_on'] = FALSE;
$db['default']['cachedir'] = '';
...
In my models, I never throw exceptions or errors. I always return false on a query that returns null. If there is an SQL error in your code you will be notified by codeigniter automatically. I would stay away from throwing exceptions if your query returns no results.
You can then handle the query if it returns false with your controller.
EDIT: Also you should check the data being queried into the database in the controller, and not the model. In my opinion the model should be used strictly for querying data, not for error/data checking, you can do this before hand in the controller when it is submitted.
An example:
Model
function search_replies($term){
$this->db->like('ticket_id', $term);
$this->db->or_like('reply_from', $term);
$this->db->or_like('reply_from_name', $term);
$this->db->or_like('reply_content', $term);
$query = $this->db->get($this->table_ticket_replies);
// Returns the result if the number of rows is greater than 0, returns false otherwise
if ($query->num_rows() > 0) return $query->result();
return false;
}
Controller
function example_controller(){
if($this->search_model->search_replies('Test')){
$data['results'] = $this->search_model->search_replies('Test');
}
$this->load->view('search_results', $data);
}
View
<?php
if(isset($results)){
echo 'Retrieved Results';
foreach($results as $result){
}
} else{
?>
<h2>No results for search term!</h2>
<?php
}
?>
You have probably missed out this part of the form validation page in the user guide:
Showing Errors Individually
Usage:
echo form_error('field_name');
Just put in whatever field name you are using in place of "field_name", for example "username" or "email" etc.
Since today I have started validating form data in my Model layer instead of in my Controllers. I am going to shorten the code snippets as much as possible.
This is a method from my User domain object (setLastName() method is basically the same)
public function setFirstName($firstName) {
if(!$firstName) throw new \InvalidArgumentException('Some message');
if( strlen($firstName) < 2 || strlen($firstName) > 20 ) throw new \LengthException('Some message');
if(preg_match('/[^a-zA-Z\'.-\s]/', $firstName)) throw new FormatException('Some message');
$this->firstName = $firstName;
}
In my Controller I have something like this
$userService = $this->serviceFactory->build('User');
try {
$userService->register('John', 'M');
}
catch(\InvalidArgumentException $ex) {
}
catch(\LengthException $ex) {
}
catch(etc etc)
In my UserService method register() I have something like
$user->setFirstName($firstName);
$user->setLastName($lastName);
When running the setFirstName() method it will successfully set the supplied first name. The setLastName() method will throw a LengthException as it is too short.
That is what I want but when that comes back to the service layer and then to the controller and I catch it I know that a LengthException was thrown but I can't give the user a proper message like "The supplied last name was too short" because I do not know for which field the exception was thrown, just the type of exception.
How do I get around this? Thanks.
View instances should be requesting information from the model layer. Controllers is not responsible for passing the information.
This would also mean that you obsessive-compulsive use of exceptions, that cause your abstraction layers to leak, would be completely pointless. "Error" is just a state of model layer. It is an expected situation, not an exception.
Controllers in MVC are responsible for changing the state of model layer and (quite rarely) the state of current view instance. They should not be receiving any feedback from services.
Instead of returning some message, why not return a useful error message such as "supplied First name is too short". This can then be returned to the user.
Alternatively you can see that when extending exceptions you can specify additional information such as numeric codes - you could of course use this.
Or of course you can create a subclass of Exception for difference circumstances, but you could end up with hundreds of Exception subclasses which would of course be messy.
I have the same question. I think that most of the people who say that all the validation should be done in the model never developed a full PHP MVC application themselves and only know books and theory. Never a piece of code is seen about that topic.
Anyway I have thought of a possible solution. What do you think about the code below:
// Controller
$user = User::make(
$_POST['lastname'], $_POST['firstname'],
$_POST['gender'], [...]
);
if(is_array($user)) {
// store the errors in a view variable and forward, or store in session and redirect
$_SESSION['errors'] = $user;
$this->_redirect('add');
exit;
}
// Model
public static make($lastname, $firstname, $gender, [...]) {
$errors = array();
if(/* test firstname */) $errors[] = 'model_error_firstname';
if(/* test lastname */) $errors[] = 'model_error_lastname';
if(!empty($errors)) return $errors;
return new User($lastname, $firstname, $gender, [...]);
}
The model would have a static function that will return either an array with errors in case something went wrong, or a new model object if validation was ok.
In your controller you test whether an array was returned or not.
Maybe I would put the constructor of the User as private, because if you build a user with the constructor directly, you would skip all the validations. But this does not mean that it becomes a Singleton.
Maybe I would also sanitize the form fields and make them safe before passing them to the model.
The keys like model_error_xyz would be found in a translation file with the appropriate text.
Update:
Actually I think that you could just throw a custom exception from the constructor and that contains an array of messages. But the reason that I didn't propose that, is that it leads to half-constructed objects, at least in Java for example, but hey, PHP is not Java...
You would also have to validate each setter function :( it seems tedious to do validation in the model instance.
Any thoughts are welcome.