Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yii2: exclude specific controller actions from '$this->goBack()'

Tags:

yii2

yii2-user

I have views from various controller actions which are solely to be run from an iframe placed in another view.

Currently, when the iframe loads, and I go to the log in page to log in, on success the login controller (using yii2 user module) calls $this->goBack(), redirecting me to the iframe source URL (since it's the last page visited), rather than the original page containing the iframe.

Basically, I'd like to exclude specific controller actions from being set as the return URL when $this->goBack() is called. Bonus points if all actions loaded in iframes are automatically excluded from $this->goBack().

like image 713
Dean Avatar asked Nov 08 '22 23:11

Dean


1 Answers

Ok, I'll have a go at this! This code is totally untested! Your problem is that the action has no way of knowing whether it's been called from an iframe or not, unless you give it one. So, the basis of my attempt at an answer is that all urls for iframes should have an additional get parameter. Lets call that caller. So each iframe should look something like

<iframe url="index.php?r=controller/action&caller=this-controller/action</iframe>

Now you can always test the request url to see if it was called from an iframe. In addition, every link within the iframe should have this parameter added to it's url.

So, now we have at least two problems. Firstly, how to automatically add caller as a get parameter, without having to re-write every url, and secondly, how to reconfigure the goBack() method so it knows the difference between the two types of request.

The first problem can be relatively easily resolved by adding another view layer in between the controller and the view you want I'll call it iframe. So in your controller action, add this;

$view = 'The name of the view you want to render';
$this->render('iframe', 'view' => $view);//Add in any other parameters you want to pass

Your iframe view file should contain something like this;

<iframe src="<?php Url::to(['however you generate the url for your iframe', 'caller' => Url::to($this->context->route)]); ?>">
    <?php $this->render($view); ?>//Pass additional parameters to the view if needed
</iframe>

Now we have a way of testing a controller/action call to see if it being requested by am iframe. The caller parameter is important because it allows us to extract a string to use as the value for goBack() and other methods.

Next, we need to extend UrlManager, as all request, response, Url:to() and goBack() methods and classes ultimately use the UrlManager to complete the methods for generating urls.

So, create a new UrlManager. We'll copy most of the code from the existing UrlManager, just adding some spiciness of our own. I've stored mine in commands, but put your where you like and change the namespace accordingly.

<?php

namespace app\commands;

use Yii;
use yii\web\UrlManager;

class CustomUrlManager extends UrlManager {

    public function createUrl($params){
        $request = Yii::$app()->request;
        $caller = $request->get('caller');
        if ($caller && !$params['caller']){
           $params['caller'] = $caller;
        }
        return parent::createUrl($params);
    }

}

So now, the iframe generates a caller parameter, and every link within the iframe will also have caller appended as a parameter, as long ass you've used either Url::to() (or variants on that method) or Yii::$app->UrlManager to generate your links.

Now all we need to do is customise the goBack() method of your controller to send any goBack() requests to the original source iframe.

public function goBack($defaultUrl = null)
    {
        $caller = Yii::$app->request->get('caller');
        if ($caller){
            return Yii::$app->getResponse()->redirect($caller);
        }
        return Yii::$app->getResponse()->redirect(Yii::$app->getUser()->getReturnUrl($defaultUrl));
    }

Finally you need to configure Yii to use your new UrlManager, in your config file;

'components' => [
    'urlManager' => [
        'class' => 'app/commands/CustomUrlManager'
    ]
]

I'd love to know if this works, it's been an interesting challenge!

like image 179
Joe Miller Avatar answered Dec 25 '22 09:12

Joe Miller