How to organize the Yii structure for backend and frontend

How to organize the yii structure for backend and frontend? This question is so frequently asked in the internet. After reading the some articles, like

http://www.yiiframework.com/wiki/63/organize-directories-for-applications-with-front-end-and-back-end-using-webapplicationend-behavior/

http://www.yiiframework.com/wiki/33/organize-directories-for-applications-with-front-end-and-back-end

http://www.yiiframework.com/wiki/374/yiiboilerplate-setup-a-professional-project-structure-in-seconds/

http://www.yiiframework.com/wiki/155/the-directory-structure-of-the-yii-project-site/#hh1

Unfortunately, I have NOT decided which is the best one for me. Now I can only write the rules I need to consider:
1. The frontend and backend controller should be seperated. The controller of frontend should be NOT accessed from backend, and vice versa.
2. Some components and extensions are able to be used for both frontend and backend.
3. The module should be self-sufficient. It can contain admin code and front code, but it should not be separated into two different folders. The controller of frontend should be NOT accessed from backend, and vice versa.
4. The multi-language files should be used in Models and Modules.
5. The frontend or backend code can use common lib code, but the common lib code should not use the two ends’ codes.
6. For development with namespace, it should be configued easily.

To be continued all the time.

[Solved] Yii – How to load modules dynamically from db or directory

In Yii application development, we can set up modules configuration in config.php.  Sometimes, we need to load modules configuration from database or directory glob. After searching the topic in google and comparing some solutions, I found the elegant way to implement this. Hope it can help.

You can modify the index.php file of your Yii application.

require_once($yii);

class ExtendableWebApp extends CWebApplication {
        protected function init() {
                // this example dynamically loads every module which can be found
                // under `modules` directory
                // this can be easily done to load modules
                // based on MySQL db or any other as well
                foreach (glob(dirname(__FILE__).'/protected/modules/*', GLOB_ONLYDIR) as $moduleDirectory) {
                        $this->setModules(array(basename($moduleDirectory)));
                }
                return parent::init();
        }
}

$app=new ExtendableWebApp($config);
$app->run();

Reference: http://www.yiiframework.com/forum/index.php/topic/23467-dynamically-load-modules-models-and-configurations/

[Solved]Yii CGridView 400 Error – ‘delete’ Sends GET request not POST

Problem: Two CGridViews are added to one of my view file and when I click the delete button, I am redirected to “400 Error” page with message “Your request is invalid.”

Solution:
1. Check the “id” property of each CGridView and make sure they are totally different
2. Check if all the javascript file needed are loaded. ( My problem is that I missed the file jquery.ba-bbq.js)

and How I found the problem solution?
1. download and install chrome browser
2. Ctl + Shift + I   (hot key)
chrome-console
3. Click the delete button to perform what you want
4. Check the error message in console
chrome-console2
5. You can find the error: “Uncaught TypeError: undefined is not a function” at jquery.yiigridview.js Line 329,
6. Then you can check the line (In my case, the $.param.querystring is not a function and it is defined in jquery.ba-bbq.js)

Hope it can help

 

Yii: How to disable CGridview sorting

In our application development, sometimes we want to disable CGridview sorting for performance issue.

$this->widget('zii.widgets.CGridView', array(
        ......
        'enableSorting' => false,
        ......
    )
);

This configuration also applies to CListView. The enableSorting property is defined in parent class of CGridView or CListView.

‘enableSorting’ => false,

Yii User Authetication and Authorization Module Tutorial

Commonly, user authetication includes the functions, such as user creation with username and password, user login, user logout, etc. While user authorization should include rights tree creation(operation rights or data rights), rights assignment to user, user right checking.

Yii has a built-in authentication/authorization (auth) framework which is easy to use and can be customized for special needs. Also, there are some modules tailored to the needs.

Yii’s build-in authentication/authorization framework allows you integrate your own user management to Yii’s role-base access control framework. By the way, if you want to create a new application with user and rights management module, using yii framwork may be your best choice, because it is so fast for you.

Here, I will explain the following topics, 

1. I DON’T have my own user management system, I just want to create a new application with user authentication system and authorization system.

