Friday, December 14, 2012

Setting Up Pagination

There are a few different ways to set up pagination with CGridView and CListView that all ultimately do the same task. It is fairly easy to do when using CDataProvider to populate CGridView and CListView. Take the following example:

public function actionIndex()
{
$dataProvider=new CActiveDataProvider('NewTask', array(
'criteria'=>array(
'condition'=>'status=0',
'order'=>'import_date DESC',
),
'pagination'=>array(
'pageSize'=>5,
),
));
$this->render('index',array(
'dataProvider'=>$dataProvider,
));
}
In this example the 'pagination' property of CDataProvider is used to set the page size to 5. This can be accomplished in the exact same manner however with different syntax.

public function actionProgress()
{
$dataProvider=new CActiveDataProvider('NewTask', array(
'criteria'=>array(
'condition'=>'status>0',
'order'=>'import_date ASC',
),
));
$dataProvider->pagination->pageSize=4;
$this->render('progress',array(
'dataProvider'=>$dataProvider,
));

}
In this example the 'pagination' property is set to a page size of 4. This is useful if you want to display all the items contained in the $dataProvider array. To display all items use the 'totalItemCount' property of CActiveDataProvider.
$dataProvider->pagination->pageSize=$dataProvider->totalItemCount;
What happens when CDataProvider is not obviously used as the data provider for a view? In the view called admin you may find that this is the case. Take the following example of the controller method called actionAdmin.

public function actionAdmin()
{
$model=new NewTask('search');
$model->unsetAttributes();  // clear any default values
if(isset($_GET['NewTask']))
$model->attributes=$_GET['NewTask'];

$this->render('admin',array(
'model'=>$model,
));
}

In this case the data is not being sent to the view with $dataProvider and you will notice in the admin view that CGridView will be similar to:

<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'export-task-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'id',......
The important part to notice is 'dataProvider'=>$model->search()' . To control the pagination the search function inside of the respective model needs to be changed.

public function search()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.

$criteria=new CDbCriteria;

$criteria->compare('id',$this->id);
$criteria->compare('client_id',$this->client_id,true);
$criteria->compare('user_id',$this->user_id,true);
$criteria->compare('status',$this->status);

return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
'pagination'=>array(
'pageSize'=>5,
),
));
}
}
When CActiveDataProvider is returned to the admin view the pagination will be set to 5 (as in this example).
There are also a number of properties in CGridView that are worth knowing about such as 'enableSorting', 'enablePagination', 'summaryText' and 'template' just to name a view. These properties enable you to change the behaviour and appearance of CGridView. As an example of the syntax (using TbGridview):

<?php $this->widget('bootstrap.widgets.TbGridView', array(
'type'=>'striped bordered condensed',
'id'=>'new-task-grid',
'template'=>"{items}",
'dataProvider'=>$model->search(),
'enablePagination'=>true,
 'summaryText'=>'Displaying {start}-{end} of {count} results.',
 'template' => "{summary}{items}{pager}",
'filter'=>$model,
'columns'=>array(

References:

Friday, December 7, 2012

Create Ordered Drop Down List in CGridView

In CGridView it can be really useful to have a drop down to quickly filter a list. For example you may want to create a filter based on Business Name. To quickly filter the list in CGridView adding a drop down list is an excellent option.


For the drop down to be useful often time you will need to create an ordered drop down list and using Yii's Active Record makes this fairly easy to do. Firstly the code:

<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'client-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'provider_id',
array(
'name'=>'business_name',
'filter'=>CHtml::listData(Client::model()->findAll(array('condition'=>'active=1',
'order'=>'business_name ASC')),
'business_name','business_name'), //REALLY IMPORTANT
'type'=>'raw',
'value'=>'CHtml::link($data->business_name, array("client/view","id"=>$data->id))',
'htmlOptions'=>array('width'=>'100'),
),
'business_address',
'business_suburb',
'business_postcode',
                array(
'class'=>'CButtonColumn',
),
),
)); ?>

You will notice the I have used a query to return the results to the filter and ordered the Business Name. Take note of  'business_name','business_name' - If you do not use the actual attribute name in this format you will not get the drop down list returning the correct result or the filter will not work. Do not be tempted to use 'id', 'business_name' as you would typically do as this will return a valid drop down list however the filter function will not work. If you want to add a hyperlink to the filter results then ensure you use 'type'=>'raw' (see CFormatter for more information on the various 'types' available).

