Yii
Yii is a high-performance PHP framework best for developing Web 2.0 applications.
Slackware installation
http://www.yiiframework.com/doc/guide/1.1/en/topics.url#hiding-x-23x
- 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
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 |
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 |
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
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:
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:
- 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/
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
- 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:
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
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:
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
Updates Yii CGridView
http://jsasitorn.com/2013/02/dynamic-update-yii-cgridview/
var datax = { 'url':'/basex/controller/action?filtersajax=gridx&page=1¶mx=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',