2. I have a user management system, I need to integrate RBAC system to it.  (Please refer to section “rbac management module installation and integration to yii-user”)

Please Pay Attention the the text I comment with red color

New application

I recommend two modules rbac mangement (http://www.yiiframework.com/extension/rbac-manager/)  and user management module (http://www.yiiframework.com/extension/yii-user/). They are LGPL or New BSD licensed.

There are THREE steps included, yii-user module installation, rbac management module installation and integration between the two module

yii-user module installation

Some from http://www.yiiframework.com/extension/yii-user/

    1. copy yii-user module to protected/modules (if the modules, not exist, please create it) and name the module with “user”
    2. Edit config/main.php
    // autoloading model and component classes
    'import'=>array(
        # added for yii-user
        'application.modules.user.models.*',
        'application.modules.user.components.*',
    ),
    #...
    'modules'=>array(
        #... added for yii-user
        'user'=>array(
            'hash' => 'md5', # encrypting method (php hash function)
            'sendActivationMail' => true, # send activation email
            'loginNotActiv' => false, # allow access for non-activated users
            'activeAfterRegister' => false,# activate user on registration (only sendActivationMail = false)
            'autoLogin' => true, # automatically login from registration
            'registrationUrl' => array('/user/registration'), # registration path
            'recoveryUrl' => array('/user/recovery'), # recovery password path
            'loginUrl' => array('/user/login'), # login form path
            'returnUrl' => array('/user/profile'), # page after login
            'returnLogoutUrl' => array('/user/login'), # page after logout
        ),
        # ...
    ),
    #...
    // application components
    'components'=>array(
        'db'=>array(
        #...
            'tablePrefix' => 'tbl_',
        #...
        ),
        #...
        'user'=>array(
            // enable cookie-based authentication
            'class' => 'WebUser',
            'allowAutoLogin'=>true,
            'loginUrl' => array('/user/login'),
        ),
    #...
    ),
    #...
);
        
    1. Edit config/console.php
    'modules'=>array(
        'user'=>array(
            'hash' => 'md5', # encrypting method (php hash function)
            'sendActivationMail' => true, # send activation email
            'loginNotActiv' => false, # allow access for non-activated users
            'activeAfterRegister' => false, # activate user on registration (only sendActivationMail = false)
            'autoLogin' => true, # automatically login from registration
            'registrationUrl' => array('/user/registration'), # registration path
            'recoveryUrl' => array('/user/recovery'), # recovery password path
            'loginUrl' => array('/user/login'), # login form path
            'returnUrl' => array('/user/profile'), # page after login
            'returnLogoutUrl' => array('/user/login'), # page after logout
        ),
        #...
    ),
    #...
  1. open commnd line widown (cmd or ..)cd your_app_dir_with_file_yiic.php_in_it
  2. yiic migrate –migrationPath=application.modules.user.migrations

Notice:
1.  Please do NOT forget to config “console” configuration file
2. You may come up against problem at last step. The keys are a) the yiic must be the one of your application, not the one in framework library. b) the migrationPath must be full namespace. 

rbac management module installation and integration to yii-user

Please refer to http://www.yiiframework.com/extension/rbac-manager/

for integration, the rbac module should be configured,

  1. Copy rabc folder to protected/modules/rbac.
  2. Execute modules/rbac/data/schema.sql to create table structure and initial data
  3. To make sure there is a user with id 1 in table xx_users
  4. add modules in main.php
    'rbac' => array(
    'tableUser' => 'xxxx_users', // xxxx_ is the table prefix, users is the table of yii-user module
    'columnUserid' => 'id', // The PRIMARY column of the User Table
    'columnUsername' => 'username', // used to display name and could be same as columnUserid
    'columnEmail' => 'email' // email (only for display)
     ),
    
  5. add the following line to layouts/main.php for best look of rbac backend 
    <link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/rbac.css" />;
  6. add authManager
    'authManager'=>array(
                'class'=>'CDbAuthManager', // Database driven Yii-Auth Manager
                'connectionID'=>'db', // db connection as above
            'defaultRoles'=>array('registered'), // default Role for logged in users
            'showErrors'=>true, // show eval()-errors in buisnessRules
        ),</pre>
    
  7. you can http://hostname/applicationname/index.php?r=rbac to check the rbac backend

