Kinh nghiệm lập trình website mã nguồn mở PHP

Lập trình module Magento 2 tìm hiểu controller

Giới thiệu

Magento 2 được xây dựng theo hướng module hóa, đó là những tính năng được đóng gói riêng biệt, phục vụ cho mục đích phát triển nâng cấp và bảo trì, tăng khả năng mở rộng của ứng dụng.
Thông thường mọi thao tác tùy biến trong Magento đều bắt đầu từ việc xây dựng 1 module.

Để lập trình module Magento 2, bạn cần làm các bước sau:

  1. Tạo thư mục chứa module
  2. Tạo registration.php để khai báo module với hệ thống
  3. Tạo etc/module.xml để cung cấp thông tin phiên bản của module
  4. Chạy lệnh php bin/magento setup:upgrade để cài đặt module mới.
  5. Hoàn tất và kiểm tra module.

Trong bài viết này, ta sẽ cùng tìm hiểu cách viết 1 module, tìm hiểu về controller trong Magento 2, và ứng dụng thực tế gọi tới 1 API bên ngoài để lấy thông tin tỉ giá ngoại tệ.

Bài này nằm trong loạt bài xây dựng website TMDT Magento 2.
Chương 3 – Lập trình extension cơ bản
+ Bài 1 – Cài đặt môi trường lập trình + Module Hello World

Magento Architecture – Kiến trúc

Kiến trúc đóng vai trò rất quan trọng cho sự phát triển trong các framework, đó là cách xắp xếp, tổ chức thiết kế các thành phần kết hợp lại với nhau, sao cho không những thực hiện các chức năng, mà còn giúp lập trình viên dễ dàng tìm hiểu, xây dựng, và tái sử dụng code.

Kiến trúc Magento 1

Trước đây, Magento 1 được biết đến với kiểu cấu trúc rườm rà, râu ông nọ cắm cằm bà kia. Khai báo module đặt 1 nơi, module chính lại đặt 1 nẻo, phần giao diện lại đặt nơi khác. Khiến việc bóc tách module mang qua dự án khác gây nhiều phiền hà:
Lập trình module Magento 2 tìm hiểu controller
Cũng không trách được các nhà sản xuất từ công ty mẹ Varien của Magento, bởi thời Magento 1 cách đây cũng đã 10 năm rồi. Điều tuyệt vời là những hạn chế này sẽ được khắc phục triệt để ở Magento 2, kiến trúc lúc này đã trở nên vô cùng hợp lý và khoa học.

Kiến trúc Magento 2 – module-based

Với Magento 2, 1 module được đóng gói tập trung tại 1 nơi, bao gồm tất cả các chức năng: khai báo, xử lý chức năng, giao diện và dịch thuật ngôn ngữ:
Lập trình module Magento 2 tìm hiểu controller
Trong hình trên, bạn thấy toàn bộ module ta tạo ra sẽ nằm trong thư mục app/code
Module name sẽ gom nhóm theo namespace, ví dụ công ty của bạn viết ra 5 modules sẽ đặt bên trong 1 thư mục, bạn mua thêm 2 module trên marketplace của Amasty nữa sẽ đặt trong thư mục Amasty.
Trong hình ví dụ là có 3 namespace/công ty: Apac, Magento và Nam. Trong đó ông Nam viết 2 module tên là Currency và OnePay

Tiến hành lập trình module Magento 2

Khai báo module

1. Tạo thư mục chứa module: Nam/Currency đặt tại app/code
app\code\Nam\Currency: mục tiêu bài này sẽ xây dựng module lấy tỉ giá ngoại tệ, đổi USD sang VND

2. Tạo registration.php để khai báo module với hệ thống
app\code\Nam\Currency\registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Nam_Currency',
    __DIR__
);

Trong đó ta truyền tên module Nam_Currency

3. Tạo etc/module.xml để cung cấp thông tin phiên bản của module
app\code\Nam\Currency\etc\module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noTestSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Nam_Currency" setup_version="1.0.0" active="true">
    </module>
</config>

Ta cung cấp tên module, phiên bản module hiện tại 1.0.0 và trạng thái, trạng thái mặc định nếu bỏ trống sẽ là true nên ta có thể bỏ qua.

4. Chạy lệnh php bin/magento setup:upgrade để cài đặt module mới.
Vì lệnh này sẽ thực hiện xóa cache, di compile, nếu ta muốn giữ lại thì ta sẽ truyền thêm tham số: php bin/magento setup:upgrade --keep-generated
Vậy là xong, ta đã hoàn thành thủ tục tạo module

Tạo controller

Theo hướng tiếp cận yêu cầu, từ website muốn gọi ra bên ngoài (API lấy currency) ta sẽ sử dụng thông qua 1 Controller.
Request Flow
Lập trình module Magento 2 tìm hiểu controller
Mỗi khi Magento nhận được request, nó sẽ chạy qua các bước chính như hình trên
– Bắt đầu bằng index.php, load lên file bootstrap, Bootstrap này sẽ khởi tạo class App – chính là Magento\Framework\App\Http – class chịu trách nhiệm load cấu hình trong admin
– Tiếp theo Routing sẽ được thực thi, về cơ bản Routing sẽ lặp qua Array toàn bộ các Routes khai báo cho tới khi tìm đúng Route của Request hiện tại.
– Sau khi Routing chạy, tới lượt: Controller và Renderer giao diện.

Vậy bước đầu tiên trước khi tạo controller, ta phải khai báo 1 router, router chịu trách nhiệm phân tích URL request và tìm ra class Controller thích hợp để chạy
1. Khai báo route
app\code\Nam\Currency\etc\frontend\routes.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="currency" frontName="currency">
            <module name="Nam_Currency"/>
        </route>
    </router>