Sunday, December 2, 2012

Render, Redirect and Variables with Yii

The primary purpose of a View is to display data and accessing data in the View is relatively easy. The easiest way to show how to do this is by example.

In your Controller there are various methods such as actionIndex, actionView, actionCreate etc. Let's break down a simple actionIndex for a model called Client.
public function actionIndex()
{
$dataProvider=new CActiveDataProvider('Client'); //first part
$this->render('index',array(                         //second part
'dataProvider'=>$dataProvider,                  
));
}
The first part of this method is loading data into the $dataProvider array. The second part is responsible for selecting/rendering the appropriate View and pushing the data into the View.

In the second part of the above method (the code responsible for rendering the view) you will notice the first parameter is 'index' and this is the View that will be displayed in the browser. The second parameter 'dataProvider' is the data that is sent to the View called 'index'. Lets look at the CController Class Reference and specifically the method called render().
public string render(string $view, array $data=NULL, boolean $return=false)
You will see the 'render' method takes 3 parameters - the name of the View (a string) , the data passed to the view (an array) and finally a boolean value which we won't worry about. This boolean value can be null (omitted) so it is not required. How do we access the data that is passed to the view? All we need to do is use '$data->property' where property is any of fields in the 'Client' table e.g. $data->name,
$data->address.

It is also possible to send more than one variable to the view and all you need to do is separate these variables with comma's.

public function actionIndex()
{
  $myVariable = "Hello World";
$dataProvider=new CActiveDataProvider('Client');
$this->render('index',array(                      
'dataProvider'=>$dataProvider,   'myVariable'=>$myVariable,            
));
}
To access this additional variable in the 'index' view you only need to refer to the variable name.
<?php echo "This is my variable - ".$myVariable; ?>

You will notice up until now we have been using the 'render' method of the class 'CController'. There is another method you will see in your controller called 'redirect()'. The 'redirect' method is a little different to the 'render' method in terms of how you access the data you push to the view.
public void redirect(mixed $url, boolean $terminate=true, integer $statusCode=302)
Let's just focus on the url parameter -  the documentation says basically that the url parameter can be just the URL to be redirected to OR if the parameter is an array, the first element must be a route to a controller action and the rest are GET parameters in name-value pairs. This means that when using the 'redirect' method the view can be passed to the method as an array along with any variables that need to be pushed to the view. An example of the syntax of the redirect method is:
$this->redirect(array('index','id'=>$model->id, 'myVariable'=>$myVariable,));
Notice that the view called 'index' is in an array with a variable called $myVariable which is a different syntax to the 'render' method. This is the trick - you cannot access the variable called 'myVariable' in the same way as previously with the 'render' method. As the documentation says you need to use the 'GET' method to access this variable. So in the view called 'index' you can access the variable called 'myVariable' by using the following:
<?php echo "This is my variable - ".$_GET['myVariable']; ?>
This is an important difference between the 'render' and 'redirect' method in a controller and is easy to overlook. To access variables pushed to a View, there is an important difference depending on whether the controller uses the 'render' method (in which case you access the pushed variables by the variable name) or the 'redirect' method (variables pushed to the view must be access by the GET method).

This is a good example of how important the Class Reference can be if you are having difficulty with a particular method. The reference may seem a little confusing however once you become accustomed to the syntax the explanation of the parameters can often resolve problems you may experience with a particular method.

References:

  1. Render Method
  2. Redirect Method


Friday, November 30, 2012

Creating a Drop Down List using Constants

In designing databases often time we will use integers to represent data such as 'status' for example rather than actually storing "Created/In Progress/Completed". This is fine however when it comes to entering data into your view you have to know that 1 = In Progress, 0 = Created. It would be easier if you could have a drop down list. With Yii it is not difficult to achieve just that.

The first thing you need to do is create these constants in the Model. Let us assume we have a model called 'Job'. In the Job model create the constants you require.

