ZF2 - How to change the error/404 response page? Not just template but to set a new ViewModel
Asked Answered
G

7

6

By default the page is set like this in the Application module.config array:

'template_map' => array(
    'error/404' => __DIR__ . '/../view/error/404.phtml'

I want to change the page. I want new ViewModel for it full of variables. It means that simply to change the template is not enough:

'error/404' => __DIR__ . '/../view/error/my_new_404_template.phtml'

But I can't understand how to make it. I can't see the way the request comes to the 'error/404'.

  • How to create new ViewModel for it?

  • How to attach variables to it?

  • How the route comes to 'error/404' to catch it to change?

For example, I have such a method for 'error/404' page:

public function pageNotFoundAction() {

    $view = new ViewModel();

    $view->setTemplate('error/404');     // set my template

    $sm = $this->getServiceLocator()->get('SessionManager');
    $cont = new Container('SomeNamespace', $sm);

    $view->var1 = $cont->offsetGet('someValue1');   // the "error/404" template
    $view->var2 = $cont->offsetGet('someValue2');   //       is full of variables
    $view->var3 = $cont->offsetGet('someValue3');
    $view->var4 = "One more view variable";

    // And now I return it somewhere and want it to be called
    //    in case of "the page is not found"
    return $view;
}

How to make such a change? I can't get the system they've created to deal with things like 'error/404'. Please help.

UPD 1

Even more complicated task. How to have several 'error/404' pages? Would like to ask guys who created the framework whether they know that 'not_found_template' cannot have an array of 'error/404' pages. How it can be? If I set this option like this:

'template_map' => array(
    'error/404' => __DIR__ . '/../view/error/404.phtml',
    'my-page/one_more_error404' => __DIR__ . '/../view/my-page/my-page/one_more_error404.phtml',
    'other_page/second_404' => __DIR__ . '/../view/other-page/one_more_error404.phtml',

'not_found_template' => array(
    'error/404',
    'my-page/one_more_error404',
    'other-page/second_404',
);

it throws an error. 'not_found_template' force you to have only one 'error/404' template?

Gilley answered 10/12, 2012 at 19:31 Comment(4)
It maybe worth taking a look in 'Zend\Mvc\View\RouteNotFoundStrategy' for clues. In our application this strategy was being triggered by setting the 404 status on our response.Shillyshally
@DrBeza's comment, I think that path is Zend\Mvc\View\Http\RouteNotFoundStrategy for anyone else that is looking.Drynurse
@cr125rider Thank you for adding the correct location (I was using an older beta version of ZF2 at the time).Shillyshally
@Shillyshally No problem. Those versions changed things around a lot and there is a lot of confusing data around on SO and the rest of the internet about what version of ZF2 the examples are for. Mine was for ZF2.1!Drynurse
O
5

There is another way. To catch an EVENT_DISPATCH_ERROR and totally rebuild viewModel. Cavern is that layout – is a root viewModel, and content appended into layout by default is another viewModel (child). These points are not such clear described in official docs.

Here is how it can look like in your Module.php:

public function onBootstrap(MvcEvent $event)
{
    $app = $event->getParam( 'application' );
    $eventManager = $app->getEventManager();


    /** attach Front layout for 404 errors */
    $eventManager->attach( MvcEvent::EVENT_DISPATCH_ERROR, function( MvcEvent $event ){

        /** here you can retrieve anything from your serviceManager */
        $serviceManager = $event->getApplication()->getServiceManager();
        $someVar = $serviceManager->get( 'Some\Factory' )->getSomeValue();

        /** here you redefine layout used to publish an error */
        $layout = $serviceManager->get( 'viewManager' )->getViewModel();
        $layout->setTemplate( 'layout/start' );

        /** here you redefine template used to the error exactly and pass custom variable into ViewModel */
        $viewModel = $event->getViewModel();
        $viewModel->setVariables( array( 'someVar' => $someVar ) )
                  ->setTemplate( 'error/404' );
    });
}
Olwen answered 6/11, 2013 at 13:41 Comment(2)
This code didn't work for me at first, the reason was in line: $viewModel = $event->getResult(); I changed it to: $viewModel = $event->getViewModel(); and now it works like a charmDisputatious
@Yaronius probably that could happen if there is no template map defined for for error displaying in module.config.php by key view_manager[ 'not_found_template' ]. I believe in this case no viewModel would be created and you will get NULL on $event->getResult() call. Anyway your suggestion is more universal, so I fixed my reply. Thanks!Olwen
T
4

I used this to manage 404 error (I moved my website from spip to ZF2 based cms) :

In module onBootstrap function :

$eventManager->getSharedManager()->attach('*', MvcEvent::EVENT_DISPATCH_ERROR, array($this, 'onDispatchError'), -100);

Then

public function onDispatchError(MvcEvent $event)
{
    $response = $event->getResponse();
    if ($response->getStatusCode() == 404) {
        $url = $event->getRouter()->assemble(array(), array('name' => 'index'));
        $requestUri = $event->getRequest()->getRequestUri();
        $response->getHeaders()->addHeaderLine('Location', "$url?url=$requestUri");
        $response->setStatusCode(200);
        $response->sendHeaders();
        $event->stopPropagation(true);
    } elseif($response->getStatusCode() == 500){
        //DO SOMETHING else?
        return;
     }
}

In this code we never return a 404 error, we just call the route (in my example index) with the requested url as param

I hope that help you.

Tillo answered 15/8, 2013 at 11:21 Comment(2)
Well I was looking for the answer for far to long. You sir got a upvote from meHeadstand
$event->stopPropagation(true); doesn't stop the application for me :( This is the right answer, but i can't stop the application unless i use exit or die which is not right wayDynamotor
T
2

I am not sure I follow what you are trying to achieve, can you give a clear example of what you are trying to do?

If you are simply trying to add variables to the view model passed you could even do this in your controller, check out AbstractActionController

/**
 * Action called if matched action does not exist
 *
 * @return array
 */
public function notFoundAction()
{
    $response   = $this->response;
    $event      = $this->getEvent();
    $routeMatch = $event->getRouteMatch();
    $routeMatch->setParam('action', 'not-found');

    if ($response instanceof HttpResponse) {
        return $this->createHttpNotFoundModel($response);
    }
    return $this->createConsoleNotFoundModel($response);
}

/**
 * Create an HTTP view model representing a "not found" page
 *
 * @param  HttpResponse $response
 * @return ViewModel
 */
protected function createHttpNotFoundModel(HttpResponse $response)
{
    $response->setStatusCode(404);

    // Add in extra stuff from your ServiceLocator here...

    // $viewModel->setTemplate(..); 

    return new ViewModel(array(
        'content' => 'Page not found',
    ));
}
Troll answered 16/1, 2013 at 15:18 Comment(0)
D
2

The First thing is to create the views :

modules/Yourapplication/view/yourapplication/error/index.phtml

modules/Yourapplication/view/yourapplication/error/404.phtml

The second thing is to register the views in the module config:

In module.config.php of your application

'view_manager' => array(
        //[...]
        'not_found_template'       => 'error/404',
        'exception_template'       => 'error/index',
        'template_map' => array(
        //[...]
        'error/404'   => __DIR__ . '/../view/yourapplication/error/404.phtml',
        'error/index' => __DIR__ . '/../view/yourapplication/error/index.phtml',
        ),
   // [...]
    ),
Dapple answered 9/7, 2013 at 12:33 Comment(0)
L
2

in module.php you can also change template and layout.

function onBootstrap(EventInterface $e) {
  $app = $e->getApplication();
  $evt = $app->getEventManager();
  $evt->attach(MvcEvent::EVENT_DISPATCH_ERROR, array($this,'onDispatchError'), 100);    
}

function onDispatchError(MvcEvent $e) {
  $vm = $e->getViewModel();
  $vm->setTemplate('layout/blank');
}
Lustral answered 25/4, 2015 at 6:42 Comment(0)
C
2

Or more simply (where you want) :

/* @var \Zend\Mvc\View\Http\RouteNotFoundStrategy $strategy */
$strategy = $this->getServiceLocator()->get('HttpRouteNotFoundStrategy');
$strategy->setNotFoundTemplate('application/other/404');

$view = new ViewModel();
//$view->setTemplate('application/other/404');
return $view;
Casto answered 24/6, 2016 at 8:13 Comment(0)
O
0

You should first detach default notfoundstrategy and in your Module.php and in case of a 404, you should return a new viewmodel from the controller. Please see this post: http://www.cagataygurturk.com/zf2-controller-specific-not-found-page/

Osuna answered 17/4, 2015 at 12:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.