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.

Defining multiple layouts in YII Frameworks

Most applications use a single layout for all their views. However, there are situations when
multiple layouts are needed. For example, an application can use different layouts at different
pages: Two additional columns for blog, one additional column for articles, and no additional
columns for portfolio.
Getting ready
Set up a new application using yiic webapp.
How to do it...
1. Create two layouts in protected/views/layouts: blog and articles. Blog will
contain the following code:
<?php $this->beginContent('//layouts/main')?>
<div>
<?php echo $content?>
</div>
<div class="sidebar tags">
<ul>
<li><a href="#php">PHP</a></li>
<li><a href="#yii">Yii</a></li>
</ul>
</div>
<div class="sidebar links">
<ul>
<li><a href="http://yiiframework.com/">Yiiframework</a></li>
<li><a href="http://php.net/">PHP</a></li>
</ul>
</div>
<?php $this->endContent()?>
2. Articles will contain the following code:
<?php $this->beginContent('//layouts/main')?>
<div>
<?php echo $content?>
</div>
<div class="sidebar toc">
<ul>
<li><a href="#intro">1. Introduction</a></li>
<li><a href="#quick-start">2. Quick start</a></li>
</ul>
</div>
<?php $this->endContent()?>
3. Create three controllers named BlogController, ArticleController, and
PortfolioController with index actions in both:
class BlogController extends Controller
{
function actionIndex()
{
$this->layout = 'blog';
$this->render('//site/index');
}
}
class ArticleController extends Controller
{
function actionIndex()
{
$this->layout = 'articles';
$this->render('//site/index');
}
}
class PortfolioController extends Controller
{
function actionIndex()
{
$this->render('//site/index');
}
}
4. Now try http://example.com/blog, http://example.com/article, and
http://example.com/portfolio.
How it works...
We defined two additional layouts for blog and articles. As we don't want to copy-paste
common parts from the main layout, we apply additional layout decorators using $this-
>beginContent and $this->endContent, as shown on the following diagram:
So, we are using a view rendered inside articles layout as main's $content.

Using decorators in YII frameworks

In Yii, we can enclose content into a decorator. The common usage of decorators is
layout. Yes, when you are rendering a view using the render method of your controller, Yii
automatically decorates it with the main layout. Let's create a simple decorator that will
properly format quotes.
Getting ready
Set up a new application using yiic webapp.
How to do it...
1. First, we will create a decorator file protected/views/decorators/quote.php:
<div class="quote">
&ldquo;<?php echo $content?>&rdquo;, <?php echo $author?>
</div>
2. Now in protected/views/site/index.php, we will use our decorator:
<?php $this->beginContent('//decorators/quote', array('author' =>
'Edward A. Murphy'))?>
If anything bad can happen, it probably will
<?php $this->endContent()?>
3. Now, your homepage should include the following markup:
<div class="quote">
&ldquo;If anything bad can happen, it probably will&rdquo;,
Edward A. Murphy
</div>
How it works...
Decorators are pretty simple. Everything between beginContent and endContent
is rendered into a $content variable and passed into a decorator template. Then, the
decorator template is rendered and inserted in the place where endContent was called. We
can pass additional variables into decorator using a second parameter of beginContent,
such as the one we did for the author.

Using clips in YII

One of the Yii features you can use in your views is clips. The basic idea is that you can record
some output and then reuse it later in a view. A good example will be defining additional
content regions for your layout and filling them elsewhere.
Getting ready
Set up a new application using yiic webapp.
How to do it...
1. For our example, we need to define two regions in our layout: beforeContent and
footer. Open protected/views/layouts/main.php and insert the following
just before the content output (<?php echo $content; ?>):
<?php if(!empty($this->clips['beforeContent'])) echo
$this->clips['beforeContent']?>
Then, insert the following into <div id="footer">:
<?php if(!empty($this->clips['footer'])) echo
$this->clips['footer']?>
2. That is it. Now, we need to fill these regions somehow. We will use a controller
action for the beforeContent region. Open protected/controllers/
SiteController.php and add the following to actionIndex:
$this->beginClip('beforeContent');
echo 'Your IP is '.Yii::app()->request->userHostAddress;
$this->endClip();
3. As for footer, we will set its content from a view. Open protected/views/site/
index.php and add the following:
<?php $this->beginClip('footer')?>
This application was built with Yii.
<?php $this->endClip()?>
4. Now, when you open your website's index page, you should get your IP just before the
page content and "built with" note in the footer.
How it works...
We mark regions with the code that just checks for existence of a clip specified and, if
clip exists, the code outputs it. Then, we record content for clips we defined using special
controller methods named beginClip and endClip.

Reusing views with partials in YII frameworks

