<<TableOfContents(2)>>
## page was renamed from Yii
= 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

{{{#!highlight sh
 cd ~/Downloads
 wget https://github.com/yiisoft/yii/releases/download/1.1.15/yii-1.1.15.022a51.tar.gz
 cp yii-1.1.15.022a51.tar.gz /vars/www/htdocs
 cd /vars/www/htdocs
 tar xvzf yii-1.1.15.022a51.tar.gz
 cd yii-1.1.15.022a51
 chown apache * -R
 chmod 755 /etc/rc.d/rc.httpd
 vim /etc/httpd/httpd.conf
}}}
{{{
<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
}}}
 * vim /etc/httpd/vhosts.conf
{{{
<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>
}}}
 * vim /etc/hosts
{{{
127.0.0.1 localhostyii 
}}} 
 * vim /etc/httpd/php.ini
{{{
date.timezone="Europe/Lisbon"
}}}
 * vim /var/www/htdocs/yii-1.1.15.022a51/demos/blog/.htaccess 
{{{
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
}}}
 * vim blog/protected/config/main.php 
{{{
                'urlManager'=>array(
                        'showScriptName'=>false,
}}}
 * /etc/rc.d/rc.httpd start
 * Open URL http://localhostyii/demos/blog/index.php
 * http://localhostyii/demos/blog/post/index

== 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 ||

{{{#!highlight php
 public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('rememberMe', 'boolean'),
            array('password', 'authenticate'),
            array('namex', 'length','min'=>14), // min of 14 chars length
        );
    }
}}}

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

Override the relations() method of CActiveRecord.

Types relationship:
 * self::BELONGS_TO 
 * self::HAS_MANY 
 * self::HAS_ONE 
 * self::MANY_MANY 

{{{#!highlight php
public function relations()
    {
        return array(
            'posts'=>array(self::HAS_MANY, 'Post', 'author_id'),
            'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
        );
    }
}}}

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

