Fully ajax website with Yii — Part 1
This article will be suitable for who made fully ajax site like Gmail with best PHP Framework — Yii.
Main requirements are:
- All pages should be loaded via ajax.
- All forms should be submitted via ajax.
- Pages should be reachable by search engines.
- History support.
- Window title support
- Minimal additional code.
Here is my solution.
I get for example simple web application which you can made with help of yiic utility.
cd /home/www/mywebsite wget http://yii.googlecode.com/files/yii-1.1.7.r3135.tar.gz gunzip yii-1.1.7.r3135.tar.gz tar -xvf yii-1.1.7.r3135.tar mv yii-1.1.7.r3135/framework/ ./ php framework/yiic.php webapp ./
That is it your application has been created!
Add this code to the protected/views/layout/main.php file before </head> tag.
<?php $cs = Yii::app()->getClientScript(); $cs->registerCoreScript('jquery'); $cs->registerCoreScript('bbq'); ?> <?php $script=<<<HTML function applychanges(obj) { for(k in obj) { jQuery(k).html(obj[k]); } } HTML; $cs->registerScript('applychanges', $script, CClientScript::POS_HEAD); ?> <?php $script=<<<HTML var headline = $("#loading"); $(document).ajaxSend(function() { headline .attr("class", "activity") }); $(document).ajaxStop(function() { headline.removeAttr("class") }); HTML; $cs->registerScript('loading-indicator', $script, CClientScript::POS_READY); ?> <?php $script=<<<HTML // handling links and forms $("a:not(.direct)").live("click", function(){ var href = $(this).attr( "href" ); if(href=="#") return false; $.bbq.pushState({ url: href}); return false; }); $("form:not(.direct)").live("submit", function(){ var url = ""; var type = jQuery(this).attr("method"); if(type==undefined) type = "get"; if(type=="get") { var action = jQuery(this).attr("action"); if(action.indexOf("?")==-1) url = action + "?" + jQuery(this).serialize(); else url = action + "&" + jQuery(this).serialize(); $.bbq.pushState({ url: url}); } else { jQuery.ajax({ type:"post", data:$(this).serialize(), url:jQuery(this).attr("action"), success:applychanges }); } return false; }) $(window).bind( "hashchange", function(e) { var url = $.bbq.getState( "url" ); if(!url) return; $("a").each(function(){ var href = $(this).attr( "href" ); if ( href === url ) { $(this).addClass( "current" ); $(this).parent("li").addClass( "active" ); } else { $(this).removeClass( "current" ); $(this).parent("li").removeClass( "active" ); } }); jQuery.ajax({ type:"get", url:url, success:applychanges }); }); $(window).trigger( "hashchange" ); HTML; $cs->registerScript('ajaxlinks-and-forms', $script, CClientScript::POS_READY); ?>
Next code should be added after <body> tag
<div id="loading"><?php echo Yii::t('main','Loading');?></div>We’ve jus added javascript code which will handle all client side job
Then we extend Base controller class «Controller».
protected/componens/Controller.php; Add new methods below:
public function ajaxRender($file, $data=array()) { $data['title'] = CHtml::encode($this->pageTitle); header('Content-type: text/x-json'); echo CJSON::encode($data); Yii::app()->end(); } public function render($file, $params = array(), $data=array()) { if(Yii::app()->request->isAjaxRequest){ if(Yii::app()->user->hasFlash('updatedata')) { $flashdata = Yii::app()->user->getFlash('updatedata'); $data = $data + $flashdata; } $data['#content'] = parent::renderPartial($file, $params, true); $data['title'] = CHtml::encode($this->pageTitle); header('Content-type: text/x-json'); echo CJSON::encode($data); Yii::app()->end(); } else { echo parent::render($file, $params, true); } } public function redirect($url) { if(is_array($url) && isset($url[0])) $url = $url[0]; if(Yii::app()->request->isAjaxRequest){ $data = array(); if(Yii::app()->user->hasFlash('updatedata')) { $flashdata = Yii::app()->user->getFlash('updatedata'); $data = $data + $flashdata; } $data['#content']= '<script type="text/javascript"> jQuery.ajax({ url:"'.$url.'", type:"get", success:applychanges }); </script>'; header('Content-type: text/x-json'); echo CJSON::encode($data); Yii::app()->end(); } else { parent::redirect($url); } } public function refresh() { if(Yii::app()->request->isAjaxRequest){ $data = array(); if(Yii::app()->user->hasFlash('updatedata')) { $flashdata = Yii::app()->user->getFlash('updatedata'); $data = $data + $flashdata; } $data['#content']= '<script type="text/javascript"> var url = $.bbq.getState( "url" ); jQuery.ajax({ url:url, type:"get", success:applychanges }); </script>'; header('Content-type: text/x-json'); echo CJSON::encode($data); Yii::app()->end(); } else { parent::refresh(); } }
Then you have to override some Request methods to support CWebUser redirects:
Put content below to new file with name protected/components/EHttpRequest.php
<?php class EHttpRequest extends CHttpRequest { public function redirect($url,$terminate=true,$statusCode=302) { if(is_array($url) && isset($url[0])) $url = $url[0]; if(Yii::app()->request->isAjaxRequest){ $data = array(); if(Yii::app()->user->hasFlash('updatedata')) { $flashdata = Yii::app()->user->getFlash('updatedata'); $data = $data + $flashdata; } $data['#content']= '<script type="text/javascript"> jQuery.ajax({ url:"'.$url.'", type:"get", success:applychanges }); </script>'; header('Content-type: text/x-json'); echo CJSON::encode($data); Yii::app()->end(); } else { parent::redirect($url); } } public function refresh() { if(Yii::app()->request->isAjaxRequest){ $data = array(); if(Yii::app()->user->hasFlash('updatedata')) { $flashdata = Yii::app()->user->getFlash('updatedata'); $data = $data + $flashdata; } $data['#content']= '<script type="text/javascript"> var url = $.bbq.getState( "url" ); jQuery.ajax({ url:url, type:"get", success:applychanges }); </script>'; header('Content-type: text/x-json'); echo CJSON::encode($data); Yii::app()->end(); } else { parent::refresh(); } } }
Set new class name for request component in your main config protected/config/main.php
It should looks like this
.... 'components'=>array( 'user'=>array( // enable cookie-based authentication 'allowAutoLogin'=>true, ), 'request'=>array( 'class'=>'EHttpRequest', ), ....
That’s all. Now you have full ajax simple web application.
Offcourse it has some limitations and issues, further I’ll extend this article.
P.S. I have forgotten to say about styles.
Just add these lines to your css file.
#loading { position:fixed; padding:3px; background:#80B646; color:#fff; display:none; z-index:999; right:0; top:0; } #loading.activity { display:block; }
10 comments to “Fully ajax website with Yii — Part 1”
Май 4th, 2011 at 08:53
Hi!
Your article is very nice, I intend to make a fully ajax website by yii, I follow your instruction and i got an error, i can not figure where the problem is, can you help me?
I use yii 1.1.7
This is the error message:
Declaration of EHttpRequest::redirect() should be compatible with that of CHttpRequest::redirect()
E:\wwwroot\ngaymua\protected\components\EHttpRequest.php(3)
01 request->isAjaxRequest){
10
11 $data = array();
12
13 if(Yii::app()->user->hasFlash(‘updatedata’)) {
14 $flashdata = Yii::app()->user->getFlash(‘updatedata’);
15 $data = $data + $flashdata;
Thanks for your help.
Май 4th, 2011 at 22:11
This Post Has Been Updated
Май 5th, 2011 at 07:04
Thanks for your update.
Your new update also need to be applied to function redirect and refresh in Controller.php
Now it’s work.
Thanks so much.
Август 16th, 2011 at 12:22
I’m new to Yii and Ajax. Correct me if I am wrong but this tutorial has got so many gaps in the codes that it does not work at all. Has Tpoxa actually tested this? Refresh and Redirect classes are incompatible. I had to add the following to make it work:
- public function redirect($url,$terminate=true,$statusCode=302)
- public function refresh($terminate=true,$anchor=»)
JQuery registration is incomplete. No code in the body tag section. the $script variable is not assigned any value.
appreciate it if you test before you write a tutorial
thanks
Сентябрь 4th, 2011 at 22:01
>
you have this part broken in your code, which is about javascript, i would really like to see it )
Сентябрь 4th, 2011 at 22:02
echo Yii::t(‘main’,'Loading’);
Сентябрь 8th, 2011 at 21:21
Hello, nice post, but i can’t see all the content from «protected/components/EHttpRequest.php» in this page.
And i can’t see what to put above te part of html.
Can you send me this information, please?
Thank you, very much.
Сентябрь 9th, 2011 at 01:18
Thank you, guy! Now i can see
Декабрь 29th, 2011 at 07:44
hi… i have error
PHP Error
Declaration of Controller::redirect() should be compatible with that of CController::redirect()
C:\xampp\htdocs\testyii\protected\components\Controller.php(7)
01 <?php
02 /**
03 * Controller is the customized base controller class.
04 * All controller classes for this application should extend from this base class.
05 */
06 class Controller extends CController
07 {
08 /**
09 * @var string the default layout for the controller view. Defaults to '//layouts/column1',
10 * meaning using a single column layout. See 'protected/views/layouts/column1.php'.
11 */
12 public $layout='//layouts/column1';
13 /**
14 * @var array context menu items. This property will be assigned to {@link CMenu::items}.
15 */
16 public $menu=array();
17 /**
18 * @var array the breadcrumbs of the current page. The value of this property will
19 * be assigned to {@link CBreadcrumbs::links}. Please refer to {@link CBreadcrumbs::links}
Stack Trace
#0
+
C:\xampp\htdocs\yii\framework\YiiBase.php(421): include("C:\xampp\htdocs\testyii\protected\components\Controller.php")
#1
unknown(0): YiiBase::autoload("Controller")
how i can solve this… ??
Декабрь 29th, 2011 at 10:43
ok soleve by dodgek
- public function redirect($url,$terminate=true,$statusCode=302)
- public function refresh($terminate=true,$anchor=»)