0 follower

Bộ điều khiển (Controller)

Controller thuộc một phần trong mẫu thiết kế MVC. 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, controllers sẽ phân tích thông tin yêu cầu được gửi đến, gửi dữ liệu qua models để xử lý, và gán kết quả xử lý từ model vào views, và cuối cùng là gửi phản hồi.

Hành động (Actions)

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à : viewcreate:

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 để 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 viewview. 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, 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)

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;
  • Một định danh của Controller: 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 :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:

ModuleID/ControllerID/ActionID

Như vậy nếu user truy cập vào đường dẫn sau https://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.

Tạo Controller

Trong mỗi Ứ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 Ứ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 :

namespace app\controllers;

use yii\web\Controller;

class SiteController extends Controller
{
}

Định danh Controller (Controller ID)

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ụ, articlepost-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 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

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 controller namespace.

Xem ví dụ sau, giả sử 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. 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@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 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.

Controller Map

Bạn có thể cấu hình thông tin về mục 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 controller map trong mục cấu hình ứng dụng. Ví dụ:

[
    '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

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, thì route mặc định sẽ được gọi. Chẳng hạn Web applications, có giá trị là 'site', trong khi đó ứng dụng console, có route mặc định là help. Vì vậy, nếu truy cập vào URL sau https://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 như sau:

[
    'defaultRoute' => 'main',
]

Tạo Actions

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à, indexhello-world:

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)

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?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.

Inline Actions

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

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\ViewActionyii\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:

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

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 được gửi tới user như những phản hồi.

  • Chẳng hạn với Ứ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 ứng dụng console, kết quả trả về là số nguyên tương ứng với thuộc tính 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 redirect() sẽ trả về một đối tượng):

public function actionForward()
{
    // điều hướng tới URL https://example.com
    return $this->redirect('https://example.com');
}

Các tham số của Action

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 Ứ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 ứ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$version.

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:

  • https://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.
  • https://hostname/index.php?r=post/view&id=123&version=2: biến $id$version sẽ nhận giá trị tương ứng là '123''2'.
  • https://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.
  • https://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:

public function actionView(array $id, $version = null)
{
    // ...
}

Nếu yêu cầu là https://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à https://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 để biết thêm thông tin.

Action mặc định

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

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

Khi xử lý yêu cầu, ứng dụng sẽ khởi tạo controller dựa theo các yêu cầu tại route. 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ì 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 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.

Thực hành

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;
  • gọi các phương thức từ models 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 để gửi phản hồi;
  • KHÔNG NÊN xử lý dữ liệu - nên xử lý ở tầng model;
  • 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.

Found a typo or you think this page needs improvement?
Edit it on github !