Reputation: 7083
In codeigniter, as you know, a page of the form: /class/function/ID
, where class is the controller name, function is the method within the controller, and ID is the parameter to pass to that method.
The typical usage would be (for a book site for example) to pass the book id to the function which would then query the database for appropriate book. My problem is this: I was messing around and randomly (in the url string) typed in an ID that is not present in the database (with normal point and click browsing this would never happen) and I get database errors due to the residual queries I attempt to perform using a non-existent ID.
I have written code to check if there are any rows returned before attempting to use the ID, but if the ID is non-existent I would like the user to get a 404 error page rather than a blank page or something (since this seems like proper functionality). This would need to be a true 404 page (not simply loading a view that looks like a 404 page) so as not to screw with search engines. Okay - so my question is this: within normal program logic flow (as described above) how can I force a 404 error using codeigniter? Thanks.
Update: code igniter has a show_404('page')
function but I don't think this will generate a true HTTP 404 error...
Upvotes: 27
Views: 48091
Reputation: 845
I had the same problem with you and I found a complete solution for this in CodeIgniter 3. Here I would like to share step by step how to solve it. Of course, we need to support a custom 404 page to satisfy SEO requirement.
class ErrorController extends CI_Controller
{
public function __construct()
{
parent::__construct();
}
public function index()
{
$this->output->set_status_header('404');
return $this->load->view('errors/error_404');
}
}
<div>
<p>We are so sorry. The page you requested could not be found.</p>
</div>
$route['404_override'] = 'ErrorController';
$config['subclass_prefix'] = 'MY_';
class MY_Exceptions extends CI_Exceptions {
public function __construct() {
parent::__construct();
}
function show_404($page = '', $log_error = TRUE) {
$CI = &get_instance();
$CI->output->set_status_header('404');
$CI->load->view('errors/error_404');
echo $CI->output->get_output();
exit;
}
}
abstract class MY_Model extends CI_Model
{
protected $table = 'table_name';
public function __construct()
{
parent::__construct();
$this->load->database();
}
public function find($id)
{
$result = $this->db->get_where($this->table, ['id' => $id]);
$data = $result->row_object();
if (!$data) {
show_404();
}
return $data;
}
}
Upvotes: 4
Reputation: 81
Only follows these steps:
Step 1 Update your application/config/routes.php file
$route['404_override'] = 'error/error_404';
Step 2 Create your own controller in controllers folder ex. error.php
<?php
class Error extends CI_Controller
{
function error_404()
{
$data["heading"] = "404 Page Not Found";
$data["message"] = "The page you requested was not found ";
$this->load->view('error',$data);
}
}
?>
Step 3 Create your view in views folder ex. error.php
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252" />
<title><?php echo $heading;?></title>
</head>
<body>
<?php echo $message;?>
</body>
</html>
Upvotes: 8
Reputation: 30766
Yes show_404() WILL send out a 404 but it looks like hell. There have been a few hacks suggested here, but why hack when you can use built in features?
Upgrade to CI 2.0 and you'll be able to use the amazing:
$route['404_override'] = 'errors/error_404';
Then you can have a general errors controller without having to worry about trying to load views, libraries and helpers WY too early in the CI instance to function properly.
Upvotes: 3
Reputation: 3221
If you want a custom error page you can do the following thing.In your Libraries create a file name MY_Exceptions and extend it with CI_Exceptions.And then override the show_404() function.In this function you can now create an instance of your Controller class using &get_instance() function.And using this instance you can load your custom 404 Error page.
class MY_Exceptions extends CI_Exceptions {
public function __construct(){
parent::__construct();
}
function show_404($page = ''){ // error page logic
header("HTTP/1.1 404 Not Found");
$heading = "404 Page Not Found";
$message = "The page you requested was not found ";
$CI =& get_instance();
$CI->load->view('/*Name of you custom 404 error page.*/');
}
Upvotes: 8
Reputation:
Create controller in your application/controllers folder.
class Error extends Controller
{
function error_404()
{
$this->load->view('error');
}
}
Then in your application/library extend the Router class by creating application/libraries/MY_Router.php
class MY_Router extends CI_Router
{
private $error_controller = 'error';
private $error_method_404 = 'error_404';
function MY_Router()
{
parent::CI_Router();
}
// this is just the same method as in Router.php, with show_404() replaced by $this->error_404();
function _validate_request($segments)
{
// Does the requested controller exist in the root folder?
if(file_exists(APPPATH.'controllers/'.$segments[0].EXT))
{
return $segments;
}
// Is the controller in a sub-folder?
if(is_dir(APPPATH.'controllers/'.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);
if(count($segments) > 0)
{
// Does the requested controller exist in the sub-folder?
if(!file_exists(APPPATH.'controllers/'.$this->fetch_directory().$segments[0].EXT))
{
return $this->error_404();
}
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
// Does the default controller exist in the sub-folder?
if(!file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
{
$this->directory = '';
return array();
}
}
return $segments;
}
// Can't find the requested controller...
return $this->error_404();
}
function error_404()
{
$segments = array();
$segments[] = $this->error_controller;
$segments[] = $this->error_method_404;
return $segments;
}
function fetch_class()
{
// if method doesn't exist in class, change
// class to error and method to error_404
$this->check_method();
return $this->class;
}
function check_method()
{
$class = $this->class;
if (class_exists($class))
{
if ($class == 'doc')
{
return;
}
if (! in_array('_remap', array_map('strtolower', get_class_methods($class)))
&& ! in_array(strtolower($this->method), array_map('strtolower', get_class_methods($class))))
{
$this->class = $this->error_controller;
$this->method = $this->error_method_404;
include(APPPATH.'controllers/'.$this->fetch_directory().$this->error_controller.EXT);
}
}
}
}
If the page does not exist, it will be routed to error controller
Upvotes: 0
Reputation: 95424
show_404()
actually sends the proper headers for a search engine to register it as a 404 page (it sends 404 status).
Use a Firefox addon to check the headers received when calling show_404()
. You will see it sends the proper HTTP Status Code.
Check the default application/errors/error_404.php
. The first line is:
<?php header("HTTP/1.1 404 Not Found"); ?>
That line sets the HTTP Status as 404. It's all you need for the search engine to read your page as a 404 page.
Upvotes: 52