CodeIgniter Forums
DRY approach with different request type - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Using CodeIgniter (https://forum.codeigniter.com/forum-5.html)
+--- Forum: Best Practices (https://forum.codeigniter.com/forum-12.html)
+--- Thread: DRY approach with different request type (/thread-66380.html)



DRY approach with different request type - geekita - 10-17-2016

Hi! I've multiple controller classes, each containing a lot of controllers.

Class1/ControllerA
Class1/ControllerB
Class1/ControllerC
Class2/ControllerD
Class2/ControllerE
Class2/ControllerF
...

Each controller, regardless of the containing class, must execute a check before executing the rest of the function. In order to avoid adding the same code to each controller I was thinking about putting common code into an helper function called once from each class' constructor.
The check function must interrupt the execution flow, then show an error page. This behaviour can be easily obtained using redirect or show_404 functions.

PHP Code:
class Class1 {
    
    public function 
__construct() {
        
parent::__construct();
        
        
check_failed();
    }
    
}

// somewhere else...

function check_failed() {
    if (
check_condition_failed) {
        
redirect(); // or show_404
    
}


The problem is that I cannot predict if the request it's a plain http request or an ajax request! Please, read comments into following code

PHP Code:
class Class1 {
    
    public function 
__construct() {
        
parent::__construct();
        
        
check_failed();
    }
    
    public function 
index() {
        
// unfortunately index is reached if this is an ajax request
    
}
    
}

// somewhere else...

function check_failed() {
    if (
check_condition_failed) {
        if (
is_ajax_request()) {
            
// what to do at this point in order to interrupt the __construct execution?
        
}
        else {
            
redirect(); // or show_404
        
}
    }


So I'm looking for a common pattern that minimizes code duplication about routine checks and manages correctly both plain and ajax requests. Any suggestion? Thanks in advance!


RE: DRY approach with different request type - JayAdra - 10-17-2016

You're better off extending the controllers from a "base" one which has this check in it.

http://www.codeigniter.com/userguide3/general/core_classes.html?highlight=my_controller#extending-core-class

PHP Code:
class Welcome extends MY_Controller {

 
       public function __construct()
 
       {
 
               parent::__construct();
 
       }

 
       public function index()
 
       {
 
               $this->load->view('welcome_message');
 
       }


Then in MY_Controller you can put your check code in the constructor and redirect if it fails, as you said.


RE: DRY approach with different request type - geekita - 10-18-2016

Thanks but this moves the problem into superclass. The question is: How to interrupt correctly and gracefully the constructor when is reached by an ajax request?


RE: DRY approach with different request type - JayAdra - 10-18-2016

Well what behavior do you want when it fails to validate? Do you want the page which sent the ajax request to show an error message, or to redirect to another page?

Either way, you would need to return the response you want (either an error message which you can then display, or a url to redirect to, which you can do with Javascript). Return a JSON response containing the relevant info and in the callback for the ajax call, do whatever you need to do with it.

So this outputting might be something like:

PHP Code:
//Set response variables you need
$response['success'] = false;
$response['message'] = "Validation error";
$response['redirect'] = "/error";

//Output json response
$this->output
 
->set_content_type('application/json')
 ->
set_output(json_encode($response))
 ->
_display();

exit; 

For non-ajax requests, you can just use the PHP redirect function.


RE: DRY approach with different request type - dave friend - 10-18-2016

Assuming you always want to reject AJAX requests this should work.
It responds to any AJAX request with an "Internal Server Error"

PHP Code:
function check_failed()
{
 if(
is_ajax_request())
 {
 
$this->output
 
->set_status_header(500)
 ->
set_header("Content-Type: text/plain")
 ->
set_output("Internal Server Error")
 ->
_display();
 exit();
 }
 elseif(
check_condition_failed)
 {
 
redirect(); // or show_404
 
}


And you will be much more DRY by putting this in a "base" controller that will be extended by the "Class#" controllers.


RE: DRY approach with different request type - geekita - 10-18-2016

That's the trick, what's _display function? I've previously tried to use exit but it seems that it stops everything, even output of json response. So, there's no way to "interrupt" __construct execution without using undocumented features?


RE: DRY approach with different request type - dave friend - 10-18-2016

Quote:That's the trick, what's _display function?

Yup. That's it. _display documentation

CI calls this as part of the normal shutdown. But exit() interrupts that process so you have to make the call yourself before calling exit.


RE: DRY approach with different request type - geekita - 10-18-2016

Thanks a lot! I totally missed that part, it's not even an undocumented feature!