简体   繁体   中英

Cakephp 3 redirect in beforeFilter of parent class

In our CakePHP 3 application we found a different behaviour. We're sure that it worked well in CakePHP 2, so I suppose something changed in new version.

When user visits this url: /b2controller/myMethod , these methods run:

AppController::beforeFilter()  
BController::beforeFilter()  
B2Controller::beforeFilter()  
B2Controller::myMethod()  
B2Controller::myMethod2()    

then user is redirected to this url /ccontroller/myMethod10/

But we need this:

When user visits /b2controller/myMethod and $isOk condition is true , then redirect user to /ccontroller/myMethod10/ , without running BController::beforeFilter() , B2Controller::beforeFilter() , B2Controller::myMethod() and BController::MyMethod2() .

Our minimal code is like this:

class AppController {
  function beforeFilter(Event $event) {
        // set $isOk variable
        if ($isOk == TRUE) {
           return $this->redirect('/ccontroller/myMethod10/');
        }
        $aa=1;
        $ab=2;
  }
}

class BController extends AppController {
  function beforeFilter(Event $event) {
    parent::beforeFilter($event);

    $a=1;
    $b=2;
  }

  function myOtherMethod() {
      myOtherMethod2();
  }

  function myOtherMethod2() {
      ...
      ...
  }
}

class B2Controller extends BController {
  function beforeFilter(Event $event) {
    parent::beforeFilter($event);

    $m1=1;
    $m2=2;
  }

  function myMethod() {
      myMethod2();
  }

  function myMethod2() {
      ...
      ...
  }
}

class CController extends AppController {
  function beforeFilter(Event $event) {
    parent::beforeFilter($event);
  }

  function myMethod10() {
      ...
      ...
      ...
  }
}

How can I make user to redirect to another controller action, from the beforeFilter of main class ? Note that redirect occurs. But user is redirected after calling myMethod() and myMethod2() .

Also note that there is other controllers like CController that uses beforeFilter redirect behaviour.

Here are 3 methods that works:

Method 1 - Override startupProcess in your controller(s)

Override the startupProcess method of AppController :

// In your AppController
public function startupProcess() {
    // Compute $isOk
    if ($isOk) {
        return $this->redirect('/c/myMethod10');
    }
    return parent::startupProcess();
}

This is a short and quite clean method, so I would go for this one if you can. If this does not fit your needs, see below.

Note: If you use this method, your components may not be initialized when you compute $isOk since the initialization is done by parent::startupProcess .

Method 2 - Send the response from AppController :

One easy but not really clean way may be to send the response from AppController::beforeFilter :

public function beforeFilter(\Cake\Event\Event $event) {
    // Compute $isOk
    if ($isOk) {
        $this->response = $this->redirect('/c/myMethod10');
        $this->response->send();
        die();
    }
}

Method 3 - Use Dispatcher Filters

A more "clean" way would be to use Dispatcher Filters :

In src/Routing/Filter/RedirectFilter.php :

<?php

namespace App\Routing\Filter;

use Cake\Event\Event;
use Cake\Routing\DispatcherFilter;

class RedirectFilter extends DispatcherFilter {

    public function beforeDispatch(Event $event) {
        // Compute $isOk
        if ($isOk) {
            $response = $event->data['response'];
            // The code bellow mainly comes from the source of Controller.php
            $response->statusCode(302);
            $response->location(\Cake\Routing\Router::url('/c/myMethod10', true));
            return $response;
        }
    }
}

In config/bootstrap.php :

DispatcherFactory::add('Redirect');

And you can remove the redirection in your AppController . This may be the cleanest way if you are able to compute $isOk from the DispatcherFilter .

Note that if you have beforeRedirect event, these will not be triggered with this method.


Edit: This was my previous answer which does not work very well if you have multiple B -like controllers.

You need to return the Response object returned by $this->redirect() . One way of achieving this is by doing the following:

class BController extends AppController {

    public function beforeFilter(\Cake\Event\Event $event) {
        $result = parent::beforeFilter($event);
        if ($result instanceof \Cake\Network\Response) {
            return $result;
        } 
        // Your stuff
    }

}

The code bellow the if is executed only if there was no redirection ( parent::beforeFilter($event) did not return a Response object).

Note: I do not know how you compute isOk , but be careful of infinite redirection loop if you call $this->redirect() when calling /ccontroller/mymethod10 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM