Yii

Yii is a high-performance PHP framework best for developing Web 2.0 applications.

http://www.yiiframework.com/

Slackware installation

http://www.yiiframework.com/doc/guide/1.1/en/topics.url#hiding-x-23x

<IfModule dir_module>
    DirectoryIndex index.html index.php
</IfModule>
LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so
Include /etc/httpd/mod_php.conf  # enable the php module

<VirtualHost *:80>
    ServerName localhostyii
    DocumentRoot "/var/www/htdocs/yii-1.1.15.022a51"
    <Directory "/var/www/htdocs/yii-1.1.15.022a51">
      Require all granted
      AllowOverride all
    </Directory>
</VirtualHost>

127.0.0.1 localhostyii 

date.timezone="Europe/Lisbon"

Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
RewriteBase /demos/blog
# 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

                'urlManager'=>array(
                        'showScriptName'=>false,

Validation rules ActiveRecord CModel

http://www.yiiframework.com/doc/guide/1.1/en/form.model#declaring-validation-rules

We specify the validation rules in the rules() method which should return an array of rule configurations.

Alias

Class

boolean

CBooleanValidator

captcha

CCaptchaValidator

compare

CCompareValidator

email

CEmailValidator

date

CDateValidator

default

CDefaultValueValidator

exist

CExistValidator

file

CFileValidator

filter

CFilterValidator

in

CRangeValidator

length

CStringValidator

match

CRegularExpressionValidator

numerical

CNumberValidator

required

CRequiredValidator

type

CTypeValidator

unique

CUniqueValidator

url

CUrlValidator

   1  public function rules()
   2     {
   3         return array(
   4             array('username, password', 'required'),
   5             array('rememberMe', 'boolean'),
   6             array('password', 'authenticate'),
   7             array('namex', 'length','min'=>14), // min of 14 chars length
   8         );
   9     }

Relations ActiveRecord

http://www.yiiframework.com/doc/guide/1.1/en/database.arr#declaring-relationship

Override the relations() method of CActiveRecord.

Types relationship:

   1 public function relations()
   2     {
   3         return array(
   4             'posts'=>array(self::HAS_MANY, 'Post', 'author_id'),
   5             'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
   6         );
   7     }

ActiveResource extension

Adapted from http://www.yiiframework.com/extension/activeresource

Steps:

'activeresource'=>array(
    'class'=>'EActiveResourceConnection',
    'site'=>'http://api.aRESTservice.com',
    'contentType'=>'application/json',
    'acceptType'=>'application/json',
    'queryCacheId'=>'SomeCacheComponent'
)

ActiveResource implementation:

   1 class Person extends EActiveResource
   2      {
   3      /* The id that uniquely identifies a person. This attribute is not defined
   4       * as a property      
   5       * because we don't want to send it back to the service like a name, surname or    
   6       * gender etc.
   7       */
   8      public $id;
   9  
  10      public static function model($className=__CLASS__)
  11      {
  12          return parent::model($className);
  13      }
  14  
  15      /* Define the resource property of this class */
  16      public function rest()
  17      {
  18          return CMap::mergeArray(
  19          parent::rest(),
  20          array(
  21         'resource'=>'people',
  22              )
  23     );
  24      }
  25  
  26      /* Let's define some properties and their datatypes*/
  27      public function properties()
  28      {
  29          return array(
  30              'name'=>array('type'=>'string'),
  31              'surname'=>array('type'=>'string'),
  32              'gender'=>array('type'=>'string'),
  33              'age'=>array('type'=>'integer'),
  34              'married'=>array('type'=>'boolean'),
  35              'salary'=>array('type'=>'double'),
  36          );
  37      }
  38  
  39      /* Define rules as usual */
  40      public function rules()
  41      {
  42          return array(
  43              array('name,surname,gender,age,married,salary','safe'),
  44              array('age','numerical','integerOnly'=>true),
  45              array('married','boolean'),
  46              array('salary','numerical')
  47          );
  48      }
  49  
  50      /* Add some custom labels for forms etc. */
  51      public function attributeLabels()
  52      {
  53          return array(
  54              'name'=>'First name',
  55              'surname'=>'Last name',
  56              'salary'=>'Your monthly salary',
  57          );
  58      }
  59  }

Sample usage:

/* GET to http://api.example.com/person/1 and populates a single Person model*/
    $person=Person::model()->findById(1);
 
    /* GET to http://api.example.com/person and populating Person models */
    $persons=Person::model()->findAll();
 
    /* create a resource*/
    $person=new Person;
    $person->name='A name';
    $person->age=21;
    $person->save(); POST request. Returns false if the model doesn't validate
 
    /* Updating a resource (sending a PUT request)
    $person=Person::model()->findById(1);
    $person->name='Another name';
    $person->save(); //PUT request. Returns false if the model doesn't validate
 
    //or short version
    Person::model()->updateById(1,array('name'=>'Another name'));
 
    /* DELETE a resource */
    $person=Person::model()->findById(1);
    $person->destroy(); //DELETE to http://api.example.com/person/1
 
    //or short version
    Person::model()->deleteById(1);
 
    //setting attributes
    $person->attributes=$_POST['Person'];
    if($person->save())
        echo 'yipiie'; //model validated and was saved/updated

Other methods for EActiveResource:

Log

Levels:

Yii::log('logged message'); , uses info level and saves the data in protected/runtime/application.log . The logging levels for CLogRouter + CFileLogRoute in protected/config/main.php .

Trace

Yii:trace(string $msg, string $category='application')

Sample controller actions Blog demo

Path: http://localhostyii/demos/blog/site/

   1   public function actionRxvalue()
   2         {
   3           $m = new Tag();
   4           $m->name=$_GET['value'];
   5           $m->save();
   6           header('Content-type: application/json');
   7           echo( json_encode( array('value'=>$_GET['value']) )  );
   8         }
   9 
  10         public function actionDelvalue()
  11         {
  12           $m = Tag::model()->find('id=:id' , array(':id' => $_GET['value'] )  );
  13           $m->delete();
  14           header('Content-type: application/json');
  15           echo( json_encode( array('value'=>$_GET['value']) )  );
  16         }

Javascript AJAX calls

   1 $(document).ready(function(){
   2   $('#addTag').click(function(){ 
   3        console.log('Clicked');  
   4        $.ajax({
   5          url: '/demos/blog/site/rxvalue',
   6          data: {'value':$('#tag').val() },
   7          success: rxSuccess   
   8        });
   9     } );
  10   $('.delTag').click( delTag  );
  11 } );
  12 
  13 function delTag(){
  14   $.ajax({
  15          url: '/demos/blog/site/delvalue',
  16          data: {'value':  $(this).attr('id')   },
  17          success: delSuccess   
  18        });
  19   return false;
  20 }
  21 
  22 function rxSuccess(data){
  23   $('#listx').append('<li>' + data.value + '</li>');    
  24   console.log(data);
  25 }
  26 
  27 function delSuccess(data){
  28   console.log('Delete !' + data.value  ); 
  29   $('li#' +  data.value  ).remove();
  30 }

CGridView + CArrayDataProvider with pagination, sort and filtering

views/site/test.php:

<h1>Test 1234</h1>

<?php
 $this->widget('zii.widgets.grid.CGridView', 
     array( 'dataProvider'=>$dp, 
            'enableSorting'=>true,
            'columns'=>array('id','name' ) ,
            'filter'=>Tag::model() ,   
 )) ;
?>

models/Tag.php:

        public function search(){
           if( isset( $_GET[get_class($this)] )  ){
               Yii::app()->session['filterId']= trim($_GET[get_class($this)]['id']);
               Yii::app()->session['filterName']= trim($_GET[get_class($this)]['name']);
           }

           $posts = $this->findAll();
           // filtering
           $filtered = array();
           $filterId= Yii::app()->session['filterId'];
           $filterName= Yii::app()->session['filterName'];

           foreach($posts as $p){

             if(  strlen($filterId)   >0 ){
                 if(strpos($p->id ,  $filterId  ) !==false) $filtered[]=$p;
             }

             if(  strlen(  $filterName )  >0 ){
                 if(strpos($p->name,  $filterName  ) !==false) $filtered[]=$p;
             }

           }

           if( strlen(Yii::app()->session['filterId'])==0 && strlen(Yii::app()->session['filterName'])==0 ) {
              $filtered=$posts;
           }

           $dataProvider=new CArrayDataProvider(  $filtered, 
                       array(  'pagination'=>array( 'pageSize'=>4, ) , 
                               'id'=>'id', 
                               'sort'=>array('attributes'=>array('id','name') , 'multiSort'=>false ) 
                       ) );

           return $dataProvider; 
        }

controllers/SiteController.php:

        public function actionTest()
        {
                syslog(LOG_INFO,'actionTest called'); 
                $dp = Tag::model()->search();
                $this->render('test', array(  'dp'=>$dp   ) );
        }

$this in a view

In a view, $this refers to the controller that called render to that view.

Get absolute URL inside a view

   1 echo( $this->createAbsoluteUrl($this->getRoute() ) );

Register Javascript file

   1 Yii::app()->getClientScript()->registerScriptFile( Yii::app()->baseUrl . '/js/test.js' );

Save state in components

http://www.yiiframework.com/doc/api/1.1/CHtml#statefulForm-detail

Generates a stateful form tag. A stateful form tag is similar to form except that it renders an additional hidden field for storing persistent page states. You should use this method to generate a form tag if you want to access persistent page states when the form is submitted.

Widget

Widget definition in components/MyWidget.php

<?php
class MyWidget extends CWidget
{
    public $titley;

    public function init()
    {
        // this method is called by CController::beginWidget()
    }
 
    public function run()
    {
        // this method is called by CController::endWidget()
        $this->render('mywidget' , array( 'titlexxx'=>$this->titley  )  );
    }
}
?>

View in components/views/mywidget.php

<h1> My widget  <?php echo( $titlexxx  ); ?>   !!!!  </h1>

Invocation in other view

<?php
  $this->widget('MyWidget', array('titley'=>' extra text') );
?>

Error handling

Fatal error handling

index.php

   1 <?php
   2 // change the following paths if necessary
   3 $yii=dirname(__FILE__).'/../../framework/yii.php';
   4 $config=dirname(__FILE__).'/protected/config/main.php';
   5 // remove the following line when in production mode
   6 defined('YII_DEBUG') or define('YII_DEBUG',true);
   7 // if debug true calls exception.php
   8 // if debug false calls error.php
   9 // locations /themes/xxx/views/system/exception.php
  10 // locations /themes/xxx/views/system/error.php
  11 // may also be in site/error.php
  12 
  13 require_once($yii);
  14 
  15 register_shutdown_function('shutdown');
  16 function shutdown()
  17 {
  18     $error = error_get_last();
  19     if(is_null($error)==FALSE && $error['type']==E_ERROR){
  20         Yii::app()->getRequest()->redirect( Yii::app()->baseUrl . '/site/fatalerror' );
  21     }
  22 }
  23 
  24 
  25 /*set_error_handler('error_handler');
  26 function error_handler($errno,  $errstr){
  27     echo($errstr);
  28 }
  29 
  30 set_exception_handler('exception_handler');
  31 function exception_handler($ex){
  32     echo($ex->getMessage() );
  33 }*/
  34 
  35 
  36 Yii::createWebApplication($config)->run();

protected/controllers/SiteController.php:

   1         public function actionErrorTest()
   2         {
   3             $val = 10 / 0;
   4         }
   5         
   6         public function actionExceptionTest1()
   7         {
   8             raise(Exception('Ups'));
   9         }
  10         
  11         public function actionExceptionTest2()
  12         {
  13             throw new Exception('Ups');
  14         }
  15 
  16         public function actionFatalerror()
  17         {
  18             $this->render('fatalerror' );
  19         }

views/site/fatalerror.php

<h1>Fatal error ocurred</h1>

protected/views/system/exception.php:

<h2>Exception <?php echo $data['message']; ?></h2>

<div class="error">
<?php 
echo CHtml::encode($message);
echo( CHtml::encode('In debug mode: ' . YII_DEBUG) );
?> 
</div>

If it desired to use the original exception handler in /framework/views/system/exception.php rename or delete this file.

Sitemap generator

// after return array ...
'controllerMap'=>array(
                'sitemap'=>array(
                        'class'=>'ext.sitemapgenerator.SGController',
                        'config'=>array(
                                'sitemap.xml'=>array(
                                        'aliases'=>array(
                                                'application.controllers',
                                        ),
                                ),
                        ),
                ),
        ),
// inside components, urlmanager, rules
                                /* sitemap */
                                array(
                                        'class'=>'ext.sitemapgenerator.SGUrlRule',
                                        'route'=>'/sitemap', // Optional. Default: '/sitemap'
                                        ),                              

Info:

Remove any files inside the folder with the controllers, ending with ~.

Access http://localhostyii/demos/blog/sitemap.xml or http://localhostyii/demos/blog/sitemap .

Patch for sitemap extension

Patch for file SitemapGenerator.php:

--- /tmp/sitemapgenerator/SitemapGenerator.php  2011-10-25 18:31:57.000000000 +0100
+++ SitemapGenerator.php        2014-08-19 22:33:39.292021021 +0100
@@ -435,12 +435,15 @@
                $raw=array_filter($raw);
                $data=array();
                foreach ($raw as $param) {
-                       list($key,$val)=explode('=',$param,2);
+                   if(is_null($param)===false && strlen(trim($param))>0){
+                       list($key,$val)=explode('=',$param,2);
+                       
+                       if (empty($val))
+                           throw new Exception(Yii::t('sitemapgenerator.msg',"Option '{key}' cannot be empty.",array('{key}'=>$key)));
+                       
+                       $data[$key]=$val;
+                       }
 
-                       if (empty($val))
-                               throw new Exception(Yii::t('sitemapgenerator.msg',"Option '{key}' cannot be empty.",array('{key}'=>$key)));
-
-                       $data[$key]=$val;
                }
                return $data;
        }