Steps:
 * Get source code from https://github.com/Haensel/ActiveResource/
 * Add the extension to Yii by placing it in your application's extension folder (for example '/protected/extensions')
 * Edit your applications main.php config file and add 'application.extensions.EActiveResource.*' to your import definitions
 * Add the configuration for your resources to the main config
{{{
'activeresource'=>array(
    'class'=>'EActiveResourceConnection',
    'site'=>'http://api.aRESTservice.com',
    'contentType'=>'application/json',
    'acceptType'=>'application/json',
    'queryCacheId'=>'SomeCacheComponent'
)
}}}
 * Create a class extending EActiveResource like this (don't forget the model() method!):

ActiveResource implementation:
{{{#!highlight php
class Person extends EActiveResource
     {
     /* The id that uniquely identifies a person. This attribute is not defined
      * as a property      
      * because we don't want to send it back to the service like a name, surname or    
      * gender etc.
      */
     public $id;
 
     public static function model($className=__CLASS__)
     {
         return parent::model($className);
     }
 
     /* Define the resource property of this class */
     public function rest()
     {
         return CMap::mergeArray(
         parent::rest(),
         array(
        'resource'=>'people',
             )
    );
     }
 
     /* Let's define some properties and their datatypes*/
     public function properties()
     {
         return array(
             'name'=>array('type'=>'string'),
             'surname'=>array('type'=>'string'),
             'gender'=>array('type'=>'string'),
             'age'=>array('type'=>'integer'),
             'married'=>array('type'=>'boolean'),
             'salary'=>array('type'=>'double'),
         );
     }
 
     /* Define rules as usual */
     public function rules()
     {
         return array(
             array('name,surname,gender,age,married,salary','safe'),
             array('age','numerical','integerOnly'=>true),
             array('married','boolean'),
             array('salary','numerical')
         );
     }
 
     /* Add some custom labels for forms etc. */
     public function attributeLabels()
     {
         return array(
             'name'=>'First name',
             'surname'=>'Last name',
             'salary'=>'Your monthly salary',
         );
     }
 }
}}}

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:
 * public function init()
 * public function setAttribute($name,$value)
 * public function getAttributesToSend($attributes=null)
 * protected function beforeSave()
 * public function sendRequest($uri,$method,$params,$data)


== Log ==
 * Yii::log($message, $level, $category);
 * Yii::log(string $msg, CLogger::LEVEL_INFO , 'application')
 * Yii::log(string $msg, CLogger::LEVEL_ERROR , 'application')
 * Yii::log(string $msg, CLogger::LEVEL_ERROR , 'application')

Levels:
 * trace: this is the level used by Yii::trace. It is for tracing the execution flow of the application during development.
 * info: this is for logging general information.
 * profile: this is for performance profile which is to be described shortly.
 * warning: this is for warning messages.
 * error: this is for fatal error messages.


'''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/
{{{#!highlight php
  public function actionRxvalue()
        {
          $m = new Tag();
          $m->name=$_GET['value'];
          $m->save();
          header('Content-type: application/json');
          echo( json_encode( array('value'=>$_GET['value']) )  );
        }

        public function actionDelvalue()
        {
          $m = Tag::model()->find('id=:id' , array(':id' => $_GET['value'] )  );
          $m->delete();
          header('Content-type: application/json');
          echo( json_encode( array('value'=>$_GET['value']) )  );
        }
}}}

Javascript AJAX calls
{{{#!highlight javascript
$(document).ready(function(){
  $('#addTag').click(function(){ 
       console.log('Clicked');  
       $.ajax({
         url: '/demos/blog/site/rxvalue',
         data: {'value':$('#tag').val() },
         success: rxSuccess   
       });
    } );
  $('.delTag').click( delTag  );
} );

function delTag(){
  $.ajax({
         url: '/demos/blog/site/delvalue',
         data: {'value':  $(this).attr('id')   },
         success: delSuccess   
       });
  return false;
}

function rxSuccess(data){
  $('#listx').append('<li>' + data.value + '</li>');    
  console.log(data);
}

function delSuccess(data){
  console.log('Delete !' + data.value  ); 
  $('li#' +  data.value  ).remove();
}
}}}

== 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
{{{#!highlight php
echo( $this->createAbsoluteUrl($this->getRoute() ) );
}}}

== Register Javascript file ==
{{{#!highlight php
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
{{{#!highlight php
<?php
// change the following paths if necessary
$yii=dirname(__FILE__).'/../../framework/yii.php';
$config=dirname(__FILE__).'/protected/config/main.php';
// remove the following line when in production mode
defined('YII_DEBUG') or define('YII_DEBUG',true);
// if debug true calls exception.php
// if debug false calls error.php
// locations /themes/xxx/views/system/exception.php
// locations /themes/xxx/views/system/error.php
// may also be in site/error.php

require_once($yii);

register_shutdown_function('shutdown');
function shutdown()
{
    $error = error_get_last();
    if(is_null($error)==FALSE && $error['type']==E_ERROR){
        Yii::app()->getRequest()->redirect( Yii::app()->baseUrl . '/site/fatalerror' );
    }
}


/*set_error_handler('error_handler');
function error_handler($errno,  $errstr){
    echo($errstr);
}

set_exception_handler('exception_handler');
function exception_handler($ex){
    echo($ex->getMessage() );
}*/


Yii::createWebApplication($config)->run();
}}}

protected/controllers/SiteController.php:
{{{#!highlight php
	public function actionErrorTest()
	{
	    $val = 10 / 0;
	}
	
	public function actionExceptionTest1()
	{
	    raise(Exception('Ups'));
	}
	
	public function actionExceptionTest2()
	{
	    throw new Exception('Ups');
	}

	public function actionFatalerror()
	{
	    $this->render('fatalerror' );
	}
}}}

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 ==
{{{#!highlight sh
 cd /tmp
 wget  http://yii-sitemapgenerator.googlecode.com/files/sitemapgenerator-v0.81a.zip  #BSD license
 unzip sitemapgenerator-v0.81a.zip 
 cd sitemapgenerator
 mkdir -p /var/www/htdocs/yii-1.1.15.022a51/demos/blog/protected/extensions/sitemapgenerator
 cp * -r /var/www/htdocs/yii-1.1.15.022a51/demos/blog/protected/extensions/sitemapgenerator
 nano /var/www/htdocs/yii-1.1.15.022a51/demos/blog/protected/config/main.php
}}}
{{{
// 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:
 * http://www.yiiframework.com/extension/sitemapgenerator/
 * http://code.google.com/p/yii-sitemapgenerator/wiki/Installation

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 ===
{{{#!highlight sh
 diff -u originalFile newFile > patchFile # patch generation
 patch destinationFile patchFile # apply patch 
}}}

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:
 * Download/clone JsTrans in extension folder (folder should be 'JsTrans')
 * Import the extension in main config: ('ext.JsTrans.*')
 * cp JsTrans.zip /tmp
 * cd /tmp
 * unzip JsTrans.zip
 * mkdir -p appx/protected/extensions/JsTrans
 * cd /tmp/JsTrans
 * cp * -r /home/vborrego/workspace/m2madmin/protected/extensions/JsTrans


protected/config/main.php:
{{{
'components'=>array(
    ...
    'jsTrans'=>array( 
        'class'=>'ext.JsTrans.JsTrans', ),

}}}

== Sitemap UL and LI ==
Item.php in controllers:
{{{#!highlight php
<?php
class Item{    
    private $name;
    private $childs;
    
    function __construct($name){
        $this->name = $name;
        $this->childs = [];
    }
    
    function getName(){
        return $this->name;
    }
      
    function addChild($child){
        $this->childs[]= $child;
    }
    
    function getChilds(){
        return $this->childs;
    }
    
    function toString(){
        $ret =  '<li>' . $this->name . '<ul>'; 
        
        foreach($this->childs as $child){
            $ret = $ret . $child->toString();
        }
        
        $ret = $ret . '</ul></li>';
        return $ret;
    }
    
	public static function attachChild($node , $newNode, $parentNodeName){
	    if( $node->getName() == $parentNodeName ){
	        $hasNode=false;
	        
	        foreach($node->getChilds() as $child){
	            if( $child->getName()==$newNode->getName() ){
	                $hasNode=true;
	            }
	        }
	        
	        if($hasNode==false) $node->addChild($newNode);
	    }
	    else{
	        foreach($node->getChilds() as $child){
	            self::attachChild($child , $newNode, $parentNodeName);
	        }
	    }	
	}    
}
?>
}}}

Controller action:
{{{#!highlight php
<?php
	public function actionSitemap(){
	    // http://localhostyii/demos/blog/sitemap
	    Yii::import('application.controllers.Item');
	    $base = Yii::app()->request->hostInfo . Yii::app()->request->baseUrl;
	    $doc = DOMDOcument::loadXML( file_get_contents( $base . '/sitemap.xml' ) );
	    $elements = $doc->getElementsByTagName('loc');
	    $relUrls=[];
	    
	    foreach($elements as $item){
	        $relUrls[] = str_replace($base,'', $item->nodeValue ) ; 
	    }	    
	    
	    $root = new Item($base);
	    $nodes[]=$root;
	    
	    foreach($relUrls as $r){	        
	        $splitted = split('/',$r);
	        $level=1;
	        $previousName=$base;
	        
	        foreach($splitted as $s){
	            if( strlen($s) > 0){	                
	                Item::attachChild($root, new Item($s) ,$previousName);	                
	                $previousName=$s;
	                $level++;
	            }
	        }
	    }
	    
	    echo( $root->toString() );	    
	}	
?>
}}}


== Set base URLs for Yii ==
{{{#!highlight html
<script type="" src="<?php echo Yii::app()->theme->baseUrl; ?>/js/sample.js"></script>
<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):
 'filterSelector'=>'{filter}, #myfilter',