Replace the logic of site/login with new user/login

Replace the logic of site/logout with new user/logout

Done

Yii Model rules validation

http://www.yiiframework.com/wiki/56/

This is a reference to be used for Model rule validation and is compiled from the Yii documentation and code. The purpose is to have all the information gathered in one place instead of scattered. This reference is not an intro. See The Definitive Guide to Yii, Declaring Validation Rules for a tutorial.

How validation works

The CModel class uses a method named CModel::rules() to return an array with the rules for validation.

public function rules()
{
    return array(
        array('username, password', 'required'),
        array('password_repeat', 'required', 'on'=>'register'),
        array('password', 'compare', 'compareAttribute'=>'password_repeat', 'on'=>'register'),
    );
}

The code above is an example of what the CModel::rules() function may look like. In the main array, each line is an array that declare a new validation rule (described in next section).

These rules are applied by the CModel::validate() method which returns a boolean value. By default, the method CActiveRecord::save() automatically calls this validation, and requires it to succeed before it tries to save the record.

Parameters of a validator

public function rules()
{
    return array(
        array('username, password', 'required'),
        array('password_repeat', 'required', 'on'=>'register'),
        array('password', 'compare', 'compareAttribute'=>'password_repeat', 'on'=>'register'),
    );
}
  • attribute list: specifies the attributes (separated by commas) to be validated;
  • validator name: specifies the validator to be used. See the next section for details.
  • on: this specifies the scenarios when the validation rule should be performed. Separate different scenarios with commas. If this option is not set, the rule will be applied in any scenario. See the section [Scenarios][#Scenarios] for details.
  • message: replaces the default error message if validation fails.
  • …validation parameters…: any number of extra parameters to be used by the specified validator.

Choice of validators

Yii will look for validators in the specific order:

  1. A method in the model class with the same name as the validator specified.
  2. A built-in validator in Yii that extends the CValidator class.
  3. A path/alias to a home made class (extending CValidator) that is not built in.

Here is an example of these 3 kinds:

public function rules()
{
    return array(
        array('password', 'validateLogin'), // Custom validation method in this object
        array('username, password', 'required'), // built-in alias for CRequiredValidator
        array('username', 'ext.MyValidators.MyLoginValidator'), // Custom validation class
    );
}

public validateLogin() {

Scenarios

Each model object has a scenario property. Some scenarios are built-in and will be assigned automatically by Yii, but you can define your own.

For instance, a CActiveRecord read from the database has the “update” scenario, while a new record has the “insert” scenario.

$modelA = User::model()->findByPk(1); // $model->scenario = 'update'
$modelB = new User();                 // $model->scenario = 'insert'
$modelB->scenario = 'light';          // custom scenario
if ($modelB->validate()) {            // will only apply rules of the "light" scenario

As shown in the example of the first section, the rules can be restricted to a specific scenario through the "on" => "scenario" parameter.

Validation rules reference

For each rule, the list of its specific arguments is given with the eventual default value (in italics).

boolean : CBooleanValidator

Validates that the attribute value is either trueValue or falseValue.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. falseValue, the value representing false status. (0)
  3. strict, whether the comparison to trueValue and falseValue is strict. (false)
  4. trueValue, the value representing true status. (1)

captcha : CCaptchaValidator

Validates that the attribute value is the same as the verification code displayed in the CAPTCHA.

  1. allowEmpty, whether the attribute value can be null or empty. (false)
  2. caseSensitive, whether the comparison is case sensitive. (false)

compare : CCompareValidator

Compares the specified attribute value with another value and validates if they are equal.

  1. allowEmpty, whether the attribute value can be null or empty. (false)
  2. compareAttribute, the name of the attribute to be compared with.
  3. compareValue, the constant value to be compared with.
  4. operator, the operator for comparison. (“==”)
  5. strict, whether the comparison is strict (both value and type must be the same). (false)

date : CDateValidator

Validates that the attribute value is a valid date, time or datetime.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. format, the format pattern that the date value should follow. This can be either a string or an array representing multiple formats. The formats are described in CDateTimeParser API. (‘MM/dd/yyyy’)
  3. timestampAttribute, name of the attribute that will receive date parsing result. (null)

NOTE: The date validator was added in Yii 1.1.7 – it’s not available prior to that

Example:

array('org_datetime', 'date', 'format'=>'yyyy-M-d H:m:s'),

default : CDefaultValueValidator

Sets the attributes with the specified value. It does not do validation. Its existence is mainly to allow specifying attribute default values in a dynamic way.

  1. setOnEmpty, whether to set the default value only when the attribute value is null or empty string. (true)
  2. value, the default value to be set to the specified attributes.

email : CEmailValidator

Validates that the attribute value is a valid email address.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. allowName, whether to allow name in the email address. (false)
  3. checkMX, whether to check the MX record for the email address. (false)
  4. checkPort, whether to check port 25 for the email address. (false)
  5. fullPattern, the regular expression used to validate email addresses with the name part. Requires that the property “allowname” is on.
  6. pattern, the regular expression used to validate the attribute value.

exist : CExistValidator

Validates that the attribute value exists in a table.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. attributeName, the ActiveRecord class attribute name that should be used to look for the attribute value being validated. (null, meaning same attribute name)
  3. className, the ActiveRecord class name that should be used to look for the attribute value being validated. (null, meaning same class name)
  4. criteria, additional query criteria. (array())

file : CFileValidator

Verifies if an attribute is receiving a valid uploaded file.

  1. allowEmpty, whether the attribute requires a file to be uploaded or not. (false)
  2. maxFiles, the maximum file count the given attribute can hold. (1)
  3. maxSize, the maximum number of bytes required for the uploaded file. (null, meaning no limit)
  4. minSize, the minimum number of bytes required for the uploaded file. (null, meaning no limit)
  5. tooLarge, the error message used when the uploaded file is too large.
  6. tooMany, the error message used if the count of multiple uploads exceeds limit.
  7. tooSmall, the error message used when the uploaded file is too small.
  8. types, a list of file name extensions that are allowed to be uploaded. (null, meaning all extensions)
  9. wrongType, the error message used when the uploaded file has an extension name that is not listed among extensions.

filter : CFilterValidator

Transforms the data being validated based on a filter.

  1. filter, the filter method.

Examples:

  • With a custom function
array('mycode','filter','filter'=>'strtoupper'),
  • With a custom method
array('eanupc','filter','filter'=>array($this,'keepProductCode')),
array('eanupc','filter','filter'=>array(self,'formatProductCode')),
...
public function keepProductCode($code){
    // returns a new value (uses $this) ...
}
static public function formatProductCode($code){
    // returns a new value (cannot use $this) ...
}

NOTE: The filter validator maybe use with other validators for the same attribute. In this case one must pay attention to the order of the validators list as the filter validator will change the attribute value.

in : CRangeValidator

Validates that the attribute value is among the list (specified via range).

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. not, whether to invert the validation logic. Since Yii 1.1.5. (false)
  3. range, list of valid values that the attribute value should be among.
  4. strict, whether the comparison is strict (both type and value must be the same). (false)

Example:

array('language','in','range'=>array('en','fr','zn'),'allowEmpty'=>false),

length : CStringValidator

Validates that the attribute value is of certain length.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. encoding , string encoding. (null, meaning unchecked)
  3. is, exact length. (null, meaning no limit)
  4. max, maximum length. (null, meaning no limit)
  5. min, minimum length. (null, meaning no limit)
  6. tooLong, user-defined error message used when the value is too long.
  7. tooShort, user-defined error message used when the value is too short.

numerical : CNumberValidator

Validates that the attribute value is a number.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. integerOnly, whether the attribute value can only be an integer. (false)
  3. max, upper limit of the number. (null, meaning no limit)
  4. min, lower limit of the number. (null, meaning no limit)
  5. tooBig, user-defined error message used when the value is too big.
  6. tooSmall, user-defined error message used when the value is too small.

Example:

array('quantity','numerical',
    'integerOnly'=>true,
    'min'=>1,
    'max'=>250,
    'tooSmall'=>'You must order at least 1 piece',
    'tooBig'=>'You cannot order more than 250 pieces at once'),

match : CRegularExpressionValidator

Validates that the attribute value matches to the specified regular expression.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. not, whether to invert the validation logic. Since Yii 1.1.5. (false)
  3. pattern, the regular expression to be matched with.

required : CRequiredValidator

Validates that the specified attribute does not have null or empty value.

  1. requiredValue, the desired value that the attribute must have. (null)
  2. strict, whether the comparison to “requiredValue” is strict. (false)

safe : CSafeValidator

Marks the associated attributes to be safe for massive assignments.

type : CTypeValidator

Verifies if the attribute is of the type specified by type. (integer, float, string, date, time, datetime). Since 1.1.7 you should use CDateValidator to validate dates.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. dateFormat, the format pattern that the date value should follow. (‘MM/dd/yyyy’)
  3. datetimeFormat, the format pattern that the datetime value should follow. (‘MM/dd/yyyy hh:mm’)
  4. timeFormat, the format pattern that the time value should follow. (‘hh:mm’)
  5. type, the data type that the attribute should be. (‘string’)

Example of time rule;

array('org_starttime, org_finishtime', 'type', 'type'=>'time', 'timeFormat'=>'hh:mm'),

unique : CUniqueValidator

Validates that the attribute value is unique in the corresponding database table.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. attributeName, the ActiveRecord class attribute name that should be used to look for the attribute value being validated. (null, meaning same attribute name)
  3. caseSensitive, whether the comparison is case sensitive. (true)
  4. className, the ActiveRecord class name that should be used to look for the attribute value being validated. (null, meaning same class name)
  5. criteria, additional query criteria. (array())
  6. skipOnError, whether this validation rule should be skipped if when there is already a validation error for the current attribute. (true)

By default, CUniqueValidator works with a single attribute that’s presumed to be unique across the whole model table, but it can work with multi-attribute unique constraints as well. See CUniqueValidator::c2215 for an example.

unsafe : CUnsafeValidator

Marks the associated attributes to be unsafe so that they cannot be massively assigned.

url : CUrlValidator

Validates that the attribute value is a valid “http” or “https” URL.

  1. allowEmpty, whether the attribute value can be null or empty. (true)
  2. defaultScheme, the default URI scheme. It will be prepended to the input, if the input doesn’t contain one. Since Yii 1.1.7. (null, meaning the url must contain a scheme part)
  3. pattern, the regular expression used to validates the attribute value.
  4. validSchemes, the allowed URI scheme. (array(‘http’, ‘https’))

Selected readings

Built in validators in Yii

The validators are all the classes that inherit from CValidator, so the API documentation contains an always up-to-date list in the subclasses description of CValidator.

Yii CGridView CButtonColumn – Ajax – 3/3

Create custom button button with AJAX function in CGridView

http://www.yiiframework.com/wiki/410/create-custom-button-button-with-ajax-function-in-cgridview/

Introduction

CButtonColumn in CGridView can be customised to include user built buttons. To learn a great deal about including custom buttons, read this excellent wiki. This tutorial will show how to call an action from one custom button using AJAX instead of regular GET calls.
How to

Create a custom button in your grid. You can follow this tutorial for that.
In the ‘url’ parameter of your button, you call your controller action:

'url'=>'Yii::app()->controller->createUrl("myAction",array("id"=>$data->primaryKey))'

In the ‘click’ function of your button you add the following code:

'click'=>"function(){
    $.fn.yiiGridView.update('my-grid', {  //change my-grid to your grid's name
        type:'POST',
        url:$(this).attr('href'),
        success:function(data) {
              $.fn.yiiGridView.update('my-grid'); //change my-grid to your grid's name
        }
    })
    return false;
  }
",

This will call the controller function using AJAX instead of redirecting the user to the url using GET parameters.
Example

We will create a button that sends email to the user and then prints a success flash screen.

In your controller:

public function actionEmail($id)
{
  $model = $this->loadModel($id);
  $email = $model->email; //this is the user email
  if (mail($email, 'My Subject', 'my message'))
    echo 'email sent to '.$email;
  // if AJAX request, we should not redirect the browser
  if(!isset($_GET['ajax']))
     $this->redirect(Yii::app()->request->urlReferrer);
}

In your view file, in a CButtonColumn of the grid:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{email}{view}{update}{delete}',
    'buttons'=>array
    (
        'email' => array
        (
            'label'=>'Send an e-mail to this user',
            'imageUrl'=>Yii::app()->request->baseUrl.'/images/email.png',
            'click'=>"function(){
                                    $.fn.yiiGridView.update('user-grid', {
                                        type:'POST',
                                        url:$(this).attr('href'),
                                        success:function(data) {
                                              $('#AjFlash').html(data).fadeIn().animate({opacity: 1.0}, 3000).fadeOut('slow');

                                              $.fn.yiiGridView.update('user-grid');
                                        }
                                    })
                                    return false;
                              }
                     ",
            'url'=>'Yii::app()->controller->createUrl("email",array("id"=>$data->primaryKey))',
        ),
    )

In your view file you also need to include the div for the flash message:

</pre>
<div class="flash-success" id="AjFlash" style="display: none;"></div>
<pre>

Yii CGridView CButtonColumn – 2/3

How to toggle Label and imageUrl in CButtonColumn according to different data?

eg. There is status of “lock” in CGridView, if the status is locked, the image in button should be set as unlock. if the data status is unlocked, the image must be set to lock image.

Step 1: Create a sub-class of CButtonColumn

Yii::import('zii.widgets.grid.CButtonColumn');

class ExprButtonColumn extends CButtonColumn
{
    protected function renderButton($id, $button, $row, $data)
    {
        if (isset($button['label']) && strpos($button['label'], '$') > -1)
        {
            $button['label']= $this->evaluateExpression($button['label'],array('data'=>$data,'row'=>$row));
        }
        if (isset($button['imageUrl']) && strpos($button['imageUrl'], '$') > -1)
        {
            $button['imageUrl']= $this->evaluateExpression($button['imageUrl'],array('data'=>$data,'row'=>$row));
        }
        parent::renderButton($id, $button, $row, $data);
    }
}

Step 2: use the new sub-class to configure in template

array(
    'class'=>'ExprButtonColumn',
    'template'=>'{view}{lock}',
    'buttons'=>array(
        'view'=>array(
            'label'=>'view project',
            'url'=>'Yii::app()->controller->createUrl("project/view",array("qid"=>$data->id))',
        ),
        'lock'=>array(
            'label'=>'$data->lock_flag == "1" ? "Unlock The Project" : "Lock The Project"',
            'imageUrl'=>'Yii::app()->request->baseUrl."/images/".($data->lock_flag == "1" ? "unlock.gif" : "lock.gif")',
            'url'=>'Yii::app()->controller->createUrl("project/lock",array("qid"=>$data->id))',
        ),
    ),
),

Yii CGridView CButtonColumn – 1/3

Using CButtonColumn to customize buttons in CGridView

http://www.yiiframework.com/wiki/106/using-cbuttoncolumn-to-customize-buttons-in-cgridview/

Introduction

CGridView is a one of most flexible widgets in Yii and example its flexibility is CButtonColumn used to build buttons for steering model in each grid row. Here in this how-to we will explain ways user can customize CButtonColumn to flexibly fit it to its needs.

Basic customization

In default look CButtonColumn contains three buttons in this order: {view}, {update} and {delete}. Their meaning and behaviour should be obvious.

The easiest way to customize look and behaviour of them is to use series of CButtonColumn properties, like: updateButtonImageUrl (path to image for update button), updateButtonLabel (label for the update button; not HTML-encoded), updateButtonOptions (HTML options for this button, used in the way as many htmlOptions property for many widgets) and updateButtonUrl (a PHP expresion that is evaluated for button and whose result is used as the URL). Respective properties you’ll find for other default buttons.

A few remarks:

  1. For delete button only, there is deleteConfirmation property (string), which can be used for customizing message being displayed as confirmation of delete operation.
  2. In PHP expression used for xxxButtonUrl property, you can use variable $row for the row number (zero-based); $data for the data model for the row and $this for the column object.
  3. If you provide an empty string for xxxButtonImageUrl or set it to false, a textual link will be used instead.

Info: Delete confirmation message (and other textual elements of CButtonColumn or CGridView) can be also customized by creating zii.php file in protected/messages/languageID/ (or even better – copying one from for example yii/messages/de/ and translating or customizing it to own needs). Don’t forget to set ‘coreMessages’=>array(‘basePath’=>’protected/messages’) in components part of your config.php file for the application to force Yii to look in your own messages folder instead of build-in one inside framework directory.

More flexible customizing

If using above properties for customizing many aspects of many buttons is a bit messy in code or if there is need for more complex customization or introduction of new buttons there is a better, more flexible way – by using template and buttons properties.

You can use template property to change order of build-in buttons or remove some of them like this:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{delete}{update}',
)</pre>