class Job extends CActiveRecord
{
const STATUS_CREATED=0;
const STATUS_PROGRESS=1;
const STATUS_COMPLETED=2;

You now need to create a function to get the constants and again create this function in your model.

public function getStatus()
{
return array (
self::STATUS_CREATED=>'Created',
self::STATUS_PROGRESS=>'In Progress',
self::STATUS_COMPLETED=>'Completed',
);
}
All that is required now is to create a drop down list in your view. To do this you can take advantage of Yii's build in dropDownList.
<div class="wide form">
.......

<div class="row">
<?php echo $form->labelEx($model,'status'); ?>
<?php echo $form->dropDownList($model, 'status', $model->getStatus()); ?>
<?php echo $form->error($model,'status'); ?>
</div>
If you open up the form you will notice that there is a drop down list populated by Created/In Progress/Completed. When you save the form the value of 0,1 or 2 is written to the database.
You can take this a step further. Continuing with the example of a model called Job you should be able to list a specific job in your view and instead of the Status displaying a value of 0, 1 or 2 you can output the corresponding text. The way to do this is to create an additional function inside the Job model.

public function getActiveStatusText ()
{
$activeStatus=$this->getActiveStatus();
return isset($activeStatus[$this->status]) ? $activeStatus[$this->status] : "unkown status({$this->status})";
}
Then to use this in your view (e.g. CDetailView or Bootstraps TbDetailView)

<?php $this->widget('bootstrap.widgets.TbDetailView', array(
'type'=>'striped bordered condensed',
'data'=>$model,
'attributes'=>array(
'job_name',
'username',
array(
'name'=>'status',
'value'=>CHtml::encode($model->getActiveStatusText()),
),
),
)); ?>

That's all there is to it. There are other methods for creating this functionality however I find this the easiest and quickest to implement.

References:

  1. Understanding Virtual Attributes and get/set methods 
  2. Managing constants easily



Thursday, November 29, 2012

Debugging variables in Yii 

It was quite a while before I started to use this feature and now I would not be without it. As your application becomes more complex it is often necessary to see that your variables and query results are what they should be. You can even see what errors may be occurring and this is invaluable.

The first thing you need to do is configure your main configuration file. You will find this under  "Protected/config/main.php". In "main.php" you need to find a parameter called 'log' and here we use a class called "CWebLogRoute". To configure CWebLogRoute add the following
'log'=>array(
'class'=>'CLogRouter',
'routes'=>array(
array(
'class'=>'CWebLogRoute',
'levels'=>'trace',
'categories'=>'vardump',
'showInFireBug'=>true,
),
),
),
As you can see the output is written to FireBug which is an essential tool not only for Yii but for general web development. This will also work with Chrome Developer tools  - to use it in Chrome simply click anywhere on the page and select Inspect Element and go to the Console.

To write the value of a variable to the Console in Yii all you need to do is add the following to your controller or view.
$test = array(1,2,3,4);
echo Yii::trace(CVarDumper::dumpAsString($test),'vardump');
I found this particularly useful when writing code to write data to the database. You can see if your code is successful by using:

echo Yii::trace(CVarDumper::dumpAsString($model->save()),'vardump');
If the data is written successfully to your database this will return 'true' in the console. If it returns 'false' and you are not sure why then output the errors.

echo Yii::trace(CVarDumper::dumpAsString($model->errors)),'vardump');
CWebLogRoute is absolutely invaluable in debugging Yii applications.

References:

  1. http://www.yiiframework.com/doc/api/1.1/CWebLogRoute
  2. How to log and debug variables using CWebLogRoute 

How to do a count of records in Yii

Incorporating a database into your website is a fairly standard requirement and Yii makes if easy. One really important section of the "Definitive Guide to Yii" is "Working with Databases" and I would encourage you to read this. Some of the syntax can be a little confusing and often time I found if I could see an example then it is relatively straight forward to extrapolate. Hopefully the example to follow may help you understand some of the syntax.

If you need to count how many records in your database match specific criteria then this is relatively simple to do.  Lets assume we have a table called 'job'  and a column called 'zipcode' and we want to count how many rows in the table have a zipcode = 4000. First you need to define the count property in your Model class before you can access or use the variable in your controller.


