Yii 1.1 Development Cookbook - Vietnamese
Transcript of Yii 1.1 Development Cookbook - Vietnamese
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Yii 1.1 Application Development Cookbook
Public: packpub
Year : 2011
http://www.seodrupal.vn
Translate by : longt8x email : [email protected]
Everything you want supported please send request to email or yahoo : [email protected], thank you!
Bạn vào trang chủ : http://www.yiiframework.com/download/
Download phiên bản mới nhất về sau đó giải nén (extract here), sau đó rename lại là cookbook, copy folder cookbook mới giải nén vào C:\xampp\htdocs\, bạn phải down xampp về rồi cài đặt setup thành công thì chạy xampp center thiết lập apache và mysql running.
http://localhost/cookbook/requirements/index.php
Phần mềm chạy local: Xampp, Wamp, ko nên dùng appserv vì bị lỗi( nguyên nhân mình ko biết, tốt nhất nên tránh).
Hoặc xài luôn host thật nếu nhà có điều kiện.
1. Chuẩn bịTrước tiên bạn phải cấu hình windows để sử dụng được php với cmd
-Giả sử bạn cài apache mặc định trên WinXP (C:\xampp\htdocs).
-Bạn thiết lập lại biến môi trường (Environment Variables) bằng cách vào: Start -> My Computer (right click!) -> Advanced Tab -> Environment Variables -> Click Path in System variables (windows 7 là Path) -> Edit.
-Click vào biến PATH và chọn Edit. Lưu ý là đừng có xoá bỏ các đường dẫn đã tồn tại trong textbox mà ngăn cách chúng với nhau bằng dấu ";".
-Tiếp đó bạn thêm vào những đường dẫn sau: "C:\xampp\php" và " C:\xampp\htdocs\
cookbook\framework". Lưu ý sửa đường dẫn cho phù hợp với máy bạn nha .-Khởi động máy tính lại.
2.Tạo ứng dụng Yii mới-YiiRoot là thư mục nơi bạn đã cài đặt Yii
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
-Webroot là thư mục góc chứa web-Từ dòng lệnh, đến webroot của bạn và thực hiện: Vào start -> run->cmd
C:\
Cd\
Cd C:\xampp\htdocs\
C:\xampp\htdocs\>cookbook/framework/yiic webapp skybook
Nếu không được thì gõ tiếp cd cookbook
Gõ tiếp cd framework -> gõ yiic webapp skybook.
[skybook]-> Tên ứng dụng Create a Web application under '/Webroot/skybook'? [Yes|No] Yes[y]
Vậy là khung xương của Yii đã được tạo ra thành công
Trích dẫn:skybook/
index.php file ứng dụng
index-test.php file kiểm tra chức năng
assets/ chứa tài nguyên ứng dụng
css/ chứa CSS
images/ chứa hình ảnh
themes/ chứa giao diện
protected/ chứa các file được bảo vệ
Vào cookbook/framework/ chuyển folder mới tạo là skybook ra ngoài folder website C:/xampp/htdocs. Đường dẫn mới của app là C:/xampp/htdocs/skybook.
copy folder framework của cookbook sang skybook.
Vào file index.php thay đổi đường dẫn thành : $yii=dirname(__FILE__).'/framework/yii.php';
Bạn có thể truy cập vào ứng dụng từ trình duyệt
http://localhost/skybook/index.php
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Ok, hoàn tất cấu hình để tiến hành coder.
CHƯƠNG 1 - Giới thiệu về Yii framework
1 – Sử dụng getter and setter ( Hàm nhận và hàm thiết lập).
PHP cũng giống như các công nghệ khác như Java, C# , đều có cấu trúc lập trình hướng đối tượng và xây dựng công nghệ theo các class(lớp), yii framework được xây dựng trên nền php cũng không ngoại lệ, tuy nhiên điều khác biệt là nó kế thừa từ Ccomponent (class ảo cho bất kỳ class nào trong Yii).
Cách thực hiện:
Chúng ta theo dõi định nghĩa lớp trong php với hàm thiết lập (setter) và hàm nhận lại (getter)
class MyClass{private $property;
// ham nhan lay giá trịpublic function getProperty(){return $this->property;}// ham khởi tạo giá trịpublic function setProperty($value){$this->property = $value;}}// tạo mới đối tượng$object = new MyClass();// thiết lập đối tượng$object->setProperty('value');// trả về giá trịecho $object->getProperty();
Trong Yii chúng ta thực hiện nhờ kế thừa từ Ccomponent như sau :
// extending CComponent is necessaryclass MyClass extends CComponent{
private $property;public function getProperty(){return $this->property;}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public function setProperty($value){$this->property = $value;}}$object = new MyClass();$object->property = 'value'; // cũng như php : $object->setProperty('value');echo $object->property; // cũng như $object->getProperty();Ngoài ra Yii còn hỗ trợ các hàm sau thực hiện việc thiết lập và nhận giá trị : __get, __set, __isset, và __unset
Cách dùng như sau : vdpublic function __get($name){$getter='get'.$name;if(method_exists($this,$getter))return $this->$getter();
}
2 – Sử dụng Yii Event ( Các sự kiện trong Yii)
Hầu hết các class đều được kế thừa từ Yii Component (Ccomponent) vì thế nên chúng ta có thể sử lý tốt các sự kiện từ ứng dụng. Một sự kiện tương ứng là một lời nhắn, một thông điệp tới ứng dụng để yêu cầu thực hiện công việc nào đó, chúng ta có thể đăng ký nhiều sự kiện xử lí để thực hiện tùy theo loại sự kiện, một hàm xử lý có thể nhận được các tham số từ các sự kiện với các tham số được định nghĩa, Sử dụng các sự kiện cho phép đạt được sự linh hoạt trong ứng dụng.
Cách thực hiện thế nào ?Để định nghĩa một sự kiện trong lớp con của Ccomponent, bạn sẽ thêm phương thức (method) với tên bắt đầu bằng từ “on”, ví dụ nếu bạn thêm onRegister method , bạn sẽ nhận được tương ứng một sự kiện bạn đã khai báo.
Các loại sự kiện được sử dụng giống như :- Định nghĩa một sự kiện được thêm tuơng ứng một phương thức,- Kích hoạt một hay nhiều sự kiện xử lý.- Thành phần nâng cao (raise) của một sự kiện được sử dụng bởi
Ccomponent ::raiseEvent method - Tất cả các thể hiện xử lý được gọi tự động.Hãy nhìn vào cách chúng ta có thể kích hoạt một sự kiện xử lý từ một sự kiện.Chúng ta sử dụng Ccomponent::attachEventHandler method.Nó chấp nhận các tham số sau đây:$name : Tên sự kiện.
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$handler: Xử lý sự kiện,yêu cầu của bạn trước mỗi sự kiện thường là một hàm tiêu chuẩn gọi lại (standard function callback) php để sử dụng.Bạn có thể thực hiện hàm gọi lại như sau : Sử dụng hàm cục bộ (global function) và chỉ nhận tên giống như các chuỗi ví dụ “ my_function”.Sử dụng các phương thức tĩnh từ lớp (static class method).Bạn sẽ chỉ trả về giá trị array(‘Tên lớp (Name class)’,’Tên phương thức tĩnh(static method Name)’).Sử dụng object method : array($object,’object method’)Tạo và nhận hàm định danh sử dụng create_function như sau:Trước tiên định nghĩa componentpublic function init() { parent::init(); $component = Yii::app()->{$this->component}; }$component->attachEventHandler(‘onClick’,create_function(‘$event’,’echo “click!”));
Với phiên bản PHP 5.3 trở lên bạn chỉ cần viết:$component->attachEventHandler(‘onclick’,function($event){echo ‘click’;});
Để code ngắn hơn bạn chỉ cần viết như sau : $component->onClick=$handler; hoặc$component->onClick->add($handler);
Để quản lý việc xử lý sự kiện bạn sử dụng hàm get handler list (Clist) trong Ccomponent::getEventHandlers và làm việc với nó.Ví dụ : $component->getEventHandlers(‘onClick’)->add($handler);
Để thêm một xử lý vào đầu danh sách các xử lý ta viết:$component->getEventHandlers(‘onClick’)->insertAt(0,$handler);
Để xóa một xử lý bạn sử dụng Ccomponent::detachEventHandler:$component->detachEventHandler(‘onClick’,$handler);
Yii application có 2 sự kiện được sử lý trong các trường hợp sau ;Capplication::onBeginRequest và Capplication::onEndRequest hãy sử dụng chúng .Đặt cấu hình thiết lập như sau vào file index.php trước khi chạy ứng dụng:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
require_once($yii);$app = Yii::createWebApplication($config);// Kích hoạt sử lý trước khi bắt đầu ứng dụngYii::app()->onBeginRequest = function($event){// starting output buffering with gzip handlerreturn ob_start("ob_gzhandler");};// Kích hoạt sử lý sau khi kết thúc ứng dụngYii::app()->onEndRequest = function($event){// releasing output bufferreturn ob_end_flush();};$app->run();
Comment là một tiêu chuẩn của AR model generate với Gii, Post là một phương thức ngoại lệ trong Gii-generated model. Chúng ta sẽ custom sự kiện NewCommentEvent để thực hiện cả post và comment model và sử lý class Notifier làm việc:
Bắt đầu vào protected/components/ tạo NewCommentEvent.php
class NewCommentEvent extends CModelEvent {
public $comment;
public $post;
}
Rất đơn giản chỉ bao gồm 2 thuộc tính.
Bây giờ bạn di chuyển tới protected/models/Post.php. Tất cả tiêu chuẩn AR method được xây dựng như sau:
class Post extends CActiveRecord {
function addComment(Comment $comment){
$comment->post_id = $this->id;
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
// tạo mới sự kiện từ class trước
$event = new NewCommentEvent($this);
$event->post = $this;
$event->comment = $comment;
// kích hoạt sự kiện mới
$this->onNewComment($event);
return $event->isValid;
}
// định nghĩa sự kiện onNewComment
public function onNewComment($event) {
$this->raiseEvent('onNewComment', $event);
}
}
Tiếp theo tạo trong protected/components/ file Notifier.php
class Notifier {
function comment($event){
$text = "Có một comment mới từ {$event->comment->author} trong post {$event->post->title}";
mail('[email protected]', 'New comment', $text);
}
}
Tiếp theo vào trong protected/controllers/ tạo file PostController.php
Class PostController extends Ccontroller
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{ // tạo action thêm comment
function actionAddComment()
{
$post = Post::model()->findByPk(10);
$notifier = new Notifier();
// kích hoạt sử lý sự kiện
$post->onNewComment = array($notifier, 'comment');
// dữ liệu thật được dưới dạng request $_POST
$comment = new Comment();
$comment->author = 'Sam Dark';
$comment->text = 'Yii events are amazing!';
// thêm comment
$post->addComment($comment);
}
}
Như vậy là bạn đã biết cách kích hoạt và sử lý một sự kiện , tuy nhiên nhiều trường hợp không nhất thiết phải kích hoạt sử lý sự kiện mà chúng ta cũng có thể kích hoạt sử lý sự kiện từ các component đã tồn tại và chúng ta chỉ cần overriding các class cơ bản đó . Ví dụ chúng ta có form model UserForm sử dụng để thu thập thông tin của người dùng và chúng ta cần hiển thị đầy đủ họ tên của user.
Thật may, Cmodel class cơ bản trong Yii model có thể mở rộng form models.
Cmodel::afterValidate được gọi sau khi form được submit thành công.
Ta vào protected/models/ tạo file UserForm.php
class UserForm extends CFormModel
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
public $firstName; //tên
public $lastName; //họ
public $fullName; //tên đầy đủ
public function rules()
{
return array(
//tập hợp quy tắc : firstname,lastname được yêu cầu (k rỗng)
array('firstName, lastName', 'required'),
);
}
// $event ở đây được thiết lập từ CEvent
// tạo ra và thiết lập một sự kiện( event) khi method được gọi .
// CModel::afterValidate().
function afterValidate()
{
//Nếu phương thức được gọi thì sự kiện sẽ tự động nạp dữ liệu sau:
$this->fullName = $this->firstName.' '.$this->lastName;
// điều quan trọng là nó phải được gọi lại từ lớp cha (parent class)
// đó là điều giúp các sử lý sự kiện khác được gọi lại.
return parent::afterValidate();
}
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Chúng ta cần gọi lại từ lớp cha (parent class) của afterValidate() vì hàm định nghĩa onAfterValidate() thực chất là raiseEvent:
Protected function afterValidate()
{
$this->onAfterValidate(new CEvent($this));
}
2. Sử dụng import và autoloadingKhi lập trình với php hầu hết các class và hàm tạo đều được load bởi hai phương thức là : “include” và “require”. Tuy nhiên bạn cũng có thể sử dụng loader nhờ SPL class, yii sử dụng công nghệ này vào Yii Base và việc xây dựng các lớp tiêu chuẩn trong yii CdbCriteria. Mặc định autoloader (YiiBase::autoload) sẽ được sử dụng.Hầu hết tất cả các lớp được load khi cần including hoặc importing . Yii hoàn tất bởi YiiBase::$_coreClasses map, vì thế mà việc load trong Yii rất nhanh.Zii class cũng như Cmenu, extension class hoặc class của bạn định nghĩa cũng được tự động load.
3. Cấu hình Component(Thành phần)Yii là một framework custom, hầu như mọi thứ đều có thể custom một cách dễ dàng đó là ưu điểm của yii, trước tiên chúng ta tìm hiểu file config của Yii trong protected/con fig /main.phpĐể có thể sử dụng database kết nối mysql ta chỉ cần bỏ 2 dòng /* ---- */ để array dưới đây được kích hoạt.return array(…'components'=>array('db'=>array('class'=>'system.db.CDbConnection','connectionString'=>'mysql:host=localhost;dbname=database_name','username'=>'root','password'=>'','charset'=>'utf8',),…),…);
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Khi đã có thể kết nối tới database rồi bạn chỉ cần khai báo Yii::app()->db; là ta đã có thể làm việc trực tiếp với CSDL.
Một số component trong Yii bạn nên biết :
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
4: Làm việc với Request
Với yii bạn có thể trả về dữ liệu dưới dạng request mà php đưa ra như $_SERVER,$_GET,$_POST, nhưng tốt hơn bạn nên làm theo việc sử dụng của Yii là :ChttpRequest class , lớp này giải quyết những ngoại lệ từ server, quản lý cookies,cung cấp thêm vấn đề bảo mật,và đặc biệt là hướng đối tượng .
Bạn có thể truy nhập request component trong yii bằng việc sử dụng : Yii::app()->getRequest(). Vì thế nên xem một số hàm hữu ích , các method trả về khác biệt từ URL :
Để chắc chắn các request được truyền đúng ta có thêm các hàm kiểm tra : IsPostRequest (Kiểm tra hàm post), getIsAjaxRequest (kiểm tra ajax loader), getRequestType (kiểm tra request thuộc loại get hay post hay ajax).
Ví dụ chúng ta muốn kiểm tra xem aJax đã được load hay chưa ta xem ví dụ sau :
class TestController extends CController
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public function actionIndex()
{
if(Yii::app()->request->isAjaxRequest)s
$this->renderPartial('test');
else
$this->render('test');
}
}
Ngoài ra bạn cũng có thể dùng các hàm thiết lập thông dụng của PHP để kiểm tra như isset, ispost ….
class TestController extends CController
{
public function actionIndex()
{
$request = Yii::app()->request;
$param = $request->getParam('id', 1);
// equals to
$param = isset($_REQUEST['id']) ? $_REQUEST['id'] : 1;
$param = $request->getQuery('id');
// equals to
$param = isset($_GET['id']) ? $_GET['id'] : null;
$param = $request->getPost('id', 1);
// equals to
$param = isset($_POST['id']) ? $_POST['id'] : 1;
}
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Điều cuối cùng chúng tôi muốn bạn biết thêm trong chương 1 này chính là hàm getCookies, nó trả về từ CcookieCollection class cho phép chúng ta làm việc với cookie, Vì CcookieCollection được kế thừa từ Cmap nên chúng ta có thể sử dụng method sau:
class TestController extends CController
{
public function actionIndex()
{
$request = Yii::app()->request;
// nhận giá trị của cookie
$cookie = $request->cookies['test'];
if($cookie)
// in giá trị cookie
echo $cookie->value;
else {
// tạo mới cookie
$cookie=new CHttpCookie('test','I am a cookie!');
$request->cookies['test'] = $cookie;
}
}
Nếu bạn làm việc với nhiều giá trị của cookie , bạn muốn code của mình ngắn gọn hãy sử dụng class sau:
class Cookie
{
public static function get($name)
{
$cookie=Yii::app()->request->cookies[$name];
if(!$cookie)
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
return null;
return $cookie->value;
}
public static function set($name, $value, $expiration=0)
{
$cookie=new CHttpCookie($name,$value);
$cookie->expire = $expiration;
Yii::app()->request->cookies[$name]=$cookie;
}
}
Bạn thay đổi code trong TestController như sau:
class TestController extends CController
{
public function actionIndex()
{
$cookie = Cookie::get('test');
if($cookie)
echo $cookie;
else
Cookie::set('test','I am a cookie!!');
}
}
Lưu ý, chương một với các bạn mới học sẽ không thể làm thử được vì không có database để thử, xin vui lòng down source code bản English để test app của họ.
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
CHƯƠNG 2 : ROUTER (Định tuyến), Controller (Điều khiển), and views (khung nhìn)
Trong chương này bạn sẽ được học:
Cấu hình quy tắc url,
Generating Url by path
Sử dụng biểu thức quy tắc (regular expression) trong URL rules
Tạo url rules cho các trang tĩnh.
Cung cấp url rules ở mỗi thời điểm.
Sử dụng controller cơ bản
Sử dụng action cơ bản
Hiển thị trang động với CViewAction
Sử dụng flash messages ( Cờ thông điệp)
Sử dụng controller context trong view
Sử dụng clips
Sử dụng decorator
Định nghĩa nhiều layout (giao diện)
Paginating and sorting data (Phân trang và sắp xếp dữ liệu)
1. Cấu hình quy tắc URL
Trước tiên bạn vào protected/config/main.php
Tìm dòng sau:
// application components
'components'=>array(
…
// uncomment the following to enable URLs in path-formathttp://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
/*
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\
d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
),
),
Bỏ hai dấu /* và */ sau đó xóa mọi thứ trong rules để ta bắt đầu thực hành. 'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
),
),
'db'=>array('connectionString'=>'sqlite:'.dirname(__FILE__).'/../data/testdrive.db',),
Vào protected/controllers tạo websiteController.php với dòng code sau:
class WebsiteController extends CController
{
public function actionIndex()
{
echo "index";
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public function actionPage($alias)
{
echo "Page is $alias.";
} }
Thay đổi file .htaccess trong protected như sau :
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
# 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
Thêm đoạn sau vào main.php như sau:
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'home' => 'website/index',
'<alias:about>' => 'website/page',
'page/<alias>' => 'website/page',
),
),
Lần lượt gõ vào trình duyệt các đường dẫn sau:
http://localhost/skybook/index.php/page/test
http://localhost/skybook/index.php/home
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
http://localhost/skybook/index.php/about
http://localhost/skybook/index.php/page/about
Bạn sẽ thấy biến alias trong action page sẽ thay đổi tùy theo đường dẫn thứ 2 sau page bạn điền vào, đây chính là url định danh (url alias).
Thực tế điều gì đã xảy ra:
Với quy tắc : ‘home’=>’website/index’ , trong Yii mỗi controller và action đều có một quy tắc chung mặc định là moduleId (tên module)/controllerID(tên controller)/actionID(tên action). Ví dụ trong trường hợp home đó là module : default, controller website, và action là index.
‘page/<alias>’=>’website/page’,
Ở đây , chúng ta định nghĩa một alias parameter ( tham số định danh) đặc biệt trong Url sau /page/. Nó có thể nhận về các tham số ảo từ tham số $alias trong action Page của website controller.
Ở đây bạn định nghĩa trả về tham số cho nó bằng việc thiết lập quy tắc:
‘<alias:about>’=>’website/page’, đồng nghĩa với việc tham số $alias sẽ chỉ nhận được tham số ảo là about.
2. Generating Url By Path
Khi chúng ta cần generate Url của index và page action trong website controller.Quyết định nơi chúng ta cần nó,có những cách làm khác nhau nhưng cơ bản hãy nhìn một số phương thức generate url sau:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
CHtml::link(), và một số phương thức CHtml như form,refresh và ajaxLink tất cả chấp nhận url và thuộc tính sử dụng trong views. Định dạng chung cho việc trả về dữ liệu như sau:
Url string: Trả về đường dẫn url dạng kí tự.
Array(quy tắc cục bộ,tham số=>giá trị, tham số => giá trị…). Một số trường hợp url sẽ được generated.
Định dạng của quy tắc vẫn theo thứ tự : modulesID/controllerID/actionId.
Tham số là biến $_GET sẽ nhận một action với định tuyến đặc biệt, cho ví dụ nếu chúng ta muốn tạo một url từ websitecontroller ::actionIndex và nhận tham số $_GET[‘name’] ta chỉ cần làm như sau:
echo CHtml::link('Click me!', array('website/index',
'name' => 'Đệt cụ bọn tàu khựa đã làm ra Yii (Quiang Xue)'));
URl rất hữu ích khi sử dụng controller . Trong controller bạn có thể sử dụng createUrl để tạo mới url và createAbsoluteUrl để hiển thị thong tin về url:
class WebsiteController extends CController
{
public function actionTest()
{
echo $this->createUrl('website/page', 'alias' => 'about');
echo $this->createAbsoluteUrl('website/page',
'alias' => 'test');
}
// the rest of the methods
}
Khi bạn không muốn hiện khởi tạo của controller , khi bạn thực thi ứng dụng bạn có thể sử dụng method sau:
echo Yii::app()->createUrl('website/page', 'alias' => 'about');
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
echo Yii::app()->createAbsoluteUrl('website/page', 'alias' => 'test');
3.Sử dụng biểu thức quy tắc trong Url rules
Ngựa quen đường cũ và gà lại thèm rượu nên ta xóa dữ liệu trong rules array().
Tiếp theo vào protected/controllers tạo file PostController.php mới với dòng sau:
class PostController extends CController
{
public function actionView($alias)
{
echo "Showing post with alias $alias.";
}
public function actionIndex($order = 'DESC')
{
echo "Showing posts ordered $order.";
}
public function actionHello($name)
{
echo "Hello, $name!";
}
}
Cấu hình lại .htaccess trong protected như sau:
Options +FollowSymLinks
IndexIgnore */*
RewriteEngine on
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
# 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
Tiếp theo thay đổi lại trong main.php phần rules như sau:
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'post/<alias:[-a-z]+>' => 'post/view',
'(posts|archive)' => 'post/index',
'(posts|archive)/<order:(DESC|ASC)>' => 'post/index',
'sayhello/<name>' => 'post/hello',
),
),
Sau đó vào trình duyệt tự sướng:
http://localhost/skybook/index.php/post/test-post thành công
http://localhost/skybook/index.php/post/test-9 hi sinh (vì hàm ngoại lệ yêu cầu dạng kí tự k phải dạng số)
http://localhost/skybook/index.php/achive hi sinh vì index thuộc dạng số không thuộc kí tự
http://localhost/skybook/index.php/posts/ASC thành công
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
4.Tạo Url rules cho trang tĩnh
Một website chứa các thuộc tính tĩnh thường là :/about,/contact,/tos, lệnh sử lý đến trang đó chỉ đơn giản là controller action. Ta sẽ tìm một cách để tạo ra Url rules cho các thuộc tính của trang.
Lại vào main.php phần rules để hót phân chó (xóa toàn bộ mọi thứ trong array của rules).
Thay đổi lại như sau:
'<alias:about>' => 'website/page',
'<alias:contact>' => 'website/page',
'<alias:tos>' => 'website/page',
'<alias:(about|contact|tos)>' => 'website/page',
'tos' => array('website/page', 'defaultParams' => array('alias' =>
'terms_of_service')),
6. Sử dụng controller cơ bản
Đầu tiên ta vào protected/components tạo SecureController.php
<?php
class SecureController extends Controller
{
public function filters()
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
return array(
'accessControl',
);
}
public function accessRules()
{
return array(
array('allow',
'users'=>array('@'),
),
array('deny',
'users'=>array('*'),
),
);
}
}
Vào protected/config/ main.php tìm đoạn
/*
'gii'=>array(
'class'=>'system.gii.GiiModule',
'password'=>'Enter Your Password Here',
// If removed, Gii defaults to localhost only. Edit carefully to taste.
'ipFilters'=>array('127.0.0.1','::1'),
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
),
*/
Bỏ 2 dòng check và điền password cho gii (generated database)
'gii'=>array(
'class'=>'system.gii.GiiModule',
'password'=>'1111',
'ipFilters'=>array('127.0.0.1','::1'),
),
Tiếp theo vào http://localhost/skybook/index.php/gii/default/login
Điền mật khẩu là 1111
Tiếp theo chọn Controller Generator gõ SecureController
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Nhấn generate để tạo controller và action index
Địt mẹ máy như lồn, gõ nhanh quá word báo : please slow character @@
Generate xong đồng nghĩa với việc bạn đã tạo xong controller SecureController , bạn có thể vào protected/controllers để tìm thấy file này và trong views.
Thật nhanh chóng đúng không ? ta không cần phải mất công tạo tuy nhiên @@ không khuyến khích vì các dự lớn đòi hỏi khả năng custom cao vì thế nên chỉ generate models cho chóng xong, còn controller thì tốt nhất tự tạo file .
Cấu trúc của YII vẫn tuân thủ MVC, (model/views/controller) .
7. Sử dụng cơ bản Action
Vào protected/config/main.php tìm đoạn sau:
'db'=>array(
'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db',
),
// uncomment the following to use a MySQL database
'db'=>array(
'connectionString' => 'mysql:host=localhost;dbname=testdrive',
'emulatePrepare' => true,
'username' => 'root',
'password' => '',
'charset' => 'utf8',
),
Thay thế bằng skybook hoặc bạn để nguyên, vào localhost/phpmyadmin tạo table testdrive nếu bạn để nguyên, hoặc nếu thay thế = skybook thì tạo bảng skybook.
Vào phpmyadmin với table mới tạo với query:
CREATE TABLE `post` (
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
`id` int(10) unsigned NOT NULL auto_increment,
`created_on` int(11) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL auto_increment,
`username` varchar(200) NOT NULL,
`password` char(40) NOT NULL,
PRIMARY KEY (`id`)
);
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Nhấn go để query được thực hiện.
Tiếp theo vào GII generated 2 bảng mới tạo bằng cách chọn model generated
Gõ lần lượt Post và User trong table Name rồi generated
Nếu nó báo override thì vẫn generated
Để kiểm tra vào protected/models sẽ tìm thấy 2 file mới tạo.
Tiếp theo vào PostController viết lại như sau:
class PostController extends CController
{
function actionIndex()
{
$posts = Post::model()->findAll();
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$this->render('index', array(
'posts' => $posts,
));
}
function actionDelete($id)
{
$post = Post::model()->findByPk($id);
if(!$post)
throw new CHttpException(404);
if($post->delete())
$this->redirect('post/index');
throw new CHttpException(500);
}
}
Tiếp theo vào protected/components tạo file DeleteAction.php
class DeleteAction extends CAction
{
function run()
{
if(empty($_GET['id']))
throw new CHttpException(404);
$post = Post::model()->findByPk($_GET['id']);
if(!$post)
throw new CHttpException(404);
if($post->delete())
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$this->redirect('post/index');
throw new CHttpException(500);
}
}
Hãy sử dụng component mới này trong PostController ở actionDelete
class PostController extends CController
{
function actions()
{
return array(
'delete' => 'DeleteAction',
);
}
…
}
OK.chúng ta sử dụng delete action trong component cho post controller nhưng vẫn còn user controller, ta custom lại delete action:
class DeleteAction extends CAction
{
public $pk = 'id';
public $redirectTo = 'index';
public $modelClass;
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
function run()
{
if(empty($_GET[$this->pk]))
throw new CHttpException(404);
$model = CActiveRecord::model($this->modelClass)
->findByPk($_GET[$this->pk]);
if(!$model)
throw new CHttpException(404);
if($model->delete())
$this->redirect($this->redirectTo);
throw new CHttpException(500);
}
}
Bây giờ chúng ta có thể sử dụng action cho cả 2 controller .Với post controller ta làm như sau:
class PostController extends CController
{
function actions()
{
return array(
'delete' => array(
'class' => 'DeleteAction',
'modelClass' => 'Post',
);
);
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
…
}
Với user controller ta cũng khai báo action mới trong component
class UserController extends CController
{
function actions()
{
return array(
'delete' => array(
'class' => 'DeleteAction',
'modelClass' => 'User',
);
);
}
…
}
Với cách này ta có thể tiết kiệm thời gian xây dựng hàm với những hàm có cấu trúc giống nhau.
8. Sử dụng Flash Messages
Khi bạn chỉnh sửa model với form,hay xóa item trong models hoặc làm với những thao tác user, bạn luôn luôn phải đưa ra lời thong báo tới user công việc đó đã thực hiện xong chưa hay đã hoàn thành,…Flash messages sẽ giúp hiển thị những lời thong báo ví dụ :”xóa thành công, cập nhật thành công,…”
Trở lại protected/controllers/websitecontroller viết lại như sau:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
class WebsiteController extends CController
{
function actionOk()
{
Yii::app()->user->setFlash('success', 'Everything went
fine!');
$this->redirect('index');
}
function actionBad()
{
Yii::app()->user->setFlash('error', 'Everything went wrong!');
$this->redirect('index');
}
function actionIndex()
{
$this->render('index');
}
}
Vào protected/views/website tạo index.php
<?php if(Yii::app()->user->hasFlash('success')):?>
<div class="flash-notice">
<?php echo Yii::app()->user->getFlash('success')?>
</div>
<?php endif?>
<?php if(Yii::app()->user->hasFlash('error')):?>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<div class="flash-error">
<?php echo Yii::app()->user->getFlash('error')?>
</div>
<?php endif?>
Vào trình duyệt http://localhost/skybook/index.php/website/ok
http://localhost/skybook/index.php/website/bad
9. Sử dụng controller context trong views
Tạo controller sau:
class WebsiteController extends CController
{
function actionIndex()
{
$this->pageTitle = 'Controller context test';
$this->render('index');
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
function hello()
{
if(!empty($_GET['name']))
echo 'Hello, '.$_GET['name'].'!';
}
}
Tạo views :
<h1><?php echo $this->pageTitle?></h1>
<p>Hello call. <?php $this->hello()?></p>
<?php $this->widget('zii.widgets.CMenu',array(
'items'=>array(
array('label'=>'Home', 'url'=>array('index')),
array('label'=>'Yiiframework home',
'url'=>'http://yiiframework.ru/',
),
))?
10. Sử dụng lại views với partial
class WebsiteController extends CController
{
function actionIndex()
{
$this->render('index');
}http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
Tạo protected/views/common/youtube.php
<object width="480" height="385"><param name="movie"
value="http://www.youtube.com/v/S6u7ylr0zIg?fs=1 "></
param><param name="allowFullScreen" value="true"></
param><param name="allowscriptaccess" value="always"></
param><embed src="http://www.youtube.com/v/S6u7ylr0zIg?fs=1"
type="application/x-shockwave-flash" allowscriptaccess="always"
allowfullscreen="true" width="480" height="385"></embed></object>
Chỉnh sửa lại :
<object width="<?php echo!empty($width) ? $width : 480?>"
height="<?php echo!empty($height) ? $height: 385?>"><param
name="movie" value="http://www.youtube.com/v/<?php echo
$id?>?fs=1 "></param><param name="allowFullScreen" value="true"></
param><param name="allowscriptaccess" value="always"></
param><embed src="http://www.youtube.com/v/<?php echo $id?>?fs=1"
type="application/x-shockwave-flash" allowscriptaccess="always"
allowfullscreen="true" width="<?php echo !empty($width) ? $width
: 480?>" height="<?php echo !empty($height) ? $height: 385?>"></
embed></object>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Tạo protected/website/index.php
<?php $this->renderPartial('////common/youtube', array(
'id' => '8Rp-CaIKvQs', // you can get this id by simply looking
at video URL
'width' => 320,
'height' => 256,
))?>
Giờ ta sẽ thử gửi email ,them mới action trong website controller
class WebsiteController extends CController
{
function actionSendmails()
{
$users = User::model->findAll();
foreach($users as $user)
{
$this->sendEmail('welcome', $user->email, 'Welcome to the website!', array('user' => $user));
}
echo 'Emails were sent.';
}
function sendEmail($template, $to, $subject, $data)
{
mail($to, $subject, $this->renderPartial
('//email/'.$template, $data, true));
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
Tiếp theo tạo protected/views/email/welcome.php:
Hello <?php echo $user->name?>,
Welcome to the website!
You can go check our new videos section. There are funny raccoons.
Yours,
Website team.
11 . Sử dụng clip
Khi chúng ta cần định nghĩa 2 vị trí trong layout : trước content và footer, mở protected/views/layouts/main.php them đoạn sau trước output (<?php echo $content;?>)
<?php if(!empty($this->clips['beforeContent'])) echo
$this->clips['beforeContent']?>
Và them đoạn sau trước footer
<?php if(!empty($this->clips['footer']))
Echo $this->clips['footer']; ?>
Bây giờ chúng ta cần điền vào vị trí của chúng, mở controller action cho beforeContent region. Mở protected/controllers/SiteController.php và them đoạn sau vào actionIndex:
$this->beginClip('beforeContent');
echo 'Your IP is '.Yii::app()->request->userHostAddress;
$this->endClip();
Tiếp theo mở protected/views/site/index.php và them đoạn sau:
<?php $this->beginClip('footer')?>
Ứng dụng được xây dựng bởi Tàu khựa.
<?php $this->endClip()?>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Hoàn tất bạn có thể mở index bạn sẽ thấy địa chỉ IP và “ứng dụng…” ở gần footer.
12 . Định nghĩa nhiều Layout.
Hầu hết ứng dụng sử dụng một layout duy nhất cho tất cả khung nhìn,tuy nhiên nhiều dự án đòi hỏi cấu hình và thẩm mỹ khác nhau dẫn tới có nhiều layout khác nhau vì thế nên yii cho phép tạo dựng nhiều layout một cách chuyên biệt và hiệu quả.
Ta tạo 2 layout trong protected/views/layouts: blog và articles. Blog chứa đựng nội dung sau:
<?php $this->beginContent('//layouts/main')?>
<div>
<?php echo $content?>
</div>
<div class="sidebar tags">
<ul>
<li><a href="#php">PHP</a></li>
<li><a href="#yii">Yii</a></li>
</ul>
</div>
<div class="sidebar links">
<ul>
<li><a href="http://yiiframework.com/">Yiiframework</a></li>
<li><a href="http://php.net/">PHP</a></li>
</ul>
</div>
<?php $this->endContent()?>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Article chứa nội dung sau:
<?php $this->beginContent('//layouts/main')?>
<div>
<?php echo $content?>
</div>
<div class="sidebar toc">
<ul>
<li><a href="#intro">1. Introduction</a></li>
<li><a href="#quick-start">2. Quick start</a></li>
</ul>
</div>
<?php $this->endContent()?>
Tạo 3 controller là BlogController,ArticleController và PorfolioController với indexAction
class BlogController extends Controller
{
function actionIndex()
{
$this->layout = 'blog';
$this->render('//site/index');
}
}
class ArticleController extends Controller
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
function actionIndex()
{
$this->layout = 'articles';
$this->render('//site/index');
}
}
class PortfolioController extends Controller
{
function actionIndex()
{
$this->render('//site/index');
}
}
Giờ hãy thử vào http://localhost/skybook/index.php/blog, http://localhost/skybook/index.php/article and
http://localhost/skybook/index.php/portfolio.
Chúng ta vừa định nghĩa 2 layout cho blog và articles. Nếu chúng ta không muốn copy-pase một phần của layout chính,chúng ta thử them layout được định nghĩa bằng cách sử dụng $this->beginContent và this->endContent; thể hiện ở hình dưới đây:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
13. Phân trang và sắp xếp dữ liệu
Trước khi kết thúc chương 2 ta sẽ đi vào tìm hiểu ActiveRecord với cơ chế phân trang , sắp xếp dữ liệu theo tiêu chuẩn.
Vào protected/controllers/PostController.php
class PostController extends Controller
{
function actionIndex()
{
$criteria = new CDbCriteria();
$count=Post::model()->count($criteria);
$pages=new CPagination($count);
// elements per page
$pages->pageSize=5;
$pages->applyLimit($criteria);
// sorting
$sort = new CSort('Post');
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$sort->attributes = array(
'id',
'title',
);
$sort->applyOrder($criteria);
$models = Post::model()->findAll($criteria);
$this->render('index', array(
'models' => $models,
'pages' => $pages,
'sort' => $sort,
));
}
}
Tiếp theo xây dựng views: protected/views/post/index.php
<p><?php echo $sort->link('id')?></p>
<p><?php echo $sort->link('title')?></p>
<ol>
<?php foreach($models as $model):?>
<li>
<h2><?php echo $model->id?> - <?php echo $model->title?></h2>
</li>
<?php endforeach?>
</ol>
<?php $this->widget('CLinkPager', array(
'pages' => $pages,
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
))?>
Để thực hiện kiểm tra bạn vào phpmyadmin và thêm vào bảng post khoảng 20 item, sau đó vào trình duyệt xem kết quả: http://localhost/skybook/post
CHƯƠNG 3 AJAX Và jQuery
Trong chương này bạn sẽ khám phá:
Tải một block với AJAX
Quản lý asserts
Including resources với page
Làm việc với JSON
Nhận thiết lập từ PHP và Javascript
Xử lý biến số của input
1. Tải một Block với AJAX
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Tạo một controller mới trong protected/controllers tên QuoteController
: <?php
class QuoteController extends Controller
{
private $quotes = array(
array('Walking on water and developing software from a
specification are easy if both are frozen.', 'Edward V Berard'),
array('It always takes longer than you expect, even when you
take into account Hofstadter’s Law.', 'Hofstadter’s
Law'),
array('Always code as if the guy who ends up maintaining
your code will be a violent psychopath who knows where you live.',
'Rick Osborne'),
array('I have always wished for my computer to be as easy to
use as my telephone; my wish has come true because I can no longer
figure out how to use my telephone.', 'Bjarne Stroustrup'),
array('Java is to JavaScript what Car is to Carpet.', 'Chris
Heilmann'),
);
private function getRandomQuote()
{
return $this->quotes[array_rand($this->quotes, 1)];
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
function actionIndex()
{
$this->render('index', array(
'quote' => $this->getRandomQuote()
));
}
//ajax callback
function actionGetQuote()
{
$this->renderPartial('_quote', array(
'quote' => $this->getRandomQuote(),
));
}
}
Tiếp theo tạo views : protected/views/quote/index.php
<h2>Quote of the day</h2>
<div id="quote-of-the-day">
<?php $this->renderPartial('_quote', array(
'quote' => $quote,
))?>
</div>
<?php echo CHtml::ajaxLink('Next quote', array('getQuote'),
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
array('update' => '#quote-of-the-day'))?>
Tạo views thứ 2 là protected/views/quote/_quote.php
“<?php echo $quote[0]?>”, <?php echo $quote[1]?>
Vào trình duyệt gõ : http://localhost/skybook/index.php/quote/index
Để có thể xem cấu hình ajax tạo ra bạn dùng firebug trỏ vào vùng next quote sẽ hiển thị javascript tạo ra ajax từ Chtml::ajaxLink.
2. Quản lý Assert
Một trong những khả năng tuyệt vời của Yii là có thể bảo vệ tài sản (mã nguồn) một cách hiệu quả
Khi thực thi một extension được kích hoạt javascript,css,images thì folder gốc không thể truy nhập được từ trình duyệt.
Khi bạn cần truy nhập lại tài sản (css,js,image) của bạn chỉ cần combine javascript
Khi bạn sử dụng assert nhiều lần trong phân trang và tránh trùng lặp
Hãy bắt đầu kế hoạch trước, bạn có thể thay đổi widgets tới bất kì thư mục nào, nó nằm trong protected/components. Nó chấp nhận có một hoặc 2 class trong đó, nhưng khi số lượng class tăng lên, nó sẽ tạo ra vấn đề,Tạo một assert trong widget với protected/extensions/facebook_events.và đặt trong đó ajax-loader.gif bạn vui lòng download tại http://ajaxload.info . Tiếp theo tạo facebook_event.css và facebook_event.js
Tạo EFacebookEvents.php trong folder facebook_events
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php
class EFacebookEvents extends CWidget
{
public $keyword;
private $loadingImageUrl;
protected $url = "https://graph.facebook.com/search?q=%s&type=
event&callback=?";
protected function getUrl()
{
return sprintf($this->url, urlencode($this->keyword));
}
public function init()
{ // khoi tao duong dan file assets
$assetsDir = dirname(__FILE__).'/assets';
$cs = Yii::app()->getClientScript();
$cs->registerCoreScript("jquery");
// thiet lap duong dan toi file js
$cs->registerScriptFile(
Yii::app()->assetManager->publish(
$assetsDir.'/facebook_events.js'
),
CClientScript::POS_END
);
//thiet lap duong dan toi file css
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$cs->registerCssFile(
Yii::app()->assetManager->publish(
$assetsDir.'/facebook_events.css'
)
);
// thiet lap duong dan toi file images
// asset can be accessed with
$this->loadingImageUrl = Yii::app()->assetManager->publish(
$assetsDir.'/ajax-loader.gif'
);
}
public function run()
{
$this->render("body", array(
'url' => $this->getUrl(),
'loadingImageUrl' => $this->loadingImageUrl,,
'keyword' => $this->keyword,
));
}
}
Tiếp theo vào protected/extensions/facebook_events/ tạo views/body.php
<div class="facebook-events" data-url="<?php echo $url?>">
<h2><?php echo $keyword?> events</h2>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<div class="data">
<?php echo CHtml::image($loadingImageUrl)?>
</div>
</div>
Tiếp theo vào file facebook_events.js :
jQuery(function($){
$(".facebook-events").each(function(){
var url = $(this).data("url");
var container = $(".data", this);
$.getJSON(url,function(json){
var html = "<ul>";
$.each(json.data,function(){
html += "<li>"+
"<p><strong>" + this.name + "</strong>
</p><p>"+this.location
"</p></li>";
});
html += "</ul>";
container.html(html);
});
});
});
Vào file facebook_events.css:
.facebook-events {
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
padding: 10px;
width: 400px;
float: left;
}
.facebook-events ul {
padding: 0;
}
.facebook-events li {
list-style: none;
border: 1px solid #ccc;
padding: 10px;
margin: 2px;
}
Vào protected/views/site/index.php
<?php $this->widget("ext.facebook_events.EFacebookEvents", array(
'keyword' => 'php',
))?>
<?php $this->widget("ext.facebook_events.EFacebookEvents", array(
'keyword' => 'jquery',
))?>
Giờ vào trang chủ để xem kết quả:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Cách thức hoạt động:
Khi chúng ta sử dụng $this->widget trong site/index view, 2 phương thức trong EFacebookEvents đều chạy init cái mà public assert và kết nối chúng tới trang, sau đó chạy render html.
Những gì có trong thư mục :Assert
Nếu bạn kiểm tra sẽ thấy những dòng sau:
Thư mục như 1a6630a0 được sử dụng để ngăn chặn va chạm của các tập tin với những cái tên tương tự từ các thư mục khác nhau. Tên của thư mục
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
là một hàm băm mã hóa path assert directory.Trước đó assert từ mỗi thư mục được copy tới cùng một nơi,điều này nghĩa là thư mục image,css,js public của bạn có thể tham chiếu tới images,css,js từ các đường dẫn lien quan.
3.Including resource tới trang
Yii có tên class đặc biệt là CClientScript để giúp cho việc include script css.
Cách thức như :
Yii::app()->clientScript->registerScriptFile("http://example.com/js/main.js");
Đối với core jquery:
Yii::app()->clientScript->registerCoreScript('jquery');
Đối với CSS;
Yii::app()->clientScript->registerCssFile('http://example.com/css/main.css');
Yii::app()->registerCss('myCSS', 'body {margin: 0; padding: 0}','all');
Đăng ký linker resources:
Yii::app()->clientScript->registerLinkTag(
'alternate',
'application/rss+xml',
$this->createUrl('rss/articles')
);
Đăng ký metaTag:
Yii::app()->clientScript->registerMetaTag(' text/html;charset=utf-8',
null, 'Content-Type');
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
4. Làm việc với JSON
JSON rất đơn giản,dễ sử dụng, là sử dụng định dạng AJAX application data.YII cũng có làm việc với nó.
Đầu tiên ta vào phpmyadmin và tạo mới table : Tin tức:
CREATE TABLE `news` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`created_on` int(11) unsigned NOT NULL,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)
Tiếp theo bạn vào GII rồi gererated Model, bạn gõ news và nhấn generated. Sau khi thành công kiểm tra tại protected/models xem đã có file new chưa ?
Vào protected/controllers tạo NewsController.php
<?php
class NewsController extends Controller
{
//tao bo loc.
public function filters()
{
return array(
'ajaxOnly + data',
);
}
public function actionIndex()
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
$this->render('index');
}
public function actionData()
{
$criteria = new CDbCriteria();
$criteria->order = 'created_on DESC';
$criteria->limit = 10;
$news = News::model()->findAll($criteria);
echo CJSON::encode($news);
}
public function actionAddRandomNews()
{
$news = new News();
$news->title = "Item #".rand(1, 10000);
$news->created_on = time();
$news->save();
echo "OK";
}
}
Tiếp theo bạn tạo views: protected/views/news/index.php
<div class="news-list">
Loading…
</div>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php Yii::app()->clientScript->registerCoreScript("jquery")?>
<script type="text/javascript">
jQuery(function($) {
var newsList = $('.news-list');
function updateNews(){
newsList.html("Loading…");
$.ajax({
url: "<?php echo $this->createUrl('data')?>",
dataType: 'json',
cache: false,
success: function(data) {
var out = "<ol>";
$(data).each(function(){
out+="<li>"+this.title+"</li>";
});
out += "</ol>";
newsList.html(out);
}
});
}
updateNews();
setInterval(function(){
updateNews()
}, 2000);
});
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
</script>
Sau thực hiện xong vào trình duyệt gõ: http://localhost/skybook/index.php/news/index
5: Xử lí biến số của input
Đôi khi ứng dụng yêu cầu nhập form chứa biến số của input,một chức năng quản lý ứng dụng có thể cung cấp hình ảnh,nơi bạn có thể them một hoặc nhiều chức năng trong danh sách task,bạn có thể xem ví dụ dưới đây:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Bởi mặc định, trang sẽ hiển thị một vài chức năng và hai button: Add task sẽ them thuộc tính và save là lưu lại thong tin của form.
Ta sẽ tạo protected/models file Task.php:
<?php
class Task extends CFormModel
{
public $title;
public $text;
public function rules() //thiet lap quy tac
{
return array(
array('title', 'required'), // tieu de khong rong
array('text', 'safe'), //text duoc bao ve
);
}
}
Tiếp theo vào protected/controllers/TaskController.php
<?php
class TaskController extends Controller
{
public function filters()
{
return array(
'ajaxOnly + field'
);
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
public function actionIndex()
{
$models = array();
if(!empty($_POST['Task'])) //kiem tra co request post hay k
{
foreach($_POST['Task'] as $taskData)
{
$model = new Task();
$model->setAttributes($taskData);
if($model->validate())
$models[] = $model;
}
}
if(!empty($models)){
// Neu ban muon luu lai du lieu co the luu tai day
}
else
$models[] = new Task();
$this->render('index', array(
'models' => $models,
));
}
public function actionField($index)
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$model = new Task();
$this->renderPartial('_task', array(
'model' => $model,
'index' => $index,
));
}
}
Ta tạo view: protected/views/task/index.php
<div class="form">
<?php echo CHtml::beginForm()?>
<ul class="tasks">
<?php for($i=0; $i<count($models); $i++):?>
<?php $this->renderPartial('_task', array(
'model' => $models[$i],
'index' => $i,
))?>
<?php endfor ?>
</ul>
<div class="row buttons">
<?php echo CHtml::button('Add task',
array('class' => 'tasks-add'))?>
<?php Yii::app()->clientScript->registerCoreScript
("jquery")?>
<script>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$(".tasks-add").click(function(){
$.ajax({
success: function(html){
$(".tasks").append(html);
},
type: 'get',
url: '<?php echo $this->createUrl('field')?>',
data: {
index: $(".tasks li").size()
},
cache: false,
dataType: 'html'
});
});
</script>
<?php echo CHtml::submitButton('Save')?>
</div>
<?php echo CHtml::endForm()?>
</div>
Cuối cùng tạo views cho partial: protected/views/task/_task.php
<li>
<div class="row">
<?php echo CHtml::activeLabel($model, "[$index]title")?>
<?php echo CHtml::activeTextField($model, "[$index]title")?>
</div>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<div class="row">
<?php echo CHtml::activeLabel($model, "[$index]text")?>
<?php echo CHtml::activeTextArea($model, "[$index]text")?>
</div>
</li>
Vào trình duyệt gõ http://localhost/skybook/task/index để xem kết quả.
CHƯƠNG 4 LÀM VIỆC VỚI FORM
Chương này bạn sẽ khám phá:
Uploading files
Thêm Captcha
Custom Captcha
Tạo một custom widget với Cwidget
1. Uploading files
Trong folder protected bạn tạo một thư mục mới có tên: uploads
Chúng ta sẽ bắt đầu với model, vì vậy sẽ tạo protected/models/Upload.php
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php
class Upload extends CFormModel
{
public $file;
public function rules()
{
return array(//yeu cau thuoc tinh file có thể them các thuộc tính khác cách nhau dấu //phẩy
array('file', 'file', 'types'=>'zip'),
);
}
}
Trong protected/controllers tạo UploadController.php
<?php
class UploadController extends Controller
{
function actionIndex()
{
$dir = Yii::getPathOfAlias('application.uploads'); //hien thi duong dan upload
$uploaded = false;
$model=new Upload(); //goi model
if(isset($_POST['Upload']))
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$model->attributes=$_POST['Upload'];
$file=CUploadedFile::getInstance($model,'file');
if($model->validate()){ //validate form thanh cong thi luu file do vao thu muc
$uploaded = $file->saveAs($dir.'/'.$file->getName());
}
}
$this->render('index', array( //hien thi thong tin ten file, duong dan
'model' => $model,
'uploaded' => $uploaded,
'dir' => $dir,
));
}
}
Tiếp theo ta tạo views: protected/views/upload/index.php
<?php if($uploaded):?>
<p>File was uploaded. Check <?php echo $dir?>.</p>
<?php endif ?>
<?php echo CHtml::beginForm('','post',array
('enctype'=>'multipart/form-data'))?>
<?php echo CHtml::error($model, 'file')?>
<?php echo CHtml::activeFileField($model, 'file')?>
<?php echo CHtml::submitButton('Upload')?>
<?php echo CHtml::endForm()?>
Vào trình duyệt gõ: http://localhost/skybook/index.php/upload/index
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
2. Thêm Captcha
Tạo protected/models/EmailForm.php
<?phpclass EmailForm extends CFormModel
{
public $email;
function rules(){
return array(
array('email', 'email'),
);
}
}
Tạo protected/controller/EmailController.php
<?php
class EmailController extends Controller
{http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public function actionIndex()
{
$success = false;
$model = new EmailForm();
if(!empty($_POST['EmailForm']))
{
$model->setAttributes($_POST['EmailForm']);
if($model->validate())
{
$success = true;
// handle form here
}
}
$this->render('index', array(
'model' => $model,
'success' => $success,
));
}
}
Tạo views: protected/email/index.php
<?php if($success):?>
<p>Success!</p>
<?php endif?>
<?php echo CHtml::beginForm()?>
<p>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php echo CHtml::activeLabel($model, 'email')?>
<?php echo CHtml::activeTextField($model, 'email')?>
<?php echo CHtml::error($model, 'email')?>
</p>
<p>
<?php echo CHtml::submitButton()?>
</p>
<?php echo CHtml::endForm()?>
Vào http://localhost/skybook/index.php/email/index test
3.Custom Captcha
Ta có thể custom captcha một cách dễ dàng nhờ CCaptchaAction
Trong protected/components tạo MathCaptchaAction.php
<?php
class MathCaptchaAction extends CCaptchaAction
{
protected function generateVerifyCode()http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
return mt_rand((int)$this->minLength,
(int)$this->maxLength);
}
public function renderImage($code)
{
parent::renderImage($this->getText($code));
}
protected function getText($code)
{
$code = (int)$code;
$rand = mt_rand(1, $code-1);
$op = mt_rand(0, 1);
if($op)
return $code-$rand.»+».$rand;
else
return $code+$rand.»-».$rand;
}
}
Trong controller trước them action sau:
public function actions()
{
return array(
'captcha'=>array(
'class'=>'MathCaptchaAction',
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'minLength' => 1,
'maxLength' => 10,
),
);
}
Ta có kết quả:
4. Tạo một custom input widget với CInputWidget
Trong protected/components tạo RangeInputField.php
<?php
class RangeInputField extends CInputWidget
{
public $attributeFrom;
public $attributeTo;
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public $nameFrom;
public $nameTo;
public $valueFrom;
public $valueTo;
function run()
{
if($this->hasModel())
{
echo CHtml::activeTextField
($this->model, $this->attributeFrom);
echo ' → ';
echo CHtml::activeTextField
($this->model, $this->attributeTo);
}
else {
echo CHtml::textField($this->nameFrom, $this->valueFrom);
echo ' → ';
echo CHtml::textField($this->nameTo, $this->valueTo);
}
}
}
Tiếp theo vào protected/models RangeForm.php
<?php
class RangeForm extends CFormModel
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public $from;
public $to;
function rules()
{
return array(
array('from, to', 'numerical', 'integerOnly' => true),
array('from', 'compare', 'compareAttribute' => 'to',
'operator' => '<=', 'skipOnError' => true),
);
}
}
Tiếp theo vào protected/controllers tạo RangeController.php
<?php
class RangeController extends Controller
{
function actionIndex()
{
$success = false;
$model = new RangeForm();
if(!empty($_POST['RangeForm']))
{
$model->setAttributes($_POST['RangeForm']);
if($model->validate())
$success = true;
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$this->render('index', array(
'model' => $model,
'success' => $success,
));
}
}
Tiếp theo vào protected/views/ tạo range/index.php
<?php if($success):?>
<p>Success!</p>
<?php endif?>
<?php echo CHtml::errorSummary($model)?>
<?php echo CHtml::beginForm()?>
<?php $this->widget('RangeInputField', array(
'model' => $model,
'attributeFrom' => 'from',
'attributeTo' => 'to',
))?>
<?php echo CHtml::submitButton('Submit')?>
<?php echo CHtml::endForm()?>
Vào trình duyệt gõ: http://localhost/skybook/index.php/range/index
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
CHƯƠNG 5 Database ,ActiveRecord,và Model Tricks
Trong chương này bạn sẽ khám phá:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Nhận dữ liệu từ database
Sử dụng scopes để hiển thị model với các ngôn ngữ khác nhau
Truy cập models field với AR
Applying Markdown và HTML
Hightlight code với Yii
Tự động timestamp
Thiết lập author tự động
Thực thi các bảng kế thừa đơn
Sử dụng CDbCriteria
1. Nhận dữ liệu từ database
Hầu hết ứng dụng ngày nay đều sử dụng database,dù là một website nhỏ cho tới mạng xã hội , ít nhất một phần được bảo vệ bởi database,Yii giới thiệu 3 cách làm việc trên database:
Active Record
Query Builder
SQL và DAO
Để thực hiện bạn vào : http://dev.mysql.com/doc/sakila/en/sakila.html để tải gói database về sau đó vào phpmyadmin import vào csdlTiếp theo vào Gii tạo model actor và field tables
Ta vào protected /controller tạo DbController.php
<?phpclass DbController extends Controller{protected function afterAction($action){$time = sprintf('%0.5f', Yii::getLogger()
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
->getExecutionTime());$memory = round(memory_get_peak_usage()/(1024*1024),2)."MB";echo "Time: $time, memory: $memory";parent::afterAction($action);}public function actionAr(){$actors = Actor::model()->findAll(array('with' => 'films','order' => 't.first_name, t.last_name, films.title'));echo '<ol>';foreach($actors as $actor){echo '<li>';echo $actor->first_name.' '.$actor->last_name;echo '<ol>';foreach($actor->films as $film){echo '<li>';echo $film->title;echo '</li>';}echo '</ol>';echo '</li>';}echo '</ol>';}public function actionQueryBuilder(){$rows = Yii::app()->db->createCommand()->from('actor')->join('film_actor', 'actor.actor_id=film_actor.actor_id')->leftJoin('film', 'film.film_id=film_actor.film_id')->order('actor.first_name, actor.last_name, film.title')->queryAll();$this->renderRows($rows);}public function actionSql(){$sql = "SELECT *
FROM actor aJOIN film_actor fa ON fa.actor_id = a.actor_idJOIN film f ON fa.film_id = f.film_idORDER BY a.first_name, a.last_name, f.title";
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$rows = Yii::app()->db->createCommand($sql)->queryAll();$this->renderRows($rows);}public function renderRows($rows){$lastActorName = null;echo '<ol>';foreach($rows as $row){$actorName = $row['first_name'].' '.$row['last_name'];if($actorName!=$lastActorName){if($lastActorName!==null){echo '</ol>';echo '</li>';}$lastActorName = $actorName;echo '<li>';echo $actorName;echo '<ol>';}echo '<li>';echo $row['title'];echo '</li>';}echo '</ol>';}}
Sauk hi thực hiện xong bạn chạy trên trình duyệt và vào databse để kiểm tra dữ liệu mới tạo.
2. Sử dụng Scopes để hiển thị models cho những ngôn ngữ khác nhau
Đa ngôn ngữ trong website của bạn là việc điều khiển không hề dễ dàng,bạn cần phải thay đổi giao diện,thay đổi tin nhắn,định dạng ngày tháng,và rất nhiều thứ khác. Yii giúp bạn làm việc hiệu quả hơn với CLDR (Unicode Common Locate Data Repository) dữ liệu và cung cấp công cụ thay đổi ngôn ngữ. Khi tới ứng dụng với nhiều ngôn ngữ, bạn có thể sẽ tìm ra cách của riêng bạn.
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Để bắt đầu tạo mới databse: vào phpmyadmin và chọn mục SQL :
DROP TABLE IF EXISTS `post`;CREATE TABLE IF NOT EXISTS `post` (`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,`lang` VARCHAR(5) NOT NULL DEFAULT 'en',`title` VARCHAR(255) NOT NULL,`text` TEXT NOT NULL,PRIMARY KEY (`id`));INSERT INTO `post`(`id`,`lang`,`title`,`text`)VALUES (1,'en_us','Yii news','Text in English'),(2,'de','Yii Nachrichten','Text in Deutsch');
Sau khi hoàn tất vào GII chọn model generated gõ Post vào modelname và generated
Sauk hi generated bạn vào models/Post.php và viết lại method giống như sau:
class Post extends CActiveRecord
{
public function defaultScope()
{
return array(
'condition' => "lang=:lang",
'params' => array(
':lang' => Yii::app()->language,
),
);
}
public function lang($lang){
$this->getDbCriteria()->mergeWith(array(
'condition' => "lang=:lang",
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'params' => array(
':lang' => $lang,
),
));
return $this;
}
}
Tiếp theo vào protected/controllers/DbtestController.php
<?php
class DbtestController extends CController
{
public function actionIndex()
{
// Hiển thị ngôn ngữ mặc định
$posts = Post::model()->findAll();
echo '<h1>Default language</h1>';
foreach($posts as $post)
{
echo '<h2>'.$post->title.'</h2>';
echo $post->text;
}
// Hiển thị ngôn ngữ của Đức
$posts = Post::model()->lang('de')->findAll();
echo '<h1>German</h1>';
foreach($posts as $post)
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
echo '<h2>'.$post->title.'</h2>';
echo $post->text;
}
}
}
Sauk hi hoàn thành vào trình duyệt gõ : http://localhost/skybook/dbtest/index
3. Truy cập model field với Active-Record event-like method
Vào phpmyadmin chọn mục SQL và thực hiện query sau:
DROP TABLE IF EXISTS `post`;
CREATE TABLE IF NOT EXISTS `post` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`text` TEXT NOT NULL,
PRIMARY KEY (`id`)
);
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Tiếp theo vào Gii generated model Post
Sau đó bạn vào models/Post.php và thêm hàm sau:
protected function beforeSave()
{
$this->text = preg_replace('~((?:https?|ftps?)://.*?)( |$)~iu',
'<a href="\1">\1</a>\2', $this->text);
return parent::beforeSave();
}
Tiếp theo vào protected/controllers tạo TestController.php
<?php
class TestController extends CController
{
function actionIndex()
{
$post=new Post();
$post->title='links test';
$post->text='test http://www.đập chết mẹ tàu khựa.vn/ test';
$post->save();
print_r($post->text);
}
}
Vào tình duyệt gõ http://localhost/skybook/test/index để xem kết quả.
Cách làm việc như sau:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
beforeSave (trước khi lưu) được định nghĩa trong CActiveRecord class và chỉ thực hiện hàm trước khi lưu models.Bởi sử dụng biểu thức quy tắc, chúng thay thế mọi thứ giống một url với một link được sử dụng và gọi lại để hàm thực thi cha,sự kiện thực tế là raised propery , trong hàm saving bạn có thể trả về giá trị false.
Một số method bạn nên biết:
AfterConstruct : Hàm được gọi sau khi một model được khởi tạo bởi mã nguồn
beforeDelete/afterDelete : Hàm được gọi trước/sau khi bản ghi bị xóa
beforeSave/afterSave: Hàm được khởi tạo trước khi/sau khi lưu bản ghi thành công.
beforeValidate/afterValidate: Hàm được gọi khi khởi tạo trước/sau khi validation form kết thúc.
4. Highlight code với Yii
Nếu bạn post code,công ty của bạn lien quan đến nghề báo và wiki blog,sẽ luôn luôn tốt hơn khi hệ thống có highlight (hiệu ứng nháy), và một người đọc code cũng cảm thấy thoải mái.
Để thực hiện ta vào phpmyadmin chọn mục SQL và thực hiện query sau:
CREATE TABLE `snippet` (
`id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(255) NOT NULL,
`code` text NOT NULL,
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
`html` text NOT NULL,
`language` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
);
Tiếp theo vào Giii generated model Snippet
Sauk hi generate thành công bạn thay đổi hàm rules trong Snippet.php ở protected/models/Snippet.php
public function rules()
{
return array(
array('title, code, language', 'required'),
array('title', 'length', 'max'=>255),
array('language', 'length', 'max' => 20),
);
}
Thêm method sau khi validate:
protected function afterValidate()
{
$highlighter = new CTextHighlighter();
$highlighter->language = $this->language;
$this->html = $highlighter->highlight($this->code);
return parent::afterValidate();
}
public function getSupportedLanguages()
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
return array(
'php' => 'PHP',
'css' => 'CSS',
'html' => 'HTML',
'javascript' => 'JavaScript',
);
}
Tiếp theo vào protected/controller tạo SnippetController.php
<?php
class SnippetController extends CController
{
public function actionIndex()
{
$criteria = new CDbCriteria();
$criteria->order = 'id DESC';
$models = Snippet::model()->findAll();
$this->render('index', array(
'models' => $models,
));
}
public function actionView($id)
{
$model = Snippet::model()->findByPk($id);
if(!$model)
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
throw new CException(404);
$this->render('view', array(
'model' => $model,
));
}
public function actionAdd()
{
$model = new Snippet();
$data = Yii::app()->request->getPost('Snippet');
if($data)
{
$model->setAttributes($data);
if($model->save())
$this->redirect(array('view', 'id' => $model->id));
}
$this->render('add', array(
'model' => $model,
));
}
public function actionEdit($id){
$model = Snippet::model()->findByPk($id);
if(!$model)
throw new CHttpException(404);
$data = Yii::app()->request->getPost('Snippet');
if($data)
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
$model->setAttributes($data);
if($model->save())
$this->redirect(array('view', 'id' => $model->id));
}
$this->render('edit', array(
'model' => $model,
));
}
}
Tiếp theo ta tạo view trong protected/views/snippet/index.php
<h2>Snippets</h2>
<?php echo CHtml::link('Add snippet', array('add'))?>
<ol>
<?php foreach($models as $model):?>
<li>
<?php echo CHtml::link(
CHtml::encode($model->title),
array('view', 'id' => $model->id)
)?>
</li>
<?php endforeach?>
</ol>
Tạo tiếp view thứ 2 : protected/views/snippet/view.php
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<h2><?php echo CHtml::link('Snippets', array('index'))?> → <?php
echo CHtml::encode($model->title)?>
</h2>
<?php echo CHtml::link('Edit', array
('edit', 'id' => $model->id))?>
<div>
<?php echo $model->html?>
</div>
Tạo view add : protected/views/snippet/add.php
<h2><?php echo CHtml::link('Snippets', array('index'))?> → Add
snippet
</h2>
<?php $this->renderPartial('_form', array('model' => $model))?>
Tạo view edit: protected/views/snippet/edit.php
<h2><?php echo CHtml::link('Snippets', array('index'))?> → Edit
snippet
</h2>
<?php $this->renderPartial('_form', array('model' => $model))?>
Tạo view _form : protected/views/snippet/_form.php
<?php echo CHtml::beginForm()?>
<ul>
<li>
<?php echo CHtml::activeLabel($model, 'title')?>
<?php echo CHtml::activeTextField($model, 'title')?>
</li>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<li>
<?php echo CHtml::activeLabel($model, 'code')?>
<?php echo CHtml::activeTextArea($model, 'code')?>
</li>
<li>
<?php echo CHtml::activeLabel($model, 'language')?>
<?php echo CHtml::activeDropDownList($model, 'language',
$model->getSupportedLanguages())?>
</li>
<li>
<?php echo CHtml::submitButton('Save')?>
</li>
</ul>
<?php echo CHtml::endForm()?>
Sauk hi hoàn thành vào trình duyệt gõ : http://localhost/skybook/snippet/index
Từ từ tận hưởng hiệu ứng:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
5. Thực thi kế thừa bảng
Dữ liệu quan hệ thường không hỗ trợ kế thừa . Nếu bạn muốn thực hiện kế thừa database, bạn cũng có thể được hỗ trợ từ Yii
Cấu trúc kế thừa đơn giản như:
Xe
|-> Xe thể thao
|->Xe du lịch
|->Xe gia đình.
Để bắt đầu ta vào phpmyadmin chọn sql và chạy query sau:
CREATE TABLE `car` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`type` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `car` (`name`, `type`)
VALUES ('Ford Focus', 'family'),
('Opel Astra', 'family'),
('Kia Ceed', 'family'),
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
('Porsche Boxster', 'sport'),
('Ferrari 550', 'sport');
Ta generated model Car trong Gii
Trong Protected/models/Car.php ta sửa lại như sau:
<?php
class Car extends CActiveRecord
{
public static function model($className=__CLASS__)
{
return parent::model($className);
}
public function tableName()
{
return 'car';
}
protected function instantiate($attributes)
{
switch($attributes['type'])
{
case 'sport':
$class='SportCar';
break;
case 'family':
$class='FamilyCar';
break;
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
default:
$class=get_class($this);
}
$model=new $class(null);
return $model;
}
}
Sau đó ta kế thừa cho protected/models/SportCar.php
<?php
class SportCar extends Car
{
public static function model($className=__CLASS__)
{
return parent::model($className);
}
public function defaultScope()
{
return array(
'condition'=>"type='sport'",
);
}
}
Tiếp theo ta cũng kế thừa cho protected/models/FamilyCar.php
<?php
class FamilyCar extends Car
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
public static function model($className=__CLASS__)
{
return parent::model($className);
}
public function defaultScope()
{
return array(
'condition'=>"type='family'",
);
}
}
Trong protected/controller tạo TestController.php
<?php
class TestController extends CController
{
public function actionIndex()
{
echo "<h1>All cars</h1>";
$cars = Car::model()->findAll();
foreach($cars as $car)
{
// Mỗi chiếc xe có thể là của class car, sportcar, hoặc familycar
echo get_class($car).' '.$car->name."<br />";
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
echo "<h1>Sport cars only</h1>";
$sportCars = SportCar::model()->findAll();
foreach($sportCars as $car)
{
echo get_class($car).' '.$car->name."<br />";
}
}
}
Xong xuôi vào trình duyệt gõ : http://localhost/skybook/test/index
6. Sử dụng CdbCriteria
Khi sử dụng Active Record method như findAll, hoặc find, chúng ta có thể nhận tiêu chuẩn giống như tham số,nó có thể trả về mảng hay một khởi tạo của CdbCriteria class.Class này hiển thị query tiêu chuẩn, giống như điều kiện,ordering by,limit/offset, vv…
Thường thường nó hay được sử dụng như dưới đây:
$criteria = new CDbCriteria();
$criteria->limit = 10;
$criteria->order= 'id DESC';
$criteria->with = array('comments');
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$criteria->compare('approved', 1);
$criteria->addInCondition('id', array(4, 8, 15, 16, 23, 42));
$posts = Post::model()->findAll($criteria);
Cách làm việc:
Bản thân class Criteria không xây dựng query, nhưng chỉ hiển thị dữ liệu hoặc cho phép điều chỉnh chúng, Hàm làm việc thực tế nằm trong Active Record method nơi các tiêu chuẩn của class này sử dụng.
Dịch code có thể đọc như sau:
Hiển thị 10 dòng post với comment từ approved post với id trong khoảng 4,8,15,16,24 hoặc 42 order by id.
Select * from post p (định danh post) JOIN (nối) comment c(định danh comment) ON (điều kiện nối) p.id=c.post_id where p.approval=1 and p.id in (4,8,16,15,24,42) order by p.id DESC LIMIT 10.
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
CHƯƠNG 6 KẾ THỪA Yii
Trong chương này, bạn sẽ khám phá:
Sử dụng data provides
Sử dụng grids (lưới)
Sử dụng lists (danh sách)
Tạo custom grid column( Cột lưới)
Yii có thư viện hữu ích gọi là Zii, nó đồng hành cùng với framework và một số class khác giúp cho việc phát triển trở lên dễ dàng hơn bao giờ hết,hầu hết thành phần chủ yếu là grid và list cái cho phép bạn xây dựng dữ liệu trong Admin và user website rất nhanh và đẹp. Trong chương này bạn sẽ học cách điều chỉnh component và những thứ bạn cần,bạn sẽ học về data provides,chúng là một phần trong core framework.
1 Sử dụng data provides (nhà cung cấp dữ liệu)
Data provides được sử dụng để triệu tập dữ liệu model giống như sắp xếp, phân trang, và querying.Chúng được sử dụng với grids và lists. Bởi vì cả 2 widget và provider là một tiêu chuẩn (chuẩn mực),bạn có thể hiển thị giống như dữ liệu sử dụng khác widget và bạn có thể hiện dữ liệu cho một widget từ various provider (tài sản được cung cấp).
Để thực hiện coder bạn vào http://dev.mysql.com/doc/sakila/en/sakila.html download database sau đó import vào csdl thong qua phpmyadmin.
Sử dụng Gii để tạo model film.
Vào protected/views/ tạo grid/index.php
<?php $this->widget('zii.widgets.grid.CGridView',
array('dataProvider' => $dataProvider,
))?>
Sau đó,tạo protected/controllers/GridController.php
<?php
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
class GridController extends Controller
{
public function actionAR()
{
$dataProvider = new CActiveDataProvider('Film', array(
'pagination'=>array(
'pageSize'=>10,
),
'sort'=>array(
'defaultOrder'=> array('title'=>false),
)
));
$this->render('index', array(
'dataProvider' => $dataProvider,
));
}
public function actionArray()
{
$yiiDevelopers = array(
array(
'name'=>'Qiang Xue',
'id'=>'2',
'forumName'=>'qiang',
'memberSince'=>'Jan 2008',
'location'=>'Washington DC, USA',
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'duty'=>'founder and project lead',
'active'=>true,
),
array(
'name'=>'Wei Zhuo',
'id'=>'3',
'forumName'=>'wei',
'memberSince'=>'Jan 2008',
'location'=>'Sydney, Australia',
'duty'=>'project site maintenance and development',
'active'=>true,
),
array(
'name'=>'Sebastián Thierer',
'id'=>'54',
'forumName'=>'sebas',
'memberSince'=>'Sep 2009',
'location'=>'Argentina',
'duty'=>'component development',
'active'=>true,
),
array(
'name'=>'Alexander Makarov',
'id'=>'415',
'forumName'=>'samdark',
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'memberSince'=>'Mar 2010',
'location'=>'Russia',
'duty'=>'core framework development',
'active'=>true,
),
array(
'name'=>'Maurizio Domba',
'id'=>'2650',
'forumName'=>'mdomba',
'memberSince'=>'Aug 2010',
'location'=>'Croatia',
'duty'=>'core framework development',
'active'=>true,
),
array(
'name'=>'Y!!',
'id'=>'1644',
'forumName'=>'Y!!',
'memberSince'=>'Aug 2010',
'location'=>'Germany',
'duty'=>'core framework development',
'active'=>true,
),
array(
'name'=>'Jeffrey Winesett',
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'id'=>'15',
'forumName'=>'jefftulsa',
'memberSince'=>'Sep 2010',
'location'=>'Austin, TX, USA',
'duty'=>'documentation and marketing',
'active'=>true,
),
array(
'name'=>'Jonah Turnquist',
'id'=>'127',
'forumName'=>'jonah',
'memberSince'=>'Sep 2009 - Aug 2010',
'location'=>'California, US',
'duty'=>'component development',
'active'=>false,
),
array(
'name'=>'István Beregszászi',
'id'=>'1286',
'forumName'=>'pestaa',
'memberSince'=>'Sep 2009 - Mar 2010',
'location'=>'Hungary',
'duty'=>'core framework development',
'active'=>false,
),
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
);
$dataProvider = new CArrayDataProvider(
$yiiDevelopers, array(
'sort'=>array(
'attributes'=>array('name', 'id', 'active'),
'defaultOrder'=>array('active' => true, 'name' => false),
),
'pagination'=>array(
'pageSize'=>10,
),
));
$this->render('index', array(
'dataProvider' => $dataProvider,
));
}
public function actionSQL()
{
$count=Yii::app()->db->createCommand('SELECT COUNT(*)
FROM film')->queryScalar();
$sql='SELECT * FROM film';
$dataProvider=new CSqlDataProvider($sql, array(
'keyField'=>'film_id',
'totalItemCount'=>$count,
'sort'=>array(
'attributes'=>array('title'),
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'defaultOrder'=>array('title' => false),
),
'pagination'=>array(
'pageSize'=>10,
),
));
$this->render('index', array(
'dataProvider' => $dataProvider,
));
}
}
Vào localhost /skybook/grid/aR , grid/array và grid/sql để xem kết quả:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
2. Sử dụng Zii Grid
Zii grid rất hữu ích và nhanh chóng trong việc tạo khung lưới cho các trang quản trị admin hoặc bất kỳ trang nào yêu cầu về số liệu.
Để bắt đầu bạn vào website : http://dev.mysql.com/doc/sakila/en/
sakila.html. down load database sau do giải nén vào phpmyadmin, chọn table của bạn và import db.
Sử dụng Gii tạo model generated cho customer, address,city.
Tiếp theo vào Gii chọn controller generated : Customer
Chạy customer controller và tới quản trị Manage Customer link trong gii bạn sẽ có kết quả như sau:
3. Sử dụng lists
Zii list là công cụ khá tốt cho việc trình bày dữ liệu ở bất kỳ data provider để kết thúc user khi xử lí phân trang và sắp xếp tự động.CListView là rất dễ dàng trong việc custom vì nó cho phép xây dựng bất kỳ thuộc tính nào trong list page.
Vào website : http://dev.mysql.com/doc/sakila/en/
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
sakila.html download cơ sở dữ liệu rồi vào phpmyadmin import vào database
Vào gii model generated tạo : customer,store,address,city
Tiếp theo mở gii,chọn CRug generator và nhấn Customer tới model class field, nhấn preview và generated
Gii sẽ generated controller trong protected/controllers
Chạy index action của customer controller ta có kết quả sau :
http://localhost/skybook/customer/index
Thêm tiêu chuẩn sắp xếp:
bạn vào protected/views/customer/index.php và thay thế bằng nội dung sau :
<?php $this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_view',
'sortableAttributes'=>array(
'last_name',
'email',
),
)); ?>
Customer templates:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Thay thế views với nội dung sau:
<?php $this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_view',
'sortableAttributes'=>array(
'last_name',
'email',
),
'template' => '{sorter} {pager} {items} {sorter} {pager}',
)); ?>
Customer markup data display
<?php $this->widget('zii.widgets.CListView', array(
'dataProvider'=>$dataProvider,
'itemView'=>'_view',
'itemsTagName' => 'ol',
'itemsCssClass' => 'customers',
'sortableAttributes'=>array(
'last_name',
'email',
),
'template' => '{sorter} {pager} {items} {sorter} {pager}',
)); ?>
Tiếp theo vào protected/views/customer tạo _views.php
<li>
<h2>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php
$title = CHtml::encode($data->first_name.' '.$data->last_name);
echo CHtml::link($title, array('view', 'id'=>
$data->customer_id));
?>
</h2>
<ul>
<li>
<strong><?php echo CHtml::encode($data->
getAttributeLabel('store_id')); ?>:</strong>
<?php echo CHtml::encode($data->store->
address->address.', '.$data->store->address->city->city.',
'.$data->store->address->district); ?>
</li>
<li>
<strong><?php echo CHtml::encode($data->
getAttributeLabel('email')); ?>:</strong>
<?php echo CHtml::encode($data->email); ?>
</li>
<li>
<strong><?php echo CHtml::encode($data->
getAttributeLabel('address_id')); ?>:</strong>
<?php echo CHtml::encode($data->address->address.',
'.$data->address->city->city.',
'.$data->address->district); ?>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
</li>
<li>
<strong><?php echo CHtml::encode($data->
getAttributeLabel('active')); ?>:</strong>
<?php echo $data->active ? 'Yes' : 'No'; ?>
</li>
</ul>
</li>
Vào protected/assets tạo customer.css
ol.customers {
list-style: none;
margin: 1em 0;
}
ol.customers>li {
margin: 1em;
padding:1em;
background: #fcfcfc;
border: 1px solid #9aafe5;
}
Quay lại file _views trong protected/views/customer và thêm đoạn sau vào dưới cùng
<?php Yii::app()->clientScript->registerCssFile(
Yii::app()->assetManager->publish(Yii::getPathOfAlias('application.
assets').'/customers.css'))?>
Thử lại trên trình duyệt bằng việc F5:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
4. Tạo customer Grid column
Hầu hết thời gian bạn không cần tạo khung lưới cho các thuộc tính của bạn khi mà Yii đã cung cấp đầy đủ công cụ thực hiện vì thế nên bạn chỉ cần tạo custom cho riêng mình.
Ta bắt đầu tạo custom grid column cho phép toggling Y/N giá trị và thay đổi giá trị trong models nhờ AJAX
Bạn vào : http://dev.mysql.com/doc/sakila/en/
sakila.html tải database về sau đó vào phpmyadmin import và db của bạn.
Vào GII model generated tạo : customer
Mở Gii chọn CRUD generated chọn : Customer và generated
Cách thực hiện : Trong table chúng ta có một field active (kích hoạt) , chúng ta muốn toggle với flag column . Column sẽ hiển thị 2 trường Y và N quyết định giá trị trả về được cliking hay ko.
Vào protected/components/ tạo FlagColumn.php
<?php
class FlagColumn extends CGridColumn
{
public $name;
public $sortable=true;
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public $callbackUrl = array('flag');
private $_flagClass = "flag_link";
public function init() {
parent::init();
$cs=Yii::app()->getClientScript();
$gridId = $this->grid->getId();
$script = <<<SCRIPT
jQuery(".{$this->_flagClass}").live("click", function(e){
e.preventDefault();
var link = this;
$.ajax({
dataType: "json",
cache: false,
url: link.href,
success: function(data){
$('#$gridId').yiiGridView.update('$gridId');
}
});
});
SCRIPT;
$cs->registerScript(__CLASS__.$gridId.'#flag_link', $script);
}
protected function renderDataCellContent($row, $data) {
$value=CHtml::value($data,$this->name);
$this->callbackUrl['pk'] = $data->primaryKey;
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$this->callbackUrl['name'] = urlencode($this->name);
$this->callbackUrl['value'] = (int)empty($value);
$link = CHtml::normalizeUrl($this->callbackUrl);
echo CHtml::link(!empty($value) ? 'Y' : 'N', $link, array(
'class' => $this->_flagClass,
));
}
protected function renderHeaderCellContent()
{
if($this->grid->enableSorting && $this->sortable &&
$this->name!==null)
echo $this->grid->dataProvider->getSort()->link(
$this->name,$this->header);
else if($this->name!==null && $this->header===null)
{
if($this->grid->dataProvider instanceof CActiveDataProvider)
echo CHtml::encode($this->grid->dataProvider->
model->getAttributeLabel($this->name));
else
echo CHtml::encode($this->name);
}
else
parent::renderHeaderCellContent();
}
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Tiếp theo vào protected/controllers/CustomerController.php và thay đổi action Flag
public function actionFlag($pk, $name, $value){
$model = $this->loadModel($pk);
$model->{$name} = $value;
$model->save(false);
if(!Yii::app()->request->isAjaxRequest){
$this->redirect('admin');
}
}
Cuối cùng vào protected/views/customer/admin.php
Thay đổi dòng widget:
<?php $this->widget('zii.widgets.grid.CGridView', array(
'id'=>'customer-grid',
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
'customer_id',
'store_id',
'first_name',
'last_name',
'email',
'address_id',
array(
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'class' => 'FlagColumn',
'name' => 'active',
),
/*
'create_date',
'last_update',
*/
array(
'class'=>'CButtonColumn',
),
),
)); ?>
Sau k hi thành công vào Http://localhost/skybook/customer/admin
Bạn đã có thể thấy một loạt công cụ kích hoạt, CRUD hiện lên, customer thật dễ dàng đúng ko , đệt mợ tàu khựa.
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
CHƯƠNG 7 KẾ THỪA Yii
Trong chương này bạn sẽ khám phá:
Tạo component
Tạo controller action
Tạo reusable controller
Tạo một widget
Tạo CLI commands
Tạo filter
Tạo modules
Tạo một customer view renderer
Làm mở rộng distribution-ready
1. Tạo component
Nếu bạn có một vài đoạn code có thể tái sử dụng được nhưng bạn không biết cách thức,widget,behavior hãy sử dụng component. Component kế thừa từ CComponent hoặc CApplicationComponent.Sau đó một component có thể kích hoạt tới ứng dụng và cấu hình sử dụng trong protected/config/main.php.
Để ví dụ chúng ta định nghĩa: EImageManage ứng dụng thành phần sẽ dùng để resize ảnh sử dụng thư viện GD, kích hoạt nó tới ứng dụng và sử dụng nó.
Vào protected/components tạo EImageManage.php
<?php
class EImageManager extends CApplicationComponent
{
protected $image;http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
protected $width;
protected $height;
protected $newWidth;
protected $newHeight;
public function resize($width = false, $height = false){
if($width!==false) $this->newWidth = $width;
if($height!==false) $this->newHeight = $height;
return $this;
}
public function load($filePath)
{
list($this->width, $this->height, $type) =
getimagesize($filePath);
switch ($type)
{
case IMAGETYPE_GIF:
$this->image = imagecreatefromgif($filePath);
break;
case IMAGETYPE_JPEG:
$this->image = imagecreatefromjpeg($filePath);
break;
case IMAGETYPE_PNG:
$this->image = imagecreatefrompng($filePath);
break;
default:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
throw new CException('Unsupported image type ' . $type);
}
return $this;
}
public function save($filePath)
{
$ext = pathinfo($filePath, PATHINFO_EXTENSION);
$newImage = imagecreatetruecolor($this->newWidth,
$this->newHeight);
imagecopyresampled($newImage, $this->image, 0, 0, 0, 0,
$this->newWidth, $this->newHeight, $this->width,
$this->height);
switch($ext)
{
case 'jpg':
case 'jpeg':
imagejpeg($newImage, $filePath);
break;
case 'png':
imagepng($newImage, $filePath);
break;
case 'gif':
imagegif($newImage, $filePath);
break;
default:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
throw new CException("Unsupported image type ", $ext);
}
imagedestroy($newImage);
if(!is_file($filePath))
throw new CException("Failed to write image.");
}
function __destruct()
{
imagedestroy($this->image);
}
}
Tiếp theo vào main.php trong protected/config để kích hoạt:
Tìm và thêm vào đoạn sau :
…
// application components
'components'=>array(
'image' => array(
'class' => 'EImageManager',
),
…
Tiếp theo chúng ta sẽ sử dụng đoạn component mới như sau:
Yii::app()->image
->load(Yii::getPathOfAlias('webroot').'/src.png')
->resize(100,100)
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
->save(Yii::getPathOfAlias('webroot').'/dst.png');
Tải application component đã tồn tại
Hầu hết thời gian chúng ta cần tạo ứng dụng thành phần của riêng ta, tuy nhiên việc tải code đã tồn tại giúp ta giảm thời gian lập trình và tiết kiệm công sức.
Ví dụ để hiện user role từ databse sử dụng Yii::app()->user->role có thể kế thừa từ CwebUser component giống như sau:
<?php
class WebUser extends CWebUser {
private $_model = null;
function getRole() {
if($user = $this->getModel()){
return $user->role;
}
else return 'guest';
}
private function getModel(){
if($this->_model === null){
if($this->id === null) return null;
$this->_model = User::model()->findByPk($this->id);
}
return $this->_model;
}
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Và trong main.php ta phải cấu hình lại như sau:
…
// application components
'components'=>array(
'user'=>array(
'class' => 'WebUser',
// other properties
),
…
2. Tạo controller action
Trước tiên bạn vào phpmyadmin chọn sql và thực hiện query sau:
CREATE TABLE `post` (
`id` int(11) NOT NULL auto_increment,
`text` text,
`title` varchar(255) default NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `comment` (
`id` int(11) NOT NULL auto_increment,
`text` text,
PRIMARY KEY (`id`)
);
Generated model với : post và comment trong Gii.
Vào protected/extensions/actions tạo EDeleteAction.phphttp://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php
class EDeleteAction extends CAction
{
public $modelName;
public $redirectTo = array('index');
/**
* Runs the action.
* This method is invoked by the controller owning this action.
*/
public function run($pk)
{
CActiveRecord::model($this->modelName)->deleteByPk($pk);
if(Yii::app()->getRequest()->getIsAjaxRequest())
{
Yii::app()->end(200, true);
}
else
{
$this->getController()->redirect($this->redirectTo);
}
}
}
Tiếp theo vào kích hoạt controller : protected/controllers/ tạo DeleteController.php:
<?php
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
class DeleteController extends CController
{
public function actions()
{
return array(
'deletePost' => array(
'class' => 'ext.actions.EDeleteAction',
'modelName' => 'Post',
'redirectTo' => array('indexPosts'),
),
'deleteComment' => array(
'class' => 'ext.actions.EDeleteAction',
'modelName' => 'Comment',
'redirectTo' => array('indexComments'),
),
);
}
public function actionIndexPosts()
{
echo "I'm index action for Posts.";
}
public function actionIndexComments()
{
echo "I'm index action for Comments.";
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
Bây giờ bạn có thể vào trình duyệt gõ: http://localhost/skybook/delete/deletePost/pk, http://localhost/skybook/delete/deleteComment/pk
3. Tạo reusable Controller
Trong Yii bạn có thể tái sử dụng controller , nếu bạn tạo nhiều ứng dụng với controller có thuộc tính giống nhau, di chuyển tất cả các lệnh code tới controller tái sử dụng sẽ tiết kiệm nhiều thời gian cho bạn.
Trong bài này,chúng ta sẽ tạo đơn giản api controller sẽ được thi hành với JSON,CRUD API cho một model, nó sẽ đưa input data từ POST hoặc GET và sẽ sử lý bằng JSON data tiếp theo là HTTP code
Vào Gii,tạo 1 model generated là Post
Tiếp theo vào protected/extensions/ tạo json_api tạo JsonApiController.php:
<?php
class JsonApiController extends CController
{
const RESPONSE_OK = 'OK';
const RESPONSE_NO_DATA = 'No data';
const RESPONSE_NOT_FOUND = 'Not found';
const RESPONSE_VALIDATION_ERRORS = 'Validation errors';
public $modelName;
public function init()
{
parent::init();
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
if(empty($this->modelName))
throw new CException("You should set modelName before
using JsonApiController.");
}
public function actionCreate()
{
if(empty($_POST))
$this->respond(400, self::RESPONSE_NO_DATA);
$model = new $this->modelName;
$model->setAttributes($_POST);
if($model->save())
$this->respond(200, self::RESPONSE_OK);
else
$this->respond(400, self::RESPONSE_VALIDATION_ERRORS,
$model->getErrors());
}
public function actionGet($pk)
{
$model = CActiveRecord::model
($this->modelName)->findByPk($pk);
if(!$model)
$this->respond(404, self::RESPONSE_NOT_FOUND);
$this->respond(200, self::RESPONSE_OK,
$model->getAttributes());
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public function actionUpdate($pk)
{
if(empty($_POST))
$this->respond(400, self::RESPONSE_NO_DATA);
$model = CActiveRecord::model
($this->modelName)->findByPk($pk);
if(!$model)
$this->respond(404, self::RESPONSE_NOT_FOUND);
$model->setAttributes($_POST);
if($model->save())
$this->respond(200, self::RESPONSE_OK);
else
$this->respond(400, self::RESPONSE_VALIDATION_ERRORS,
$model->getErrors());
}
public function actionDelete($pk)
{
if(CActiveRecord::model($this->modelName)->deleteByPk($pk))
{
$this->respond(200, self::RESPONSE_OK);
}
else {
$this->respond(404, self::RESPONSE_NOT_FOUND);
}
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
protected function respond($httpCode, $status, $data = array())
{
$response['status'] = $status;
$response['data'] = $data;
echo CJSON::encode($response);
Yii::app()->end($httpCode, true);
}
}
Tiếp theo bạn cần kết nối t ới ứng dụng với protected/conFig/main.php
Nó có thể hoàn thành việc thêm con troller,cấu hình tới controllerMap property của CwebApplication và tat hay thế cấu hình sau trong main.php
…
'controllerMap' => array(
'api' => array(
'class' => 'ext.json_api.JsonApiController',
'modelName' => 'Post',
),
),
…
Chúng ta cần kết nối tới controller và đặc biệt nó sẽ làm việc với post model.
Bạn sẽ cần form tới post data nhưng nếu bạn có một số dữ liệu tồn tại bạn có thể sử dụng phương thức nhận bằng cách http://localhost/skybook/api/get/pk/1
Ứng dụng trả về cho bạn như sau:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{"status":"OK","data":{"id":"1","text":"post1",
"title":"post1","is_deleted":"0"}}
4. Tạo một widget
Một widget là một phần tái sử dụng của khung nhìn views nó không chỉ render một số dữ liệu mà còn đọc một số logic. Nó có thể là một sự kiện nhận dữ liệu từ models và sử dụng nó trong bản thân views.
Hãy tạo một widget và vẽ một chart biểu đồ nghệ thuật sử dụng google API.
Trong protected/extensions/ tạo folder chart, tạo EChartWidget.php:
<?php
class EChartWidget extends CWidget
{
public $title;
public $data=array();
public $labels=array();
public function run()
{
echo "<img
src=\"http://chart.apis.google.com/chart?chtt=".urlencode
($this->title)."&cht=pc&chs=300x150&chd=".
$this->encodeData($this->data)."&chl=".implode
('|', $this->labels)."\">";
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
protected function encodeData($data)
{
$maxValue=max($data);
$chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx
yz0123456789';
$chartData="s:";
for($i=0;$i<count($data);$i++)
{
$currentValue=$data[$i];
if($currentValue>-1)
$chartData.=substr($chars,61*($currentValue/$maxValue),1);
else
$chartData.='_';
}
return $chartData."&chxt=y&chxl=0:|0|".$maxValue;
}
}
Tiếp theo vào protected/controllers/ tạo ChartController.php
<?php
class ChartController extends CController
{
public function actionIndex()
{
$value = rand(10, 90);
$this->widget('ext.chart.EChartWidget', array(
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'title' => 'Do you like it?',
'data' => array(
$value, 100-$value
),
'labels' => array(
'No',
'Yes',
),
));
}
}
Vào trình duyệt gõ : http://localhost/skybook/chart/index lưu ý nhà phải kết nối với internet mới truy nhập vào được google thì mới thấy cái biểu đồ này.
5. Tạo filters (bộ lọc)
Một filters là một class có thể chạy trước/sau một action đang thực hiện. Nó có thể sử dụng và thay đổi nội dung thực hiện hoặc thay đổi output, trong ví dụ này bạn sẽ thực thi một output đơn giản được filter với compress HTML output và xóa tất cả mọi định dạng.
Vào protected/extensions/ tạo compress_html folder, tạo ECompressHtmlFilter.php
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php
class ECompressHtmlFilter extends CFilter
{
protected function preFilter($filterChain)
{
ob_start();
return parent::preFilter($filterChain);
}
protected function postFilter($filterChain)
{
$out = ob_get_clean();
echo preg_replace("~>(\s+|\t+|\n+)<~", "><", $out);
parent::postFilter($filterChain);
}
}
Tiếp theo chúng ta cần kết nối tới ứng dụng, chúng ta vào protected/controllers/ SiteController và thêm filter:
public function filters()
{
return array(
array(
'ext.compress_html.ECompressHtmlFilter'
),
);
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Chạy ứng dụng và kiểm tra source code ta sẽ thấy như sau:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//
EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.
dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
lang="en"><head><meta http-equiv="Content-Type" content="text/
html; charset=utf-8" /><meta name="language" content="en" /><!--
blueprint CSS framework -->
6. Tạo một modules
Nếu bạn muốn tạo một ứng dụng đầy đủ và muốn sử dụng nó với việc custom theo í bạn, hầu hết điều bạn muốn đó là việc bạn cần phải tạo một modules.
Trong bài này,chúng ta sẽ nhìn việc tạo modules wiki,chúng ta sẽ không focus user và quản lý phân quyền và để mọi người điều khiển mọi thứ.
Trước tiên vào php myadmin chọn SQL : tạo query sau : CREATE TABLE `wiki` (
`id` varchar(255) NOT NULL,
`text` text NOT NULL,
PRIMARY KEY (`id`)
)
Generated : wiki model với Gii
Generated : wiki module với Gii
Di chuyển protected/models/wiki.php tới protected/modules/wiki/models/wiki.php
Thêm wiki tới moduels section của protected/config/main.php
'modules'=>array(
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
// uncomment the following to enable the Gii tool
'gii'=>array(
'class'=>'system.gii.GiiModule',
'password'=>false,
),
'wiki'
),
Cách thực hiện: Đầu tiên hãy thêm wiki link tới CMarkdownParser. Tạo protected/modules/wiki/components/wikiMarkdownParser.php:
<?php
class WikiMarkdownParser extends CMarkdownParser
{
public function transform($text)
{
$text = preg_replace_callback('~\[\[(.*?)(?:\|(.*?))?\]\]~',
array($this, 'processWikiLinks'), $text);
return parent::transform($text);
}
protected function processWikiLinks($matches)
{
$page = $matches[1];
$title = isset($matches[2]) ? $matches[2] : $matches[1];
return CHtml::link(CHtml::encode($title), array(
'view', 'id' => $page,
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
));
}
}
Tiếp theo sử dụng getHtml method từ protected/modules/wiki/models/wiki.php
public function getHtml()
{
$parser = new WikiMarkdownParser();
return $parser->transform($this->text);
}
Chúng ta tạo customer protected/modules/wiki/controller/DefaultController. Chúng ta chỉ cần 2 action là views và edit.
Tạo protected/modules/wiki/controller/DefaultController.php
class DefaultController extends Controller
{
public function actionIndex()
{
$this->actionView('index');
}
public function actionView($id)
{
$model = Wiki::model()->findByPk($id);
if(!$model)
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$this->actionEdit($id);
Yii::app()->end();
}
$this->render('view', array(
'model' => $model,
));
}
public function actionEdit($id)
{
$model = Wiki::model()->findByPk($id);
if(!$model)
{
$model = new Wiki();
$model->id = $id;
}
if(!empty($_POST['Wiki']))
{
if(!empty($_POST['Wiki']['text']))
{
$model->text = $_POST['Wiki']['text'];
if($model->save())
$this->redirect(array('view', 'id' => $id));
}
else
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Wiki::model()->deleteByPk($id);
}
}
$this->render('edit', array(
'model' => $model
));
}
}
Tiếp theo tạo views : protected/modules/wiki/views/default/view.php
<h2>
<?php echo CHtml::encode($model->id)?>
[<?php echo CHtml::link('edit', array('edit', 'id' =>
$model->id))?>]
</h2>
<?php echo $model->html ?>
Tạo views edit: protected/modules/wiki/views/default/edit.php
<h2>Editing <?php echo CHtml::encode($model->id)?></h2>
<?php echo CHtml::beginForm()?>
<?php echo CHtml::activeTextArea($model, 'text',
array('cols' => 100, 'rows' => 20))?>
<br /><br />
<?php echo CHtml::submitButton('Done')?>
<?php echo CHtml::endForm()?>
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Xong xuôi vào http://localhost/skybook/wiki/index và localhost/skybook/wiki/edit
Tiếp theo vào main.php trong protected/config thay đổi :
'modules'=>array(
// uncomment the following to enable the Gii tool
'gii'=>array(
'class'=>'system.gii.GiiModule',
'password'=>false,
),
'wiki'=>array(
'class' => 'ext.wiki.WikiModule'
),
),
Mỗi modules được tạo chứa một main modules class giống wikimodule nơi chúng ta có thể định nghĩa cấu hình thuộc tính,định nghĩa import, thay đổi đường dẫn, kích hoạt controller, và nhiều hơn..
Mặc định mỗi moduels generated với Gii chạy index action của default controller:
public function actionIndex()
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
$this->actionView('index');
}
Trong wiki module indexaction tat hay đổi như sau:
$model = Wiki::model()->findByPk($id);
if(!$model)
{
$this->actionEdit($id);
Yii::app()->end();
}
$this->render('view', array(
'model' => $model,
));
Nếu modules này giữ một ID chúng ta sẽ hiển thị nó và sử dụng trong views.
Nếu không có trang nào giữ ID, chúng ta xử lý bởi action khác:
$model = Wiki::model()->findByPk($id);
if(!$model)
{
$model = new Wiki();
$model->id = $id;
}
if(!empty($_POST['Wiki']))
{
if(!empty($_POST['Wiki']['text']))
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
{
$model->text = $_POST['Wiki']['text'];
if($model->save())
$this->redirect(array('view', 'id' => $id));
}
else
{
Wiki::model()->deleteByPk($id);
}
}
$this->render('edit', array(
'model' => $model
));
NẾU KHÔNG CÓ modules nào sử dụng ID mới tạo,nếu có một models chúng ta edit nó,edit form data từ POST và kiểm tra nó rỗng hay không chúng ta delete models,nếu có ký tự chúng ta lưu lại.
6. Tạo một custom view renderer
Có nhiều PHP templates được yii nhận là native PHP và Prado templates. Nếu bạn muốn sử dụng nó trong Yii bạn có thể định nghĩa chúng.
Bây giờ chúng ta sẽ nhúng Smarty 3.0 vào Yii.
Bạn vào trang chủ : http://www.smarty.net/. Tải phiên bản mới nhất về.
Giải nén (extract here ) rồi vào folder đó copy folder libs của smarty tới protected/vendors/smarty .
Cách thực hiện như sau:
Vào protected/extensions/smarty tạo ESmartyViewRenderer.php:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php
class ESmartyViewRenderer extends CApplicationComponent
implements IViewRenderer
{
public $fileExtension='.tpl';
public $filePermission=0755;
private $smarty;
function init()
{
Yii::import('application.vendors.smarty.*');
spl_autoload_unregister(array('YiiBase','autoload'));
require_once('Smarty.class.php');
spl_autoload_register(array('YiiBase','autoload'));
$this->smarty = new Smarty();
$this->smarty->template_dir = '';
$compileDir = Yii::app()->getRuntimePath
().'/smarty/compiled/';
if(!file_exists($compileDir)){
mkdir($compileDir, $this->filePermission, true);
}
$this->smarty->compile_dir = $compileDir;
$this->smarty->assign('Yii', Yii::app());
}
/**
* Render một file viws.
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
* Phương thức được yêu cầu từ {@link IViewRenderer}.
* @param CBaseController the controller or widget who is
rendering the view file.
* @param string the view file path
* @param mixed the data to be passed to the view
* @param boolean whether the rendering result should be
returned
* @return mixed the rendering result, or null if the rendering
result is not needed.
*/
public function renderFile($context,$sourceFile,$data,$return) {
// current controller properties will be accessible as
{this.property}
$data['this'] = $context;
if(!is_file($sourceFile) || ($file=realpath($sourceFile))=
==false)
throw new CException(Yii::t('ext','View file
"$sourceFile" does not exist.', array('{file}'=>$sourceFile)));
$this->smarty->assign($data);
if($return)
return $this->smarty->fetch($sourceFile);
else
$this->smarty->display($sourceFile);
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
Tiếp theo bạn cần kết nối views renderer với ứng dụng. Trong protected/config/main.php chúng ta cần tải viewRenderer component:
…
// application components
'components'=>array(
…
'viewRenderer'=>array(
'class'=>'ext.smarty.ESmartyViewRenderer',
),
),
…
Lưu ý, trong component có dữ liệu thì bạn để nguyên chỉ việc thêm vào :
'viewRenderer'=>array(
'class'=>'ext.smarty.ESmartyViewRenderer',
),
Bây giờ kiểm tra nó, Tạo protected/controllers/ Tạo SmartyController.php
<?php
class SmartyController extends Controller
{
function actionNative()
{
$this->render('native', array(
'username' => 'Alexander',
));
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
function actionSmarty()
{
$this->render('smarty', array(
'username' => 'Alexander',
));
}
}
Tiếp theo t ạo views: protected/views/smarty/native.php:
Hello, <?php echo $username?>!
protected/views/smarty/smarty.tpl:
Hello, {$username}!
Bây giờ vào trình duyệt gõ : http://localhost/skybook/smarty/native
Bạn sẽ thấy dòng : Hello,Alexander!
7. Làm extensions distribution-ready
Trong chương này bạn đã được học rất nhiều các tạo thuộc tính từ Yii extensions. Bây giờ chúng ta nói về cách chia sẻ thành quả của bạn với mọi người và tại sao nó lại quan trong.
Hãy check trong form sau những điều bạn suy nghĩ là đúng nhất về extensions
Nội dung trong sang, dễ đọc và sử dụng trong API
Tài liệu tốt
Mọi người đều có thể tìm thấy nó
Extensions áp dụng được hầu hết trong ứng dụng user
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Sẽ là thành phần chính
Tốt cho việc test code, tạo nền tảng cho ý tưởng với bài test
Bạn cần được cung cấp hỗ trợ cho nó
Tất nhiên tất cả yếu tố trên đều được yêu cầu thực sự cần thiết để tạo ra một sản phẩm tốt.
CHƯƠNG 8 BẢO MẬT
Bạn sẽ khám phá điều gì trong chương cuối này ?
Sử dụng controller filter
Sử dụng CHtml và CHtmlPurifier tới sự kiện XSS
Khám phá SQL injection
Khám phá CSRF
Sử dụng RBAC
1. Sử dụng controller filter
Trong nhiều trường hợp,chúng ta cần lọc ra dữ liệu hoặc thực hiện một số action cơ bản trong data,ví dụ với custom filter, chúng ta có thể lọc ra lượt truy cập bởi IP,sức mạnh của user với việc sử dụng HTTPS,hoặc redirect user từ một phần quan trọng trong cấu hình trang để sử dụng trong ứng dụng. Yii có xây dựng hai bộ lọc ,đầu tiên là CInlineFilter nó cho phép sử dụng controller method như một bộ lọc, và cái thứ hai là (là một thứ chúng
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
ta sẽ focus nó) CAccessControlFilter nó cho phép điều khiển truy cập tới dữ liệu của controller action.
Trong bài học này ta sẽ thực hiện những việc sau:
Giới hạn truy cập tới controller action với chỉ các user định danh
Giới hạn truy cập tới controller action từ IPs đặc biệt
Giới hạn truy cập tới người dùng đặc biệt
Giới hạn truy cập cho user của một trình duyệt đặc biệt, trong trường hợp chúng ta cũng có thể hiển thị custom messager
Trước tiên vào protected/controllers tạo AccessController.php
<?php
class AccessController extends CController
{
public function actionAuthOnly()
{
echo "Looks like you are authorized to run me.";
}
public function actionIp()
{
echo "Your IP is in our list. Lucky you!";
}
public function actionUser()
{
echo "You're the right man. Welcome!";
}
}
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Tiếp theo ta thêm bộ lọc sau:
public function filters()
{
return array(
'accessControl',
);
}
Tiếp theo ta thêm access Rules (quy tắc truy cập)
{
return array(
array(
'deny',
'expression' => 'strpos($_SERVER[\'HTTP_USER_AGENT\'],
\'MSIE\') !== FALSE',
'message' => "You're using the wrong browser, sorry.",
),
array(
'allow',
'actions' => array('authOnly'),
'users' => array('@'),
),
array(
'allow',
'actions' => array('ip'),
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'ips' => array('127.0.0.1'),
),
array(
'allow',
'actions' => array('user'),
'users' => array('admin'),
),
array('deny'),
);
}
http://localhost/skybook/access/authonly , thay autheronly bằng ip, user để test
Bây giờ chúng ta sẽ thử controller trên trình duyệt sử dụng cả 2 tài khoản admin và demo sẽ đều có kết quả sau
2. Sử dụng CHtml và CHtmlPurifier tới sự kiện XSS
XSS là viết tắt của cross-site scripting và là một loại lỗ hổng mà cho phép tríchmột kịch bản phía máy khách (thông thường, JavaScript) trong trang được xem bởi người dùng khác. Xem xét sức mạnh của kịch bản phía máy khách này có thể dẫn đến hậu quả rất nghiêm trọng như bỏ qua kiểm tra an ninh, nhận được một thông tin người dùng, hoặc rò rỉ dữ liệu.
Để test ta vào protected/controllers tạo XssController.php
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
<?php
class XssController extends CController
{
public function actionSimple()
{
echo 'Hello, '.$_GET['username'].'!';
}
}
Bây giờ vào trình duyệt gõ : http://localhost/skybook/xss/simple?username=Longt8x, tuy nhiên tài sản chính không xuất hiện lên output , ta gõ tiếp ; http://localhost/skybook/xss/simple?username=<script>alert(‘XSS’);</script>
Đương nhiên trong ví dụ này khách chỉ sử dụng một hàm alert thong số, nhưng trong form data của bạn chứa nhiều dữ liệu mật thì điều thất thoát là khó tránh, Yii đưa ra giải pháp sau:
class XssController extends CController
{
public function actionSimple()
{
echo 'Hello, '.CHtml::encode($_GET['username']).'!';
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
}
}
Đối với một link bạn chỉ cần sử dụng như sau:
echo CHtml::link(CHtml::encode($_GET['username']), array());
Tiếp theo sử dụng CHtmlPurifier giúp loại bỏ những thong tin thừa do phía máy khách tự nhập vào
public function actionHtml()
{
$this->beginWidget('CHtmlPurifier');
echo $_GET['html'];
$this->endWidget();
}
public function actionHtml()
{
$purifier=new CHtmlPurifier();
echo $purifier->purify($_GET['html']);
}
Ta vào trình duyệt gõ http://localhost/skybook/xss/html?html=Hello,<strong>username</strong>!<script>alert(‘XSS’);</script> sẽ có kết quả sau:
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
3. Sự kiện SQL injection
SQL injection là một loại injection code được sử dụng để làm tổn thương ở cấp cơ sở dữ liệu và cho phép thực hiện bất kỳ SQL cho phép người sử dụng độc hại để thực hiện hành động chẳng hạn như xóa dữ liệu hoặc nâng cao đặc quyền của họ.
Trước tiên vào phpmyadmin chọn sql và thêm query sau:
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(100) NOT NULL,
`password` varchar(32) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `user`(`id`,`username`,`password`) VALUES ( '1','Alex'
,'202cb962ac59075b964b07152d234b70');
INSERT INTO `user`(`id`,`username`,`password`) VALUES ( '2','Qiang
','202cb962ac59075b964b07152d234b70');
Tiếp theo generated model : User sử dụng Gii
Vào protected/controllers/ tạo SqlController.php
<?php
class SqlController extends CController
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
public function actionSimple()
{
$userName = $_GET['username'];
$password = md5($_GET['password']);
$sql = "SELECT * FROM user WHERE username = '$userName'
AND password = '$password' LIMIT 1;";
$user = Yii::app()->db->createCommand($sql)->queryRow();
if($user)
{
echo "Success";
}
else
{
echo "Failure";
}
}
}
Vào trình duyệt gõ : http://localhost/skybook/sql/simple?username=test&password=test bạn sẽ thấy trình duyệt in : Failure.
Bây giờ thay http://localhost/skybook/sql/simple?username=%27+or+%271%27%3D%271%2
7%3B+--&password=whatever.
Sẽ thấy ‘ hoặc ‘1’ =’1’; --
Nó tương đương với query :
SELECT * FROM user WHERE username = '' or
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'1'='1'; --' AND password = '008c5926ca861023c1d2a36653fd88e2'
LIMIT 1;
Để fix vấn đề này bạn thêm vào controller action mới:
public function actionPrepared()
{
$userName = $_GET['username'];
$password = md5($_GET['password']);
$sql = "SELECT * FROM user WHERE username = :username
AND password = :password LIMIT 1;";
$command = Yii::app()->db->createCommand($sql);
$command->bindValue('username', $userName);
$command->bindValue('password', $password);
$user = $command->queryRow();
if($user)
{
echo "Success";
}
else
{
echo "Failure";
}
}
Bây giờ vào http://localhost/skybook/sql/preparedSẽ hiện ra chữ failure, như vậy thong tin đã được bảo toàn.
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
4. Sự kiện CSRF
CSRF hay XSRF viết tắt cross-site là một request giả ,một vài thủ thuật sử dụng độc hại của trình duyệt user để âm thầm thực hiện một yêu cầu HTTP đến với trang web khi người dùng đăng nhập. Một ví dụ về cuộc tấn công này là chèn một thẻ hình ảnh vô hình với src trỏ đếnhttp://example.com/site/logout. Thậm chí nếu các thẻ hình ảnh được chèn vào trong một trang web khác,ngay lập tức bạn sẽ được đăng nhập từ example.com. Hậu quả của CSRF có thể sẽ rất nghiêm trọng: phá hủy dữ liệu trang web, ngăn chặn tất cả các người sử dụng trang web đăng nhập vào, phơi bày tin dữ liệu, và hơn thế nữa…
Một số thực trạng của CSRF:
Như CSRF nên được thực hiện bởi người sử dụng trình duyệt của nạn nhân, kẻ tấn công có thể không thường thay đổi tiêu đề HTTP được gửi.
Yii include một token generation và token checking. Thêm nữa có thể tự động insert một token trong HTML form:
Vào protected/config/main.php thêm đoạn sau:
'components'=>array(
…
'request'=>array(
'enableCsrfValidation'=>true,
),
…
),
Sauk hi cấu hình ứng dụng bạn sử dụng C:Html::beginForm và CHtml::endForm khởi tạo của HTML form.
public function actionCreate()
{
echo CHtml::beginForm();
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
echo CHtml::submitButton();
echo CHtml::endForm();
}
Yii sẽ tự động thêm một token field ẩn sau:
<form action="/csrf/create" method="post">
<div style="display:none"><input type="hidden" value="e4d1021e79ac
269e8d6289043a7a8bc154d7115a" name="YII_CSRF_TOKEN" />
Nếu bạn lưu form html và thử submit,bạn sẽ nhận được một lời thong báo giống như sau:
Bảo mật mức cao:
Nếu ứng dụng của bạn yêu cầu độ bảo mật cao hơn nữa, bạn hãy vào protected/config/main.php
'components' => array(
...
'user'=>array(
// enable cookie-based authentication
'allowAutoLogin'=>false,
),
...
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
),
Sau đó cho session timeout hoạt động
'components' => array(
...
'session' => array(
'timeout' => 200,
),
...
),
Sử dụng thuộc tính GET và POST
HTTP khẳng định không sử dụng GET cho các hoạt động thay đổi dữ liệu hoặc trạng thái. Gắn bó với nguyên tắc này là một cách tốt. Nó không ngăn cản tất cả các loại CSRF, nhưng ít nhất sẽ làm cho một số mũi trích như <img src = trở lên vô nghĩa....
5 . Sử dụng RBAC
RBAC là phương pháp kiểm soát truy cập mạnh mẽ nhất có sẵn trong Yii. Nó được mô tả trong tài liệu hướng dẫn,nhưng kể từ khi nó là khá phức tạp và mạnh mẽ, nó không phải là dễ dàng như vậy để hiểu làm thế nào nó thực sựhoạt động mà không nhận được hood .Trong bài này, chúng ta sẽ làm rõ vai trò của hệ thống phân cấp từ tiêu chuẩn hướng dẫn, import nó, và giải thích những gì đang xảy ra trong nội bộ.
Vào protected/config/main.php sửa lại thong số giống như sau:
return array(
'components'=>array(
…
'authManager'=>array(
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
'class'=>'CDbAuthManager',
'connectionID'=>'db',
),
),
…
);
Thêm vào protected/components/UserIdentity.php đoạn sau:
$users=array(
// username => password
'demo'=>'demo',
'admin'=>'admin',
'readerA'=>'123',
'authorB'=>'123',
'editorC'=>'123',
'adminD'=>'123',
);
Tạo protected/controllers/RbacController.php
<?php
class RbacController extends CController
{
public function filters()
{
return array(
'accessControl',
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
);
}
public function accessRules()
{
return array(
array(
'allow',
'actions' => array('deletePost'),
'roles' => array('deletePost'),
),
array(
'allow',
'actions' => array('init', 'test'),
),
array('deny'),
);
}
public function actionInit()
{
$auth=Yii::app()->authManager;
$auth->createOperation('createPost','create a post');
$auth->createOperation('readPost','read a post');
$auth->createOperation('updatePost','update a post');
$auth->createOperation('deletePost','delete a post');
$bizRule='return Yii::app()->user->id==$params
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
["post"]->authID;';
$task=$auth->createTask('updateOwnPost','update a
post by author himself',$bizRule);
$task->addChild('updatePost');
$role=$auth->createRole('reader');
$role->addChild('readPost');
$role=$auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
$role=$auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
$role=$auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');
$auth->assign('reader','readerA');
$auth->assign('author','authorB');
$auth->assign('editor','editorC');
$auth->assign('admin','adminD');
echo "Done.";
}
public function actionDeletePost()
{
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
echo "Post deleted.";
}
public function actionTest()
{
$post = new stdClass();
$post->authID = 'authorB';
echo "Current permissions:<br />";
echo "<ul>";
echo "<li>Create post: ".Yii::app()->user->checkAccess
('createPost')."</li>";
echo "<li>Read post: ".Yii::app()->user->checkAccess
('readPost')."</li>";
echo "<li>Update post: ".Yii::app()->user->checkAccess
('updatePost', array('post' => $post))."</li>";
echo "<li>Delete post: ".Yii::app()->user->checkAccess
('deletePost')."</li>";
echo "</ul>";
}
}
Bây giờ vào localhost/skybook/rbac/init để xem kết quả
http://www.seodrupal.vn | Learn Drupal Online
153
[YII 1.1 APP DEVELOPMENT COOKBOOK ] August 28, 2012
Đặt tên các nút RBAC Một hệ thống cấp bậc phức tạp trở nên khó hiểu mà không cần sử dụng một số loại đặt tên một hội nghị. Một quy ước giúp không làm cho chúng ta nhầm lẫn như sau: [Group_] [own_] entity_action Trường hợp riêng được sử dụng khi quy tắc xác định khả năng sửa đổi một phần tử chỉ khi người sử dụng hiện nay là chủ sở hữu của các yếu tố và nhóm chỉ là một không gian tên. Thực thể là một tên của thực thể, chúng tôi đang làm việc và hành động là hành động mà chúng tôi đang thực hiện. Ví dụ, nếu chúng ta cần phải tạo ra một quy tắc để xác định nếu người dùng có thể xóa một bài đăng blog, chúng tôi sẽ đặt tên nó như là blog_post_delete. Nếu quy tắc xác định nếu một người dùng có thể chỉnh sửa các blog của riêng bình luận, tên sẽ được blog_own_comment_edit. Một cách để giữ cho hệ thống phân cấp đơn giản và hiệu quả Theo những đề nghị khi có thể để tối đa hóa hiệu suất và giảm hệ thống phân cấp phức tạp: ff Tránh gắn nhiều vai trò một người dùng duy nhất. ff Không kết nối các nút cùng loại. Vì vậy, ví dụ, tránh kết nối một trong những nhiệm vụ một số khác. Để giữ cho hệ thống phân cấp còn đơn giản, chúng ta có thể tránh việc tạo ra và sử dụng các nút thêm trong một số trường hợp bằng cách thay thế chúng bằng các điều kiện bổ sung. Một ví dụ là các sửa đổi của Post. Chúng ta có thể tạo ra một nút blog_own_post_edit với bizRule như sau: return Yii::app()->user->id==$params["post"]->author_id;Ngoài ra, chúng ta có thể thêm cùng một logic thường xuyên lựa chọn bài như sau: $post = Post::model()->findByAttributes(array(
'id' => $id,
'author_id' => Yii::app()->user->id,
));
If(!$post)
throw new CHttpException(404);
Bằng cách sử dụng cách thứ hai, chúng ta sẽ tránh nhận được một nút hệ thống phân cấp RBAC do lưu kho.
http://www.seodrupal.vn | Learn Drupal Online