</config>

Ta chú ý 2 điểm sau:
1. file routes.xml đặt trong thư mục etc/frontend mục đích là giới hạn tác dụng bên ngoài frontend, không ảnh hưởng gì tới các trang trong admin.
2. route frontName=”currency” mục đích khi người dùng gõ URL bắt đầu bằng currency dạng: http://website.com/currency hoặc http://website.com/currency/abc thì Magento sẽ đi vào module của mình là Nam_Currency để tìm kiếm Controller thực thi.
Chú ý: trong quá trình làm việc mình có các kinh nghiệm sau khi đặt tên frontName
Thứ nhất: route id có tác dụng giúp hệ thống phân biệt các routers và rất quan trọng, người ta thường đặt route id giống frontName, nhưng trong trường hợp frontName = lien-he thì ta sẽ đặt route id = lien-he luôn, thì sẽ gây ra lỗi bởi vì route id không chấp nhận dấu gạch nối (dash)
Thứ hai: frontName phải lớn hơn hoặc bằng 3 ký tự (các phiên bản trước 2.3)

2. Tạo class Controller
app\code\Nam\Currency\Controller\Index\Index.php

<?php
namespace Nam\Currency\Controller\Index;

class Index extends \Magento\Framework\App\Action\Action
{
    public function execute()
    {
        $layout = $this->_view->getLayout();
        $block = $layout->createBlock('Magento\Framework\View\Element\Text');
        $block->setText('Hello world from Nam currency !');
        $this->getResponse()->appendBody($block->toHtml());
    }
}

Kết quả khi truy cập đường dẫn: /currency
Lập trình module Magento 2 tìm hiểu controller
Phân tích:
Cấu trúc URL trong Magento 1 và 2 gồm: domain/front-name/controller-name/action-name
Trong Controller phía trên:

FrontNamecurrencyvd: /currency, /currency/view, /currency/usd …
ControllerIndexTrong Magento 2, Controller có dạng 1 folder nằm trong folder Controller, chứa các file action. Mặc định controller Index sẽ có thể bỏ qua. Vd: /catalog/product/view/123 sẽ có Controller/Product/View.php frontName=catalog, trong class action View.php ta sẽ lấy được id=123
ActionIndexAction class là các file nằm bên trong 1 folder controller, mỗi 1 class action sẽ thực thi hàm chính execute()

Q: Tại sao controller của tôi không hoạt động?
1. Kiểm tra module của mình có được hệ thống nhận diện hay không?
app\etc\config.php – kiểm tra tên module Nam_Currency có trong này hay không.
– database table setup_module – kiểm tra tên module Nam_Currency có trong này hay không.
2. Nếu chưa có thì bạn cần chạy lệnh: php bin/magento setup:upgrade --keep-generated
3. Nếu ra lỗi 404 thì do khai báo Router sai, nếu ra trang trắng hay 503 thì mở file app\bootstrap.php để mở dòng ini_set() show lỗi lên.

Nếu như bạn hiển thị dòng text lên thì xin chúc mừng bạn, chúng ta sẽ tiếp tục với Controller.
Đây được coi là 1 ví dụ Hello World trong Magento 2, tiếp theo ta sẽ học cách gọi API lấy tỉ giá USD và VND

Controller call API


1. Trong bài viết này mình sẽ sử dụng 1 API tên là https://free.currencyconverterapi.com/, khá tiện lợi không cần phải đăng ký.
2. Trong Magento 2 mình biết 2 cách để request 1 API bên ngoài đó là: Zend_Http_Client
ZendClientFactory, hôm nay ta sẽ dùng Zend_Http_Client trước.

app\code\Nam\Currency\Controller\Index\Index.php

$uri = 'http://free.currencyconverterapi.com/api/v5/convert?q=USD_VND&compact=y';
$httpClient = new \Zend\Http\Client();
$httpClient->setUri($uri);
$httpClient->setOptions(array(
    'timeout' => 30
));
try {
    $response = \Zend\Json\Decoder::decode($httpClient->send()->getBody());
    if (isset($response->USD_VND) && isset($response->USD_VND->val)) {
        $block->addText(' 1 USD = ' . $response->USD_VND->val . ' VND');
    }
} catch (\Exception $e) {
    $block->setText($e->getMessage());
}

Ta có thể truy cập http://free.currencyconverterapi.com/api/v5/convert?q=USD_VND&compact=y để xem response data json:

{"USD_VND":{"val":23034}}

Nhờ đó hàm Decoder::decode() có thể chuyển json thành object và ta dùng -> để truy cập value
Chú ý hàm $block->addText() sẽ nối thêm chuỗi, khác với hàm $block->setText() là ghi đè giá trị. (Xem chi tiết tại vendor\magento\framework\View\Element\Text.php)
Q: tại sao controller hiển thị không cập nhật sau khi code mới?
Trang của bạn đã bị cache, clear bằng lệnh:
php bin/magento cache:clean

Tổng kết

Qua bài viết Lập trình module Magento 2 hôm nay, bạn đã làm quen với module trong Magento 2, tạo 1 ví dụ Hello World, hiểu về Controller và cách sử dụng API. Trong bài viết tiếp theo ta sẽ tìm hiểu về Layout, Block, Template.

Mục lục loạt bài Magento 2

Lập trình module Magento 2 tìm hiểu controller
Đánh giá bài viết

4 phản hồi

  1. Loạt bài hay quá anh ơi, hóng anh hướng dẫn custom phần shipping address sao cho phù hợp với Việt Nam như bỏ zip code, company chẳng hạn. Thanks

Gửi phản hồi

Your email address will not be published. Required fields are marked *