## 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
* 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
{{{
DirectoryIndex index.html index.php
LoadModule rewrite_module lib/httpd/modules/mod_rewrite.so
Include /etc/httpd/mod_php.conf # enable the php module
}}}
* vim /etc/httpd/vhosts.conf
{{{
ServerName localhostyii
DocumentRoot "/var/www/htdocs/yii-1.1.15.022a51"
Require all granted
AllowOverride all
}}}
* 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('
' + data.value + '');
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:
{{{
Test 1234
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
{{{
render('mywidget' , array( 'titlexxx'=>$this->titley ) );
}
}
?>
}}}
View in components/views/mywidget.php
{{{
My widget !!!!
}}}
Invocation in other view
{{{
widget('MyWidget', array('titley'=>' extra text') );
?>
}}}
== Error handling ==
=== Fatal error handling ===
index.php
{{{#!highlight php
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
{{{
Fatal error ocurred
}}}
'''protected/views/system/exception.php''':
{{{
Exception
}}}
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:
* 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 ===
* 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
name = $name;
$this->childs = [];
}
function getName(){
return $this->name;
}
function addChild($child){
$this->childs[]= $child;
}
function getChilds(){
return $this->childs;
}
function toString(){
$ret = '' . $this->name . '';
foreach($this->childs as $child){
$ret = $ret . $child->toString();
}
$ret = $ret . '
';
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
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