In CGridView’s buttons column build upon above example there will be no view button and delete and update buttons will be in other than default order (delete first).

You can use the same property to introduce new buttons:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{up}{down}{delete}',
)</pre>

For new buttons (and of course – for existing, build-in ones too!) you have to specify look and behaviour. buttons property of CButtonColumn is used for it. This property is an array of buttons id (which names must correspond to the one provided in template property) and each button is another array holding its specific properties.

Here you can use:

'buttonID' =&gt; array
(
    'label'=>'...',     //Text label of the button.
    'url'=>'...',       //A PHP expression for generating the URL of the button.
    'imageUrl'=>'...',  //Image URL of the button.
    'options'=>array(), //HTML options for the button tag.
    'click'=>'...',     //A JS function to be invoked when the button is clicked.
    'visible'=>'...',   //A PHP expression for determining whether the button is visible.
)

Please, note: Text in label property is displayed only, if you have a textual link! If you are using images (build-in or own) instead of text links, text hold in this property will be rendered as image’s alt parameter. If you want to change text of tooltip, which is displayed when user hovers your image button, you have to edit options property instead and give the text to its title parameter, like this:

'buttonID' => array
(
    'label'=>'Text shown as alt text to image or as label to text link...',
    'options'=>array('title'=>'Text shown as tooltip when user hovers image...'),
)