class Job extends CActiveRecord
{
//count for number of zips
public $zipcount;

Now in your controller you need to create the query and one way is create an instance of  CDbCriteria If your not confident with Object Oriented programming creating an instance just means we use the 'new' keyword to create an object.



$criteria = new CDbCriteria;
$criteria->select = 'zipcode, COUNT(zipcode) as zipcount';
$criteria->condition='zipcode=4000';
$myquery=Job::model()->find($criteria);

Notice the 'as zipcount'. This alias 'zipcount' must be the same as the variable name you declared in your model. Now to access this 'zipcount' variable in your controller all you need to use is: $myquery->zipcount

Sometimes there may be a more complex query and CDbCriteria is perfect. Lets assume in the Job table we want to know how many 'doctors' live in this zip code and lets assume that the column 'job_id' holds this value. Lets assume for a doctor, job_id=1. So to count how many doctors live in the zip code of 4000 we would use.


$criteria = new CDbCriteria;
$criteria->select = 'job_id, COUNT(job_id) as zipcount';
$criteria->condition='job_id=:jobId and zipcode=4000';
$criteria->params=array(':jobId'=>1);
$myquery=Job::model()->find($criteria);
I used  'params'  just for the purpose of example as it displays the syntax you need to use. It is useful when you do not have a hard-coded value but rather a variable. For example say the variable '$jobtype' holds a job description value you would use 'params' as
$criteria->condition='job_id=:jobId and zipcode=4000';
$criteria->params=array(':jobId'=>$jobtype);


That is the basic requirements if you need to do a count in the database. CDbCriteria allows you to create very complex queries and I would encourage you to have a read of the Yii Documentation for more information.

References:

  1. http://www.yiiframework.com/forum/index.php/topic/7996-how-to-get-record-count/page__p__40356__hl__count+record+#entry40356
  2. http://www.yiiframework.com/doc/guide/1.1/en/database.ar


Thursday, November 22, 2012

Yii - My Journey


When I was asked to write a web application I looked at the various frameworks around for PHP and decided to use Yii. This is relatively new framework compared to other more mature frameworks however it has a very active user base.

Starting off with any framework can be a very steep learning curve and choosing one is almost just as hard. I had some slight experience with the ZEND Framework however I found it very overwhelming. There is a mountain of documentation however getting started with this Framework I found difficult. I had to write a lot of code to do something simple. I played with CakePHP a few years ago and I found it to be really rigid. If you wanted to deviate from the Cake conventions then you pay the price. I had no experience with Code Igniter however after reading an article by SHELDMANDU I decided to give Yii a try.

Now don't be fooled. If you are not experienced with PHP and have little Object Oriented Programming experience then Yii is not going to be a breeze however even for a novice PHP developer you can get a lot done in a short amount of time with very little effort. 

Yii is based on the Model View Controller architecture. Now there is no point me explaining this when there is so much written about it however for a good overview have a look at the Yii website. In a nutshell the MVC architecture separates the data and logic into the Model, the View contains the actual layout and forms and the Controller acts as a conduit between the Model and the View. It is really worth understanding this concept.

So how do you get started using the Yii Framework. In my opinion you can read and read (and read) however until you start writing the code you do not learn anything. I would recommend that you go to the Yii website and download Yii then in my opinion head on over to Larry Ullman's website and do the 8 part tutorial called "Introduction to the Yii Framework". It is the best (in my opinion) beginners tutorial that covers everything from how to install the framework to writing a basic Employee/Department application. Larry Ullman writes in easy to understand language and does not leave out big chunks of information that you have to figure out for yourself like some other tutorials. This 8 part tutorial will allow you to develop a small application quickly and you will be on your way to using the Yii Framework.

Where do you go from there? I would recommend you write an application for yourself. Think of an application you would like to develop and jump in feet first. You can read the "Definitive Guide to Yii" and I would recommend at some stage you do however it still comes back to the fact (or opinion) that writing code is the only way to learn. The Yii forums are really worth using when you get stuck (and you will) because the user base is very active and more experienced Yii users are very happy to help and give advice. Every time I had a problem I could either find the answer in the forum or someone brighter than me would solve my problem. Do not be afraid to ask for help.

Where is this blog going to go->I found that the same questions seem to get asked over and over (maybe in a different way but the concept is usually the same) so I thought I would post the problems I had when I first started in the hope that it will help others. It is a real shift from writing desktop applications to web applications and hopefully this blog will help people starting out with the Yii Framework.