Issue submitted in http://code.google.com/p/yii-sitemapgenerator/issues/detail?id=4 .

Yii translations in Javascript

http://www.yiiframework.com/extension/jstrans/

http://www.yiiframework.com/extension/jstrans/files/JsTrans.zip

Installation:

protected/config/main.php:

'components'=>array(
    ...
    'jsTrans'=>array( 
        'class'=>'ext.JsTrans.JsTrans', ),

Sitemap UL and LI

Item.php in controllers:

   1 <?php
   2 class Item{    
   3     private $name;
   4     private $childs;
   5     
   6     function __construct($name){
   7         $this->name = $name;
   8         $this->childs = [];
   9     }
  10     
  11     function getName(){
  12         return $this->name;
  13     }
  14       
  15     function addChild($child){
  16         $this->childs[]= $child;
  17     }
  18     
  19     function getChilds(){
  20         return $this->childs;
  21     }
  22     
  23     function toString(){
  24         $ret =  '<li>' . $this->name . '<ul>'; 
  25         
  26         foreach($this->childs as $child){
  27             $ret = $ret . $child->toString();
  28         }
  29         
  30         $ret = $ret . '</ul></li>';
  31         return $ret;
  32     }
  33     
  34         public static function attachChild($node , $newNode, $parentNodeName){
  35             if( $node->getName() == $parentNodeName ){
  36                 $hasNode=false;
  37                 
  38                 foreach($node->getChilds() as $child){
  39                     if( $child->getName()==$newNode->getName() ){
  40                         $hasNode=true;
  41                     }
  42                 }
  43                 
  44                 if($hasNode==false) $node->addChild($newNode);
  45             }
  46             else{
  47                 foreach($node->getChilds() as $child){
  48                     self::attachChild($child , $newNode, $parentNodeName);
  49                 }
  50             }   
  51         }    
  52 }
  53 ?>

Controller action:

   1 <?php
   2         public function actionSitemap(){
   3             // http://localhostyii/demos/blog/sitemap
   4             Yii::import('application.controllers.Item');
   5             $base = Yii::app()->request->hostInfo . Yii::app()->request->baseUrl;
   6             $doc = DOMDOcument::loadXML( file_get_contents( $base . '/sitemap.xml' ) );
   7             $elements = $doc->getElementsByTagName('loc');
   8             $relUrls=[];
   9             
  10             foreach($elements as $item){
  11                 $relUrls[] = str_replace($base,'', $item->nodeValue ) ; 
  12             }       
  13             
  14             $root = new Item($base);
  15             $nodes[]=$root;
  16             
  17             foreach($relUrls as $r){            
  18                 $splitted = split('/',$r);
  19                 $level=1;
  20                 $previousName=$base;
  21                 
  22                 foreach($splitted as $s){
  23                     if( strlen($s) > 0){                        
  24                         Item::attachChild($root, new Item($s) ,$previousName);                  
  25                         $previousName=$s;
  26                         $level++;
  27                     }
  28                 }
  29             }
  30             
  31             echo( $root->toString() );      
  32         }       
  33 ?>

Set base URLs for Yii

   1 <script type="" src="<?php echo Yii::app()->theme->baseUrl; ?>/js/sample.js"></script>
   2 <script type="text/javascript">setBaseUrl('<?php echo Yii::app()->baseUrl; ?>');

Updates Yii CGridView

http://jsasitorn.com/2013/02/dynamic-update-yii-cgridview/

var datax = 
    {
      'url':'/basex/controller/action?filtersajax=gridx&page=1&paramx=2' ,
      'type': "POST",
       'data': {update:"now"}
    }
  $.fn.yiiGridView.update('gridx' , datax);

Request to action controller

// JavaScript
function getStuff(){
    var data={'strx':'Hello stuff'}; 
    $.ajax({
        // type (default: 'GET') 
        type: 'POST',
        cache: false,
        // contentType (default: 'application/x-www-form-urlencoded; charset=UTF-8')        
        data: datax,
        dataType : 'json', /*The type of data that you're expecting back from the server*/
        url: 'http://localhost/controllerx/actionx',
        success: getStuffHandler 
    }); 
}

function getStuffHandler(response){
    console.log(response);
}

// PHP actionx inside controllerx
public function actionActionx(){
  $data = array('strx'=>$_REQUEST['strx'] ) ;
  $data['replyx']='Pong';                       
  header('Content-Type: application/json');
  echo( json_encode( $reply ) );
}

Extra filter CGridView

http://www.yiiframework.com/doc/api/1.1/CGridView#filterSelector-detail

the jQuery selector of filter input fields. The token '{filter}' is recognized and it will be replaced with the grid filters selector. Defaults to '{filter}'.

Note: if this value is empty an exception will be thrown.

Example (adding a custom selector to the default one):

PHP/Yii (last edited 2015-03-12 21:46:18 by 54)