I have a question about error handling in codeigniter.
My first question is who should call the show_error method? The model or the controller or the view?
I was going to put it in the model for this particular case because the model is where the error is generated, but since most of my business logic is in the controller I decided to do it there. BUt I'd like to know if there is a "correct" way to do this.
My second question is this. In the model, i have added two functions- one to return data, and the other to return an error message. My controller tests for an error condition after calling my model, and attempts to display it. But its always empty.
My model looks something like this:
public function errormessage()
{
return $this->_emess;
}
public someotherfunction()
{
if ( $switch_obj->connect() )
{
$retdata = $switch->showInterfaceAll();
$switch->disconnect();
$this->_data = $retdata;
return true;
}
else
{
print 'debug: assigning error message in model:';
$this->_emess = $switch->errormessage();
print $this->_emess;
return false;
}
}
Then in my controller, i have the following logic:
if ($this->switches_model->someotherfunction($this->uri->segment(7) ) )
{
$data['listofports'] = $this->switches_model->data;
}
else {
print '<BR>in error path<BR>';
show_error($this->switches_model->errormessage(), 123);
}
From my debug print statements in the model, i know that an error message has been set. But by the time the controller tries to display it using the show_error() method, i'm getting the following error message:
No status text available. Please check your status code number or
supply your own message text.
To prove that its not because the model has already been destroyed, I tried adding a destructor in my model and printing out a debug line...
public function __destruct()
{
print 'in the destructor';
}
The message "in error path" is printed before "in the destructor" so I'm assuming that the model is still alive and well...
Any suggestions would be appreciated.
Thanks.
UPDATE 1
I found my problem. You need to pass a legit status code, if you're going to pass one at all.
I thought you could create custom status numbers, but they must be HTTP codes. But if someone could comment on question 1 about who should call show_error() that'd be much appreciated.
Thanks.
The short answer to your first question is no, there is not a "correct" way to do it.
In your question, you said:
since most of my business logic is in the controller I decided to do it there.
This is up for debate and is probably not a debate that should be had on this site but I have found that generally, the controller is meant to be more of a dispatcher then anything else. So, your controller should be as small as possible. That being said, since the show_error() function is also deciding what view to display, I would call that a dispatching function and would put it in the controller. If you were not using that function but were using log_message() instead to store the error in a log and continue processing, then I would put that in the model because you can continue through the process after using that function. Again, this is personal choice and can be done either place but that is how I usually look at it.
Related
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.
I often get an FatalErrorException that says Call to a member function method() on null. This happens mostly when I use (in blade) long chained sentences where one among them (models) is null. For example:
$file->owners()->first()->categories()->first()->title
so when for example here categories returns null I get this exception. I have to check each method one by one. I can't check them at a time like:
!empty($file->owners()->first()->categories()->first()->title)
!is_null($file->owners()->first()->categories()->first()->title)
isset($file->owners()->first()->categories()->first()->title)
count($file->owners()->first()->categories()->first()->title)
I still get the exception by using these, because (I guess) before getting the final parameter (here 'title') the process goes trough all methods and before it can't get to the final one the exception comes. Actually in controllers this could be guidable to make all these checks but in blade this does not come so relevant to me. Besides, this is a loop. So I am looking how I could do this check at once.
Well if you get null from the first first() call you cannot continue the method chaining. You can always use try ... catch:
try {
$file->owners()->first()->categories()->first()->title
} catch (\Exception $e) {
// do on fail
}
I'd recommend using View Presenter in your project. It really helps you to keep your code clean by moving all extra logic from your views and put it in a dedicated presenter class.
Watch this laracasts video to learn more.
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.
Here is the code using CodeIgniter:
The problem I encounter:
The controller will have some functions call view, and it
separated, but it is still very close with the logic itself, if the
controller change to return in JSON or XML to display result, it seems
very trouble.
Seems many method, but each one is depends another.
I think it is difficult to track the code.
Please give some suggestions thank you.
*Please reminded that, it is only the controller class. the load view is actually prepare the data for the view, won't render the page. also the doXXX function call model is only use the model method, it won't have any SQL statement. The MVC is separated, but the controller also have the functions related to the view or model, make it quite messy.
class User extends CI_Controller
{
public function register()
{
//check is logged in or not
//if not logged in , show the register page
}
public function show_register_page()
{
//generate the UI needed data , and call the view to render, and will the user will post back a valid_register function
}
public function valid_register()
{
//do all the valid logic, if success,
//do the do_register
//if fail, valid_register_fail
}
public function valid_register_fail()
{
//check is logged in or not
//show the valid register fail page
}
public function show_valid_register_fail_page()
{
//generate the UI needed data , and call the view to render
}
public function do_register()
{
//insert data in the db, the Model will be called
//if something go wrong in db, show the error page
//if everything is success, show the register success
}
public function show_db_error_page()
{
//generate the UI needed data , and call the view to render
}
public function show_register_success()
{
//generate the UI needed data , and call the view to render
}
}
1. The controller will have some functions call view, and it
separated, but it is still very close with the logic itself, if the
controller change to return in JSON or XML to display result, it seems
very trouble.
Depends on how you organized your code and what you actually pass into the view (template). If that's well structured, you can have one view for HTML, one for XML and one for json, where-as json normally just encodes the view variable's (see json_encodeDocs).
2. Seems many method, but each one is depends another.
Well, just don't do it :) The names look like you wanted to "code that into". Keep it apart. Make those function actually actions that a user performs:
register - that action handles the registration process
Make a login controller out of it that handles anything you need:
login - the login action
lost_password - the lost password action
register - the registration action
activate - the registration activation action
Everything else does not belong in there. There is no need for an action to display some page - the controller itself can decide which view to pick.
Next to that you don't need to display database errors. CI takes care of that. Just put only in what's needed and keep things simple. That should help you to reduce the number of methods and the code therein as well.
3. I think it is difficult to track the code.
Sure. Too many functions with not really speaking names. Keep things simple. It's not easy, but give naming and reducing the overall logic some love.
I'm looking for the "best practice" way to achieve a message / notification system. I'm using an OOP-based approach for the script and would like to do something along the lines of this:
if(!$something)
$messages->add('Something doesn\'t exist!');
The add() method in the messages class looks somewhat like this:
class messages {
public function add($new) {
$messages = $THIS_IS_WHAT_IM_LOOKING_FOR; //array
$messages[] = $new;
$THIS_IS_WHAT_IM_LOOKING_FOR = $messages;
}
}
In the end, there is a method in which reads out $messages and returns every message as nicely formatted HTML.
So the questions is - what type of variable should I be using for $THIS_IS_WHAT_IM_LOOKING_FOR?
I don't want to make this use the database. Querying the db every time just for some messages that occur at runtime and disappear after 5 seconds just seems like overkill.
Using global constants for this is apparently worst practice, since constants are not meant to be variables that change over time. I don't even know if it would work.
I don't want to always pass in and return the existing $messages array through the method every time I want to add a new message.
I even tried using a session var for this, but that is obviously not suited for this purpose at all (it will always be 1 pageload too late).
Any suggestions?
Thanks!
EDIT: Added after I caused some confusion with the above...
The $messages array should be global: I need to be able to add to it through various different classes as well as at the top-level of the whole script.
The best comparison that comes to mind is to use a database to store all the messages that occur at runtime, and when it's output-time, query the database and output every message. The exception to this comparison is just that the lifetime of the $messages array is the page load (they accumulate during page load, and vanish right after).
So, for example, say I have 10 different actions running one after the other in the script. Each one of these actions make use of a different class. Each one of these classes should be able to post to $messages->add(). After all 10 actions have run, it's "output time", and the $messages array can contain up to 10 different messages which were added via all the different classes.
I hope this clarifies it a bit.
I'm not exactly clear about what you want to do, but a good way would be to simply use a private variable:
class messages {
private $messages = array();
public function add($new) {
$this->messages[] = $new;
}
public function output() {
// Whatever; e.g. a foreach loop that echoes all the messages
}
}
I think you need either a instance field.