There are similar remarks for above mentioned properties like the one described in first part of this text:

  1. In PHP expression used for url or visible properties, you can use variable $row for the row number (zero-based) and $data for the data model for the row.
  2. If you provide an empty string for imageUrl or set it to false, a textual link will be used instead.

Finally here is an example of introducing new buttons to CButtonColumn:

array
(
    'class'=>'CButtonColumn',
    'template'=>'{email}{down}{delete}',
    'buttons'=>array
    (
        'email' => array
        (
            'label'=>'Send an e-mail to this user',
            'imageUrl'=>Yii::app()->request->baseUrl.'/images/email.png',
            'url'=>'Yii::app()->createUrl("users/email", array("id"=>$data->id))',
        ),
        'down' => array
        (
            'label'=>'[-]',
            'url'=>'"#"',
            'visible'=>'$data->score > 0',
            'click'=>'function(){alert("Going down!");}',
        ),
    ),
),

Please note, that since jQuery is used here, any function passed to click should be surrounded by proper jQuery function call. That is why, we are using ‘click’=>’function(){alert(“Going down!”);}’ instead of simple ‘click’=>’alert(“Going down!”);’.

Above example also shows (email button) how to create a valid URL containing controller and view plus current user ID (or any other data from model for current row). It also explains how to use baseUrl function from CHttpRequest class to set an image for button to be a file stored outside protected folder.

