[Solved]"prg Pattern" With Yii?

I have a simple question:[color="#0000FF"] What’s the best way to implement the PRG Pattern in Yii?[/color]

I’m building a SAAS, so in my case I try to create the PRG Pattern with this architecture:

1- createActionUrl($actionName, $params = null) //Custom function that append action triggers to $_GET

[indent]returns the target url + $_GET["actionWidget"] + $_GET["actionWidgetID"] + $_GET["prg"]//$params[/indent]

2- actionListener()

[indent]if ( isset($_GET[‘actionWidget’]) ) then call the action on the selected “widget”[/indent]

3- CController::afterAction($action) //This is where I implement the PRG Pattern




        public function afterAction($action) 

        {

                $this->postRedirectGet();


                parent::afterAction($action);

        }

        

        public function postRedirectGet()

        {

                if( isset($_GET['actionWidget']) )

                {             

                        $get = $_GET;

                        if( isset($_GET['actionWidgetClass']) )

                        {

                                unset($get['actionWidgetClass']);

                        }

                        if( isset($_GET['actionWidgetID']) )

                        {

                                unset($get['actionWidgetID']);

                        }

                        if( isset($_GET["prg"]) )

                        {

                                unset($get['prg']);

                        }                        

                        unset($get["actionWidget"]);

                        

                        $param = implode("/", $get); //I use path URL


                        $this->redirect( Yii::app()->request->requestUri . $param );//Here I try to stitch up my redirect

                }                

        }



My logic:

[list=1]

[*]POST my form to createActionUrl; //Works

[*]Execute the action in my selected widget(write in DB); //Works

[*]CController::afterAction(); // call postRedirectGet() //Works

[*]postRedirectGet() //clean up the $_GET of all "action" and "prg" params //Works

[*]Stitch up a new $cleanUrl;

[*]$this->redirect( $cleanUrl );

[*]Load fresh page.

[/list]

I get: :-X

[indent][color="#FF0000"]

Cannot modify header information - headers already sent by (output started at /var/www/gtbHosting/protected/yii/framework/web/CController.php:794)

/var/www/gtbHosting/protected/yii/framework/web/CHttpRequest.php(715)

703 /**

704 * Redirects the browser to the specified URL.

705 * @param string $url URL to be redirected to. If the URL is a relative one, the base URL of

706 * the application will be inserted at the beginning.

707 * @param boolean $terminate whether to terminate the current application

708 * @param integer $statusCode the HTTP status code. Defaults to 302. See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html}

709 * for details about HTTP status code.

710 */

711 public function redirect($url,$terminate=true,$statusCode=302)

712 {

713 if(strpos($url,’/’)===0)

714 $url=$this->getHostInfo().$url;

715 header('Location: '.$url, true, $statusCode);

716 if($terminate)

717 Yii::app()->end();

718 }

719

[/color][/indent]

I read that it’s because I echo something out BEFORE the redirect, which in my case I guess it’s because I render the page with my collection of widgets.

In any case, is the CController::afterAction() the right place to implement this?

Why I’m having a [color="#FF0000"]“Cannot modify header information”[/color] error?

I would appreciate any thought on my approach to this problem thank you O0

Perhaps you saved your files using UTF-8 encoding. If so, save them as “UTF-8 without BOM” because BOM (Byte Order Mark) is a 3-bytes-length code used in early UTF-8 encoding to tell the receiver about the encoding of the incoming bytes/characters and in today modern encoding, it’s not necessary. If the receiver gets BOM, in face, it started to receive the data and so, you cannot modify headers. Hope to explained good enough.

Thank you for your answer.

I changed the encoding of my files "without BOM" and I still have the error.

I wonder if I can make a redirect in the afterAction(), is this the cause of the problem!?

Yii by default use output buffering so it captures all output from an action so headers can be set in an action. Did you disable that manually?

Thank you for you quick answer.

No I didn’t touch the output buffering.

It should logically redirect; or maybe the output flush is executed before the “afterAction()”? I don’t get it

I’ve browsed the code and it seems the buffering I mention happens only when rendering views, that is you can’t output headers after calling render(). So you can’t do a redirect in afterAction. You could try to do it in a beforeRender() method.

[SOLVED]

Ok, I’m gonna post my version of the PRG Pattern.

Big thx to nineinchnick, it really gave me a place where to start. It’s impossible to redirect after beforeRender().

Basically, I use sessions to pass information through a redirect, use it and then automatically flush it.




class Controller extends CController

{

//Flush the $_SESSION["prg"];

        protected function afterAction($action) 

        {                

                        

                if( isset($_SESSION["prg"]) )

                        if( isset($_SESSION["prg"]["prgRedirect-9wQRATtO4nS8"]) && $_SESSION["prg"]["prgRedirect-9wQRATtO4nS8"] == true )                        

                                $_SESSION["prg"]["prgRedirect-9wQRATtO4nS8"] = false;

                         else 

                                unset( $_SESSION["prg"] );                        

                        

                parent::afterAction($action);

        }

        

              

         //This is my way to create a new url, you probably don't need any of these unset.

        public function postRedirectGet( $url = null )

        {

                    if( isset($_GET['actionWidget']) )

                    {                                   

                                $get = $_GET;

                                if( isset($_GET['actionWidgetClass']) )

                                {

                                        unset($get['actionWidgetClass']);

                                }

                                if( isset($_GET['actionWidgetID']) )

                                {

                                        unset($get['actionWidgetID']);

                                }

                                if( isset($_GET["prg"]) )

                                {

                                        unset($get['prg']);

                                }  

                                

                                // This set the prgRedirect to TRUE then in the

                                // Controller::afterAction after 2 loops it unsets

                                // the hole $_SESSION["prg"]

                                $_SESSION["prg"]["prgRedirect-9wQRATtO4nS8"] = true;

                                

                                unset($get["actionWidget"]);


                                $param = "/";

                                foreach ( $get as $key=>$value ):

                                        $param .= $key . "/" . $value . "/";

                                endforeach;

                                

                                $url = Yii::app()->controller->createAbsoluteUrl(

                                                "/"

                                                .Yii::app()->getController()->id

                                                ."/"

                                                .Yii::app()->getController()->getAction()->id

                                                .$param 

                                        );

                                

                                Yii::app()->controller->redirect( $url, false , 303 );                                

                    }

        }


...



USAGE:




 public function beforeRun()

        {      

                $this->contactForm = new ContactForm_JsonModel;                

                

                if( isset($_SESSION["prg"]["POST"]["ContactForm_JsonModel"]) )

                {

                        $this->contactForm->attributes = $_SESSION["prg"]["POST"]["ContactForm_JsonModel"];

                        

                        if( $this->contactForm->validate() )                        

                        {

                                $this->messageSent = '<h2>BLABLABLA<h2>';       

                        }             

                }

        }




        public function actionSendEmail()

        {      

                if( isset($_POST) )

                        $_SESSION["prg"]["POST"] = $_POST; 

        }   




The redirect is called by:


Yii::app()->controller->postRedirectGet();

That I embedded in my custom action listener that I calls it automatically after my "actionSendEmail()". In a conventional Yii application you can probably embed it in the Controller::beforeRender()

I hope this information can be used by someone, somewhere. If you have any suggestion to make it better please don’t hesitate. :lol: