1

I want to style my app from scratch so I've created a brand new layout and assigned it as default:

class AppController extends Controller {

    public $components = array(
        'DebugKit.Toolbar',
    );

    public function beforeRender() {
        parent::beforeRender();

        $this->layout = 'app';
    }
}

However, it seems that it also affects the internal error pages generated by CakePHP that I need for development:

Actual

Is there a way to make such pages use app\View\Layouts\default.ctp instead of app.ctp?

Intended

Álvaro González
  • 142,137
  • 41
  • 261
  • 360

1 Answers1

2

You can do this by creating a custom exception renderer:-

// app/Lib/Error/AppExceptionRenderer.php
class AppExceptionRenderer extends ExceptionRenderer {

    public function render() {
        $this->controller->layout = 'default';
        parent::render();
    }

}

Then in app/Config/core.php make sure you tell Cake to use your AppExceptionRenderer class for handling exceptions:-

Configure::write('Exception', array(
    'handler' => 'ErrorHandler::handleException',
    'renderer' => 'AppExceptionRenderer',
    'log' => true
));

Using a custom exception renderer gives you greater flexibility over how you handle exceptions in your app.

Finally, move the custom layout definition from \AppController::beforeRender to \AppController::beforeFilter so it runs earlier and can be overridden in case of error:

class AppController extends Controller {
    public function beforeFilter() {
        parent::beforeFilter();
        $this->layout = 'app';
    }
}
Álvaro González
  • 142,137
  • 41
  • 261
  • 360
drmonkeyninja
  • 8,490
  • 4
  • 31
  • 59
  • There's no `\ExceptionRenderer::beforeFilter` callback. There's `\ExceptionRenderer::render` but it doesn't have any `layout` property. Furthermore, `\AppController::beforeRender` gets called *later*... :-? – Álvaro González May 10 '17 at 08:40
  • Sorry @ÁlvaroGonzález my mistake, you can access the controller from the `render` method and set the layout that way. I've updated my answer to show this. – drmonkeyninja May 10 '17 at 09:28
  • Thank you. That changes the layout back to `default` successfully, but afterwards `\AppController::beforeRender` sets it again to `app` (remember I need to set `default` back because I'm modifying it). Not sure if I'm missing something. – Álvaro González May 10 '17 at 10:01
  • I mean, I could possibly add a `$do_not_modify_layout` flag but I'm trying to avoid ugly hacks when builtin solutions exist. – Álvaro González May 10 '17 at 10:28
  • Ah, I missed that bit. Is there a reason why you are forcing all views to use a different layout in `beforeRender`? Would this not be better done in the `beforeFilter`, that way you can easily override it later in specific actions. Using a hack like `$do_not_modify_layout` shouldn't be necessary. – drmonkeyninja May 10 '17 at 11:04
  • 1
    That was the last bit, thank you very much! I've incorporated it into the answer. – Álvaro González May 10 '17 at 16:14