Specific delete confirmation

You may notice that a standard set of views generated for CRUD operations by Gii includes (in view and update views) delete menu item with confirmation. This confirmation text can be easily changed/extended to include some record (model) specific data, like record ID.

This is not so simple in CGridView (CButtonColumn), as deleteConfirmation property is not parsed. However, there is a tricky way to achieve this (thanks to mdomba for providing it!) using jQuery. Here is an example:

array
(
        'class'=>'CButtonColumn',
        'deleteConfirmation'=>"js:'Record with ID '+$(this).parent().parent().children(':first-child').text()+' will be deleted! Continue?'",
),

We can also use jQuery’s ntn-child function for retrieve contents of other column:

array
(
        'class'=>'CButtonColumn',
        'deleteConfirmation'=>"js:'Do you really want to delete record with ID '+$(this).parent().parent().children(':nth-child(2)').text()+'?'",
),

The jQuery function looks really tricky freeky at first sight. If you wish to know, why it has to be in that form, please read this forum post.

Final words

I hope this short how-to will help in better understanding of how flexible buttons in CGridView can be customized. This is especially important as there are many forum posts asking questions about this. Please, feel free to make any updates or corrections to this article, if you find something is missing or that there are mistakes in it.

Have a nice day and happy Yii-ing! :)

yii cgridview filter – customized cols – best practice

Using standard filters in CGridView custom fields

http://www.yiiframework.com/wiki/117/using-standard-filters-in-cgridview-custom-fields/

In this article I’ll try to explain how to use standard quick search fields in CGridView with customized columns.
For example:
We have a record in a database with field switch having 0 or 1 values. After that we want a user to see on or off instead 1 or 0.
So we do the usual thing:

<?php $this->widget('zii.widgets.grid.CGridView', array(
//.....
array(
    'name'=>'switch',
    'header'=>'Switch',
    'type'=>'raw',
    'value'=>'Mii::getSwitch($data->switch)'
),
//.....
));

Mii::getSwitch – is my own helper witch returns on or off according to the current switch value (in the original it returns an image).
After that the user can see on and off values in the column, but he will not be able to filter columns typing on or off in the quicksearch field.
So we do the following thing in model search() function:

public function search()
{
    // Warning: Please modify the following code to remove attributes that
    // should not be searched.
    $criteria=new CDbCriteria;
    //....other fields
    $criteria->compare('switch',$this->getSwitch($this->switch));
    return new CActiveDataProvider(get_class($this), array('criteria'=>$criteria,));
}

//function that returns usual 0 or 1 value dependently user input
public function getSwitch($switch)
{
    if(is_null($switch)) return $switch; //null shows all records
    if(is_numeric($switch)) return $switch; //here we save an ability to search with `0` or `1` value
    if($switch == 'on') {
        return 1; //all fields with `switch` = 1
    } elseif($switch == 'off') {
        return 0; //all fields with `switch` = 0
    } else {
        return null; //all fields
    }
}

