2

I have been reading the following question here: CakePHP 2.0 - How to make custom error pages?

About creating custom views for exception handling in CakePHP 2.0+ and have been using it as a base to start doing the same in my own application hence starting my own question.

However I'm not following the logic. For example how does the Throw NotFoundException know to call the notFound method in the Errors Controller as I don't see any direct relationship in terms of the naming... Unless I'm missing the point?

In any case I'm looking to add 404, 403, and 401 errors and then be able to create custom views and call them using the exception handler throughout my app.

Can anyone shed more light on this? I'm using the latest version of Cake 2.1

So I have the following code:

    App::uses('ExceptionRenderer', 'Error');

    class AppExceptionRenderer extends ExceptionRenderer {

        public function notFound($error) {
            $this->controller->redirect(array('controller' => 'errors', 'action' => 'error404'));
        }
}

And I want to replace that redirect with rendering a custom error view:

I've tried:

$this->controller->layout = null;
$this->controller->render('/Errors/error404');

But that just shows a blank page... Can anyone help me out as I don't want to do the redirect and would much rather follow conventions and render actual views with the same url when getting errors.

Update: Also noticed that the custom views ONLY get called when exceptions are manually called in the controller, and not for actual errors such as domain.com/garbageurl or something else... So it doesn't seem to be doing what I thought!

Community
  • 1
  • 1
Cameron
  • 27,963
  • 100
  • 281
  • 483

2 Answers2

1

Have a look at these files from core Cake:

Here's what's happening:

  • ErrorHandler::handleException() is your exception handler. It gets called when an exception is thrown.
  • ErrorHandler::handleException() calls ExceptionRenderer::__construct() (your custom exception renderer must extend ExceptionRenderer) which parses the name of the Exception that was thrown, and from that, sets $this->method.
  • ErrorHandler::handleException() then calls ExceptionRenderer::render() which uses call_user_func_array() to call the method whose name is $this->method.
jnrbsn
  • 2,498
  • 1
  • 18
  • 25
  • That just confused me even more :( so in simplest terms how does the NotFoundException know to call notFound method as neither one calls each other by name... – Cameron May 31 '12 at 21:52
  • Also I'm not sure redirecting the user to an error page is correct shouldn't it be rendering the error page on the actual location instead? – Cameron May 31 '12 at 21:53
  • It knows which method to use by `$method = $template = Inflector::variable(str_replace('Exception', '', get_class($exception)));` in `ExceptionRenderer`: https://github.com/cakephp/cakephp/blob/master/lib/Cake/Error/ExceptionRenderer.php#L97 Its based on the name of the Exception class thrown. – tigrang May 31 '12 at 21:55
  • @Cameron `ErrorHandler::handleException()` is your [exception handler](http://php.net/manual/en/function.set-exception-handler.php). It gets called when an exception is thrown. I updated my answer to say that. – jnrbsn May 31 '12 at 21:56
  • It's not "redirecting". It's just using a custom controller action and view based on what your exception renderer does. The URL will not change. – jnrbsn May 31 '12 at 21:58
  • That's rather odd as if I use redirect anywhere else it will actually redirect so for it NOT to redirect here isn't that breaking conventions? Although I've noticed it says controller->redirect is that different to just $this->redirect? – Cameron May 31 '12 at 22:00
  • I thought you said you did NOT want it to redirect? You can make it redirect if that's what you want. Just do that in your `notFound()` method or whatever. – jnrbsn May 31 '12 at 22:03
  • In the link I post it does a redirect in the AppExceptionRenderer – Cameron May 31 '12 at 22:04
  • You can do it that way if you want, but if you want to do what's conventional, you should look at `error404()`, `error500()`, and `pdoError()` in `ExceptionRenderer`. If you do a redirect, you'll just have to write another controller (`ErrorsController` in your example). – jnrbsn May 31 '12 at 22:11
  • I have the code in the example I posted working fine but I don't want it to do the redirect just render the custom view so anything based on the back of that is cool. – Cameron May 31 '12 at 22:25
  • I tried something like: `$this->controller->layout = null; $this->controller->render('/Errors/error404');` in place of the redirect but it just returned an empty page so I'm doing something wrong... – Cameron May 31 '12 at 22:51
0

I was just looking for the same thing and could not find a neat way to do this using AppExceptionRenderer. It just won't allow you to have separate error403 and error404 template files.

So I just did this in my /app/View/Errors/error400.ctp file instead...

<? if ($error instanceof ForbiddenException) { ?>

    <h4>Whoops! The page you attempted to access 
        requires permissions that you don't have.</h4>  

<? } else { ?>

    <h4>Whoops! We couldn't find the page you were looking for, sorry!</h4> 

<? } ?>
Simon East
  • 55,742
  • 17
  • 139
  • 133