Yii supports partials, so if you have a block without much logic that you want to reuse or want
to implement e-mail templates, partials are the right way to look.
Getting ready
1. Set up a new application using yiic webapp.
2. Create a WebsiteController as follows:
class WebsiteController extends CController
{
function actionIndex()
{
$this->render('index');
}
}
How to do it...
We will start with a reusable block. For example, we need to embed a YouTube video at
several website pages. Let's implement a reusable template for it.
1. Create a view file named protected/views/common/youtube.php and paste an
embed code from YouTube. You will get something like:
<object width="480" height="385"><param name="movie"
value="http://www.youtube.com/v/S6u7ylr0zIg?fs=1 "></
param><param name="allowFullScreen" value="true"></
param><param name="allowscriptaccess" value="always"></
param><embed src="http://www.youtube.com/v/S6u7ylr0zIg?fs=1"
type="application/x-shockwave-flash" allowscriptaccess="always"
allowfullscreen="true" width="480" height="385"></embed></object>
2. Now, we need to make it reusable. We want to be able to set video ID, width, and
height. Let's make width and height optional, as follows:
<object width="<?php echo!empty($width) ? $width : 480?>"
height="<?php echo!empty($height) ? $height: 385?>"><param
name="movie" value="http://www.youtube.com/v/<?php echo
$id?>?fs=1 "></param><param name="allowFullScreen" value="true"></
param><param name="allowscriptaccess" value="always"></
param><embed src="http://www.youtube.com/v/<?php echo $id?>?fs=1"
type="application/x-shockwave-flash" allowscriptaccess="always"
allowfullscreen="true" width="<?php echo !empty($width) ? $width
: 480?>" height="<?php echo !empty($height) ? $height: 385?>"></
embed></object>
3. Now, you can use it in your protected/views/website/index.php like this:
<?php $this->renderPartial('////common/youtube', array(
'id' => '8Rp-CaIKvQs', // you can get this id by simply looking
at video URL
'width' => 320,
'height' => 256,
))?>
Looks better, right? Note that we have used // to reference a view. This means that
Yii will look for a view starting from protected/views not taking controller name
into account.
4. Now, let's send some e-mails. As we are unable to write unique letters to thousands
of users, we will use a template but will make it customized. Let's add a new method
to protected/controllers/WebsiteController.php as follows:
class WebsiteController extends CController
{
function actionSendmails()
{
$users = User::model->findAll();
foreach($users as $user)
{
$this->sendEmail('welcome', $user->email, 'Welcome to the
website!', array('user' => $user));
}
echo 'Emails were sent.';
}
function sendEmail($template, $to, $subject, $data)
{
mail($to, $subject, $this->renderPartial
('//email/'.$template, $data, true));
}
}
5. Here is our template protected/views/email/welcome.php:
Hello <?php echo $user->name?>,
Welcome to the website!
You can go check our new videos section. There are funny raccoons.
Yours,
Website team.
How it works...
CController::renderPartial does the same template processing as
CController::render except the former does not use layout. As we can access current
controller in a view using $this, we can use its renderPartial to use view within another
view. renderPartial is also useful when dealing with AJAX as you don't need layout
rendered in this case.

yii framework Using controller context in a view

Yii views are pretty powerful and have many features. One of them is that you can use
controller context in a view. So, let's try it.
Getting ready
Set up a new application using yiic webapp.
How to do it...
1. Create a controller as follows:
class WebsiteController extends CController
{
function actionIndex()
{
$this->pageTitle = 'Controller context test';
$this->render('index');
}
function hello()
{
if(!empty($_GET['name']))
echo 'Hello, '.$_GET['name'].'!';
}
}
2. Now, we will create a view showing what we can do:
<h1><?php echo $this->pageTitle?></h1>
<p>Hello call. <?php $this->hello()?></p>
<?php $this->widget('zii.widgets.CMenu',array(
'items'=>array(
array('label'=>'Home', 'url'=>array('index')),
array('label'=>'Yiiframework home',
'url'=>'http://yiiframework.ru/',
),
))?>
How it works...
We are using $this in a view to refer to a currently running controller. When doing it, we can
call a controller method and access its properties. The most useful property is pageTitle
which refers to the current page title and there are many built-in methods that are extremely
useful in views such as renderPartials and widget.

Using flash messages in YII

When you are editing a model with a form, when you are deleting a model, or doing any
other operation, it is good to tell users if it went fine or if there was an error. Typically, after
some kind of action, such as editing a form, a redirect will happen and we need to display a
message on the page we want to go to. However, how to pass it from the current page to the
redirect target and clean afterwards? Flash messages will help us.
Getting ready
Set up a new application using yiic webapp.
How to do it...
1. Let's create a protected/controllers/WebsiteController.php controller
as follows:
class WebsiteController extends CController
{
function actionOk()
{
Yii::app()->user->setFlash('success', 'Everything went
fine!');
$this->redirect('index');
}
function actionBad()
{
Yii::app()->user->setFlash('error', 'Everything went
wrong!');
$this->redirect('index');
}
function actionIndex()
{
$this->render('index');
}
}
2. Additionally, create the protected/views/website/index.php view as follows:
<?php if(Yii::app()->user->hasFlash('success')):?>
<div class="flash-notice">
<?php echo Yii::app()->user->getFlash('success')?>
</div>
<?php endif?>
<?php if(Yii::app()->user->hasFlash('error')):?>
<div class="flash-error">
<?php echo Yii::app()->user->getFlash('error')?>
</div>
<?php endif?>
3. Now, if we go to http://example.com/website/ok, we'll be redirected to
http://example.com/website/index and a success message will be displayed.
Moreover, if we go to http://example.com/website/bad, we will be redirected
to the same page, but with an error message. Refreshing the index page will hide
the message.
How it works...
We are setting a flash message with Yii::app()->user->setFlash('success',
'Everything went fine!'), for example, calling CWebUser::setFlash. Internally,
it is saving a message into a user state, so in the lowest level, our message is being kept in
$_SESSION until Yii::app()->user->getFlash('success') is called and the
$_SESSION key is deleted.

Displaying static pages with CViewAction in YII

If you have a few static pages and aren't going to change them very frequently, then it's not
worth querying database and implementing a page management for them.
Getting ready
Set up a new application using yiic webapp.
How to do it...
1. We just need to connect CViewAction to our controller.
class SiteController extends CController
{
function actions()
{
return array(
'page'=>array(
'class'=>'CViewAction',
),
);
}
}
2. Now, put your pages into protected/views/site/pages. Name them
about.php and contact.php.
3. Now, you can try your pages by typing:
http://example.com/index.php?r=site/page&view=contact
Alternatively, you can type the following:
http://example.com/site/page/view/about
If you have configured clean URLs with path format.
How it works...
We are connecting external action named CViewAction that simply tries to find a view
named the same as the $_GET parameter supplied. If it is there, it displays it. If not, then
it will give you 404 Not found page.

Using external actions in YII

In Yii, you can define controller actions as separate classes and then connect them to your
controllers. This way, you can reuse some common functionality.
For example, you can move backend for autocomplete fields to an action and save some time
by not having to write it over and over again.
Another simple example that we will review is deleting a model.
Getting ready
1. Set up a new application using yiic webapp.
2. Create a DB schema with the following script:
CREATE TABLE `post` (
`id` int(10) unsigned NOT NULL auto_increment,
`created_on` int(11) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL auto_increment,
`username` varchar(200) NOT NULL,
`password` char(40) NOT NULL,
PRIMARY KEY (`id`)
);
3. Generate Post and User models using Gii.
How to do it...
1. Let's write a usual delete action for posts first, as follows:
class PostController extends CController
{
function actionIndex()
{
$posts = Post::model()->findAll();
$this->render('index', array(
'posts' => $posts,
));}
function actionDelete($id)
{
$post = Post::model()->findByPk($id);
if(!$post)
throw new CHttpException(404);
if($post->delete())
$this->redirect('post/index');
throw new CHttpException(500);
}
}
We have defined two actions. One lists all posts and another deletes a post specified
if it exists and redirects back to index action.
2. Now, let's do the same in a separate action class. Create DeleteAction.php in
your protected/components directory as follows:
class DeleteAction extends CAction
{
function run()
{
if(empty($_GET['id']))
throw new CHttpException(404);
$post = Post::model()->findByPk($_GET['id']);
if(!$post)
throw new CHttpException(404);
if($post->delete())
$this->redirect('post/index');
throw new CHttpException(500);
}
}
3. Let's use it inside our controller. Delete actionDelete, we will not need it anymore.
Then, add the actions method:
class PostController extends CController
{
function actions()
{
return array(
'delete' => 'DeleteAction',
);
}

}
4. OK. Now, we are using external delete action for post controller, but what about the
user controller? To use our DeleteAction with UserController we need to
customize it first. We do this as follows:
class DeleteAction extends CAction
{
public $pk = 'id';
public $redirectTo = 'index';
public $modelClass;
function run()
{
if(empty($_GET[$this->pk]))
throw new CHttpException(404);
$model = CActiveRecord::model($this->modelClass)
->findByPk($_GET[$this->pk]);
if(!$model)
throw new CHttpException(404);
if($model->delete())
$this->redirect($this->redirectTo);
throw new CHttpException(500);
}
}
5. Now, we can use this action for both post controller and user controller. For post
controller, we do this as follows:
class PostController extends CController
{
function actions()
{
return array(
'delete' => array('class' => 'DeleteAction',
'modelClass' => 'Post',
);
);
}

}
6. For user controller, we do this as follows:
class UserController extends CController
{
function actions()
{
return array(
'delete' => array(
'class' => 'DeleteAction',
'modelClass' => 'User',
);
);
}

}
7. This way, you can save yourself a lot of time by implementing and reusing external
actions for tasks of a similar type.
How it works...
Every controller can be built from external actions like a puzzle from pieces. The difference
is that you can make external actions very flexible and reuse them in many places. In the
final version of DeleteAction, we defined some public properties. As DeleteAction is
a component, we can set its properties through config. In our case, we pass config into the
actions controller method used to add actions to a module.

Using base controller in YII

In many frameworks, the concept of a base controller that is being extended by other ones is
described right in the guide. In Yii, it is not in the guide as you can achieve flexibility in many
other ways. Still, using base controller is possible and can be useful.
Getting ready
A new application using yiic webapp is to be set up.
Let's say we want to add some controllers that will be accessible only when the user is
logged in. We can surely set this constraint for each controller separately, but we will do
it in a better way.
How to do it...
1. First, we will need a base controller that our user-only controllers will use. Let's create
SecureController.php in protected/components with the following code:
<?php
class SecureController extends Controller
{
public function filters()
{
return array(
'accessControl',
);
}
public function accessRules()
{
return array(
array('allow',
'users'=>array('@'),
),
array('deny',
'users'=>array('*'),
),
);
}
}
2. Now, go to the Gii controller generator and enter SecureController into the Base
Class field. You will get something like this:
class TestController extends SecureController
{
public function actionIndex()
{
$this->render('index');
}

}
3. Now, your TestController index will be only accessible if the user is logged in,
even though we have not declared it explicitly in the TestController class.
How it works...
The trick is nothing more than a basic class inheritance. If filters or accessRules is not
found in TestController, then it will be called from SecureController.

Providing your own URL rules at runtime in YII framework

When you are developing an application with pluggable module architecture, you most likely
need to somehow inject your module-specific rules into an existing application.
Getting ready
1. Set up a new application using yiic webapp.
2. Add .htaccess, shown in official URL Management guide to your webroot.
3. Add 'showScriptName' => false to your URL manager configuration.
4. Generate the page module using Gii.
5. Don't forget to add your new module to the modules list in your application
configuration.
How to do it...
1. Create ModuleUrlManager.php in your protected/components directory with
the following code inside:
<?php
class ModuleUrlManager
{
static function collectRules()
{
if(!empty(Yii::app()->modules))
{
foreach(Yii::app()->modules as $moduleName => $config)
{
$module = Yii::app()->getModule($moduleName);
if(!empty($module->urlRules))
{
Yii::app()->getUrlManager()->addRules
($module->urlRules);
}
}
}
return true;
}
}
2. In your application configuration, add the following line:
'onBeginRequest' => array('ModuleUrlManager', 'collectRules'),
3. Now, in your page module, you can add custom rules. To do so, open PageModule.
php and add:
public $urlRules = array(
'test' => 'page/default/index',
);
4. To test if it works, open your browser and go to http://example.com/test.
This is the view content for action "index". The action belongs to the controller
"DefaultController" in the "page" module.
5. You still can override URL rules from your main application configuration file. So, what
you specify in module's urlRules is used only when the main application rules are
not matching.
Let's review the ModuleUrlManager::collectRules method.
If there are modules defined in our application, then we are checking if urlRules public
property exists. If it does, then there are some rules defined in the module and they are
added using CUrlManager::addRules.
CUrlManager::addRules description says "In order to make the new rules effective, this
method must be called before CWebApplication::processRequest".
Now, let's check how our application works. In our index.php, we have the following line:
Yii::createWebApplication($config)->run();
After being initialized with configuration, we are calling CWebApplication::run():
public function run()
{
if($this->hasEventHandler('onBeginRequest'))
$this->onBeginRequest(new CEvent($this));
$this->processRequest();
if($this->hasEventHandler('onEndRequest'))
$this->onEndRequest(new CEvent($this));
}
As we can see, there is an onBeginRequest event raised just before calling
processRequest. That is why we are attaching our class method to it.
There's more...
As instantiating all application modules on every request is not good for performance, it is
good to cache module rules. Caching strategy can vary depending on your application. Let's
implement a simple one:
<?php
class ModuleUrlManager
{
static function collectRules()
{
if(!empty(Yii::app()->modules))
{
$cache = Yii::app()->getCache();
foreach(Yii::app()->modules as $moduleName => $config)
{
$urlRules = false;
if($cache)
$urlRules = $cache->get('module.urls.'.$moduleName);
if($urlRules===false){
$urlRules = array();
$module = Yii::app()->getModule($moduleName);
if(isset($module->urlRules))
$urlRules = $module->urlRules;
if($cache)
$cache->set('module.urls.'.$moduleName, $urlRules);
}
if(!empty($urlRules))
Yii::app()->getUrlManager()->addRules($urlRules);
}
}
return true;
}
}
This implementation caches URL rules per module. So, adding new modules is not
a problem but changing existing ones requires you to flush cache manually using
Yii::app()->cache->flush().

Creating URL rules for static pages in YII framework

A website typically contains some static pages. Usually, they are /about, /contact, /tos,
and so on, and it is common to handle these pages in a single controller action. Let's find a
way to create URL rules for these types of pages

1. Create a fresh Yii application using yiic webapp as described in the official guide
and find your protected/config/main.php. It should contain the following:
// application components
'components'=>array(

// uncomment the following to enable URLs in path-format
/*
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\
d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),
2. Delete everything from rules as we are going to start from scratch.
3. In your protected/controllers, create WebsiteController with the
following code:
class WebsiteController extends CController
{
public function actionPage($alias)
{
echo "Page is $alias.";
}
}
4. Configure your application server to use clean URLs. If you are using Apache with
mod_rewrite and AllowOverride turned on you should add the following lines to
the .htaccess file under your webroot folder:
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
The most straightforward way is defining a rule for each page:
'<alias:about>' => 'website/page',
'<alias:contact>' => 'website/page',
'<alias:tos>' => 'website/page',
Using regular expressions, we can compact it to a single rule:
'<alias:(about|contact|tos)>' => 'website/page',
Now, what if we want the URL to be /tos and an alias parameter to be terms_of_service?
No problem, we can use default parameters to achieve it:
'tos' => array('website/page', 'defaultParams' => array('alias' =>
'terms_of_service')),
OK. What if we have many pages and want to be able to dynamically create pages without
adding more rules or changing existing ones?
We can achieve this with the following rule:
'<alias>' => 'website/page'
As this rule matches everything, we need to place it last, so it won't affect all other rules. In
addition, default rules with one slug, such as controller name will stop working. To overcome
this issue, we need to add default rules which we deleted in the Getting ready section of
this recipe.
How it works...
Let's read rules we just wrote.
'<alias:about>' => 'website/page',
If the URL is /about, then pass it as the alias parameter to website/page.
'<alias:(about|contact|tos)>' => 'website/page',
If the URL is /about or /contact or /tos, then pass it as the alias parameter to
website/page.
'tos' => array('website/page', 'defaultParams' => array('alias' =>
'terms_of_service')),
When the URL is /tos, pass terms_of_service as the alias parameter value.

This rule is a bit special because it uses default parameter option. Default parameter allows
you to set a value that will be used if parameter with name specified is omitted. When you
need to specify an option for the rule, you should use an array notation:
'pattern' => array('internal/route', 'option' => 'value', 'option' =>
'value', …),

Using regular expressions in URL rules in YII

One of the "hidden" features of Yii URL router is that you can use regular expressions that are
pretty powerful when it comes to strings handling.
Getting ready
1. Create a fresh Yii application using yiic webapp as described in the official guide
and find your protected/config/main.php. It should contain the following:
// application components
'components'=>array(

// uncomment the following to enable URLs in path-format
/*
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\
d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),
2. Delete everything from rules as we are going to start from scratch.
3. In your protected/controllers, create PostController.php with the
following code inside:
class PostController extends CController
{
public function actionView($alias)
{
echo "Showing post with alias $alias.";
}
public function actionIndex($order = 'DESC')
{
echo "Showing posts ordered $order.";
}
public function actionHello($name)
{
echo "Hello, $name!";
}
}
This is our application controller we are going to access using our custom URLs.
4. Configure your application server to use clean URLs. If you are using Apache with
mod_rewrite and AllowOverride turned on, then you should add the following
lines to the .htaccess file under your webroot folder:
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
How to do it...
We want our PostController actions to accept parameters according to some rules and
give "404 not found" HTTP response for all parameters that do not match. In addition, post
index should have an alias URL archive.
Let's use regular expressions to achieve it:
'post/<alias:[-a-z]+>' => 'post/view',
'(posts|archive)' => 'post/index',
'(posts|archive)/<order:(DESC|ASC)>' => 'post/index',
'sayhello/<name>' => 'post/hello',
Now, you can try the following URLs:
// success
http://example.com/post/test-post
// fail
http://example.com/post/another_post
// success
http://example.com/posts
// success
http://example.com/archive
// fail
http://example.com/archive/test
// success
http://example.com/posts/ASC
// success
The following screenshot shows that the URL http://example.com/post/test-post
has run successfully:
How it works...
You can use regular expressions in both parameter definition and the rest of the rule. Let's
read our rules one by one.
'post/<alias:[-a-z]+>' => 'post/view',
Alias parameter should contain one or more English letter or a dash. No other symbols
are allowed.
'(posts|archive)' => 'post/index',
Both posts and archive are leading to post/index.
'(posts|archive)/<order:(DESC|ASC)>' => 'post/index',
Both posts and archive are leading to post/index. Order parameter can only accept
two values: DESC and ASC.
'sayhello/<name>' => 'post/hello',
You should specify the name part but there are no restrictions on what characters are allowed.
Note that regardless of the rule used, the developer should never assume that input data
is safe.

Generating URLs by path in YII

Yii allows you not only to route your URLs to different controller actions but also to generate a
URL by specifying a proper internal route and its parameters. This is really useful because you
can focus on internal routes while developing your application and care about real URLs only
before going live.
1. Create a fresh Yii application using yiic webapp as described in the official guide
and find your protected/config/main.php. Replace rules array as follows:
// application components
'components'=>array(

// uncomment the following to enable URLs in path-format
/*
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'<alias:about>' => 'website/page',
'page/about/<alias:authors>' => 'website/page',
'page/<alias>' => 'website/page',
),
2. In your protected/controllers, create WebsiteController with the following
code inside:
class WebsiteController extends CController
{
public function actionIndex()
{
echo "index";
}
public function actionPage($alias)
{
echo "Page is $alias.";
}
}
This is our application controller that we are going to generate custom URLs for.
3. Configure your application server to use clean URLs. If you are using Apache with
mod_rewrite and AllowOverride turned on, then you should add the following
lines to the .htaccess file under your webroot folder:
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php

how url reqriting works in YII

Let's review what was done and why it works. We'll start with the right part of the first rule:
'home' => 'website/index',
What is website/index exactly?
In the Yii application, each controller and its actions have corresponding internal routes. A
format for an internal route is moduleID/controllerID/actionID. For example, the
actionPage method of WebsiteController corresponds to the website/page route.
So, in order to get the controller ID, you should take its name without the Controller postfix
and make its first letter lowercased. To get an action ID, you should take action method name
without the action prefix and, again, make its first letter lowercased.
Now, what is home?
To understand it in a better way, we need to know, at least perfunctorily, what's happening
when we access our application using different URLs.
When we are using /home, URL router checks our rules one by one starting from the top trying
to match URL entered with the rule. If the match is found, then the router is getting controller
and its action from an internal route assigned to the rule and is executing it. So, /home is the
URL pattern that defines which URLs will be processed by the rule it belongs to.You can create parameterized rules using a special syntax. Let's review the third rule:
'page/<alias>' => 'website/page',
Here, we are defining an alias parameter that should be specified in URL after
/page/. It can be virtually anything and it will be passed as $alias parameter to
WebsiteController::actionPage($alias).
You can define a pattern for such a parameter. We did it for the second rule:
'<alias:about>' => 'website/page',
Alias here should match about or else, the rule will not be applied

Configuring URL rules in YII

Yii URL router is quite powerful and does two main tasks: it resolves URLs into internal routes
and creates URLs from these routes. Router rules description is scattered over the official Yii
guide and API docs. Let's try to understand how to configure application rules by example.
Getting ready
1. Create a fresh Yii application using yiic webapp as described in the official guide
(http://www.yiiframework.com/doc/guide/) and find your protected/
config/main.php. It should contain the following:
// application components
'components'=>array(

// uncomment the following to enable URLs in path-format
/*
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\
d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),
2. Delete everything from rules as we are going to start from scratch.
3. In your protected/controllers, create WebsiteController.php with the
following code inside:
class WebsiteController extends CController
{
public function actionIndex()
{
echo "index";
}
public function actionPage($alias)
{
echo "Page is $alias.";
}
}
This is the application controller we are going to customize URLs for.
4. Configure your application server to use clean URLs. If you are using Apache with
mod_rewrite and AllowOverride turned on, then you should add the following
lines to the .htaccess file under your webroot folder:
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# if a directory or a file exists, use it directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# otherwise forward it to index.php
RewriteRule . index.php
How to do it...
Our website should display the index page at /home and all other pages at /page/<alias_
here>. Additionally, /about should lead to a page with alias about.
1. Add the following to your rules in protected/config/main.php:
'home' => 'website/index',
'<alias:about>' => 'website/page',
'page/<alias>' => 'website/page',
2. After saving your changes, you should be able to browse the following URLs:
‰‰ /home
‰‰ /about
‰‰ /page/about
‰‰ /page/test

Learn Yii Step 2 Router, Controller, and Views

we will help you to learn some handy things about Yii URL router, controllers, and
views. You will be able to make your controllers and views more flexible.
Configuring URL rules
Generating URLs by path
Using regular expressions in URL rules
Creating URL rules for static pages
Providing your own URL rules at runtime
Using base controller
Using external actions
Displaying static pages with CViewAction
Using flash messages
Using controller context in a view
Reusing views with partials
Using clips
Using decorators
Defining multiple layouts
Paginating and sorting data

Find more on http://Learnyii.ebharat.org

working with cookie values in yii

If you are working with a lot of cookie values and want to shorten the code provided, then you
can use a helper as follows:
class Cookie
{
public static function get($name)
{
$cookie=Yii::app()->request->cookies[$name];
if(!$cookie)
return null;
return $cookie->value;
}
public static function set($name, $value, $expiration=0)
{
$cookie=new CHttpCookie($name,$value);
$cookie->expire = $expiration;
Yii::app()->request->cookies[$name]=$cookie;
}
}

After you drop this code into protected/components/Cookie.php, you will be able to
perform the following:
class TestController extends CController
{
public function actionIndex()
{
$cookie = Cookie::get('test');
if($cookie)
echo $cookie;
else
Cookie::set('test','I am a cookie!!');
}
}

how request in yii works

You can access the request component in your web application by using Yii::app()-
>getRequest(). So, let's review the most useful methods and their usage, methods that
return different parts of the current URL. In the following table, returned parts are marked
with a bold font.
getUrl http://cookbook.local/test/index?var=val
getHostInfo http://cookbook.local/test/index?var=val
getPathInfo http://cookbook.local/test/index?var=val
getRequestUri http://cookbook.local/test/index?var=val
getQueryString http://cookbook.local/test/index?var=val
The methods that allow us to ensure request type are getIsPostRequest,
getIsAjaxRequest, and getRequestType.
ff For example, we can use getIsAjaxRequest to serve different content based on
request type:
class TestController extends CController
{
public function actionIndex()
{
if(Yii::app()->request->isAjaxRequest)s
$this->renderPartial('test');
else
$this->render('test');
}
}
In the preceding code, we are rendering a view without layout if the request is made
through AJAX.
ff While PHP provides superglobals for both POST and GET, Yii way allows us to omit
some additional checks:
class TestController extends CController
{
public function actionIndex()
{
$request = Yii::app()->request;
$param = $request->getParam('id', 1);
// equals to
$param = isset($_REQUEST['id']) ? $_REQUEST['id'] : 1;
$param = $request->getQuery('id');
// equals to
$param = isset($_GET['id']) ? $_GET['id'] : null;
$param = $request->getPost('id', 1);
// equals to
$param = isset($_POST['id']) ? $_POST['id'] : 1;
}
}

 getPreferredLanguage tries to determine the user's preferred language. It can't
be completely accurate, but it is good to use it as a fallback in case the user has not
specified a preferred language manually.
class TestController extends CController
{
public function actionIndex()
{
$request = Yii::app()->request;
$lang = $request->preferredLanguage;
// trying to get language setting from DB
$criteria = new CDbCriteria();
$criteria->compare('user_id', $request->getQuery('userid'));
$criteria->compare('key', 'language');
$setting = Settings::model()->find($criteria);
if($setting)
$lang = $setting->value;
Yii::app()->setLanguage($lang);
echo Yii::t('app', 'Language is: ').$lang;
}
}
 sendFile allows to initiate file download as follows:
class TestController extends CController
{
public function actionIndex()
{
$request = Yii::app()->getRequest();
$request->sendFile('test.txt', 'File content goes here.');
}
}
This action will trigger a file download and send all necessary headers, including
content type (mimetype) and content length. Mimetype, if not set manually as a third
parameter, will be guessed based on the filename's extension.
ff The last thing we are going to show in this chapter is the getCookies method. It
returns a CCookieCollection class instance that allows us to work with cookies.
As CCookieCollection extends CMap, we can use some native PHP methods
as follows:
class TestController extends CController
{
public function actionIndex()
{
$request = Yii::app()->request;
// getting a cookie
$cookie = $request->cookies['test'];
if($cookie)
// printing cookie value
echo $cookie->value;
else {
// creating new cookie
$cookie=new CHttpCookie('test','I am a cookie!');
$request->cookies['test'] = $cookie;
}
}

Working with request in yii

You can work with request data directly using PHP superglobals such as $_SERVER, $_GET,
or $_POST but the better way is to use Yii powerful CHttpRequest class that resolves
inconsistencies among different web servers, manages cookies, provides some additional
security, and has a nice set of OO methods

Using Yii core collections

Yii has a set of collection classes used mainly for internal purposes which are not described in
the Definitive Guide, but are still very useful for applications:
 Lists: CList, CTypedList
Maps: CMap, CAttributeCollection
Queue: CQueue
  Stack: CStack
How to do it…
All collections implement SPL IteratorAggregate, Traversable, and Countable. Lists
and maps also implement SPL ArrayAccess. It allows using collections like a standard PHP
construct. The following is a snippet from the CList API:
ff The following is the snippet from CList API:
// append at the end
$list[]=$item;
// $index must be between 0 and $list->Count
$list[$index]=$item;
// remove the item at $index
unset($list[$index]);
// if the list has an item at $index
if(isset($list[$index]))
// traverse each item in the list
foreach($list as $index=>$item)
// returns the number of items in the list
$n=count($list);
ff CList is an integer-indexed collection. Compared to the native PHP array, it adds
stricter checks, can be used in OO fashion, and allows to make a collection read-only:
$list = new CList();
$list->add('python');
$list->add('php');
$list->add('java')
if($list->contains('php'))
$list->remove('java');
$anotherList = new CList(array('python', 'ruby'));
$list->mergeWith($anotherList);
$list->setReadOnly(true);
print_r($list->toArray());

There is another list collection named CTypedList that ensures that the list
contains only items of a certain type:
$typedList = new CTypedList('Post');
$typedList->add(new Post());
$typedList->add(new Comment());
As we are trying to add a comment to a posts list, the preceding code will give you the
following exception:
CTypedList<Post> can only hold objects of Post class.
ff CMap allows using every value, integer or not, as a key. Just like in CList, it can also
be used in the native PHP style, has almost the same set of OO-methods, and allows
making a collection read only:
$map = new CMap();
$map->add('php', array('facebook', 'wikipedia', 'wordpress',
'drupal'));
$map->add('ruby', array('basecamp', 'twitter'));
print_r($map->getKeys());
ff There is also one handy static method named CMap::mergeArray that can be used
to recursively merge two associative arrays while replacing scalar values:
$apps1 = array(
'apps' => array(
'task tracking',
'bug tracking',
),
'is_new' => false
);
$apps2 = array(
'apps' => array(
'blog',
'task tracking',
),
'todo' => array(
'buy milk',
),
'is_new' => true
);
$apps = CMap::mergeArray($apps1, $apps2);
CVarDumper::dump($apps, 10, true);
The result of the preceding code is as follows:
array
(
'apps' => array
(
'0' => 'task tracking'
'1' => 'bug tracking'
'2' => 'blog'
'3' => 'task tracking'
)
'is_new' => true
'todo' => array
(
'0' => 'buy milk'
)
)
ff CAttributeCollection includes all CMap functionality and can work with data
just like properties:
$col = new CAttributeCollection();
// $col->add('name','Alexander');
$col->name='Alexander';
// echo $col->itemAt('name');
echo $col->name;
ff CQueue and CStack implement a queue and a stack respectively. A stack works as
LIFO: last in, first out, and the queue is FIFO: first in, first out. Same as list and map
collections these can be used in native PHP style and have OO style methods:
$queue = new CQueue();
// add some tasks
$queue->enqueue(new Task('buy milk'));
$queue->enqueue(new Task('feed a cat'));
$queue->enqueue(new Task('write yii cookbook'));
// complete a task (remove from queue and return it)
echo 'Done with '.$queue->dequeue();
echo count($queue).' items left.';
// return next item without removing it
echo 'Next one is '.$queue->peek();
foreach($queue as $task)
print_r($task);
$garage = new CStack();
// getting some cars into the garage
$garage->push(new Car('Ferrari'));
$garage->push(new Car('Porsche'));
$garage->push(new Car('Kamaz'));
// Ferrari and Porsche can't get out
// since there is…
echo $garage->peek(); // Kamaz!
// we need to get Kamaz out first
$garage->pop();
$porsche = $garage->pop();
$porsche->drive();

how Configuring widget defaults works in YII

A Yii web application provides a bunch of components. One of them is a widget factory that
since Yii 1.1.3 can be used to set widget defaults.
1. Let's use it to set pageSize application-wide. We will need to edit the application
configuration file main.php as follows:
return array(

'components'=>array(
'widgetFactory'=>array(
'widgets'=>array(
'CLinkPager'=>array(
'pageSize'=>15,
),

),
),

),
);
2. Now, the default value for CLinkPager's pageSize will be 15, so if we omit this
parameter for all the application CLinkPagers then it will be 15, application-wide.
3. Moreover, we still can override the pageSize value for a specific widget:
<?$this->widget('CLinkPager', array(
'pages' => $pages,
'pageSize' => 5,
))?>
This works much like the CSS cascade. You are setting the default overall style in an external
file, but are still able to override this through inline styles for individual widgets.

Configuring widget defaults in YII

In Yii, code pieces commonly used in views are placed into widgets. For example, a widget can
render a tag cloud or provide a custom form input type. Core widgets are highly configurable
and are used in views as follows:
<?$this->widget('CLinkPager', array(
'pages' => $pages,
'pageSize' => 15,
))?>
In the preceding code, we are using $this->widget that calls a CLinkPager widget with
an array of parameters to display a pagination. pages and pageSize are both assigned to
the corresponding public properties of CLinkPager before it is being rendered.
Note that we have changed the count of items per page to 15 in our example. If we want our
pagination to display 15 items per page on all pages of our application, then we will need to
provide a pageSize parameter with value 15 for all CLinkPager widget calls. Is there a
better way? Definitely, yes.

configuration of standard Yii application components in YII

Now, let's find out which standard Yii application components you can configure. There are two
application types bundled with Yii which are as follows:
1. Web application (CWebApplication)
2. Console application (CConsoleApplication)
Both are extended from CApplication, so both console and web applications are sharing its
components
You can get the component names from API pages (http://www.yiiframework.com/
doc/api/) and the source code of the registerCoreComponents application method,
but let's list them here so the list can be used as a reference.

how components configuration works in YII

If you have worked with Yii before, then you have probably configured a database connection:
return array(

'components'=>array(
'db'=>array(
'class'=>'system.db.CDbConnection',
'connectionString'=>'mysql:host=localhost;dbname=database_
name',
'username'=>'root',
'password'=>'',
'charset'=>'utf8',
),

),

);
This way of configuring component is used when you want to use a component across all
application parts. With the preceding configuration, you can access a component by its name,
such as Yii::app()->db.
How it works…
When you are using the Yii::app()->db component for the first time directly or through
active record model, Yii creates a component and initializes its public properties with the
corresponding values provided in db array under the components section of the main.php
application configuration file. In the preceding code, 'connectionString' the value will
be assigned to CDbConnection::connectionString, 'username' will be assigned
to CDbConnection::username, and so on.
If you want to find out what 'charset' stands for or want to know what else you can
configure in the db component, then you need to know its class. In case of db component, the
class is CDbConnection. You can refer to its API page at http://www.yiiframework.
com/doc/api/CDbConnection/ and look for its public properties you can set from config.
In the preceding code, the 'class' property is a bit special because it is used to specify
component class name. It does not exist in the CDbConnection class. Therefore, it can be
used to override a class as follows:
return array(

'components'=>array(
'db'=>array(
'class'=>'application.components.MyDbConnection',

),

),

);
This way, you can override each application component and it is very useful whenever a
standard component does not fit your application.

Configuring components in YII frameworks

Yii is a very customizable framework. Moreover, as in every customizable code, there should
be a convenient way to setup different application parts. So in Yii, this is provided through a
configuration file named main.php located at protected/config/.

how exceptions works in yii

1. Let's assume that we have an application/apis/lyrics/LyricsFinder.php
class that makes an HTTP request to an API using CURL and returns song lyrics based
on its name. This is how we can use exceptions inside of it:
// create some custom exceptions to be able to catch them
// specifically if needed
// general lyrics finder exception
class LyricsFinderException extends CException {}
// used when there is a connection problem
class LyricsFinderHTTPException extends LyricsFinderException{}
class LyricsFinder
{
private $apiUrl = 'http://example.com/lyricsapi&songtitle=%s';
function getText($songTitle)
{
$url = $this->getUrl($songTitle);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
// if there is an HTTP error, we'll throw an exception
if($result===false)
{
$errorText = curl_error($curl);
curl_close($url);
throw new LyricsFinderHTTPException($errorText);
}
curl_close($curl);
return $result;
}
private function getRequestUrl($songTitle)
{
return sprintf($this->apiUrl, urlencode($songTitle));
}
}
2. As we don't know how a specific application needs to handle its API
connection, we will leave it to the application itself by throwing a custom
LyricsFinderHTTPException. This is how we can handle it in our
protected/controllers/TestController.php:
class TestController extends CController
{
public function actionIndex($song)
{
$lyric = 'Nothing was found.';
// importing api class
Yii::import('application.apis.lyrics.LyricsFinder');
$finder = new LyricsFinder();
if(!empty($song))
{
// We don't want to show user an error.
// Instead we want to apologize and
// invite him to try again later.
try {
$lyric = $finder->getText($song);
}
// we are looking for specific exception here
catch (LyricsFinderHTTPException $e)
{
echo 'Sorry, we cannot process your request. Try again
later.';
}
}
echo $lyric;
}
}
3. Another usage of Yii exceptions is the generation of different HTTP responses
by throwing CHttpException. For example, an action that displays a blog post
represented by a Post model, loaded by its ID will look like this:
class PostController extends CController
{
function actionView()
{
if(!isset($_GET['id']))
// If there is no post ID supplied, request is definitely
wrong.
// According to HTTP specification its code is 400.
throw new ChttpException(400);
// Finding a post by its ID
$post = Post::model()->findByPk($_GET['id']);
if(!$post)
// If there is no post with ID specified we'll generate
// HTTP response with code 404 Not Found.
throw new CHttpException(404);
// If everything is OK, render a post
$this->render('post', array('model' => $post));
}
}
How it works…
Yii converts all non-fatal application errors to CException automatically.
Additionally, the default exception handler raises either the onError or an onException
event. The default event handler writes a log message with error level set to error.
Additionally, if your application's YII_DEBUG constant is set to true, unhandled exception
or error will be displayed at a handy error screen. This screen includes a call stack trace, a
code area where the exception was raised, and the file and line where you can look for the
code to fix.

Using exceptions in YII framework

Exceptions are a core PHP feature, but they are seldom used fairly. Yii makes exceptions
very useful.
There are two main areas where Yii exceptions come in handy, which are as follows:
1. Exceptions allow simplifying the process of detecting and fixing application errors and
special situations, such as database connection failure or API failure.
2. Exceptions allow generating different HTTP responses in a very clean way.
Generally, an exception should be thrown when a component cannot handle a special
situation, such as the one said earlier, and needs to leave it to higher-level components.

how import and autoloading works in YII

1. Let's assume that we have a custom class named LyricsFinder that finds lyrics
for a given song. We have put it under protected/apis/lyrics/ and in our
protected/controllers/TestController.php, we are trying to use it in
the following way:
class TestController extends CController
{
public function actionIndex($song)
{
$lyric = 'Nothing was found.';
$finder = new LyricsFinder();
if(!empty($song))
$lyric = $finder->getText($song);
echo $lyric;
}
}
2. When executing it, we will get the following PHP error:
include(LyricsFinder.php) [<a href='function.include'>function.
include</a>]: failed to open stream: No such file or directory.
3. Yii helps us there a bit because at the error screen, we can see that autoloader fails
because it doesn't know where to look for our class. Therefore, let's modify our code:
class TestController extends CController
{
public function actionIndex($song)
{
$lyric = 'Nothing was found.';
// importing a class
Yii::import('application.apis.lyrics.LyricsFinder');
$finder = new LyricsFinder();
if(!empty($song))
$lyric = $finder->getText($song);
echo $lyric;
}
}

Using import and autoloading in YII Framework

When programming with PHP, one of the most annoying things is loading additional code with
include and require. Fortunately, you can do it automatically using the SPL class loader
(http://php.net/manual/en/function.spl-autoload.php).

Autoloading is one of the features which Yii relies on. Still, there are many questions about it
on the forums. Let's get it clear and show how we can use it.
When we are using a class, for example, CDbCriteria, we are not including it explicitly so
PHP initially cannot find it and is trying to rely on the autoloading feature; SPL autoloader
to be precise. In most cases, Yii default autoloader (YiiBase::autoload) will be used.
For the sake of speed and simplicity, almost all core framework classes are loaded when
needed without including or importing them explicitly. It's done through YiiBase::$_
coreClasses map, so loading core classes is very fast. Zii classes, such as CMenu,
extension classes or your own classes are not loaded automatically, so we need to import
them first.
To import classes, we will use Yii::import:
Import does not include a class immediately by default
It does not include a class if it is not used
It will not load a class twice, so it is safe to import the same class multiple times
Find more on http://Learnyii.ebharat.org

how to send an e-mail when there is a new comment (Comment) to the blog post (Post).

We have a blog application and we need to send an e-mail
when there is a new comment (Comment) to the blog post (Post).
Comment is a standard AR model generated with Gii. Post is the same Gii-generated model
except some customized methods. We will need a custom event NewCommentEvent to store
both Post and Comment models and a handler class Notifier that will do the work.
1. Let's start with protected/components/NewCommentEvent.php:
class NewCommentEvent extends CModelEvent {
public $comment;
public $post;
}
It is pretty simple. We have just added two properties.
2. Now, let's move on to protected/models/Post.php. All standard AR methods are
omitted to emphasize on what was added:
class Post extends CActiveRecord {
// custom method for adding a comment
// to current post
function addComment(Comment $comment){
$comment->post_id = $this->id;
// creating event class instance
$event = new NewCommentEvent($this);
$event->post = $this;
$event->comment = $comment;

// triggering event
$this->onNewComment($event);
return $event->isValid;
}
// defining onNewComment event
public function onNewComment($event) {
// Event is actually triggered here. This way we can use
// onNewComment method instead of raiseEvent.
$this->raiseEvent('onNewComment', $event);
}
}
3. Now, it is time to implement a notifier. Create protected/components/
Notifier.php as follows:
class Notifier {
function comment($event){
$text = "There was new comment from
{$event->comment->author} on post {$event->post->title}";
mail('admin@example.com', 'New comment', $text);
}
}
4. Now, it is time to get these together in protected/controllers/
PostController.php:
class PostController extends CController
{
function actionAddComment()
{
$post = Post::model()->findByPk(10);
$notifier = new Notifier();
// attaching event handler
$post->onNewComment = array($notifier, 'comment');
// in the real application data should come from $_POST
$comment = new Comment();
$comment->author = 'Sam Dark';
$comment->text = 'Yii events are amazing!';
// adding comment
$post->addComment($comment);
}
}
5. After the comment has been added, admin will receive an e-mail about it.
It is not always necessary to attach an event handler. Let's look at how we can handle an
event that is already declared inside an existing component by overriding a base class
method. For example, we have a form model UserForm used to collect some information
about our application user and we need to get the complete name from the first and the
last name entered by the user.
Fortunately, in CModel, which is a base class for all Yii models including form models,
CModel::afterValidate method is defined. This method is being called after a
successful form validation. Let's use it in our protected/models/UserForm.php model:
class UserForm extends CFormModel
{
public $firstName;
public $lastName;
public $fullName;
public function rules()
{
return array(
// First name and last name are required
array('firstName, lastName', 'required'),
);
}
// $event argument here is CEvent instance that
// was created passed when an event method was called.
// This time it was happened inside of
// CModel::afterValidate().
function afterValidate()
{
// If this method was called then
// the model is already filled
// with data and data is valid
// so we can use it safely:
$this->fullName = $this->firstName.' '.$this->lastName;
// It's important to call parent class method
// so all other event handlers are called
return parent::afterValidate();
}
}

We need to call parent method inside of afterValidate because parent implementation
calls onAfterValidate that actually raises events:
protected function afterValidate()
{
$this->onAfterValidate(new CEvent($this));
}

how to translate strings to different languages using Yii

In Yii, you can translate strings to different languages
using Yii::t. As we all love perfect projects all language translations should be up to date.
If they are not, we would like to receive an e-mail about it.
Events come in handy again here. In particular, the CMessageSource::onMissingTransl
ation event that is called when the translation for a string passed to Yii::t is missing.
This time we will use the application configuration file protected/config/main.php to
attach an event handler as follows:

'components' => array(

// messages component class is CPhpMessageSource by default
'messages' => array(
// using static class method as event handler
'onMissingTranslation' => array('MyEventHandler',
'handleMissingTranslation'),
),

)


Now, we should implement our handler. Create protected/components/
MyEventHandler.php as follows:
class MyEventHandler
{
static function handleMissingTranslation($event)
{
// event class for this event is CMissingTranslationEvent
// so we can get some info about the message
$text = implode("\n", array(
'Language: '.$event->language,
'Category:'.$event->category,
'Message:'.$event->message
));
// sending email
mail('admin@example.com', 'Missing translation', $text);
}
}


Find more on http://Learnyii.ebharat.org

how to define and use handlers in YII

Find more on http://Learnyii.ebharat.org

handlers, let's review some real life examples
as follows:
It is common practice to compress your application output using gzip to save client
bandwidth and speed up page loading time. If you have an access to fine-tune your
server, then you can instruct it to do so, but in some environments such as shared
hosting, you can't.
Fortunately, PHP can gzip the application output using output buffering and
ob_gzhandler. In order to do so, we should start buffering the output when
the application starts and releases the gzipped output, when it finishes.
Yii's application component has two events that will come in handy in this case:
CApplication::onBeginRequest and CApplication::onEndRequest. Let's
use them. Put the following in index.php after configuring an application but before
running it:

require_once($yii);
$app = Yii::createWebApplication($config);
// attaching a handler to application start
Yii::app()->onBeginRequest = function($event)
{
// starting output buffering with gzip handler
return ob_start("ob_gzhandler");
};
// attaching a handler to application end
Yii::app()->onEndRequest = function($event)
{
// releasing output buffer
return ob_end_flush();
};
$app->run();

ways to define a callback in yii framework

Use a global function and just pass its name as a string, such as 'my_function'.
Use a static class method. You should pass an array: array('ClassName',
'staticMethodName') .
Use an object method: array($object, 'objectMethod').
Create and pass anonymous function using create_function as follows:
$component->attachEventHandler('onClick',
create_function('$event', 'echo "Click!";'));
Since PHP 5.3, you can use anonymous functions without create_function:
$component->attachEventHandler('onClick', function($event){
echo "Click!";
});To keep your code shorter, you can use component properties to manage event
handlers as follows:
$component->onClick=$handler;
// or:
$component->onClick->add($handler);
To manage event handlers more precisely, you can get handlers list (CList)
using CComponent::getEventHandlers and work with it. For example, you can
attach an event handler the same way as with attachEventHandler using the
following code:
$component->getEventHandlers('onClick')->add($handler);
To add an event handler to the beginning of handlers list, use:
$component->getEventHandlers('onClick')->insertAt(0, $handler);
To delete a particular handler you can use CComponent::detachEventHandler
as follows:
$component->detachEventHandler('onClick', $handler);
Alternatively, get a list of handlers as shown earlier and delete handlers from it.

learn yii : Using Yii events

Most Yii classes are extended from CComponent which allows us to achieve great application
flexibility by using events. An event is a message indicating that the application did something.
We can register several event handlers that will react to certain event types. A handler can get
parameters from an event it works with and react accordingly. Using events allows achieving
great application flexibility.
In this recipe, you will learn how to declare and use both predefined and custom events in
your application.
How to do it...
To declare an event in your CComponent child class, you should add a method with a
name starting with on. For example, if you add the onRegister method, you will get a
corresponding event declared.
Typically, events are used like this:
 Declare an event by adding a corresponding method
 Attach one or multiple event handlers
 The component raises an event by using the CComponent::raiseEvent method
 All subscribed handlers are called automatically
Let's look at how we can attach an event handler to an event. To achieve it, we can use the
CComponent::attachEventHandler method. It accepts the following two parameters:
 $name: Event name
 $handler: Event handler; a standard PHP callback should be used

learn yii Using getters and setters

Yii has many features that came from other languages, such as Java or C#. One of them is
defining properties with getters and setters for any of the class extended from CComponent
(that is, virtually any Yii class).
From this recipe, you will learn how to define your own properties using getters and setters,
how to make your properties read-only, and how to hide custom processing behind native
PHP assignments.

How to do it...
1. As PHP does not have properties at the language level, we can only use getters and
setters in the following way:
class MyClass
{
// hiding $property
private $property;
// getter
public function getProperty()
{
return $this->property;
}
// setter
public function setProperty($value)
{
$this->property = $value;
}
}
$object = new MyClass();
// setting value
$object->setProperty('value');
// getting value
echo $object->getProperty();
2. This syntax is very common in the Java world but it is a bit long to use in PHP. Still, we
want to use the same functionality C# properties gives us: calling getters and setters
like class members. With Yii, we can do it in the following way:
// extending CComponent is necessary
class MyClass extends CComponent
{
private $property;
public function getProperty()
{
return $this->property;
}
public function setProperty($value)
{
$this->property = $value;
}
}
$object = new MyClass();
$object->property = 'value'; // same as $object->
setProperty('value');
echo $object->property; // same as $object->getProperty();
3. Using this feature, you can make properties read-only or write-only while keeping the
simple PHP syntax as follows:
class MyClass extends CComponent
{
private $read = 'read only property';
private $write = 'write only property';
public function getRead()
{
return $this->read;
}
public function setWrite($value)
{
$this->write = $value;
}
}
$object = new MyClass();
// gives us an error since we are trying to write to read-only
property
$object->read = 'value';
// echoes 'read only property'
echo $object->read;
// gives us an error since we are trying to read to write-only
property
echo $object->write;
// writes 'value' to private $write
$object->write = 'value';

4. Yii uses this technique extensively because almost everything is a component. For
example, when you are calling Yii::app()->user->id to get the currently logged
in user ID, what's really called is Yii::app()->getUser()->getId().
How it works...
To use getters and setters like properties, CComponent uses the PHP magic methods: __
get, __set, __isset, and __unset (http://php.net/manual/en/language.oop5.
magic.php). The following example shows what Yii 1.1 CComponent::__get looks like:
public function __get($name)
{
$getter='get'.$name;
if(method_exists($this,$getter))
return $this->$getter();

This magic PHP method intercepts all calls to missing real properties, so when we are calling
$myClass->property, it receives property as $name parameter. If a method named
getProperty exists, then PHP uses its return value as a property value.

learn yii step 1

 Using getters and setters
 Using Yii events
 Using import and autoloading
Using exceptions
 Configuring components
Configuring widget defaults
Using Yii core collections
 Working with request

learn YII a first Example

you will find a number of styles of text that distinguish between different kinds of
information. Here are some examples of these styles, and an explanation of their meaning.
Code words in text are shown as follows: "We can include view partials through the use of the
include directive."
A block of code is set as follows:
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 0);
$yii=dirname(__FILE__).'/../framework/yii.php';
$config=dirname(__FILE__).'/../app/config/production.php';
require($yii);
Yii::createWebApplication($config)->run();

When we wish to draw your attention to a particular part of a code block, the relevant lines or
items are set in bold:
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 0);
$yii=dirname(__FILE__).'/../framework/yii.php';
$config=dirname(__FILE__).'/../app/config/production.php';
require($yii);
Yii::createWebApplication($config)->run();

yii for whome

If you are a developer with a good knowledge of PHP5, are familiar with the basics of Yii, have
checked its definitive guide, and have tried to develop applications using Yii, then this book is
for you. Knowledge of the object-oriented approach and MVC pattern will be a great advantage
as Yii uses these extensively.

What you need to learn yii

Web server:
‰‰ 2.x version of Apache web server is preferred
‰‰ Other versions and web servers will work too, but configuration details are
not provided
Database server: MySQL is recommended
‰‰ MySQL 4+ with InnoDB support, MySQL 5 or higher recommended
 PHP: PHP 5.3 is recommended
‰‰PHP 5.2 or PHP 5.3, PHP 5.3 recommended
 Yii:
‰‰ latest 1.1.x
Additionally, the following tools are not strictly required but are used for specific recipes:
 PHPUnit
 XDebug
 Selenium RC
 PEAR
 Smarty
 memcached

Learn YII What we will cover

Under the Hood provides information about the most interesting Yii features
hidden under the hood: events, import, autoloading, exceptions, component, and widget
configuration, and more.
Router, Controller, and Views is about handy things concerning the Yii URL router,
controllers, and views: URL rules, external actions and controllers, view clips, decorators,
and more.
AJAX and jQuery focuses on the Yii's client side that is built with jQuery—the most
widely used JavaScript library out there. It is very powerful and easy to learn and use. This
chapter focuses on Yii-specific tricks rather than jQuery itself.
Working with Forms. Yii makes working with forms a breeze and documentation on
it is almost complete. Still, there are some areas that need clarification and examples. Some
of the topics covered in this chapter are creating own validators and input widgets, uploading
files, using, and customizing CAPTCHA.
Testing Your Application covers both unit testing, functional testing, and generating
code coverage reports. Recipes follow a test driven development approach. You will write tests
for several small applications and then will implement functionality.
Database, Active Record, and Model Tricks is about working with databases
efficiently, when to use models and when not to, how to work with multiple databases, how
to automatically pre-process Active Record fields, and how to use powerful database criteria.
Using Zii Components covers data providers, grids, and lists: How to configure
sorting and search, how to use grids with multiple related models, how to create your own
column types, and more.
Extending Yii shows not only how to implement your own Yii extension but also how
to make your extension reusable and useful for the community. In addition, we will focus on
many things you should do to make your extension as efficient as possible.
Error Handling, Debugging, and Logging reviews logging, analyzing the exception
stack trace, and own error handler implementation.
Security provides information about keeping your application secure according
to the general web application security principle "filter input escape output". We will cover
topics such as creating your own controller filters, preventing XSS, CSRF, and SQL injections,
escaping output, and using role-based access control.
Performance Tuning shows how to configure Yii to gain extra performance. You
will learn a few best practices of developing an application that will run smoothly until you
have very high loads.
Using External Code focuses on using the third party code with Yii. We will
use Zend Framework, Kohana, and PEAR but you will be able to use any code after learning
how it works.Deployment covers various tips that are especially useful on application
deployment, when developing an application in a team, or when you just want to make
your development environment more comfortable.

About YII Framework

Yii is a very flexible and high-performance application development framework written in PHP.
It helps building web applications from small ones to large-scale enterprise applications. The
framework name stands for Yes It Is. This is often the accurate and most concise response to
inquires from those new to Yii:
Is it fast? ... Is it secure? ... Is it professional? ... Is it right for my next project? ... The answer is
Yes, it is!

Learn More from learnyii.ebharat.org