In this simple way you can teach standard filter fields to search in customized columns.

Another example:
The column shows news author name according to their id. In this situation we must return reverse value. The column returns it in the following way: id->author and customized filter must return author->id. Lets write simple function:

public function getAuthor($author)
{
     if(empty($author)) return null; //return al if author is empty
     if(is_numeric($author)) return $author; //return fields if id is entered
     $criteria=new CDbCriteria;
     $criteria->select='author_id'; //select id field
     $criteria->compare('author_login',$author,true); //seach author name. partialMatch is on
     $am=authorsModel::model()->find($criteria);
     if(empty($am)) return 0; //if author not found - returns 0
     return $am->attributes['adm_id']; //returns author id
}

That is all!

Note: This was written simple examples. Situation, when there are several authors which logins starts from equal letters must return an array with their IDs.

Example with several authors starting from equal letters:

public function getAuthor($author)
     {
         if(empty($author)) return null;
         if(is_numeric($author)) return $author;
         $criteria=new CDbCriteria;
         $criteria->select='author_id';
         $criteria->distinct = true;
         $criteria->compare('author_login',$author,true);
         $am=authorsModel::model()->findAll($criteria);
         if(empty($am)) return 0;
         if(count($am) == 1) return $am[0]->attributes['author_id'];
         $ids = array();
         foreach($am as $a_id)
         {
             $ids[] = $a_id->attributes['author_id'];
         }
         return $ids;
     }