SilverFire - Dmitry Naumenko
6 years ago
86 changed files with 3344 additions and 768 deletions
@ -0,0 +1,119 @@
|
||||
Các thành phần ứng dụng |
||||
====================== |
||||
|
||||
Mỗi ứng dụng là hiện thực của [mẫu thiết kế Service Locators](concept-service-locator.md). Mỗi ứng dụng sẽ chứa các thành phần |
||||
được gọi là *thành phần ứng dụng* giúp cung cấp các dịch vụ cho các tiến trình xử lý. Chẳng hạn, |
||||
thành phần `urlManager` đảm nhiệm chức năng cho bộ định tuyến cho các yêu cầu xử lý tới các bộ điều khiển; |
||||
thành phần `db` cung cấp các dịch vụ để giao tiếp với cơ sở dữ liệu (CSDL); và các thành phần khác. |
||||
|
||||
Mỗi thành phần ứng dụng đều có một định danh ID giúp xác định thành phần duy nhất trong cùng một ứng dụng |
||||
. Bạn có thể truy cập vào các thành phần ứng dụng qua câu lệnh sau. |
||||
|
||||
```php |
||||
\Yii::$app->componentID |
||||
``` |
||||
|
||||
Ví dụ, sử dụng câu lệnh `\Yii::$app->db` để lấy thông tin [[yii\db\Connection|kết nối tới CSDL]], |
||||
và câu lệnh `\Yii::$app->cache` để lấy thông tin [[yii\caching\Cache|primary cache]] đã đăng ký trong ứng dụng. |
||||
|
||||
Mỗi thành phần ứng dụng được tạo một lần và được truy cập trong ứng dụng. Và có bất kỳ sự truy cập nào |
||||
sau đó đều trả về cùng một thể hiện của thành phần đó. |
||||
|
||||
Bất kỳ đối tượng nào cũng có thể là thành phần ứng dụng. Bạn có thể đăng ký chúng bằng việc thiết lập các |
||||
thuộc tính [[yii\base\Application::components]] vào trong [mục cấu hình ứng dụng](structure-applications.md#application-configurations). |
||||
Ví dụ, |
||||
|
||||
```php |
||||
[ |
||||
'components' => [ |
||||
// Dung class để đăng ký thành phần "cache" |
||||
'cache' => 'yii\caching\ApcCache', |
||||
|
||||
// Dùng mảng các tham số để đăng ký thành phần "db" |
||||
'db' => [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'mysql:host=localhost;dbname=demo', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
], |
||||
|
||||
// Dùng hàm để đăng ký thành phần "search" |
||||
'search' => function () { |
||||
return new app\components\SolrService; |
||||
}, |
||||
], |
||||
] |
||||
``` |
||||
|
||||
> Lưu ý: Bạn cần đăng ký các thành phần ứng dụng một cách cẩn thận. |
||||
Các thành phần ứng dụng cũng như các biến có phạm vi toàn cục. Sử dụng quá nhiều các thành phần ứng dụng có thể khiến mã nguồn |
||||
khó kiểm tra và bảo trì. Cách tốt nhất, bạn nên khởi tạo các thành phần ở phạm vi cục bộ |
||||
và khi cần thiết có thể thêm vào ứng dụng. |
||||
|
||||
|
||||
## Thành phần tải tự động (Bootstrapping) <span id="bootstrapping-components"></span> |
||||
|
||||
Như đề cập ở trên, các thành phần ứng dụng chỉ được khởi tạo khi nó được truy cập vào lần đầu tiên. |
||||
Nếu thành phần không được truy cập tại các yêu cầu xử lý, thì sẽ không được khởi tạo. Tuy vậy , thỉnh thoảng, các thành phần ứng dụng |
||||
có thể được khởi tạo ở mỗi yêu cầu, thậm chí nó không được truy cập. |
||||
Để làm được như vậy, bạn cần liệt kê các định danh vào trong thuộc tinh [[yii\base\Application::bootstrap|bootstrap]] của ứng dụng application. |
||||
|
||||
Ví dụ, thông tin cấu hình sau sẽ chắc chắn rằng thành phần `log` luôn luôn được tải: |
||||
|
||||
```php |
||||
[ |
||||
'bootstrap' => [ |
||||
'log', |
||||
], |
||||
'components' => [ |
||||
'log' => [ |
||||
// Các thiết lập cho thành phần "log" |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
|
||||
## Các thành phần ứng dụng chính <span id="core-application-components"></span> |
||||
|
||||
Yii định nghĩa danh sách các thành phần ứng dụng chính cùng với nó là các định danh và thông tin cấu hình. Ví dụ, |
||||
thành phần [[yii\web\Application::request|request]] được dùng để lấy thông tin về các yêu cầu từ user |
||||
và xác minh rồi gửi tới các [bộ định tuyến (route)](runtime-routing.md); thành phần [[yii\base\Application::db|db]] |
||||
có chức năng thiết lập các kết nối và thông qua đó bạn có thể thực hiện các truy vấn vào CSDL. |
||||
Như vậy, các thành phần ứng dụng sẽ giúp ứng dụng Yii tiếp nhận các yêu cầu từ user. |
||||
|
||||
Phần dưới là danh sách các thành phần ứng dụng chính được xác định trước. Bạn cần phải cấu hình và tùy biến chúng |
||||
như những thành phần ứng dụng khác. Mỗi khi bạn cấu hình các thành phần này, |
||||
nếu bạn không xác định các class, thì giá trị mặc định sẽ được dùng. |
||||
|
||||
* [[yii\web\AssetManager|assetManager]]: quản lý các file tài nguyên (asset) được đóng gói và chia sẽ. |
||||
Tham khảo thêm mục [Quản lý các file tài nguyên](structure-assets.md) để biết thêm chi tiết. |
||||
* [[yii\db\Connection|db]]: thực hiện kết nối CSDL và dựa vào thành phần có thể thực hiện các câu lệnh truy vấn dữ liệu. |
||||
Lưu ý, khi bạn thiết lập thành phần này, bạn cần phải cung cấp các thông tin về các thuộc tính được yêu cầu |
||||
, như [[yii\db\Connection::dsn]]. |
||||
Tham khảo thêm tại mục [Data Access Objects](db-dao.md) để biết thêm thông tin. |
||||
* [[yii\base\Application::errorHandler|errorHandler]]: nắm giữ các ngoại lệ và lỗi của PHP. |
||||
Tham khảo thêm mục [Bắt lỗi](runtime-handling-errors.md) để biết thêm thông tin. |
||||
* [[yii\i18n\Formatter|formatter]]: định dạng dữ liệu mỗi khi gửi tới user. Ví dụ, các số có thể được |
||||
hiển thị cùng với các dấu ngăn cách phần ngàn, ngày có thể được định dạng ở dạng ngày dài. |
||||
Tham khảo thêm tại mục [Định dạng dữ liệu](output-formatting.md) để biết thêm thông tin. |
||||
* [[yii\i18n\I18N|i18n]]: hỗ trợ định dạng và dịch đa ngôn ngữ. |
||||
Tham khảo thêm tại mục [Internationalization](tutorial-i18n.md) để biết thêm thông tin. |
||||
* [[yii\log\Dispatcher|log]]: quản lý mục log. |
||||
Tham khảo thêm tại mục [Logging](runtime-logging.md) để biết thêm thông tin. |
||||
* [[yii\swiftmailer\Mailer|mail]]: hỗ trợ soạn thảo và gửi email. |
||||
Tham khảo thêm tại mục [Mailing](tutorial-mailing.md) để biết thêm thông tin.. |
||||
* [[yii\base\Application::response|response]]: represents the response being sent to end users. |
||||
Tham khảo thêm tại mục [Responses](runtime-responses.md) để biết thêm thông tin.. |
||||
* [[yii\base\Application::request|request]]: tiếp nhận các yêu cầu từ user. |
||||
Tham khảo thêm tại mục [Requests](runtime-requests.md) để biết thêm thông tin.. |
||||
* [[yii\web\Session|session]]: quản lý các phiên (session). Thành phần này chỉ được kích hoạt với |
||||
[[yii\web\Application|Ứng dụng Web]]. |
||||
Tham khảo thêm tại mục [Sessions and Cookies](runtime-sessions-cookies.md) để biết thêm thông tin.. |
||||
* [[yii\web\UrlManager|urlManager]]: xử lý thông tin về URL. |
||||
Tham khảo thêm tại mục [URL Parsing and Generation](runtime-routing.md) để biết thêm thông tin.. |
||||
* [[yii\web\User|user]]: giúp xác thực người dùng. Thành phần này chỉ được kích hoạt với |
||||
[[yii\web\Application|Ứng dụng Web]] |
||||
Tham khảo thêm tại mục [Xác thực (Authentication)](security-authentication.md) để biết thêm thông tin.. |
||||
* [[yii\web\View|view]]: hỗ trợ giao diện. |
||||
Tham khảo thêm tại mục[Views](structure-views.md) để biết thêm thông tin.. |
@ -0,0 +1,607 @@
|
||||
Ứng dụng |
||||
============ |
||||
|
||||
Mỗi ứng dụng là một đối tượng giúp quản lý tổng thể cấu trúc và vòng đời của ứng dụng Yii. |
||||
Mỗi ứng dụng Yii đều chứa một đối tượng ứng dụng, đối tượng này được khởi tạo tại mục |
||||
[entry script](structure-entry-scripts.md) và đồng thời được truy cập qua biểu thức `\Yii::$app`. |
||||
|
||||
> Gợi ý: Phụ thuộc vào từng ngữ cảnh, có khi chúng ta gọi là "một application", có nghĩa là một đối tượng ứng dụng |
||||
hoặc một hệ thống ứng dụng. |
||||
|
||||
Có 2 kiểu ứng dụng: [[yii\web\Application|Ứng dụng Web]] và |
||||
[[yii\console\Application|ứng dụng giao diện dòng lệnh]]. Tương tự như vậy, ứng dụng Web xử lý với các yêu cầu về Web, |
||||
, ứng dụng còn lại sẽ xử lý với các yêu cầu ở giao diện dòng lệnh. |
||||
|
||||
|
||||
## Cấu hình ứng dụng <span id="application-configurations"></span> |
||||
|
||||
Mỗi khi [entry script](structure-entry-scripts.md) tạo ứng dụng mới, nó sẽ tải thêm thông tin về |
||||
[cấu hình](concept-configurations.md) và gán vào trong ứng dụng, như sau: |
||||
|
||||
```php |
||||
require(__DIR__ . '/../vendor/autoload.php'); |
||||
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); |
||||
|
||||
// tải các cấu hình ứng dụng |
||||
$config = require(__DIR__ . '/../config/web.php'); |
||||
|
||||
// gán cấu hình và khởi tạo ứng dụng |
||||
(new yii\web\Application($config))->run(); |
||||
``` |
||||
|
||||
Thông thường việc [cấu hình](concept-configurations.md), ứng dụng sẽ xác định làm thế nào để |
||||
khởi tạo các thuộc tính và đối tượng ứng dụng. Do việc cấu hình ứng dụng khá phức tạp nên vậy |
||||
, chúng thường được lưu giữ tại [các file cấu hình](concept-configurations.md#configuration-files), |
||||
như file `web.php` ở ví dụ trên. |
||||
|
||||
|
||||
## Các thuộc tính của ứng dụng <span id="application-properties"></span> |
||||
|
||||
Có nhiều thuộc tính quan trọng mà bạn cần phải cấu hình trong ứng dụng. Những thuộc tính này |
||||
thường được mô tả về môi trường mà ứng dụng đang chạy. Chẳng hạn, ứng dụng cần biết làm thế nào để tải các [controllers](structure-controllers.md), |
||||
nơi lưu trữ các file tạm, vv. Trong phần dưới này, chúng ta sẽ tổng hợp thông tin về thuộc tính. |
||||
|
||||
|
||||
### Thuộc tính bắt buộc <span id="required-properties"></span> |
||||
|
||||
Ở mỗi ứng dụng, bạn cần cấu hình ít nhất 2 thuộc tính là: [[yii\base\Application::id|id]] |
||||
và [[yii\base\Application::basePath|basePath]]. |
||||
|
||||
|
||||
#### [[yii\base\Application::id|id]] <span id="id"></span> |
||||
|
||||
Thuộc tính [[yii\base\Application::id|id]] giúp đặc tả một định danh ID để phân biệt với các ứng dụng khác |
||||
. Thuộc tính chủ yếu được sử dụng trong chương trình. Mặc dù nó không được yêu cầu, để thích hợp cho khả năng tương tác |
||||
nên chỉ sử dụng các chữ cái chữ số khi mô tả một định danh của ứng dụng. |
||||
|
||||
|
||||
#### [[yii\base\Application::basePath|basePath]] <span id="basePath"></span> |
||||
|
||||
Thuộc tính [[yii\base\Application::basePath|basePath]] dùng để mô tả thư mục gốc của ứng dụng. |
||||
Nó là thư mục chứa tất cả mã nguồn của ứng dụng. Bên trong thư mục, |
||||
bạn sẽ thấy các thư mục con như `models`, `views`, và `controllers`, các thư mục con này chứa các mã nguồn |
||||
tương ứng với các thành phần trong mô hình MVC. |
||||
|
||||
Bạn phải cấu hình thuộc tính [[yii\base\Application::basePath|basePath]] bằng sử dụng các đường dẫn trực tiếp |
||||
hoặc [một bí danh](concept-aliases.md). Trong các trường hợp, các thư mục tương ứng phải tồn tại, nếu không sẽ phát sinh ra lỗi |
||||
. Đường dẫn trực tiếp được lấy qua việc gọi hàm `realpath()` . |
||||
|
||||
Thuộc tính [[yii\base\Application::basePath|basePath]] thường được dùng để lấy được các đường dẫn quan trọng khác |
||||
(vd đường dẫn dành cho thực thi). Vì vậy, bí danh `@app` được xác định là đường dẫn gốc |
||||
. Các đường dẫn trong ứng dụng được lấy từ bí danh (vd `@app/runtime` tương ứng tới đường dẫn mục runtime). |
||||
|
||||
|
||||
### Các thuộc tính quan trọng <span id="important-properties"></span> |
||||
|
||||
Các thuộc tính được mô tả trong phần này thường cần được cấu hình bởi vì mỗi ứng dụng có |
||||
các thuộc tính khác nhau. |
||||
|
||||
|
||||
#### [[yii\base\Application::aliases|aliases]] <span id="aliases"></span> |
||||
|
||||
Thuộc tính cho phép khai báo các [bí danh(aliases)](concept-aliases.md) vào trong một mảng. |
||||
Các khóa lưu trữ tên bí danh, và giá trị trong mảng tương ứng với đường dẫn được khai báo. |
||||
Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'aliases' => [ |
||||
'@name1' => 'path/to/path1', |
||||
'@name2' => 'path/to/path2', |
||||
], |
||||
] |
||||
``` |
||||
|
||||
Thuộc tính này được cung cấp cho bạn việc khai báo các bí danh trong cấu hình ứng dụng thay vì gọi phương thức |
||||
[[Yii::setAlias()]]. |
||||
|
||||
|
||||
#### [[yii\base\Application::bootstrap|bootstrap]] <span id="bootstrap"></span> |
||||
|
||||
Thuộc tính này khá quan trọng. Nó cung cấp cho bạn thông tin về mảng các thành phần (components) mà cần được |
||||
chạy trong suốt chu trình ứng dụng [[yii\base\Application::bootstrap()|bootstrapping process]]. |
||||
Ví dụ, nếu bạn muốn một [module](structure-modules.md) dùng để tùy biến các [URL](runtime-routing.md), |
||||
bạn có thể tùy biến các ID như phần tử trong các thuộc tính. |
||||
|
||||
Mỗi thành phần được liệt kê ra có thể khai báo một trong các định dạng sau: |
||||
|
||||
- một đinh danh về thành phần được tuân thủ qua [components](#components), |
||||
- một định danh về module tuân thủ theo quy định về [modules](#modules), |
||||
- một tên class, |
||||
- một mảng các cấu hình, |
||||
- một hàm dùng để khởi tạo và trả về một thành phần. |
||||
|
||||
Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'bootstrap' => [ |
||||
// một định danh về thành phần hoặc module |
||||
'demo', |
||||
|
||||
// tên class |
||||
'app\components\Profiler', |
||||
|
||||
// mảng cấu hình |
||||
[ |
||||
'class' => 'app\components\Profiler', |
||||
'level' => 3, |
||||
], |
||||
|
||||
// hàm trả về một thành phần |
||||
function () { |
||||
return new app\components\Profiler(); |
||||
} |
||||
], |
||||
] |
||||
``` |
||||
|
||||
> Lưu ý: Nếu định danh của module trùng với định danh của thành phần , ứng dụng sẽ sử dụng |
||||
> trong suốt tiền trình xử lý. Nếu bạn muốn chỉ sử dụng mỗi module, bạn cần lấy nó ở một hàm khác |
||||
> như sau: |
||||
> |
||||
> ```php |
||||
> [ |
||||
> function () { |
||||
> return Yii::$app->getModule('user'); |
||||
> }, |
||||
> ] |
||||
> ``` |
||||
|
||||
|
||||
Trong suốt quá trình xử lý, mỗi thành phần sẽ được khởi tạo. nếu lớp thành phần được hiện thực từ giao diện |
||||
[[yii\base\BootstrapInterface]], thì phương thức [[yii\base\BootstrapInterface::bootstrap()|bootstrap()]] |
||||
sẽ đồng thời được gọi. |
||||
|
||||
Một ví dụ khác trong việc cấu hình ứng dụng trong [Mẫu Basic Project](start-installation.md), |
||||
module `debug` và `gii` được cấu hình như những thành phần khi ứng dụng khởi chạy |
||||
ở môi trường phát triển: |
||||
|
||||
```php |
||||
if (YII_ENV_DEV) { |
||||
// cấu hình được thiết lập trong môi trường phát triển 'dev' |
||||
$config['bootstrap'][] = 'debug'; |
||||
$config['modules']['debug'] = 'yii\debug\Module'; |
||||
|
||||
$config['bootstrap'][] = 'gii'; |
||||
$config['modules']['gii'] = 'yii\gii\Module'; |
||||
} |
||||
``` |
||||
|
||||
> Lưu ý: Việc đưa quá nhiều các thành phần vào `bootstrap` sẽ làm giảm hiệu năng trong ứng dụng, bởi vì |
||||
mỗi khi có yêu cầu, các thành phần sẽ được chạy. Vì vậy việc sử dụng các thành phần cần sử dụng một cách khôn ngoan. |
||||
|
||||
|
||||
#### [[yii\web\Application::catchAll|catchAll]] <span id="catchAll"></span> |
||||
|
||||
Thuộc tính này chỉ được hỗ trợ với [[yii\web\Application| ứng dụng Web]]. Nó mô tả một |
||||
[hành động](structure-controllers.md) và nhận xử lý mọi yêu cầu. Thường được sử dụng mỗi khi |
||||
ứng dụng đang ở chế độ bảo trì và cần xử lý mọi yêu cầu được gửi tới. |
||||
|
||||
Thông tin được cấu hình bao gồm mảng và chứa thông tin về router và action. |
||||
Các thông tin mô tả các tham số (thông tin khóa-giá trị) để giới hạn các action. Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'catchAll' => [ |
||||
'offline/notice', |
||||
'param1' => 'value1', |
||||
'param2' => 'value2', |
||||
], |
||||
] |
||||
``` |
||||
|
||||
|
||||
#### [[yii\base\Application::components|components]] <span id="components"></span> |
||||
|
||||
Đây là thuộc tính quan trọng nhất. Nó cho phép đăng ký danh sách cách component để sử dụng ở các mục khác |
||||
được gọi là [application components](structure-application-components.md). Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'components' => [ |
||||
'cache' => [ |
||||
'class' => 'yii\caching\FileCache', |
||||
], |
||||
'user' => [ |
||||
'identityClass' => 'app\models\User', |
||||
'enableAutoLogin' => true, |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
Mỗi thành phần ứng dụng đều xác định một mảng các thông tin chứa cặp key-value. Giá trị key đại diện cho định danh của thành phần, |
||||
trong khi đó value đại diện cho tên class hoặc thông tin về [cấu hình](concept-configurations.md). |
||||
|
||||
Bạn có thể đăng ký bất kỳ thành phần nào vào ứng dụng, và các thành phần có thể truy cập ở phạm vi toàn cục |
||||
qua biểu thức `\Yii::$app->componentID`. |
||||
|
||||
Xem thêm mục [Application Components](structure-application-components.md) để biết thêm thông tin. |
||||
|
||||
|
||||
#### [[yii\base\Application::controllerMap|controllerMap]] <span id="controllerMap"></span> |
||||
|
||||
Thuộc tính này cho phép liên kết tới một định danh (ID) tới lớp của trình điều khiển. Mặc định, Yii sẽ liên kết |
||||
ID tới các lớp của trình điều khiển dựa trên [các nguyên tắc](#controllerNamespace) (chẳng hạn định danh ID của trình điều khiển `post` sẽ liên kết |
||||
tới lớp `app\controllers\PostController`). Bằng việc cấu hình những thuộc tính này, bạn có thay đổi các nguyên tắc này cho các trình điều khiển cụ thể |
||||
. Trong ví dụ sau, `account` sẽ được liên kết tới class |
||||
`app\controllers\UserController`, trong khi đó `article` sẽ liên kết tới class `app\controllers\PostController`. |
||||
|
||||
```php |
||||
[ |
||||
'controllerMap' => [ |
||||
[ |
||||
'account' => 'app\controllers\UserController', |
||||
'article' => [ |
||||
'class' => 'app\controllers\PostController', |
||||
'enableCsrfValidation' => false, |
||||
], |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
Danh sách khóa của các thuộc tính trên đại diện cho ID của trình điều khiển, giá trị của mỗi khóa sẽ đại diện về thông tin |
||||
tên class của trình điều khiển hoặc [các thông tin về cấu hình](concept-configurations.md). |
||||
|
||||
|
||||
#### [[yii\base\Application::controllerNamespace|controllerNamespace]] <span id="controllerNamespace"></span> |
||||
|
||||
Thuộc tính này xác định các thông tin tên lớp mặc định của trình điều khiển. Mặc định là |
||||
`app\controllers`. Nếu ID của trình điều khiển là `post`, theo quy ước thì tên class của trình điều khiển (không bao gồm |
||||
không gian tên) sẽ là `PostController`, và tên lớp đầy đủ sẽ là `app\controllers\PostController`. |
||||
|
||||
Các lớp trình điều khiển thường được lưu trữ ở thư mục con của thư mục chính các không gian tên. |
||||
Chẳng hạn, với ID của trình điều khiển `admin/post`, tương ứng với tên lớp đầy đủ sẽ là |
||||
`app\controllers\admin\PostController`. |
||||
|
||||
Điều này khá quan trọng vì các lớp điều khiển có thể được [tải tự động](concept-autoloading.md) |
||||
và các không gian tên của các lớp điều khiển sẽ khớp với giá trị của các thuộc tính. Nếu không thì, |
||||
bạn sẽ nhận thông báo lỗi "Không tìm thấy trang" khi truy cập vào ứng dụng. |
||||
|
||||
Trong trường hợp khác, nếu bạn muốn bỏ các quy ước này như mổ tả ở trên, bạn có thể tùy chỉnh lại các thuộc tính trong phần [controllerMap](#controllerMap). |
||||
|
||||
|
||||
#### [[yii\base\Application::language|language]] <span id="language"></span> |
||||
|
||||
Thuộc tính này mô tả thông tin về ngôn ngữ trong mỗi ứng dụng và nội dung được hiển thị tới user. |
||||
Giá trị mặc định của thuộc tính là `en`, có nghĩa là tiếng Anh. Bạn có thể tùy chỉnh thuộc tính này |
||||
rằng nếu ứng dụng của bạn hỗ trợ đa ngôn ngữ . |
||||
|
||||
Giá trị của thuộc tính được xác định theo chuẩn [quốc tế hóa](tutorial-i18n.md), |
||||
bao gồm các thông tin, định dạng ngày giờ, số, vv. Ví dụ, widget [[yii\jui\DatePicker]] |
||||
sẽ sử dụng các giá trị thuộc tính qua việc xác định ngôn ngữ nào cần được hiển thị và định dạng ngày giờ như thế nào. |
||||
|
||||
Khuyến khích bạn xác định các ngôn ngữ dựa theo [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag). |
||||
Ví dụ, `en` là chuẩn cho tiếng anh, trong khi đó `en-US` chuẩn cho tiếng anh ở Mỹ (United States). |
||||
|
||||
Xem thêm thông tin về thuộc tính này tại mục [Internationalization](tutorial-i18n.md). |
||||
|
||||
|
||||
#### [[yii\base\Application::modules|modules]] <span id="modules"></span> |
||||
|
||||
Thuộc tính này mô tả các thông tin về [modules](structure-modules.md) được chứa trong ứng dụng. |
||||
|
||||
Thuộc tính này chứa mảng các lớp về module hoặc thông tin về [cấu hình](concept-configurations.md) chứa mảng các khóa |
||||
về các định danh của module. Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'modules' => [ |
||||
// "booking" mô tả tên class |
||||
'booking' => 'app\modules\booking\BookingModule', |
||||
|
||||
// "comment" được mô tả với mảng cấu hình |
||||
'comment' => [ |
||||
'class' => 'app\modules\comment\CommentModule', |
||||
'db' => 'db', |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
Tham khảo thêm ở phần [Modules](structure-modules.md) để biết thêm thông tin. |
||||
|
||||
|
||||
#### [[yii\base\Application::name|name]] <span id="name"></span> |
||||
|
||||
Thuộc tính này mô tả tên của ứng dụng và hiển thị tới user. Khác với thuộc tính |
||||
[[yii\base\Application::id|id]], cần phải là tên duy nhất, thì thuộc tính này dùng với mục đích để hiển thị tới user; |
||||
không cần thiết phải là tên duy nhất. |
||||
|
||||
Nếu trong mã nguồn bạn không cần phải dùng tới nó thì bạn không cần phải thiết lập. |
||||
|
||||
|
||||
#### [[yii\base\Application::params|params]] <span id="params"></span> |
||||
|
||||
Thuộc tính này là mảng chứa các tham số mà có thể truy cập trong ứng dụng ở phạm vi toàn cầu. Thay vì trong mã |
||||
nguồn của bạn cần được mã hóa bởi số và ký tự, đây là cách tốt để định nghĩa các tham số của ứng dụng, định nghĩa một lần |
||||
và có thể được truy cập ở mọi nơi. Ví dụ, bạn có thể định nghĩa kích thước ảnh thumbnail |
||||
với kích thước như sau: |
||||
|
||||
```php |
||||
[ |
||||
'params' => [ |
||||
'thumbnail.size' => [128, 128], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
Bạn có thể thực hiện dòng lệnh sau để lấy tham số về kích thước ảnh thumbnail: |
||||
|
||||
```php |
||||
$size = \Yii::$app->params['thumbnail.size']; |
||||
$width = \Yii::$app->params['thumbnail.size'][0]; |
||||
``` |
||||
|
||||
Bạn có thể thay đổi kích thước ảnh thumbnail sau đó, bạn chỉ cần thay đổi vào trong mục cấu hình ứng dụng; |
||||
bạn không cần phải đụng chạm vào mã nguồn của bạn. |
||||
|
||||
|
||||
#### [[yii\base\Application::sourceLanguage|sourceLanguage]] <span id="sourceLanguage"></span> |
||||
|
||||
Thuộc tính mô tả về ngôn ngữ được sử dụng để viết mã nguồn của bạn. Giá trị mặc đinh là `'en-US'`, |
||||
nghĩa là tiếng Anh Mỹ(United States). Bạn nên cấu hình thuộc tính này nếu nội dung trong mã nguồn của bạn không phải là tiếng Anh. |
||||
|
||||
Giống như thuộc tính [language](#language), you should configure this property in terms of |
||||
an [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag). Ví dụ, `en` chuẩn cho tiếng Anh, |
||||
trong khi `en-US` chuẩn cho tiếng Anh Mỹ (United States). |
||||
|
||||
Xem thêm trong phần [Quốc tế hóa](tutorial-i18n.md) để hiểu thêm thuộc tính này. |
||||
|
||||
|
||||
#### [[yii\base\Application::timeZone|timeZone]] <span id="timeZone"></span> |
||||
|
||||
Thuộc tính này cung cấp cách khác để thiết lập time zone trong PHP. |
||||
Qua việc cấu hình thuộc tính này, chủ yếu được gọi qua hàm |
||||
[date_default_timezone_set()](http://php.net/manual/en/function.date-default-timezone-set.php). Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'timeZone' => 'America/Los_Angeles', |
||||
] |
||||
``` |
||||
|
||||
|
||||
#### [[yii\base\Application::version|version]] <span id="version"></span> |
||||
|
||||
Thuộc tính mô tả về phiên bản của ứng dụng. Mặc định là `'1.0'`. Bạn không cần phải thiết lập thuộc tính này nếu như |
||||
trong mã nguồn của bạn không dùng tới. |
||||
|
||||
|
||||
### Các thuộc tính thông dụng <span id="useful-properties"></span> |
||||
|
||||
Những thuộc tính được mô tả trong phần dưới thường có sự cấu hình khác nhau bởi vì các giá trị thường khác nhau |
||||
. Tuy nhiên, nêu bạn muốn thay đổi giá trị mặc định, bạn có thể cấu hình theo cách của bạn. |
||||
|
||||
|
||||
#### [[yii\base\Application::charset|charset]] <span id="charset"></span> |
||||
|
||||
Thuộc tính này mô tả các bộ ký tự mà ứng dụng sử dụng. Mặc định là `'UTF-8'`, hầu hết các ứng dụng đều sử dụng. |
||||
|
||||
|
||||
#### [[yii\base\Application::defaultRoute|defaultRoute]] <span id="defaultRoute"></span> |
||||
|
||||
Thuộc tính này mô tả các [route](runtime-routing.md), ứng dụng sẽ dùng route này để thực hiện khi có yêu cầu |
||||
gửi đến mà không được mô tả. Mỗi router gồm có các module ID, a controller ID, hoặc có thể là một action ID. |
||||
Ví dụ, `help`, `post/create`, hoặc `admin/post/create`. Nếu action ID không khai báo, thuộc tính sẽ lấy giá trị mặc định |
||||
được mô tả trong [[yii\base\Controller::defaultAction]]. |
||||
|
||||
Đối với [[yii\web\Application| Ứng dụng Web ]], giá trị mặc định của thuộc tính là `'site'`, nghĩa là |
||||
trình điều khiển `SiteController` được gọi và một hành động mặc định được sử dụng. Như vậy, nếu bạn |
||||
truy cập vào ứng dụng mà không cung cấp thông tin route, thì ứng dụng mặc định sẽ trả về hành động `app\controllers\SiteController::actionIndex()`. |
||||
|
||||
Đối với [[yii\console\Application| Ứng dụng console]], thì giá trị mặc định là `'help'`, đồng nghĩa hành động |
||||
[[yii\console\controllers\HelpController::actionIndex()]] sẽ được gọi. Như vậy, nếu bạn chạy dòng lệnh `yii` |
||||
mà không cung cấp các tham số nào khác, thì nó sẽ hiển thị lên màn hình trợ giúp tương ứng kết quả của action index của trình điều khiển HelpController. |
||||
|
||||
|
||||
#### [[yii\base\Application::extensions|extensions]] <span id="extensions"></span> |
||||
|
||||
Thuộc tính này mô tả về danh sách các [thành phần mở rộng (extensions)](structure-extensions.md) đã được cài và sử dụng trong ứng dụng. |
||||
Mặc định, thuộc tính sẽ nhận mảng được trả về từ file `@vendor/yiisoft/extensions.php`. File `extensions.php` |
||||
được sinh tự động khi bạn sử dụng [Composer](https://getcomposer.org) để cài các thành phần mở rộng. |
||||
Ở các trường hợp này, thuộc tính này có thể không cần cấu hình. |
||||
|
||||
Trong trường hợp, khi bạn muốn cấu hình các extension một cách thủ công, bạn có thể cấu hình thuộc tính như sau: |
||||
|
||||
```php |
||||
[ |
||||
'extensions' => [ |
||||
[ |
||||
'name' => 'tên extension', |
||||
'version' => 'phiên bản', |
||||
'bootstrap' => 'BootstrapClassName', // mặc định, giá trị thường là mảng |
||||
'alias' => [ // mặc định |
||||
'@alias1' => 'to/path1', |
||||
'@alias2' => 'to/path2', |
||||
], |
||||
], |
||||
|
||||
// ... các extensions khác ... |
||||
|
||||
], |
||||
] |
||||
``` |
||||
|
||||
Như bạn thấy ở phần trên, thuộc tính sẽ nhận thông tin bao gồm mảng các cấu hình. Mỗi extension được mô tả là mảng |
||||
bao gồm các thành phần là`name` và `version`. Nêu muốn extension cần được chạy ở tiến trình [bootstrap](runtime-bootstrapping.md) |
||||
, mỗi `bootstrap` cần được mô tả về tên lớp hoặc mảng giá trị về [cấu hình](concept-configurations.md) |
||||
. Mỗi extension có thể định nghĩa thêm các [bí danh (aliases)](concept-aliases.md). |
||||
|
||||
|
||||
#### [[yii\base\Application::layout|layout]] <span id="layout"></span> |
||||
|
||||
Thuộc tính này mô tả vê layout mặc định được dùng mỗi khi render dữ liệu ra [view](structure-views.md). |
||||
Giá trị mặc định là `'main'`, nghĩa là file `main.php` nằm trong [đường dẫn layout](#layoutPath) được dùng. |
||||
Nếu giá trị [layout path](#layoutPath) và [view path](#viewPath) nhận giá trị là mặc định, |
||||
giá trị mặc định của file layout có thể được thay thế qua bí danh `@app/views/layouts/main.php`. |
||||
|
||||
Bạn có thể cấu hình thuộc tính với giá trị `false` nếu bạn muốn tắt giá trị mặc định của layout. |
||||
|
||||
|
||||
#### [[yii\base\Application::layoutPath|layoutPath]] <span id="layoutPath"></span> |
||||
|
||||
Thuộc tính mô tả đường dẫn nơi lưu trữ file layout. Giá trị mặc định sẽ là |
||||
`layouts` thư mục con nằm trong [đường dẫn view](#viewPath). Nếu [đường dẫn view](#viewPath) nhận giá trị mặc định |
||||
, giá trị mặc định tới đường dẫn layout được thay thế như một đường dẫn của bí danh `@app/views/layouts`. |
||||
|
||||
Bạn có thể cấu hình như đường dẫn hoặc một [bí danh](concept-aliases.md). |
||||
|
||||
|
||||
#### [[yii\base\Application::runtimePath|runtimePath]] <span id="runtimePath"></span> |
||||
|
||||
Đường dẫn chứa đường dẫn tới các file tạm của ứng dụng, như file log và cache, cần được tạo ra. |
||||
Giá trị mặc định của đường dẫn có thể lấy qua bí danh `@app/runtime`. |
||||
|
||||
Bạn có thể cấu hình thuộc tính với đường dẫn hoặc một [bí danh](concept-aliases.md). Đường dẫn này cần được quyền ghi đè lên |
||||
trong quá trình ứng dụng được chạy. Và user không thể truy cập vào đường dẫn |
||||
, bởi vì các tập tin này có thể chứa các thông tin nhạy cảm. |
||||
|
||||
Yii cung cấp cách đơn giản nhất để truy cập vào đường dẫn này qua bí danh là `@runtime`. |
||||
|
||||
|
||||
#### [[yii\base\Application::viewPath|viewPath]] <span id="viewPath"></span> |
||||
|
||||
Thuộc tính này chỉ định thư mục để lưu trữ những file view trong mô hình MVC. Giá trị mặc định là |
||||
là một bí danh `@app/views`. Bạn có thể cấu hình nó với thư mục hoặc đường dẫn [alias](concept-aliases.md). |
||||
|
||||
|
||||
#### [[yii\base\Application::vendorPath|vendorPath]] <span id="vendorPath"></span> |
||||
|
||||
Thuộc tính này quy định cụ thể về thư mục được quản lý bởi [Composer](https://getcomposer.org). Thư mục này chứa các thư viện |
||||
được cung cấp bởi nhà phát triển và được dùng trong ứng dụng, bao gồm Yii framework. Giá trị mặc định là |
||||
một thư mục được cung cấp bởi bí danh `@app/vendor`. |
||||
|
||||
Thuộc tính có thể được cấu hình là thư mục hoặc là đường dẫn [alias](concept-aliases.md). Mỗi khi bạn thay đổi thuộc tính này |
||||
, bạn cần phải thay đổi thông tin cấu hình Composer cho phù hợp. |
||||
|
||||
Yii cung cấp cách thức đơn giản để truy cập vào đường dẫn này qua bí danh là `@vendor`. |
||||
|
||||
|
||||
#### [[yii\console\Application::enableCoreCommands|enableCoreCommands]] <span id="enableCoreCommands"></span> |
||||
|
||||
Thuộc tính này chỉ được hỗ trợ bởi [[yii\console\Application|ứng dụng console]]. Nó được xác định |
||||
vị trí các dòng lệnh được kích hoạt lên trong phiên bản Yii. Giá trị mặc định là `true`. |
||||
|
||||
|
||||
## Sự kiện <span id="application-events"></span> |
||||
|
||||
Ứng dụng trong chu trình hoạt động sẽ gán một vài sự kiện để nắm bắt các yêu cầu. Bạn cần liên kết tới các sự kiện |
||||
vào trong ứng dụng như sau: |
||||
|
||||
```php |
||||
[ |
||||
'on beforeRequest' => function ($event) { |
||||
// ... |
||||
}, |
||||
] |
||||
``` |
||||
|
||||
Các mô tả về cú pháp của các sự kiện `on eventName` được mô tả ở trong mục [Cấu hình](concept-configurations.md#configuration-format). |
||||
|
||||
Cách khác, bạn có thể nắm bắt các sự kiện tại [tiến trình bootstrapping](runtime-bootstrapping.md) |
||||
mỗi khi ứng dụng được khởi tạo. Ví dụ: |
||||
|
||||
```php |
||||
\Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) { |
||||
// ... |
||||
}); |
||||
``` |
||||
|
||||
### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] <span id="beforeRequest"></span> |
||||
|
||||
Sự kiện được gán *trước lúc* ứng dụng nhận các yêu cầu. Tên sự kiện gọi là `beforeRequest`. |
||||
|
||||
Mỗi nắm bắt được sự kiện, ứng dụng sẽ tải các thông tin cấu hình và khởi tạo. So it is a good place |
||||
to insert your custom code via the event mechanism to intercept the request handling process. For example, |
||||
in the event handler, you may dynamically set the [[yii\base\Application::language]] property based on some parameters. |
||||
|
||||
|
||||
### [[yii\base\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] <span id="afterRequest"></span> |
||||
|
||||
Sự kiện được gán *sau khi* ứng dụng hoàn thành việc xử lý *trước lúc* đưa phản hồi. |
||||
Tên sự kiện là `afterRequest`. |
||||
|
||||
Mỗi khi sự kiện được gán, việc nắm giữ yêu cầu xử lý thành công thì bạn có thể xử lý sau đó |
||||
các thông tin về yêu cầu xử lý hoặc nội dung phản hồi. |
||||
|
||||
Lưu ý rằng thành phần [[yii\web\Response|response]] luôn được gán một vài sự kiện mỗi lúc gửi nội dung |
||||
tới user. Những sự kiện được gán *sau* sự kiện này là. |
||||
|
||||
|
||||
### [[yii\base\Application::EVENT_BEFORE_ACTION|EVENT_BEFORE_ACTION]] <span id="beforeAction"></span> |
||||
|
||||
Sự kiện sẽ được gán *trước* khi thực hiện chạy một [hành động](structure-controllers.md). |
||||
Tên sự kiện là `beforeAction`. |
||||
|
||||
Các tham số của sự kiện được khởi tạo từ [[yii\base\ActionEvent]]. Các sự kiện cần thiết lập thuộc tính |
||||
[[yii\base\ActionEvent::isValid]] về giá trị `false` để tạm ngưng hành động. |
||||
Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'on beforeAction' => function ($event) { |
||||
if (some condition) { |
||||
$event->isValid = false; |
||||
} else { |
||||
} |
||||
}, |
||||
] |
||||
``` |
||||
|
||||
Lưu ý, có một sự kiện tương tự `beforeAction` được gán bởi [modules](structure-modules.md) |
||||
và [controllers](structure-controllers.md). Ứng dụng sẽ nắm bắt sự kiện này trước |
||||
, tiếp sau đó bởi modules (nếu có), và cuối cùng là trình điều khiển. Nếu sự kiện đều thiết lập tham số |
||||
[[yii\base\ActionEvent::isValid]] là `false`, tất cả các yêu cầu nằm trong sự kiện sẽ không được nắm giữ. |
||||
|
||||
|
||||
### [[yii\base\Application::EVENT_AFTER_ACTION|EVENT_AFTER_ACTION]] <span id="afterAction"></span> |
||||
|
||||
Tương tự, sự kiện này được gán *sau khi* khởi chạy một [hành động](structure-controllers.md). |
||||
Tên sự kiện là `afterAction`. |
||||
|
||||
Các tham số của sự kiện được khởi tạo từ [[yii\base\ActionEvent]]. Xem thuộc tính |
||||
[[yii\base\ActionEvent::result]], sự kiện này có thể truy cập hoặc chỉnh sửa kết quả trả về. |
||||
Chẳng hạn: |
||||
|
||||
```php |
||||
[ |
||||
'on afterAction' => function ($event) { |
||||
if (điều kiện) { |
||||
// thay đổi kết quả $event->result |
||||
} else { |
||||
} |
||||
}, |
||||
] |
||||
``` |
||||
|
||||
Lưu ý rằng với sự kiện `afterAction` được gán bởi [modules](structure-modules.md) |
||||
và [controllers](structure-controllers.md). Những đối tượng được gán vào trong sự kiện này được ưu tiên ngược lại |
||||
như sự kiện `beforeAction`. Đó là, trình điều khiển sẽ nắm giữ trước, |
||||
tiếp đến là module modules (nếu có), và cuối cùng là ứng dụng. |
||||
|
||||
|
||||
## Vòng đời ứng dụng <span id="application-lifecycle"></span> |
||||
|
||||
![Vòng đời ứng dụng](images/application-lifecycle.png) |
||||
|
||||
Khi một [entry script](structure-entry-scripts.md) được gọi và nắm giữ các yêu cầu, |
||||
vòng đời của ứng dụng sẽ được thực hiện như sau: |
||||
|
||||
1. Entry script sẽ tải các thông tin cấu hình trong ứng dụng ra một mảng. |
||||
2. Entry script sẽ khởi tạo mới một ứng dụng: |
||||
* Phương thức [[yii\base\Application::preInit()|preInit()]] sẽ được gọi, nhằm tải các thông tin cấu hình mà có sự ưu tiên cao |
||||
, như thuộc tính [[yii\base\Application::basePath|basePath]]. |
||||
* Đăng ký một [[yii\base\Application::errorHandler|error handler]]. |
||||
* Cấu hình các thuộc tính trong ứng dụng. |
||||
* Phương thức [[yii\base\Application::init()|init()]] sẽ được gọi và phương thức |
||||
[[yii\base\Application::bootstrap()|bootstrap()]] sẽ tải thành phần bootstrapping. |
||||
3. Entry script sẽ gọi phương thức [[yii\base\Application::run()]] để chạy ứng dụng: |
||||
* Sự kiện [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] sẽ được gán sau đó. |
||||
* Xử lý các yêu cầu: chuyển các yêu cầu vào [bộ định tuyến (route)](runtime-routing.md) và các tham số liên quan; |
||||
khởi tạo đối tượng module, controller, và action như phần mô tả ở bộ định tuyến; và khởi chạy action. |
||||
* Gán sự kiện [[yii\base\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]]. |
||||
* Gửi phản hồi tới user. |
||||
4. Entry script tiếp nhận trạng thái kết thúc từ ứng dụng hoàn tất xử lý tiến trình. |
@ -0,0 +1,444 @@
|
||||
Bộ điều khiển (Controller) |
||||
=========== |
||||
|
||||
Controller thuộc một phần trong mẫu thiết kế [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). |
||||
Controller là đối tượng được kế thừa từ class [[yii\base\Controller]] và chịu trách nhiệm xứ lý các yêu cầu và gửi phản hồi |
||||
. Đặc biệt, sau khi tiếp nhận các yêu cầu điều khiển từ [ứng dụng](structure-applications.md), |
||||
controllers sẽ phân tích thông tin yêu cầu được gửi đến, gửi dữ liệu qua [models](structure-models.md) để xử lý, và gán kết quả xử lý từ model |
||||
vào [views](structure-views.md), và cuối cùng là gửi phản hồi. |
||||
|
||||
|
||||
## Hành động (Actions) <span id="actions"></span> |
||||
|
||||
Mỗi Controller đều chứa các *action* để user có thế tìm thấy, gửi yêu cầu tới ứng dụng để xử lý |
||||
. Mỗi bộ điều khiển có thể có nhiều hành động. |
||||
|
||||
Ví dụ dưới mô tả Controller `post` cùng với 2 action là : `view` và `create`: |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use Yii; |
||||
use app\models\Post; |
||||
use yii\web\Controller; |
||||
use yii\web\NotFoundHttpException; |
||||
|
||||
class PostController extends Controller |
||||
{ |
||||
public function actionView($id) |
||||
{ |
||||
$model = Post::findOne($id); |
||||
if ($model === null) { |
||||
throw new NotFoundHttpException; |
||||
} |
||||
|
||||
return $this->render('view', [ |
||||
'model' => $model, |
||||
]); |
||||
} |
||||
|
||||
public function actionCreate() |
||||
{ |
||||
$model = new Post; |
||||
|
||||
if ($model->load(Yii::$app->request->post()) && $model->save()) { |
||||
return $this->redirect(['view', 'id' => $model->id]); |
||||
} else { |
||||
return $this->render('create', [ |
||||
'model' => $model, |
||||
]); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Với action `view` (được định nghĩa bởi phương thức `actionView()`), dòng đầu dùng [Model](structure-models.md) |
||||
để tải dữ liệu dựa theo định danh ID; Nếu Model được tải thành công, thì sẽ được hiển thị qua |
||||
[view](structure-views.md) là `view`. Còn không, ứng dụng sẽ thông báo ngoại lệ là không tìm thấy. |
||||
|
||||
Với action `create` (được định nghĩa bởi phương thức `actionCreate()`), tương tự như vậy. Trước tiên |
||||
sẽ khởi tạo [Model](structure-models.md), Model sẽ thực hiện nhận dữ liệu và lưu thông tin. Nếu cả hai việc này thành công thì Controller |
||||
sẽ điều hướng trình duyệt tới View `view` cùng với định danh ID vừa được tạo bởi Model. Còn không, Controller sẽ gọi |
||||
View `create` có chức năng hiển thị form nhập liệu. |
||||
|
||||
|
||||
## Bộ định tuyến (Routes) <span id="routes"></span> |
||||
|
||||
Người dùng có thể tìm thấy các actions qua các bộ định tuyến gọi là *routes*. Mỗi Route là chuỗi bao gồm các thông tin: |
||||
|
||||
* Một định danh của Module: chỉ tồn tại nếu bộ điều khiển thuộc về thành phần [module](structure-modules.md); |
||||
* Một định danh của [Controller](#controller-ids): là một chuỗi xác định duy nhất của Controller trong ứng dụng |
||||
(hoặc có thể là Module nếu Controller tương ứng là một Module); |
||||
* Một [Action ](#action-ids):là một chuỗi xác định duy nhất của Action trong ứng dụng. |
||||
|
||||
Mỗi Route có định dạng như sau: |
||||
|
||||
``` |
||||
ControllerID/ActionID |
||||
``` |
||||
|
||||
hoặc có định dạng sau nếu Controller được gán như một Module: |
||||
|
||||
```php |
||||
ModuleID/ControllerID/ActionID |
||||
``` |
||||
|
||||
Như vậy nếu user truy cập vào đường dẫn sau `http://hostname/index.php?r=site/index`, thì hành động `index` nằm trong bộ điều khiển `site` |
||||
sẽ được thực hiện. Để biết thêm thông tin về cách bộ định tuyến xác định các hành động, vui lòng tham khảo tại mục |
||||
[Routing và URL Generation](runtime-routing.md). |
||||
|
||||
|
||||
## Tạo Controller <span id="creating-controllers"></span> |
||||
|
||||
Trong mỗi [[yii\web\Application|Ứng dụng Web]], Controllers cần được kế thừa từ class [[yii\web\Controller]] hoặc các lớp con của nó |
||||
. Tương tự trong [[yii\console\Application|Ứng dụng console]], Controllers cần được kế thừa từ class |
||||
[[yii\console\Controller]] hoặc các lớp con của nó. Đoạn code sau được định nghĩa trong Controller `site` : |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Định danh Controller (Controller ID) <span id="controller-ids"></span> |
||||
|
||||
Thông thường, Controller được thiết kế để xử lý các yêu cầu từ các nguồn tài nguyên cụ thể. |
||||
Vì lý do này, mỗi định danh của Controller thường là danh từ cập nhật tới kiểu tài nguyên được xử lý. |
||||
Ví dụ, sử dụng `article` như định danh của Controller nhằm lưu giữ các dữ liệu về bài viết. |
||||
|
||||
Mặc định, Thông tin các định danh Controller nên chỉ chứa các ký tự : Các chữ cái viết thường, số, |
||||
dấu gạch dưới, dấu nối, và dấu gạch nghiêng phải. Ví dụ, `article` và `post-comment` đều là những định danh đúng, |
||||
trong khi đó `article?`, `PostComment`, `admin\post` đều không hợp lý. |
||||
|
||||
Các định danh Controller ID nên chứa tiền tố trong thư mục con. Ví dụ, `admin/article` xác định cho Controller `article` |
||||
nằm trong thư mục `admin` được dựa theo [[yii\base\Application::controllerNamespace|controller namespace]]. |
||||
Các ký tự hợp lệ dùng cho các tiền tố của thư mục con bao gồm: Các chữ cái viết thường, số, dấu gạch, và dấu gạch nghiêng phải |
||||
, dấu gạch nghiêng phải được dùng để phân cách như các thư mục con (vd. `panels/admin`). |
||||
|
||||
|
||||
### Tên lớp Controller <span id="controller-class-naming"></span> |
||||
|
||||
Tên lớp của các Controller được khởi tạo từ các định danh Controller theo các bước sau: |
||||
|
||||
1. Chuyển ký tự đầu tiên trong mỗi từ cách nhau bởi dấu gạch nối thành ký tự hoa. Lưu ý rằng nếu các định danh Controller |
||||
có chứa dấu gạch chéo, thì quy tắc này chỉ được áp dụng ở phần sau dấu gạch chéo cuối cùng trong các định danh. |
||||
2. Xoá các dấu gạch nối và thay thế các dấu gạch chéo xuôi(/) thành dấu gạch chéo ngược (\). |
||||
3. Thêm hậu tố `Controller`. |
||||
4. Thêm [[yii\base\Application::controllerNamespace|controller namespace]]. |
||||
|
||||
Xem ví dụ sau, giả sử [[yii\base\Application::controllerNamespace|controller namespace]] |
||||
nhận giá trị mặc định là `app\controllers`: |
||||
|
||||
* `article` thành `app\controllers\ArticleController`; |
||||
* `post-comment` thành `app\controllers\PostCommentController`; |
||||
* `admin/post-comment` thành `app\controllers\admin\PostCommentController`; |
||||
* `adminPanels/post-comment` thành `app\controllers\adminPanels\PostCommentController`. |
||||
|
||||
Các lớp Controller cần được [tự động tải](concept-autoloading.md). Vì vậy, trong ví dụ trên, |
||||
lớp của Controller `article` cần được lưu vào file có [bí danh](concept-aliases.md) |
||||
là `@app/controllers/ArticleController.php`; trong khi đó `admin/post-comment` cần được lưu vào file |
||||
là `@app/controllers/admin/PostCommentController.php`. |
||||
|
||||
> Lưu ý: Ở ví dụ với định danh Controller `admin/post-comment` hướng dẫn bạn đặt các Controller vào trong thư mục con |
||||
của [[yii\base\Application::controllerNamespace|không gian tên Controller]]. Thông tin này khá là hữu ích |
||||
mỗi khi bạn muốn quản lý các Controllers và các chuyên mục và bạn không muốn sử dụng thành phần [Modules](structure-modules.md). |
||||
|
||||
|
||||
### Controller Map <span id="controller-map"></span> |
||||
|
||||
Bạn có thể cấu hình thông tin về mục [[yii\base\Application::controllerMap|controller map]] để khắc phục những hạn chế về các định danh |
||||
và tên class của Controller được mô tả ở trên. Điều này khá hữu ích khi bạn muốn sử dụng |
||||
các Controller ở bên thứ ba và bạn không có quyền việc kiểm soát các class này. |
||||
|
||||
Bạn có thể cấu hình [[yii\base\Application::controllerMap|controller map]] trong mục |
||||
[cấu hình ứng dụng](structure-applications.md#application-configurations). Ví dụ: |
||||
|
||||
```php |
||||
[ |
||||
'controllerMap' => [ |
||||
// mô tả Controller "account" được sử dụng |
||||
'account' => 'app\controllers\UserController', |
||||
|
||||
// mô tả về cấu hình Controller"article" dạng mảng |
||||
'article' => [ |
||||
'class' => 'app\controllers\PostController', |
||||
'enableCsrfValidation' => false, |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
|
||||
### Controller mặc định <span id="default-controller"></span> |
||||
|
||||
Mỗi ứng dụng đều có một Controller mặc định được mô tả qua thuộc tính [[yii\base\Application::defaultRoute]]. |
||||
Khi một yêu cầu không được mô tả cụ thể ở mục [route](#routes), thì route mặc định sẽ được gọi. |
||||
Chẳng hạn [[yii\web\Application|Web applications]], có giá trị là `'site'`, trong khi đó [[yii\console\Application|ứng dụng console]], |
||||
có route mặc định là `help`. Vì vậy, nếu truy cập vào URL sau `http://hostname/index.php`, thì Controller `site` sẽ được gọi và xử lý yêu cầu. |
||||
|
||||
Bạn có thể thay đổi thông tin Controller mặc định tại mục [cấu hình ứng dung](structure-applications.md#application-configurations) như sau: |
||||
|
||||
```php |
||||
[ |
||||
'defaultRoute' => 'main', |
||||
] |
||||
``` |
||||
|
||||
|
||||
## Tạo Actions <span id="creating-actions"></span> |
||||
|
||||
Tạo mới một Action khá là đơn giản, bằng chỉ việc định nghĩa trong lớp Controller cùng với tên *action phương thức*. Các phương thức của mỗi Action |
||||
đều có phạm vi *toàn cục* tên của phương thức được bắt đầu bằng từ `action`. Kết quả trả về của mỗi action sẽ tương ứng với |
||||
dữ liệu được gửi tới user. Đoạn mã sau sẽ định nghĩa hai action là, `index` và `hello-world`: |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function actionIndex() |
||||
{ |
||||
return $this->render('index'); |
||||
} |
||||
|
||||
public function actionHelloWorld() |
||||
{ |
||||
return 'Hello World'; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Định danh của Action (Action ID) <span id="action-ids"></span> |
||||
|
||||
Mỗi action được dùng cho nhiệm vụ với tài nguyên cụ thể. Vì lý do này, mỗi |
||||
action ID thường là một đông từ, như `view`, `update`, vv. |
||||
|
||||
Mặc định, mỗi action ID chỉ nên chứa các chữ cái: Chữ thường, số, |
||||
dấu gạch dưới, và dấu gạch ngang. (Bạn cũng có thể sử dụng các dấu gạch ngang để nối các từ lại với nhau.) Ví dụ, |
||||
`view`, `update2`, và `comment-post` đều là những định danh hợp lệ, còn `view?` và `Update` là không hợp lệ. |
||||
|
||||
Có hai cách để tạo mới các action: inline actions và standalone actions. Với inline action được định nghĩa |
||||
như những phương thức trong lớp Controller, trong khi đó standalone action là lớp được kế thừa từ lớp |
||||
[[yii\base\Action]] hoặc là lớp con. Nếu bạn không muốn tái sử dụng các action thì bạn có thể dùng inline actions |
||||
, cách này thường hay được sử dụng hơn. Trong khi đó các standalone actions thường được tạo để sử dụng |
||||
ở những Controllers khác nhau và được dùng như [thành phần mở rộng](structure-extensions.md). |
||||
|
||||
|
||||
### Inline Actions <span id="inline-actions"></span> |
||||
|
||||
Inline actions được định nghĩa vào trong các phương thức như chúng ta đã mô tả trên. |
||||
|
||||
Tên của phương thức thường đặt tên theo định danh của action và theo các bước sau: |
||||
|
||||
1. Chuyển ký tự đầu tiên trong mỗi từ định danh của action thành ký tự in hoa. |
||||
2. Xoá dấu gạch nối. |
||||
3. Thêm tiền tố `action`. |
||||
|
||||
Ví dụ, `index` thành `actionIndex`, và `hello-world` thành `actionHelloWorld`. |
||||
|
||||
> Lưu ý: Việc đặt tên của các phương thức cần phải *cẩn thận*. Nếu bạn có phương thức là `ActionIndex`, |
||||
thì phương thức của action sẽ không xác định, như vậy, khi có yêu cầu tới action `index` |
||||
thì sẽ sinh ra lỗi. Cũng lưu ý rằng các phương thức này phải ở phạm vi public (toàn cục). Các phạm vi private (riêng) hoặc protected (bảo vệ) |
||||
thì sẽ không được định nghĩa như một action. |
||||
|
||||
|
||||
Inline actions thường được hay sử dụng hơn bởi vì việc khởi tạo đơn giản hơn. Tuy nhiên, |
||||
nếu bạn muốn tái sử dụng action ở những vị trí khác, bạn có thể tham khảo thêm ở mục *standalone action*. |
||||
|
||||
|
||||
### Standalone Actions <span id="standalone-actions"></span> |
||||
|
||||
Standalone actions được định nghĩa từ việc kế thừa từ class [[yii\base\Action]] hoặc các lớp con của nó. |
||||
Ví dụ, ở phiên bản Yii đã phát hành, các action [[yii\web\ViewAction]] và [[yii\web\ErrorAction]], đều là những |
||||
standalone actions. |
||||
|
||||
Để sử dụng standalone action, bạn cần phải khai báo ở phần *liên kết các action* bằng việc ghi đè lên phương thức |
||||
[[yii\base\Controller::actions()]] ở lớp Controller như sau: |
||||
|
||||
```php |
||||
public function actions() |
||||
{ |
||||
return [ |
||||
// khai báo action "error" bằng việc sử dụng tên class |
||||
'error' => 'yii\web\ErrorAction', |
||||
|
||||
// khai báo action "view" bằng thông tin cấu hình dạng mảng |
||||
'view' => [ |
||||
'class' => 'yii\web\ViewAction', |
||||
'viewPrefix' => '', |
||||
], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Như vậy, phương thức `actions()` sẽ trả về một mảng và chứa các khoá của các định danh action và giá trị tương ứng |
||||
tên class hoặc thông tin [cấu hình](concept-configurations.md). Không giống như inline actions, action ID được dùng cho standalone |
||||
actions có thể chứa các ký tự tuỳ ý, miễn là chúng được khai báo trong phương thức `actions()`. |
||||
|
||||
Để tạo các class standalone action, bạn nên kế thừa từ lớp [[yii\base\Action]] hoặc lớp con của nó, và hiện thực |
||||
phương thức là `run()`. Vài trò của phương thức `run()` tương tự như một phương thức của action. Chẳng hạn, |
||||
|
||||
```php |
||||
<?php |
||||
namespace app\components; |
||||
|
||||
use yii\base\Action; |
||||
|
||||
class HelloWorldAction extends Action |
||||
{ |
||||
public function run() |
||||
{ |
||||
return "Hello World"; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Kết quả trả về của Action <span id="action-results"></span> |
||||
|
||||
Kết quả trả về của phương thức action hoặc phương thức `run()` của standalone action khá quan trọng. Nó là |
||||
kết quả tương ứng của từng action. |
||||
|
||||
Giá trị trả về là đối tượng [phản hồi](runtime-responses.md) được gửi tới user như những phản hồi. |
||||
|
||||
* Chẳng hạn với [[yii\web\Application|Ứng dụng Web]], kết quả trả về bao gồm dữ liệu được gán vào thuộc tính |
||||
[[yii\web\Response::data]] và chuyển sang dữ liệu là string chuyển tới nội dung phản hồi kết quả. |
||||
* Với [[yii\console\Application|ứng dụng console]], kết quả trả về là số nguyên tương ứng với thuộc tính |
||||
[[yii\console\Response::exitStatus|exit status]] của mỗi lần thực thi lệnh. |
||||
|
||||
Ở ví dụ dưới, action sẽ trả về là chuỗi dữ liệu và được xử lý như nội dung phản hồi tới user |
||||
. Ví dụ dưới chỉ cách các action điều hướng tới trình duyệt một URL |
||||
bằng việc gửi một đối tượng phản hồi (vì phương thức [[yii\web\Controller::redirect()|redirect()]] sẽ trả về |
||||
một đối tượng): |
||||
|
||||
```php |
||||
public function actionForward() |
||||
{ |
||||
// điều hướng tới URL http://example.com |
||||
return $this->redirect('http://example.com'); |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Các tham số của Action <span id="action-parameters"></span> |
||||
|
||||
Các phương thức dành cho inline action và phương thức `run()` cho standalone actions có thể nhận các tham số, |
||||
được gọi là *các tham số action*. Giá trị nhận được từ các yêu cầu. Với [[yii\web\Application|Ứng dụng Web]], |
||||
giá trị của các tham số được nhận từ biến `$_GET` sử dụng các tham số như các khoá; |
||||
với [[yii\console\Application|ứng dụng console]], các tham số sẽ tương ứng với các đối số dòng lệnh. |
||||
|
||||
Trong ví dụ sau, action `view` (là một inline action) được khai báo hai tham số là: `$id` và `$version`. |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class PostController extends Controller |
||||
{ |
||||
public function actionView($id, $version = null) |
||||
{ |
||||
// ... |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Các tham số cho action sẽ được dùng như sau và tương ứng với các yêu cầu khác nhau: |
||||
|
||||
* `http://hostname/index.php?r=post/view&id=123`: biến `$id` sẽ nhận giá trị là |
||||
`'123'`, trong khi đó tham số `$version` nhận giá trị null vì không có đối số `version` được truyền lên. |
||||
* `http://hostname/index.php?r=post/view&id=123&version=2`: biến `$id` và `$version` sẽ nhận giá trị tương ứng là |
||||
`'123'` và `'2'`. |
||||
* `http://hostname/index.php?r=post/view`: ngoại lệ [[yii\web\BadRequestHttpException]] sẽ được gửi ra |
||||
vì tham số `$id` không được gửi lên. |
||||
* `http://hostname/index.php?r=post/view&id[]=123`: xảy ra ngoại lệ [[yii\web\BadRequestHttpException]] lý do vì |
||||
tham số `$id` nhận dữ liệu là một mảng do vậy không hợp lệ `['123']`. |
||||
|
||||
Nếu bạn muốn tham số của action nhận dữ liệu là một mảng, bạn nên khai báo biên là `array`, như sau: |
||||
|
||||
```php |
||||
public function actionView(array $id, $version = null) |
||||
{ |
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
Nếu yêu cầu là `http://hostname/index.php?r=post/view&id[]=123`, thì tham số `$id` sẽ nhận giá trị là |
||||
`['123']`. Nếu yêu cầu là `http://hostname/index.php?r=post/view&id=123`, tham số `$id` sẽ chỉ nhận |
||||
các giá trị trong mảng là giống nhau bởi vì giá trị `'123'` không là mảng và sẽ tự động chuyển vào mảng. |
||||
|
||||
Ở ví dụ trên sẽ hướng dẫn các tham số trong mỗi action hoạt động trong ứng dụng Web. Với ứng dụng console, |
||||
vui lòng tham khảo tại mục [Console Commands](tutorial-console.md) để biết thêm thông tin. |
||||
|
||||
|
||||
### Action mặc định <span id="default-action"></span> |
||||
|
||||
Mỗi controller đều có các action mặc định và đợc mô tả ở thuộc tính [[yii\base\Controller::defaultAction]]. |
||||
Mỗi khi [route](#routes) chỉ nhận giá trị là định danh của controller, router sẽ tự hiểu rằng action mặc định |
||||
của controller sẽ được gọi. |
||||
|
||||
Mặc định, action mặc định sẽ là `index`. nếu bạn muốn thay đổi giá trị này, cách đơn giản nhất là ghi đè thuộc tính |
||||
trong lớp controller, như sau: |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public $defaultAction = 'home'; |
||||
|
||||
public function actionHome() |
||||
{ |
||||
return $this->render('home'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
## Chu trình Controller <span id="controller-lifecycle"></span> |
||||
|
||||
Khi xử lý yêu cầu, [ứng dụng](structure-applications.md) sẽ khởi tạo controller |
||||
dựa theo các yêu cầu tại [route](#routes). Controller sẽ được xử lý qua các chu trình sau để xử lý các yêu cầu: |
||||
|
||||
1. Phương thức [[yii\base\Controller::init()]] sẽ được gọi sau khi controller được khởi tạo và thiết lập các cấu hình. |
||||
2. Controller sẽ tạo đối tượng action dựa trên các yêu cầu qua các định danh của action: |
||||
* Nếu định danh của action không được chỉ rõ , thì [[yii\base\Controller::defaultAction|action mặc định]] sẽ được sử dụng. |
||||
* Nếu định danh của action được tìm thấy trong phương thức [[yii\base\Controller::actions()|action map]], thì một standalone action |
||||
sẽ được khởi tạo; |
||||
* Nếu định danh của action được tìm thấy và khớp với phương thức của action, thì một inline action sẽ được; |
||||
* Mặt khác hệ thống sẽ gửi ngoại lê [[yii\base\InvalidRouteException]] ra. |
||||
3. Controller sẽ lần lượt được gọi tại phương thức `beforeAction()` trong ứng dụng, trong module (nếu controller |
||||
thuộc một module), và trong controller. |
||||
* Nếu một trong các phương thức không đợc gọi, các phần chưa được gọi trong phương thức `beforeAction()` sẽ được bỏ qua |
||||
và việc thực hiện action sẽ bị huỷ bỏ. |
||||
* Mặc định, mỗi phương thức `beforeAction()` sẽ được gán vào sự kiện `beforeAction` tới các action mà bạn cần xử lý. |
||||
4. Controller thực hiện chạy action. |
||||
* Các tham số của action sẽ được phân tích và gán từ các yêu cầu xử lý. |
||||
5. Controller sẽ thực hiện tuần tự gọi phương thức `afterAction()` trong Controller, module (nếu Controller |
||||
là module), và trong ứng dụng. |
||||
* Mặc định, mỗi phương thức `afterAction()` sẽ gọi tới một sự kiện `afterAction` tới các action mà bạn cần xử lý. |
||||
6. Ứng dụng sẽ nhận kết quả từ các action và chuyển tới thành phần [response](runtime-responses.md). |
||||
|
||||
|
||||
## Thực hành <span id="best-practices"></span> |
||||
|
||||
Với mỗi ứng dụng được thiết kế tốt, thì Controllers thường rất gọn nhẹ, mỗi action chỉ chứa khá ít dòng code. |
||||
Nếu Controller trong ứng dụng của bản khá phức tạp, thì bạn nên cấu trúc lại và chuyển sang một lớp khác. |
||||
|
||||
Sau đây gợi ý vài thủ thuật. Controllers |
||||
|
||||
* có thể truy cập dữ liệu từ các [request](runtime-requests.md); |
||||
* gọi các phương thức từ [models](structure-models.md) và các thành phần khác cùng với dữ liệu được gửi; |
||||
* dùng thành phần [views](structure-views.md) để gửi phản hồi; |
||||
* KHÔNG NÊN xử lý dữ liệu - nên xử lý ở tầng [model](structure-models.md); |
||||
* không nên nhúng mã HTML vào hoặc đoạn mã khác - mã này nên nhũng ở thành phần [views](structure-views.md). |
@ -0,0 +1,116 @@
|
||||
Entry Scripts |
||||
============= |
||||
|
||||
Entry script là tiến trình đầu tiên của ứng dụng. Một ứng dụng (hoặc |
||||
ứng dụng Web hoặc ứng dụng console) đều có một entry script. Người dùng đầu cuối tạo các request tới entry script, entry script |
||||
sẽ khởi tạo ứng dụng và nhanh chóng chuyển các yêu cầu tới chúng. |
||||
|
||||
Entry script dành cho các ứng dụng web cần được thiết lập ở dưới thư mục truy cập Web để người dùng cuối có thể truy cập |
||||
. Những mục này thường được đặt tên là `index.php`, tuy nhiên có thể sử dụng các tên khác, |
||||
được cung cấp và có thể xác định bởi các máy chủ Web. |
||||
|
||||
Entry script cho các ứng dụng console thông thường được nằm ở [đường dẫn cơ sở](structure-applications.md) |
||||
của ứng dụng và có tên là `yii` (cùng với hậu tố `.php`). Chúng được xây dựng để thực thi các ứng dụng console |
||||
thông qua dòng lệnh `./yii <route> [arguments] [options]`. |
||||
|
||||
Entry scripts có chức năng chính như sau: |
||||
|
||||
* Khai báo các hằng số ở phạm vi toàn cục; |
||||
* Đăng ký [Composer autoloader](http://getcomposer.org/doc/01-basic-usage.md#autoloading); |
||||
* Tải các file class của [[Yii]]; |
||||
* Tải cấu hình ứng dụng; |
||||
* Tạo và cấu hình các phiên bản [application](structure-applications.md); |
||||
* Gọi phương thức [[yii\base\Application::run()]] để xử lý các request được gọi tới. |
||||
|
||||
|
||||
## Ứng dụng Web <span id="web-applications"></span> |
||||
|
||||
Mã nguồn dưới đây là các dòng lệnh trong mục script trong [Mẫu ứng dụng cơ bản](start-installation.md). |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
defined('YII_ENV') or define('YII_ENV', 'dev'); |
||||
|
||||
// register Composer autoloader |
||||
require(__DIR__ . '/../vendor/autoload.php'); |
||||
|
||||
// include Yii class file |
||||
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); |
||||
|
||||
// load application configuration |
||||
$config = require(__DIR__ . '/../config/web.php'); |
||||
|
||||
// create, configure and run application |
||||
(new yii\web\Application($config))->run(); |
||||
``` |
||||
|
||||
|
||||
## Ứng dụng Console(dòng lệnh) <span id="console-applications"></span> |
||||
|
||||
Tương tự, Mã nguồn dưới đây là các dòng lệnh trong mục script của ứng dụng console: |
||||
|
||||
```php |
||||
#!/usr/bin/env php |
||||
<?php |
||||
/** |
||||
* Yii console bootstrap file. |
||||
* |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
|
||||
// fcgi doesn't have STDIN and STDOUT defined by default |
||||
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); |
||||
defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w')); |
||||
|
||||
// register Composer autoloader |
||||
require(__DIR__ . '/vendor/autoload.php'); |
||||
|
||||
// include Yii class file |
||||
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php'); |
||||
|
||||
// load application configuration |
||||
$config = require(__DIR__ . '/config/console.php'); |
||||
|
||||
$application = new yii\console\Application($config); |
||||
$exitCode = $application->run(); |
||||
exit($exitCode); |
||||
``` |
||||
|
||||
|
||||
## Định nghĩa các hằng số <span id="defining-constants"></span> |
||||
|
||||
Entry scripts thích hợp để định nghĩa các hằng ở phạm vi toàn cục. Yii hỗ trợ 3 hằng số sau: |
||||
|
||||
* `YII_DEBUG`: xác định xem ứng dụng đang chay trong chế độ debug (gỡ lỗi). Khi ở chế độ debug, ứng dụng |
||||
sẽ log các thông tin, và sẽ thông báo chi tiết về các lỗi nếu có các ngoại lệ được gửi ra. Vì lý do này |
||||
, chế độ debug nên được dùng thường xuyên trong quá trình xây dựng ứng dụng. Giá trị mặc định của hằng `YII_DEBUG` là false. |
||||
* `YII_ENV`: xác định thông tin về môi trường của ứng dụng đang chạy (sản phẩm hay đang phát triển). Điều này sẽ mô tả chi tiết trong phần |
||||
[Cấu hình](concept-configurations.md#environment-constants). Giá trị mặc định của hằng số `YII_ENV` là `'prod'`, có nghĩa là ứng dụng đang chạy là phiển bản sản phẩm |
||||
đã phát hành. |
||||
* `YII_ENABLE_ERROR_HANDLER`: mô tả nơi cho phép được giữ (handler) các lỗi được cung cấp bởi Yii. Giá trị mặc đình của hằng |
||||
số là true. |
||||
|
||||
Khi định nghĩa các hằng số, chúng ta thường sử dụng đoạn mã như sau: |
||||
|
||||
```php |
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
``` |
||||
|
||||
Khai báo trên tương đương với đoạn code sau: |
||||
|
||||
```php |
||||
if (!defined('YII_DEBUG')) { |
||||
define('YII_DEBUG', true); |
||||
} |
||||
``` |
||||
|
||||
Ta thấy đoạn code trên ngắn gọi và dễ hiểu hơn nhiều. |
||||
|
||||
Việc định nghĩa các hằng số nên được thực hiện ở phần đầu của entry script để các hằng số này có thể được gọi |
||||
ở những file php khác. |
@ -0,0 +1,529 @@
|
||||
Model |
||||
====== |
||||
|
||||
Model là phần trong kiến trúc [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). |
||||
Là đối tượng đại diện cho phần dữ liệu, phương thức xử lý và nghiệp vụ logic. |
||||
|
||||
Bạn có thể tạo mới các lớp model bằng việc kế thừa từ lớp [[yii\base\Model]] hoặc các lớp con của nó. Lớp cơ sở |
||||
[[yii\base\Model]] hỗ trợ nhiều tính năng như: |
||||
|
||||
* [Thuộc tính (Attributes)](#attributes): đại diện cho các dữ liệu nghiệp vụ và có thể truy cập như các thuộc tính |
||||
hoặc mảng các phần tử; |
||||
* [Attribute labels](#attribute-labels): tên hiển thị cho các thuộc tính; |
||||
* [Gán nhanh (Massive assignment)](#massive-assignment): hỗ trợ nhập dữ liệu cho thuộc tính trong một bước; |
||||
* [Quy tắc xác nhận (Validation rules)](#validation-rules): khai báo các quy tắc và xác thực dữ liệu được nhập vào; |
||||
* [Xuất dữ liệu (Data Exporting)](#data-exporting): cho phép xuất dữ liệu dưới dạng mảng hoặc tuỳ chọn khác. |
||||
|
||||
Lớp `Model` thường dựa trên lớp để thực hiện chức năng nâng cao, chẳng hạn [Active Record](db-active-record.md). |
||||
Vui lòng tham khảo thêm tài liệu để biết thêm thông tin. |
||||
|
||||
> Lưu ý: Model của bạn không phải bắt buộc kế thừa từ lớp [[yii\base\Model]]. Tuy nhiên, vì Yii chứa nhiều thành phần |
||||
dựng lên và hỗ trợ cho [[yii\base\Model]], vì thế nó là lớp cơ sở cho các lớp Model. |
||||
|
||||
|
||||
## Thuộc tính (Attribute) <span id="attributes"></span> |
||||
|
||||
Model đại diện cho tầng xử lý nghiệp vụ và chứa các *thuộc tính*. Mỗi thuộc tính được truy cập toàn cục như phần tử của |
||||
model. Phương thức [[yii\base\Model::attributes()]] sẽ mô tả các thuộc tính trong lớp model hiện có. |
||||
|
||||
Bạn có thể truy cập vào thuộc tính như các phần tử của các đối tượng: |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// "name" là tên thuộc tính của ContactForm |
||||
$model->name = 'example'; |
||||
echo $model->name; |
||||
``` |
||||
|
||||
Bạn có thể truy cập các thuộc tính như truy cập mảng các phần tử, nhờ sự hỗ trợ từ lớp |
||||
[ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) và [ArrayIterator](http://php.net/manual/en/class.arrayiterator.php) |
||||
bởi [[yii\base\Model]]: |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// truy cập các thuộc tính như mảng các phần tử |
||||
$model['name'] = 'example'; |
||||
echo $model['name']; |
||||
|
||||
// iterate attributes |
||||
foreach ($model as $name => $value) { |
||||
echo "$name: $value\n"; |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Định nghĩa các thuộc tính <span id="defining-attributes"></span> |
||||
|
||||
Mặc định, nếu Model của bạn được kế thừa từ lớp [[yii\base\Model]], và tất cả các biến có phạm vi *toàn cục trong lớp* |
||||
. Ví dụ, Model `ContactForm` sau có bốn thuộc tính là: `name`, `email`, |
||||
`subject` và `body`. Model `ContactForm` dùng để nhận dữ liệu từ form HTML. |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
|
||||
class ContactForm extends Model |
||||
{ |
||||
public $name; |
||||
public $email; |
||||
public $subject; |
||||
public $body; |
||||
} |
||||
``` |
||||
|
||||
|
||||
Bạn có thể ghi đè phương thức [[yii\base\Model::attributes()]] để định nghĩa các thuộc tính theo các cách khác. Phương thức nên được trả |
||||
tên của thuộc tính trong Model. Ví dụ, lớp [[yii\db\ActiveRecord]] trả về |
||||
danh sách tên của các cột liên quan tới các bảng trong CSDL như tên các thuộc tính. Bạn có thể ghi đè các phương thức như |
||||
`__get()`, `__set()` để có thể truy cập các thuộc tính như các đối tượng thông thường. |
||||
|
||||
|
||||
### Nhãn của thuộc tính <span id="attribute-labels"></span> |
||||
|
||||
Mỗi khi cần hiển thị giá trị hoặc nhận dữ liệu cho thuộc tính, bạn cần hiển thị nhãn tương ứng với các thuộc tính |
||||
. Ví dụ, với thuộc tính `firstName`, bạn cần hiển thị nhãn `First Name` |
||||
nhãn này sẽ thân thiện hơn khi hiển thị tới người dùng với việc nhập dữ liệu và hiện thông báo. |
||||
|
||||
Bạn có thể lấy tên nhãn các thuộc tính quan việc gọi phương thức [[yii\base\Model::getAttributeLabel()]]. Ví dụ, |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// hiển thị "Name" |
||||
echo $model->getAttributeLabel('name'); |
||||
``` |
||||
|
||||
Mặc định, nhãn thuộc tính sẽ tự động tạo từ tên của thuộc tính. |
||||
Phương thức [[yii\base\Model::generateAttributeLabel()]] sẽ tạo mới các nhãn cho các thuộc tính. Nó sẽ chuyển tên các biến thành các từ mới |
||||
qua việc chuyển ký tự đầu tiên thành ký tự in hoa. Ví dụ, `username` thành `Username`, |
||||
và `firstName` thành `First Name`. |
||||
|
||||
Nếu bạn không muốn việc tạo các nhản bằng cách tự động, bạn cần ghi đè phương thức [[yii\base\Model::attributeLabels()]] |
||||
để mô tả các thuộc tính. Chẳng hạn, |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
|
||||
class ContactForm extends Model |
||||
{ |
||||
public $name; |
||||
public $email; |
||||
public $subject; |
||||
public $body; |
||||
|
||||
public function attributeLabels() |
||||
{ |
||||
return [ |
||||
'name' => 'Your name', |
||||
'email' => 'Your email address', |
||||
'subject' => 'Subject', |
||||
'body' => 'Content', |
||||
]; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Với ứng dụng cần hỗ trợ đa ngôn ngữ, bạn cần dịch lại nhãn của các thuộc tính. Xem trong phương thức |
||||
[[yii\base\Model::attributeLabels()|attributeLabels()]] , như sau: |
||||
|
||||
```php |
||||
public function attributeLabels() |
||||
{ |
||||
return [ |
||||
'name' => \Yii::t('app', 'Your name'), |
||||
'email' => \Yii::t('app', 'Your email address'), |
||||
'subject' => \Yii::t('app', 'Subject'), |
||||
'body' => \Yii::t('app', 'Content'), |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Bạn có thể gán nhãn cho các thuộc tính. Chẳng hạn, dựa vào [scenario](#scenarios)của Model |
||||
đã được sử dụng , bạn có thể trả về các nhãn khác nhau cho các thuộc tính khác nhau. |
||||
|
||||
> Lưu ý: Chính xác rằng, nhãn của thuộc tính là một phần của [views](structure-views.md). Tuy nhiên việc khai báo các nhãn |
||||
vào Model thường rất tiện lợi, code dễ nhìn và tái sử dụng. |
||||
|
||||
|
||||
## Kịch bản (Scenarios) <span id="scenarios"></span> |
||||
|
||||
Model thường được sử dụng ở các *kịch bản* khác nhau . Ví dụ, Model `User` dùng để xử lý việc đăng nhập, |
||||
nhưng cũng có thể được dùng ở mục đăng ký. Ở các kịch bản khác nhau, Model có thể được dùng trong các nghiệp vụ |
||||
và xử lý logic khác nhau. Ví dụ,thuộc tính `email` có thể được yêu cầu trong mục đăng ký tài khoản mới, |
||||
nhưng không được yêu cầu khi xử lý đăng nhập. |
||||
|
||||
Mỗi Model sử dụng thuộc tính [[yii\base\Model::scenario]] để xử lý tuỳ theo kịch bản cần đợc dùng. |
||||
Mặc định, Model sẽ hỗ trợ kịch bản là `default`. Xem đoạn mã sau để hiểu 2 cách thiết lập kịch bản cho Model. |
||||
setting the scenario of a model: |
||||
|
||||
```php |
||||
// kịch bản được thiết lập qua thuộc tính |
||||
$model = new User; |
||||
$model->scenario = User::SCENARIO_LOGIN; |
||||
|
||||
// kịch bản được thiết lập qua việc cấu hình khởi tạo |
||||
$model = new User(['scenario' => User::SCENARIO_LOGIN]); |
||||
``` |
||||
|
||||
Mặc định, các kịch bản được hỗ trợ bởi model được xác định qua [các nguyên tắc xác minh](#validation-rules) được |
||||
mô tả ở Model. Tuy nhiên, bạn có thê tuỳ biến bằng cách ghi đè phương thức [[yii\base\Model::scenarios()]], |
||||
như sau: |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\db\ActiveRecord; |
||||
|
||||
class User extends ActiveRecord |
||||
{ |
||||
const SCENARIO_LOGIN = 'login'; |
||||
const SCENARIO_REGISTER = 'register'; |
||||
|
||||
public function scenarios() |
||||
{ |
||||
return [ |
||||
self::SCENARIO_LOGIN => ['username', 'password'], |
||||
self::SCENARIO_REGISTER => ['username', 'email', 'password'], |
||||
]; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Lưu ý: Như phần trên và ví dụ vừa rồi, lớp Model được kế thừa từ lớp [[yii\db\ActiveRecord]] |
||||
bởi vì lớp [Active Record](db-active-record.md) thường được sử dụng nhiều kịch bản. |
||||
|
||||
Phương thức `scenarios()` trả về một mảng có chứa các khóa là tên các kịch bản và các giá trị tương ứng là các |
||||
danh sách *thuộc tính được chọn*. An active attribute can be [massively assigned](#massive-assignment) và là đối tượng sẽ được |
||||
dùng để [xác thực (validation)](#validation-rules). Chẳng hạn ở ví dụ trên, thuộc tính `username` và `password` sẽ được chọn |
||||
ở kịch bản `login`; còn ở kịch bản `register`, sẽ có thêm thuộc tính `email` ngoài 2 thuộc tính `username` và `password`. |
||||
|
||||
Việc triển khai phương thức `scenarios()` mặc định sẻ trả về các kịch bản tìm thấy trong phương thức |
||||
[[yii\base\Model::rules()]]. Khi khi đè phương thức `scenarios()`, nếu bạn muốn khai báo các kịch bản mới, ngoài các kịch bản mặc định |
||||
in addition to the default ones, bạn có thể viết mã như sau: |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\db\ActiveRecord; |
||||
|
||||
class User extends ActiveRecord |
||||
{ |
||||
const SCENARIO_LOGIN = 'login'; |
||||
const SCENARIO_REGISTER = 'register'; |
||||
|
||||
public function scenarios() |
||||
{ |
||||
$scenarios = parent::scenarios(); |
||||
$scenarios[self::SCENARIO_LOGIN] = ['username', 'password']; |
||||
$scenarios[self::SCENARIO_REGISTER] = ['username', 'email', 'password']; |
||||
return $scenarios; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Xây dựng các kịch bản được dùng vào việc [xác thực](#validation-rules) và [massive attribute assignment](#massive-assignment). |
||||
Tuy nhiên, bạn có thể dùng vào mục đích khác. Chẳng hạn, bạn có thể khai báo các [nhãn thuộc tính](#attribute-labels) |
||||
khác nhau được dựa trên kịch bản hiện tại. |
||||
|
||||
|
||||
## Các quy tắc xác nhận (Validation Rules) <span id="validation-rules"></span> |
||||
|
||||
Khi dữ liệu cho model được chuyển lên từ người dùng cuối, dữ liệu này cần được xác thực để chắc chắn rằng dữ liệu này là hợp lệ |
||||
(được gọi là *quy tắc xác nhận*, có thể gọi *business rules*). Ví dụ, cho model `ContactForm`, |
||||
bạn muốn tất cả các thuộc tính không được để trống và thuộc tính `email` phải là địa chỉ email hợp lệ. |
||||
Nếu các giá trị cho các thuộc tính không được thỏa mãn với các quy tắc xác nhận, các thông báo lỗi sẽ được |
||||
được hiển thị để giúp người dùng sửa lỗi. |
||||
|
||||
Bạn có thể gọi phương thức [[yii\base\Model::validate()]] để xác thực các dữ liệu đã nhận. Phương thức sẽ dùng các quy tắc xác nhận |
||||
được khai báo ở phương thức [[yii\base\Model::rules()]] để xác thực mọi thuộc tính liên quan. Nếu không có lỗi nào tìm thấy |
||||
, sẽ trả về giá trị `true`. Nếu không thì, phương thức sẽ giữ các thông báo lỗi tại thuộc tính [[yii\base\Model::errors]] |
||||
và trả kết quả`false`. Ví dụ, |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// gán các thuộc tính của model từ dữ liệu người dùng |
||||
$model->attributes = \Yii::$app->request->post('ContactForm'); |
||||
|
||||
if ($model->validate()) { |
||||
// tất cả các dữ liệu nhập vào hợp lệ |
||||
} else { |
||||
// xác nhận lỗi: biến $errors chứa mảng các nội dung thông báo lỗi |
||||
$errors = $model->errors; |
||||
} |
||||
``` |
||||
|
||||
|
||||
Các quy tắc xác nhận được gắn vào model, việc ghi đè phương thức [[yii\base\Model::rules()]] cùng với việc trả về |
||||
có chứa các thuộc tính an toàn cần được xác thực. Ví dụ sau đây sẽ cho thấy các quy tắc xác nhận được khai báo cho model |
||||
`ContactForm`: |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// the name, email, subject and body attributes are required |
||||
[['name', 'email', 'subject', 'body'], 'required'], |
||||
|
||||
// the email attribute should be a valid email address |
||||
['email', 'email'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Mỗi quy tắc được dùng để xác nhận một hoặc nhiều các thuộc tính, và một thuộc tính có thể được xác nhận một hoặc nhiều quy tắc. |
||||
Vui lòng tham khảo mục [Xác nhận đầu vào](input-validation.md) để biết thêm chi tiết về cách khai báo các quy tắc xác nhận. |
||||
|
||||
Đôi khi, bạn muốn các quy tắc chỉ được áp dụng chỉ trong một số [kịch bản](#scenarios). Để làm như vậy, bạn có thể |
||||
thêm thông tin thuộc tính `on` ở mỗi quy tắc, giống như sau: |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// thuộc tính username, email và password cần được nhập ở kịch bản "register" |
||||
[['username', 'email', 'password'], 'required', 'on' => self::SCENARIO_REGISTER], |
||||
|
||||
// username và password cần được nhập ở kịch bản "login" |
||||
[['username', 'password'], 'required', 'on' => self::SCENARIO_LOGIN], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Nếu bạn không chỉ định thuộc tính `on`, quy tắc sẽ áp dụng trong tất cả các kịch bản. Một quy tắc được gọi |
||||
một *quy tắc hoạt động* nếu nó được áp dụng với kịch bản hiện tại [[yii\base\Model::scenario|scenario]]. |
||||
|
||||
Một thuộc tính được xác nhận nếu và chỉ nếu nó là thuộc tính được kích hoạt với khai báo tại phương thức `scenarios()` và |
||||
được liên kết với một hoặc nhiều quy tắc được khai báo ở phương thức `rules()`. |
||||
|
||||
|
||||
## Gán nhanh (Massive Assignment) <span id="massive-assignment"></span> |
||||
|
||||
Gán nhanh là cách tiện lợi cho việc nhập dữ liệu vào model từ người dùng với một dòng mã. |
||||
Nó nhập vào các thuộc tính của model bằng việc gán dữ liệu nhập vào qua thuộc tính [[yii\base\Model::$attributes]] |
||||
. 2 đoạn mã sau hoạt động giống nhau , cả 2 đều lấy dữ liệu trong form gửi lên từ người dùng |
||||
vào các thuộc tính của model `ContactForm`. Nhanh gọn, cách trên, sẽ dùng gán nhanh, mã của bạn trông sạch và ít lỗi hơn cách sau đó: |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
$model->attributes = \Yii::$app->request->post('ContactForm'); |
||||
``` |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
$data = \Yii::$app->request->post('ContactForm', []); |
||||
$model->name = isset($data['name']) ? $data['name'] : null; |
||||
$model->email = isset($data['email']) ? $data['email'] : null; |
||||
$model->subject = isset($data['subject']) ? $data['subject'] : null; |
||||
$model->body = isset($data['body']) ? $data['body'] : null; |
||||
``` |
||||
|
||||
|
||||
### Thuộc tính an toàn (Safe Attributes) <span id="safe-attributes"></span> |
||||
|
||||
Gán nhanh chỉ gán dữ liệu cho những thuộc tính gọi là *thuộc tính an toàn (safe attributes)* đó là các thuộc tính được liệt kê trong phương thức |
||||
[[yii\base\Model::scenarios()]] cho thuộc tính [[yii\base\Model::scenario|scenario]] của model. |
||||
Chẳng hạn, nếu model `User` có các kịch bản mô tả như sau, tiếp đến kịch bản |
||||
`login` đang được chọn, thì chỉ thuộc tính `username` và `password` có thể được gán nhanh. Bất kỳ các thuộc tính khác |
||||
sẽ được giữ nguyên. |
||||
|
||||
```php |
||||
public function scenarios() |
||||
{ |
||||
return [ |
||||
self::SCENARIO_LOGIN => ['username', 'password'], |
||||
self::SCENARIO_REGISTER => ['username', 'email', 'password'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
> Thông tin: Lý do việc gán nhanh chỉ gán dữ liệu cho các thuộc tính an toàn là bởi vì bạn muốn kiểm soát |
||||
những thuộc tính có thể được thay đổi bởi người dùng. Chẳng hạn, nếu model `User` |
||||
có thuộc tính `permission` nhằm xác định các quyền hạn của người dùng, bạn chỉ muốn |
||||
thuộc tính này chỉ được thay đổi bởi quản trị viên thông qua giao diện phụ trợ. |
||||
|
||||
Bởi vì mặc định phương thức [[yii\base\Model::scenarios()]] sẽ trả về tất cả các kịch bản và thuộc tính |
||||
nằm trong phương thức [[yii\base\Model::rules()]], nếu bạn không ghi đè phương thức này, có nghĩa là một thuộc tính là an toàn |
||||
miễn là có khai báo ở một trong các quy tắc xác nhận. |
||||
|
||||
Vì lý do này, bí danh `safe` được đưa ra bạn có thể khai báo các thuộc tính an toàn |
||||
mà không thực sự xác nhận nó. Chẳng hạn, các quy tắc sau đây khai báo thuộc tính `title` |
||||
và `description` là thuộc tính an toàn. |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['title', 'description'], 'safe'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Thuộc tính không an toàn (Unsafe Attributes) <span id="unsafe-attributes"></span> |
||||
|
||||
Như mô tả trên, khai báo phương thức [[yii\base\Model::scenarios()]] có 2 mục đích: liệt kê thuộc tính cần được xác nhận |
||||
, và xác định các thuộc tính là an toàn. Trong một số trường hợp khác, bạn muốn xác nhận thuộc tính nhưng |
||||
không muốn đánh dấu là an toàn. bạn có thể thực hiện bằng việc đặt dấu chấm than `!` vào tên thuộc tính |
||||
khi khai báo tại phương thức `scenarios()`, giốn như thuộc tính `secret` như sau: |
||||
|
||||
```php |
||||
public function scenarios() |
||||
{ |
||||
return [ |
||||
self::SCENARIO_LOGIN => ['username', 'password', '!secret'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Khi model đang ở kịch bản `login`, cả 3 thuộc tính sẽ được xác nhận. Tuy nhiên, chỉ có thuộc tính `username` |
||||
và `password` được gán nhanh. Để gán giá trị cho thuộc tính `secret`, bạn |
||||
cần được gán trực tiếp như sau, |
||||
|
||||
```php |
||||
$model->secret = $secret; |
||||
``` |
||||
|
||||
Điều tương tự có thể được thực hiện trong phương thức `rules()`: |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['username', 'password', '!secret'], 'required', 'on' => 'login'] |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Trong trường hợp này các thuộc tính `username`, `password` và `secret` là yêu cầu nhập, nhưng thuộc tính `secret` phải cần được gán trực tiếp. |
||||
|
||||
|
||||
## Xuất dữ liệu (Data Exporting) <span id="data-exporting"></span> |
||||
|
||||
Các model thường được cần trích xuất ra các định dạng khác nhau. Chẳng hạn, bạn cần chuyển dữ liệu sang của |
||||
models sang định dạng JSON hoặc Excel. Quá trình xuất có thể được chia nhỏ thành hai bước độc lập: |
||||
|
||||
- models cần được chuyển sang định dạng mảng; |
||||
- các mảng cần được chuyển đổi thành các định dạng cần chuyển. |
||||
|
||||
Bạn chỉ cần tập trung vào bước đầu tiên, bởi vì bước thứ 2 có thể được thực hiện bởi các trình định dạng dữ liệu |
||||
, chẳng hạn như [[yii\web\JsonResponseFormatter]]. |
||||
|
||||
Các đơn giản nhất để chuyển đổi model sang dạng mảng là sử dụng thuộc tính [[yii\base\Model::$attributes]]. |
||||
For example, |
||||
|
||||
```php |
||||
$post = \app\models\Post::findOne(100); |
||||
$array = $post->attributes; |
||||
``` |
||||
|
||||
Bởi mặc định, thuộc tính [[yii\base\Model::$attributes]] sẽ trả về các giá trị của *tất cả* các thuộc tính |
||||
được khai báo trong phương thức [[yii\base\Model::attributes()]]. |
||||
|
||||
Còn một cách linh hoạt và tiện lợi hơn trong việc chuyển đổi model sang định dạng mảng là sử dụng phương thức [[yii\base\Model::toArray()]] |
||||
. Cách chuyển đổi cũng tương tự như trong cách của thuộc tính [[yii\base\Model::$attributes]]. Tuy nhiên, nó cho phép bạn chọn các dữ liệu |
||||
, được gọi là *fields*, được đặt trong mảng kết quả và chúng được định dạng thế nào. |
||||
Trong thực tế, đó là cách trích xuất mặc định của các model ở việc phát triển các dịch vụ RESTful Web, như được mô tả trong |
||||
mục [Response Formatting](rest-response-formatting.md). |
||||
|
||||
|
||||
### Các trường (Fields) <span id="fields"></span> |
||||
|
||||
Một trường đơn giản là tên của thành phần thu được nằm trong mảng khi gọi phương thức [[yii\base\Model::toArray()]] |
||||
của model. |
||||
|
||||
Mặc định, tên trường sẽ tương đương với tên thuộc tính. Tuy nhiên, bạn có thể thay đổi bằng việc ghi đè |
||||
qua phương thức [[yii\base\Model::fields()|fields()]] và/hoặc phương thức [[yii\base\Model::extraFields()|extraFields()]]. Cả 2 phương thức |
||||
trả về danh sách các khai báo trường. Các trường được định nghĩa bởi phương thức `fields()` là các trường mặc định, nghĩa là phương thức |
||||
`toArray()` sẽ trả về những trường mặc định. Phương thức `extraFields()` sẽ khai báo thêm các trường bổ sung có thể được trả về |
||||
bởi phương thức `toArray()` miễn là bạn chỉ định chugns qua tham số `$expand`. Chẳng hạn, |
||||
đoạn mã sau sẽ trả về các trường được định nghĩa trong phương thức `fields()` và 2 trường `prettyName` và `fullAddress` |
||||
nếu chúng được định nghĩa trong phương thức `extraFields()`. |
||||
|
||||
```php |
||||
$array = $model->toArray([], ['prettyName', 'fullAddress']); |
||||
``` |
||||
|
||||
Bạn có thể ghi đè phương thức `fields()` để thêm, xóa, cập nhật hoặc định nghĩa lại các trường. Phương thức `fields()` |
||||
sẽ trả về dữ liệu dạng mảng. Mảng này có các khóa là tên các trường, và các giá trị của mảng tương ứng |
||||
với các trường đã định nghĩa giá trị có thể là tên các thuộc tính/biến hoặc một hàm trả về các giá trị trường tương ứng |
||||
. Trong trường hợp đặc biệt khi tên trường giống với tên thuộc tính xác định của nó, bạn có thể bỏ qua khóa mảng. Ví dụ, |
||||
|
||||
```php |
||||
// liệt kê rõ ràng các trường, sử dụng tốt nhất khi bạn nắm được các thay đổi |
||||
// trong bản CSDL hoặc các thuộc tính của model, không gây ra sự thay đổi của trường (để giữ tương thích với API). |
||||
public function fields() |
||||
{ |
||||
return [ |
||||
// tên trường giống với tên thuộc tính |
||||
'id', |
||||
|
||||
// tên trường là "email", tương ứng với tên thuộc tính là "email_address" |
||||
'email' => 'email_address', |
||||
|
||||
// tên trường là "name", giá trị được định nghĩa bởi hàm |
||||
'name' => function () { |
||||
return $this->first_name . ' ' . $this->last_name; |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
// lọc ra một số trường, nên sử dụng khi bạn muốn kế thừa các trường |
||||
// thêm vào blacklist một số trường không cần thiết. |
||||
public function fields() |
||||
{ |
||||
$fields = parent::fields(); |
||||
|
||||
// remove fields that contain sensitive information |
||||
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); |
||||
|
||||
return $fields; |
||||
} |
||||
``` |
||||
|
||||
> Cảnh báo: Bởi vì theo mặc định tất cả các thuộc tính của model sẽ liệt kê trong mảng trích xuất, bạn nên |
||||
> kiểm tra dữ liệu của bạn để chắc chắn rằng chúng không chứa các thông tin không cần thiết. Nếu có thông tin như vậy, |
||||
> bạn nên ghi đè phương thức `fields()` để lọc chúng ra. Tại ví dụ trên, chúng ta chọn |
||||
> các trường để lọc ra là `auth_key`, `password_hash` và `password_reset_token`. |
||||
|
||||
|
||||
## Bài thực hành <span id="best-practices"></span> |
||||
|
||||
Các model là phần trung tâm đại diện cho tầng dữ liệu, chứa các quy tắc và logic. Model thường được tái sử dụng |
||||
tại một số nơi khác nhau. Với một ứng dụng được thiết kế tốt, thông thường các model được chú trọng hơn |
||||
[controllers](structure-controllers.md). |
||||
|
||||
Tổng hợp mục, models |
||||
|
||||
* có thể chứa các thuộc tính đại diện cho tầng dữ liệu (business data); |
||||
* có thể chứa các quy tắc xác nhận để đảm bảo tính hợp lệ và tính toàn vẹn của dữ liệu;; |
||||
* có thể chứa các phương thức tại tầng logic (business logic); |
||||
* không nên trực tiếp xử lý các yêu cầu, session, hoặc bất cứ dữ liệu môi trường. Những dữ liệu này nên được tiến hành xử lý |
||||
bởi [controllers](structure-controllers.md) vào model; |
||||
* tránh việc nhúng mã HTML hoặc các dữ liệu hiển thị - mã này nên được đặt tại [views](structure-views.md); |
||||
* tránh có quá nhiều kịch bản [scenarios](#scenarios) trong một model. |
||||
|
||||
Bạn cần có sự xem xét các đề nghị trên mỗi khi bạn triển khai hệ thống lớn và phức tạp. |
||||
Trong các hệ thống này, cácmodel cần được chú trọng bởi vì chúng được sử dụng ở nhiều nơi và có thể chứa nhiều các quy tắc |
||||
và các xử lý nghiệp vụ. Điều này có sự ảnh hưởng tại tiến trình bảo trì mỗi thay đổi mã của bạn |
||||
có thể ảnh hưởng tới nhiều vị trí khác nhau. Để mã code của bạn dễ được bảo trì hơn, |
||||
bạn có thể được thực hiện các chiến lược sau: |
||||
|
||||
* Định nghĩa tập các lớp model cơ sở (base model) lớp này được chia sẻ qua các [ứng dụng](structure-applications.md) hoặc |
||||
[modules](structure-modules.md) khác nhau. Các model này có thể chứa tập các quy tắc và logic có thể được |
||||
dùng rộng rãi ở các lớp cần được sử dụng. |
||||
* Tại mỗi [ứng dụng](structure-applications.md) hoặc [module](structure-modules.md) có dùng model, |
||||
ta định nghĩa lớp khung model bằng việc kế thừa từ lớp model cơ sở. Lớp khung model này |
||||
có thể chứa các quy tắc logic được mô tả cụ thể chi ứng dụng hoặc module này. |
||||
|
||||
Ví dụ, với [Mẫu Dự án Advanced](https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/README.md), bạn có thể định nghĩa lớp cơ sở model |
||||
là `common\models\Post`. Tiếp đến tại ứng dụng front end, bạn định nghĩa lớp khung là |
||||
`frontend\models\Post` lớp này kế thừa từ lớp `common\models\Post`. Và tương tự cho ứng dụng back end, |
||||
bạn định nghĩa model `backend\models\Post`. Với cách giải quyết này, bạn sẽ chắc chắn rằng mã của bạn tại model `frontend\models\Post` |
||||
chỉ dùng cho ứng dụng front end, và nếu bạn thực hiện với bất kỳ thay đổi nào, bạn không cần lo lắng về |
||||
việc thay đổi này có ảnh hưởng tới ứng dụng back end. |
@ -0,0 +1,160 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\behaviors; |
||||
|
||||
use Yii; |
||||
use yii\db\BaseActiveRecord; |
||||
use yii\base\InvalidCallException; |
||||
use yii\validators\NumberValidator; |
||||
|
||||
/** |
||||
* OptimisticLockBehavior automatically upgrades a model's lock version using the column name |
||||
* returned by [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]]. |
||||
* |
||||
* Optimistic locking allows multiple users to access the same record for edits and avoids |
||||
* potential conflicts. In case when a user attempts to save the record upon some staled data |
||||
* (because another user has modified the data), a [[StaleObjectException]] exception will be thrown, |
||||
* and the update or deletion is skipped. |
||||
* |
||||
* To use this behavior, first enable optimistic lock by following the steps listed in |
||||
* [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]], remove the column name |
||||
* holding the lock version from the [[\yii\base\Model::rules()|rules()]] method of your |
||||
* ActiveRecord class, then add the following code to it: |
||||
* |
||||
* ```php |
||||
* use yii\behaviors\OptimisticLockBehavior; |
||||
* |
||||
* public function behaviors() |
||||
* { |
||||
* return [ |
||||
* OptimisticLockBehavior::className(), |
||||
* ]; |
||||
* } |
||||
* ``` |
||||
* |
||||
* By default, OptimisticLockBehavior will use [[\yii\web\Request::getBodyParam()|getBodyParam()]] to parse |
||||
* the submitted value or set it to 0 on any fail. That means a request not holding the version attribute |
||||
* may achieve a first successful update to entity, but starting from there any further try should fail |
||||
* unless the request is holding the expected version number. You can also configure the [[value]] property |
||||
* with a PHP callable to implement a different logic. |
||||
* |
||||
* OptimisticLockBehavior also provides a method named [[upgrade()]] that increases a model's |
||||
* version by one, that may be useful when you need to mark an entity as stale among connected clients |
||||
* and avoid any change to it until they load it again: |
||||
* |
||||
* ```php |
||||
* $model->upgrade(); |
||||
* ``` |
||||
* |
||||
* @author Salem Ouerdani <tunecino@gmail.com> |
||||
* @since 2.0.16 |
||||
* @see \yii\db\BaseActiveRecord::optimisticLock() for details on how to enable optimistic lock. |
||||
*/ |
||||
class OptimisticLockBehavior extends AttributeBehavior |
||||
{ |
||||
/** |
||||
* {@inheritdoc} |
||||
* |
||||
* In case of `null` value it will be directly parsed from [[\yii\web\Request::getBodyParam()|getBodyParam()]] or set to 0. |
||||
*/ |
||||
public $value; |
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public $skipUpdateOnClean = false; |
||||
/** |
||||
* @var string the attribute name holding the version value. |
||||
*/ |
||||
private $_lockAttribute; |
||||
|
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function attach($owner) |
||||
{ |
||||
parent::attach($owner); |
||||
|
||||
if (empty($this->attributes)) { |
||||
$lock = $this->getLockAttribute(); |
||||
$this->attributes = array_fill_keys(array_keys($this->events()), $lock); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
*/ |
||||
public function events() |
||||
{ |
||||
return Yii::$app->request instanceof \yii\web\Request ? [ |
||||
BaseActiveRecord::EVENT_BEFORE_INSERT => 'evaluateAttributes', |
||||
BaseActiveRecord::EVENT_BEFORE_UPDATE => 'evaluateAttributes', |
||||
BaseActiveRecord::EVENT_BEFORE_DELETE => 'evaluateAttributes', |
||||
] : []; |
||||
} |
||||
|
||||
/** |
||||
* Returns the column name to hold the version value as defined in [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]]. |
||||
* @return string the property name. |
||||
* @throws InvalidCallException if [[\yii\db\BaseActiveRecord::optimisticLock()|optimisticLock()]] is not properly configured. |
||||
* @since 2.0.16 |
||||
*/ |
||||
protected function getLockAttribute() |
||||
{ |
||||
if ($this->_lockAttribute) { |
||||
return $this->_lockAttribute; |
||||
} |
||||
|
||||
/* @var $owner BaseActiveRecord */ |
||||
$owner = $this->owner; |
||||
$lock = $owner->optimisticLock(); |
||||
if ($lock === null || $owner->hasAttribute($lock) === false) { |
||||
throw new InvalidCallException("Unable to get the optimistic lock attribute. Probably 'optimisticLock()' method is misconfigured."); |
||||
} |
||||
$this->_lockAttribute = $lock; |
||||
return $lock; |
||||
} |
||||
|
||||
/** |
||||
* {@inheritdoc} |
||||
* |
||||
* In case of `null`, value will be parsed from [[\yii\web\Request::getBodyParam()|getBodyParam()]] or set to 0. |
||||
*/ |
||||
protected function getValue($event) |
||||
{ |
||||
if ($this->value === null) { |
||||
$lock = $this->getLockAttribute(); |
||||
$input = Yii::$app->getRequest()->getBodyParam($lock); |
||||
$isValid = $input && (new NumberValidator())->validate($input); |
||||
return $isValid ? $input : 0; |
||||
} |
||||
|
||||
return parent::getValue($event); |
||||
} |
||||
|
||||
/** |
||||
* Upgrades the version value by one and stores it to database. |
||||
* |
||||
* ```php |
||||
* $model->upgrade(); |
||||
* ``` |
||||
* @throws InvalidCallException if owner is a new record. |
||||
* @since 2.0.16 |
||||
*/ |
||||
public function upgrade() |
||||
{ |
||||
/* @var $owner BaseActiveRecord */ |
||||
$owner = $this->owner; |
||||
if ($owner->getIsNewRecord()) { |
||||
throw new InvalidCallException('Upgrading the model version is not possible on a new record.'); |
||||
} |
||||
$lock = $this->getLockAttribute(); |
||||
$version = $owner->$lock ?: 0; |
||||
$owner->updateAttributes([$lock => $version + 1]); |
||||
} |
||||
} |
@ -0,0 +1,283 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yiiunit\framework\behaviors; |
||||
|
||||
use Yii; |
||||
use yii\behaviors\OptimisticLockBehavior; |
||||
use yii\web\Request; |
||||
use yii\db\ActiveRecord; |
||||
use yii\db\Connection; |
||||
use yii\db\Expression; |
||||
use yii\db\ExpressionInterface; |
||||
use yiiunit\TestCase; |
||||
|
||||
/** |
||||
* Unit test for [[\yii\behaviors\OptimisticLockBehavior]]. |
||||
* @see OptimisticLockBehavior |
||||
* |
||||
* @group behaviors |
||||
*/ |
||||
class OptimisticLockBehaviorTest extends TestCase |
||||
{ |
||||
/** |
||||
* @var Connection test db connection |
||||
*/ |
||||
protected $dbConnection; |
||||
|
||||
public static function setUpBeforeClass() |
||||
{ |
||||
if (!extension_loaded('pdo') || !extension_loaded('pdo_sqlite')) { |
||||
static::markTestSkipped('PDO and SQLite extensions are required.'); |
||||
} |
||||
} |
||||
|
||||
public function setUp() |
||||
{ |
||||
$this->mockApplication([ |
||||
'components' => [ |
||||
'db' => [ |
||||
'class' => '\yii\db\Connection', |
||||
'dsn' => 'sqlite::memory:', |
||||
], |
||||
], |
||||
]); |
||||
|
||||
$columns = [ |
||||
'id' => 'pk', |
||||
'version' => 'integer NOT NULL', |
||||
]; |
||||
Yii::$app->getDb()->createCommand()->createTable('test_auto_lock_version', $columns)->execute(); |
||||
|
||||
$columns = [ |
||||
'id' => 'pk', |
||||
'version' => 'string NOT NULL', |
||||
]; |
||||
Yii::$app->getDb()->createCommand()->createTable('test_auto_lock_version_string', $columns)->execute(); |
||||
} |
||||
|
||||
public function tearDown() |
||||
{ |
||||
Yii::$app->getDb()->close(); |
||||
parent::tearDown(); |
||||
gc_enable(); |
||||
gc_collect_cycles(); |
||||
} |
||||
|
||||
// Tests : |
||||
|
||||
public function testUpdateRecordWithinConsoleRequest() |
||||
{ |
||||
ActiveRecordLockVersion::$behaviors = [ |
||||
OptimisticLockBehavior::className(), |
||||
]; |
||||
$model = new ActiveRecordLockVersion(); |
||||
$model->version = 0; |
||||
$model->save(false); |
||||
|
||||
// upgrade model |
||||
|
||||
$model->upgrade(); |
||||
|
||||
$this->assertEquals(1, $model->version, 'updated version should equal 1'); |
||||
|
||||
// a console request should use the version number as loaded from database (the behavior should be omitted) |
||||
|
||||
$model->markAttributeDirty('version'); |
||||
|
||||
$model->save(false); |
||||
|
||||
$this->assertEquals(2, $model->version, 'updated version should equal 2'); |
||||
} |
||||
|
||||
|
||||
public function testNewRecord() |
||||
{ |
||||
// create a record without any version |
||||
|
||||
$request = new Request(); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
ActiveRecordLockVersion::$behaviors = [ |
||||
OptimisticLockBehavior::className(), |
||||
]; |
||||
$model = new ActiveRecordLockVersion(); |
||||
$model->save(false); |
||||
|
||||
$this->assertEquals(0, $model->version, 'init version should equal 0'); |
||||
|
||||
// create a record starting from version 5 |
||||
|
||||
$request->setBodyParams(['version' => 5]); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
$model = new ActiveRecordLockVersion(); |
||||
$model->save(false); |
||||
|
||||
$this->assertEquals(5, $model->version, 'init version should equal 5'); |
||||
} |
||||
|
||||
|
||||
public function testUpdateRecord() |
||||
{ |
||||
$request = new Request(); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
ActiveRecordLockVersion::$behaviors = [ |
||||
OptimisticLockBehavior::className(), |
||||
]; |
||||
$model = new ActiveRecordLockVersion(); |
||||
$model->save(false); |
||||
|
||||
// upgrade model |
||||
|
||||
$model->upgrade(); |
||||
|
||||
$this->assertEquals(1, $model->version, 'updated version should equal 1'); |
||||
|
||||
// save stale data without sending version |
||||
|
||||
$thrown = false; |
||||
|
||||
try { |
||||
$model->save(false); |
||||
} catch (\yii\db\StaleObjectException $e) { |
||||
$this->assertContains('The object being updated is outdated.', $e->getMessage()); |
||||
$thrown = true; |
||||
} |
||||
|
||||
$this->assertTrue($thrown, 'A StaleObjectException exception should have been thrown.'); |
||||
|
||||
// save stale data by sending an outdated version |
||||
|
||||
$request->setBodyParams(['version' => 0]); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
$thrown = false; |
||||
|
||||
try { |
||||
$model->save(false); |
||||
} catch (\yii\db\StaleObjectException $e) { |
||||
$this->assertContains('The object being updated is outdated.', $e->getMessage()); |
||||
$thrown = true; |
||||
} |
||||
|
||||
$this->assertTrue($thrown, 'A StaleObjectException exception should have been thrown.'); |
||||
|
||||
// save stale data by sending an 'invalid' version number |
||||
|
||||
$request->setBodyParams(['version' => 'yii']); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
$thrown = false; |
||||
|
||||
try { |
||||
$model->save(false); |
||||
} catch (\yii\db\StaleObjectException $e) { |
||||
$this->assertContains('The object being updated is outdated.', $e->getMessage()); |
||||
$thrown = true; |
||||
} |
||||
|
||||
$this->assertTrue($thrown, 'A StaleObjectException exception should have been thrown.'); |
||||
|
||||
// the behavior should set version to 0 when user input is not a valid number. |
||||
|
||||
$this->assertEquals(0, $model->version, 'updated version should equal 0'); |
||||
|
||||
// a successful update by sending the correct version |
||||
|
||||
$request->setBodyParams(['version' => '1']); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
$model->save(false); |
||||
|
||||
$this->assertEquals(2, $model->version, 'updated version should equal 2'); |
||||
} |
||||
|
||||
public function testDeleteRecord() |
||||
{ |
||||
$request = new Request(); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
ActiveRecordLockVersion::$behaviors = [ |
||||
OptimisticLockBehavior::className(), |
||||
]; |
||||
$model = new ActiveRecordLockVersion(); |
||||
$model->save(false); |
||||
|
||||
// upgrade model version to 1 |
||||
|
||||
$model->upgrade(); |
||||
|
||||
// delete stale data without sending version |
||||
|
||||
$thrown = false; |
||||
|
||||
try { |
||||
$model->delete(); |
||||
} catch (\yii\db\StaleObjectException $e) { |
||||
$this->assertContains('The object being deleted is outdated.', $e->getMessage()); |
||||
$thrown = true; |
||||
} |
||||
|
||||
$this->assertTrue($thrown, 'A StaleObjectException exception should have been thrown.'); |
||||
|
||||
// delete stale data by sending an outdated version |
||||
|
||||
$request->setBodyParams(['version' => 0]); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
$thrown = false; |
||||
|
||||
try { |
||||
$model->delete(); |
||||
} catch (\yii\db\StaleObjectException $e) { |
||||
$this->assertContains('The object being deleted is outdated.', $e->getMessage()); |
||||
$thrown = true; |
||||
} |
||||
|
||||
$this->assertTrue($thrown, 'A StaleObjectException exception should have been thrown.'); |
||||
|
||||
// a successful update by sending the correct version |
||||
|
||||
$request->setBodyParams(['version' => '1']); |
||||
Yii::$app->set('request', $request); |
||||
|
||||
$model->delete(); |
||||
|
||||
$this->assertEquals(1, $model->version, 'deleted version should remain 1'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Test Active Record class with [[OptimisticLockBehavior]] behavior attached. |
||||
* |
||||
* @property int $id |
||||
* @property int $created_at |
||||
* @property int $updated_at |
||||
*/ |
||||
class ActiveRecordLockVersion extends ActiveRecord |
||||
{ |
||||
public static $behaviors; |
||||
public static $lockAttribute = 'version'; |
||||
public static $tableName = 'test_auto_lock_version'; |
||||
|
||||
public function behaviors() |
||||
{ |
||||
return static::$behaviors; |
||||
} |
||||
|
||||
public function optimisticLock() |
||||
{ |
||||
return static::$lockAttribute; |
||||
} |
||||
|
||||
public static function tableName() |
||||
{ |
||||
return static::$tableName; |
||||
} |
||||
} |
Loading…
Reference in new issue