Tuesday, July 3, 2012

Managing assets in YII Framework

An ability to manage assets is one of the greatest parts of Yii. It is especially useful in the
following cases:
ff When you want to implement an extension that stores its JavaScript, CSS, and images
in its own folder and is not accessible from a browser
ff When you need to pre-process your assets: combine JavaScript, compress it, and
so on
ff When you use assets multiple times per page and want to avoid duplicates
While the first two cases could be considered as bonus ones, the third one solves many
widget reusing problems.
Let's create a simple Facebook event widget which will publish and use its own CSS,
JavaScript, and an image.
Getting ready
ff Create a fresh Yii application using yiic webapp as described in the official guide
ff Check that assets directory under application's webroot (where index.php is)
has write permissions; assets will be written there
ff Generate and download a preloader image from http://ajaxload.info/
How to do it...
Let's do some planning first. In Yii, you can place your widgets virtually inside any directory
and often, it is protected/components. It is acceptable to have one or two classes inside,
but when the number of classes increases, it can create problems. Therefore, let's place our
widget into protected/extensions/facebook_events. Create an assets directory
inside the widget and put inside the ajax-loader.gif you have just downloaded. Also,
create facebook_events.css and facebook_events.js in the same directory.
1. Therefore, let's start with the widget class itself protected/extensions/
facebook_events/EFacebookEvents.php:
<?php
class EFacebookEvents extends CWidget
{
public $keyword;
private $loadingImageUrl;
protected $url = "https://graph.facebook.com/search?q=%s&type=
event&callback=?";
protected function getUrl()
{
return sprintf($this->url, urlencode($this->keyword));
}
public function init()
{
$assetsDir = dirname(__FILE__).'/assets';
$cs = Yii::app()->getClientScript();
$cs->registerCoreScript("jquery");
// Publishing and registering JavaScript file
$cs->registerScriptFile(
Yii::app()->assetManager->publish(
$assetsDir.'/facebook_events.js'
),
CClientScript::POS_END
);
// Publishing and registering CSS file
$cs->registerCssFile(
Yii::app()->assetManager->publish(
$assetsDir.'/facebook_events.css'
)
);
// Publishing image. publish returns the actual URL
// asset can be accessed with
$this->loadingImageUrl = Yii::app()->assetManager->publish(
$assetsDir.'/ajax-loader.gif'
);
}
public function run()
{
$this->render("body", array(
'url' => $this->getUrl(),
'loadingImageUrl' => $this->loadingImageUrl,,
'keyword' => $this->keyword,
));
}
}
2. Now let's define body view we are using inside run method protected/
extensions/ facebook_events/views/body.php:
<div class="facebook-events" data-url="<?php echo $url?>">
<h2><?php echo $keyword?> events</h2>
<div class="data">
<?php echo CHtml::image($loadingImageUrl)?>
</div>
</div>
3. We will need to put the following into facebook_events.js:
jQuery(function($){
$(".facebook-events").each(function(){
var url = $(this).data("url");
var container = $(".data", this);
$.getJSON(url,function(json){
var html = "<ul>";
$.each(json.data,function(){
html += "<li>"+
"<p><strong>" + this.name + "</strong>
</p><p>"+this.location
"</p></li>";
});
html += "</ul>";
container.html(html);
});
});
});
4. Write the following in facebook_events.css created previously:
.facebook-events {
padding: 10px;
width: 400px;
float: left;
}
.facebook-events ul {
padding: 0;
}
.facebook-events li {
list-style: none;
border: 1px solid #ccc;
padding: 10px;
margin: 2px;
}
5. That is it. Our widget is ready. Let's use it. Open your protected/views/site/
index.php and add the following code to it:
<?php $this->widget("ext.facebook_events.EFacebookEvents", array(
'keyword' => 'php',
))?>
<?php $this->widget("ext.facebook_events.EFacebookEvents", array(
'keyword' => 'jquery',
))?>
6. Now, it is time to check our application homepage. There should be two blocks
with Facebook events named php events and jquery events,
How it works...
When we use $this->widget in the site/index view, two EFacebookEvents methods
are run: init which publishes assets and connects them to the page, and run which renders
widget HTML. First, we use CAssetManager::publish to copy our file into the assets
directory visible from the web. It returns URL that can be used to access the resource. In the
case of JavaScript and CSS, we use CClientScript methods that add necessary <script>
and <style> tags and prevent duplication. As for an image, we pass its URL to the body view
that uses it to render a placeholder with CHtml::image. When JavaScript is loaded, it makes
requests to Facebook API (described at http://developers.facebook.com/docs/api)
and replaces placeholder with the actual data received.
There's more...
There is more about working with assets.
What is inside the assets directory?
Let's check our assets directory. It should look similar to the following:
assets
1a6630a0\
main.css
2bb97318\
pager.css
4ab2ffe\
jquery.js

Directories such as 1a6630a0 are used to prevent collisions of files with similar names from
different directories. The name of the directory is a hash of complete path to the published
asset directory. Therefore, assets from the same directory are copied to the same place. This
means that if you publish both the image and the CSS file, you can reference images from
CSS using relative paths.
Publishing an entire directory
Using CAssetManager::publish, you can publish an entire directory recursively. The
difference is that single files are monitored after being published, whereas directories are not.

how to Prevent including a bundled jQuery

Sometimes, you need to suppress including a bundled jQuery. For example, if your project
code relies on version specific functionality. To achieve this, you need to configure a
clientScript application component using protected/config/main.php as follows:
return array(
// …
// application components
'components'=>array(
// …
'clientScript' => array(
'scriptMap' => array(
'jquery.js'=>false,
'jquery.min.js'=>false,
),
),
),
// …
);

Loading a block through AJAX in YII

Nowadays, it's common when a part of a page is loaded asynchronously. Let's implement
the quotes box which will display random quotes and will have the "Next quote" link to show
the next one.
Getting ready
ff Create a fresh Yii application using yiic webapp as described in the official guide
ff Configure application to use clean URLs
Carry out the following steps:
1. Create a new controller named protected/controllers/QuoteController.
php as follows:
<?php
class QuoteController extends Controller
{
private $quotes = array(
array('Walking on water and developing software from a
specification are easy if both are frozen.', 'Edward V Berard'),
array('It always takes longer than you expect, even when you
take into account Hofstadter&rsquo;s Law.', 'Hofstadter&rsquo;s
Law'),
array('Always code as if the guy who ends up maintaining
your code will be a violent psychopath who knows where you live.',
'Rick Osborne'),
array('I have always wished for my computer to be as easy to
use as my telephone; my wish has come true because I can no longer
figure out how to use my telephone.', 'Bjarne Stroustrup'),
array('Java is to JavaScript what Car is to Carpet.', 'Chris
Heilmann'),
);
private function getRandomQuote()
{
return $this->quotes[array_rand($this->quotes, 1)];
}
function actionIndex()
{
$this->render('index', array(
'quote' => $this->getRandomQuote()
));
}
function actionGetQuote()
{
$this->renderPartial('_quote', array(
'quote' => $this->getRandomQuote(),
));
}
}
2. We will require two views. The first is protected/views/quote/index.php:
<h2>Quote of the day</h2>
<div id="quote-of-the-day">
<?php $this->renderPartial('_quote', array(
'quote' => $quote,
))?>
</div>
<?php echo CHtml::ajaxLink('Next quote', array('getQuote'),
array('update' => '#quote-of-the-day'))?>
The second view named protected/views/quote/_quote.php is as follows:
&ldquo;<?php echo $quote[0]?>&rdquo;, <?php echo $quote[1]?>
3. That is it. Now, try to access your quote controller and click on the Next quote link,
How it works...
First, we define a list of quotes in the controller's private property $quotes and create
a method to get a random quote. In a real application, you will probably get a quote from a
database using DAO or an active record.
Then, we define a view for the index action and _quote which is used in the getQuote
action that renders it without layout and the index view as a partial. In the index action,
we use CHtml::ajaxLink to create a link which makes a request to the getQuote action
and updates the HTML element with the ID of quote-of-the-day. This is done with a
response CHtml::ajaxLink that generates the following code in the resulting HTML page
(reformatted):
<script type="text/javascript">
/*<![CDATA[*/
jQuery(function($) {
jQuery('body').delegate('#yt0','click',function(){jQuery.ajax({
'url':'/quote/getQuote',
'cache':false,
'success':function(html){
jQuery("#quote-of-the-day").html(html)
}
});
return false;
});
});
/*]]>*/
</script>
As jQuery is being used, Yii includes it in the page automatically and does it only once, no
matter how many times we are using it.If you want to customize the success callback, then you can do this by setting it through a
third parameter as follows:
<?php echo CHtml::ajaxLink('Next quote', array('getQuote'),
array('success' => 'js:function(data){
alert(data);
}'))?>

learn yii framework : Topic 3 AJAX and jQuery

Yii's client side is built with jQuery—the most widely used JavaScript library which is very
powerful and simple to learn and use. In this chapter, we will focus on Yii-specific tricks
rather than jQuery itself. If you need to learn more about jQuery, then please refer to its
documentation at http://docs.jquery.com/.
Loading a block through AJAX

we will cover in this Topic :
 Loading a block through AJAX
 Managing assets
 Including resources into the page
Working with JSON
 Passing configuration from PHP to JavaScript
 Handling variable number of inputs

Paginating and sorting data in YII framework

In latest Yii releases, accent was moved from using Active Record directly to grids, lists, and
data providers. Still, sometimes it is better to use active record directly. Let's see how to list
paginated AR records with ability to sort them.
Getting ready
1. Setup a new application using yiic webapp.
2. Create a database structure table post with id and title fields, add 10—20
records.
3. Generate Post model using Gii.
How to do it...
1. First, you need to create protected/controllers/PostController.php:
class PostController extends Controller
{
function actionIndex()
{
$criteria = new CDbCriteria();
$count=Post::model()->count($criteria);
$pages=new CPagination($count);
// elements per page
$pages->pageSize=5;
$pages->applyLimit($criteria);
// sorting
$sort = new CSort('Post');
$sort->attributes = array(
'id',
'title',
);
$sort->applyOrder($criteria);
$models = Post::model()->findAll($criteria);
$this->render('index', array(
'models' => $models,
'pages' => $pages,
'sort' => $sort,
));
}
}
2. Now, let's implement protected/views/post/index.php as follows:
<p><?php echo $sort->link('id')?></p>
<p><?php echo $sort->link('title')?></p>
<ol>
<?php foreach($models as $model):?>
<li>
<h2><?php echo $model->id?> - <?php echo $model->title?></h2>
</li>
<?php endforeach?>
</ol>
<?php $this->widget('CLinkPager', array(
'pages' => $pages,
))?>
3. Try to load http://example.com/post. You should get a working pagination and
links that allow sorting list by ID or by title.
How it works...
First, we get total models count and initialize new pagination component instance with it.
Then, we use the applyLimit method to apply limit and offset to criteria we have used for
count request. After that, we create sorter instance for the model, specifying model attributes
we want to sort by and applying order conditions to criteria by calling applyOrder. Then, we
pass modified criteria to findAll. At this step, we have models list, pages, data used for link
pager, and sorter that we use to generate sorting links.
In a view, we are using data we gathered. First, we are generating links with CSort::link
method. Then, we are listing models. Finally, using CLinkPager widgets we are rendering
pagination control.