Nhắc lại về ViewModel
ViewModel như là một đối tượng chứa các biến mà action của controller trả về, ViewModel sẽ chuyển các biến đó cho View (PhpRender truy cập và dựng HTML). Tuy nhiên ViewModel cũng đối tượng mà MVC của ZF biết sẽ dùng template (.phtml) nào để dựng HTML.
Action của controller thiết lập biến cho ViewModel ngay tại phương thức khởi tạo của ViewModel hoặc dử dùng phương thức setVariable
, setVariable
,
nếu muốn thay đổi template mà View dùng tới với ViewModel này thì dùng phương thức của ViewModel: setTemplate('templatename')
//Truyền biến ngay từ khởi tạo $view = new ViewModel([ 'tenbien1' => $giatri1, 'tenbien2' => $giatri2, ... ]);
//Truyền biến ngay từ khởi tạo $view = new ViewModel(); $view->setVariables([ 'tenbien1' => $giatri1, 'tenbien2' => $giatri2, ... ]); //Hoặc $view->setVariable('tenbien3', $giatri3)
Lúc này ở các View .phtml có thể truy cập và lấy giá trị của biến bằng cách gọi tên biến
<? //Trong file .phtml $var1 = $this->tenbien1; $var2 = $tenbien2; //....
ViewModel chứa ViewModel khác
Một ViewModel có thể chứa các ViewModel con, điều này giúp bạn dễ dàng hơn trong trình bày cấu trúc trang web.
Khi PhpRender làm việc, nó sẽ kiểm tra xem ViewModel có chứa các ViewModel con không, nếu có thì các ViewModel con sẽ được dựng (render) HTML trước,
kết quả HTML trả về sẽ đặt vào vị trí thích hợp của ViewModel theo chỉ số placeholder
Một placeholder
là tên biến lưu trữ trong ViewModel
Các hàm bổ xung ViewModel
Phương thức | Chi tiết |
---|---|
addChild | addChild($child, $placeholder, $append) : Thêm vào một ViewModel con $child, kết quả dựng HTML của $child lưu vào biến $placeholder của cha, $append là true thì nối thêm kết quả vào, false thì
ghi đè kết quả vào $placeholder |
getChildren() | Lấy danh sách các ViewModel con |
hasChildren() | Kiểm tra có ViewModel con không |
clearChildren() | Xóa toàn bộ ViewModel con |
count() | Trả về số lượng ViewModel con |
getIterator() | Trả về đối tượng iterator các ViewModel con (duyệt qua được bằng next) |
setTerminal() | Thiết lập cờ termial: nếu true kế thúc việc Render ở Model này |
terminate() | Kiểm tra có cờ terminal. |
setCaptureTo($placeholder) | Thiết lập kết quả sẽ gán vào $placeholder |
setAppend() | Thiết lập kết quả ghi đề hay nối thêm vào $placeholder |
isAppend() | Kiểm tra xem là ghi đề hay nối thêm vào $placeholder |
Áp dụng ViewModel con
Trở lại ví dụ về sản phẩm ỏ phần trước, bạn đã xây dựng được Controller có tên Product
(xem lại Tạo controller Product)
Trong đó bạn có Route để truy cập đến action detail
với URL: http://localhost/zf3/product/dien-thoai/dien-thoai-iphonex.html
Giờ bạn sửa lại action detail
như sau:
module/Application/src/Controller/ProductController.php
//... public function detailAction() { //Đọc biến dữ liệu từ URL đã phân tích bởi Route $categoryname = $this->params()->fromRoute('categoryname'); $productname = $this->params()->fromRoute('productname'); //Phát sinh một sản phẩm demo $product = [ 'cate' => $categoryname, 'name' => $productname, 'price'=> 10000, 'description' => 'iPhone 7 256 GB mang trên mình thiết kế quen thuộc từ thời iPhone 6, máy được trang bị bộ nhớ lưu trữ lớn, khả năng chống nước cao cấp, dàn loa stereo cho âm thanh hay và cấu hình cực mạnh.', 'img' => 'https://cdn.tgdd.vn/Products/Images/42/87838/iphone-7-256gb-5-400x460.png' ]; $view = new ViewModel( ['name' => $productname] ); return $view; } //...
Tiếp theo sửa detail.phtml
module/Application/view/application/product/detail.phtml
<?=$this->adstop?> <h1><small>Tên sản phẩm: </small><?=$this->name?></h1> <?=$this->ads?> <div class="row"> <div class="col-md-8"> <?=$this->main?> </div> <div class="col-md-4"> <?=$this->sidebar?> </div> </div>
Chạy thử với URL: http://localhost/zf3/product/dien-thoai/dien-thoai-iphonex.html
Từ detail.phtml
ta thấy nó có xuất các dữ liệu adstop
, name
, sidebar
, main
. Kết quả ở trên ta chỉ thấy
có biến name
xuất ra vì ở action có thiết lập biến này cho ViewModel. Các biến còn lại chưa thiết lập nên giá trị xuất là NULL (không có gì).
Giờ ta sẽ dùng adstop
, sidebar
, main
như là placeholder
để các ViewModel con chèn dữ liệu vào.
Tạo ViewModel con $main
Cập nhật lại action detail
như sau:
module/Application/src/Controller/ProductController.php
//...
public function detailAction() {
//Đọc biến dữ liệu từ URL đã phân tích bởi Route
$categoryname = $this->params()->fromRoute('categoryname');
$productname = $this->params()->fromRoute('productname');
//Phát sinh một sản phẩm demo
$product = [
'cate' => $categoryname,
'name' => $productname,
'price'=> 10000,
'description' => 'iPhone 7 256 GB mang trên mình thiết kế quen thuộc từ thời iPhone 6,
máy được trang bị bộ nhớ lưu trữ lớn, khả năng chống nước cao cấp,
dàn loa stereo cho âm thanh hay và cấu hình cực mạnh.',
'img' => 'https://cdn.tgdd.vn/Products/Images/42/87838/iphone-7-256gb-5-400x460.png'
];
//Tạo viewmodel con $main, và thiết lập capture kết quả
//vào placeholder có tên main của ViewModel cha
$main = new ViewModel(['product' => $product]);
$main->setTemplate('application/partial/main');
$view->addChild($main, 'main');
$view = new ViewModel(
['name' => $productname]
);
return $view;
}
//...
Ở trên ViewModel $main có sử dụng template là application/partial/main
và truyền vào nó biến $product, ta tiến hành tạo template này
module/Application/view/application/partial/main.phtml
<img src="<?=($this->product)['img']?>" /> <div> <?=($this->product)['description']?> </div>
Chạy thử với URL: http://localhost/zf3/product/dien-thoai/dien-thoai-iphonex.html
Tiếp theo tạo một ViewModel con $tintuc và thiết lập nó capture vào keyholder sidebar
Cập nhật lại action detail
như sau:
module/Application/src/Controller/ProductController.php
//...
public function detailAction() {
//Đọc biến dữ liệu từ URL đã phân tích bởi Route
$categoryname = $this->params()->fromRoute('categoryname');
$productname = $this->params()->fromRoute('productname');
//Phát sinh một sản phẩm demo
$product = [
'cate' => $categoryname,
'name' => $productname,
'price'=> 10000,
'description' => 'iPhone 7 256 GB mang trên mình thiết kế quen thuộc từ thời iPhone 6,
máy được trang bị bộ nhớ lưu trữ lớn, khả năng chống nước cao cấp,
dàn loa stereo cho âm thanh hay và cấu hình cực mạnh.',
'img' => 'https://cdn.tgdd.vn/Products/Images/42/87838/iphone-7-256gb-5-400x460.png'
];
$main = new ViewModel(['product' => $product]);
$main->setTemplate('application/partial/main');
$view->addChild($main, 'main');
//Tạo viewmodel con $tintuc, và thiết lập capture kết quả
//vào placeholder có tên sidebar của ViewModel cha
$tintuc = new ViewModel();
$tintuc->setTemplate('application/partial/tintuc');
$view->addChild($tintuc, 'sidebar', true);
$view = new ViewModel(
['name' => $productname]
);
return $view;
}
//...
Tạo template application/partial/tintuc
module/Application/view/application/partial/tintuc.phtml
<h2>Tin tức</h2> <div class="list-group"> <a href="#" class="list-group-item list-group-item-action flex-column align-items-start"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">iPhone 8 Plus bán tốt hơn iPhone 8</h5> <small>3 ngày trước</small> </div> <p class="mb-1"> <img src="https://cdn2.tgdd.vn/Files/2017/11/10/1040182/iphone8vs8plus_800x450-300x200.jpg" class="w-25"> Q3/2017: iPhone 8 Plus bán tốt hơn iPhone 8, nhưng vẫn thua iPhone 7</p> <small>Mời xem chi tiết.</small> </a> <a href="#" class="list-group-item list-group-item-action flex-column align-items-start"> <div class="d-flex w-100 justify-content-between"> <h5 class="mb-1">7 game iPhone đặc sắc dành cho ngày cuối tuần (26/11)</h5> <small>3 ngày trước</small> </div> <p class="mb-1"> <img src="https://cdn2.tgdd.vn/Files/2017/11/10/1040182/iphone8vs8plus_800x450-300x200.jpg" class="w-25"> Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit.</p> <small class="text-muted">Donec id elit non mi porta.</small> </a> </div>
Chạy thử với URL: http://localhost/zf3/product/dien-thoai/dien-thoai-iphonex.html
Bạn thấy ở Sidebar đã có mục tin tức, giờ bạn muốn thêm vào đúng vị trí này một ViewModel nữa, ví dụ hiện thị sản phẩm khác.
Cập nhật lại action detail
như sau:
module/Application/src/Controller/ProductController.php
//...
public function detailAction() {
//Đọc biến dữ liệu từ URL đã phân tích bởi Route
$categoryname = $this->params()->fromRoute('categoryname');
$productname = $this->params()->fromRoute('productname');
//Phát sinh một sản phẩm demo
$product = [
'cate' => $categoryname,
'name' => $productname,
'price'=> 10000,
'description' => 'iPhone 7 256 GB mang trên mình thiết kế quen thuộc từ thời iPhone 6,
máy được trang bị bộ nhớ lưu trữ lớn, khả năng chống nước cao cấp,
dàn loa stereo cho âm thanh hay và cấu hình cực mạnh.',
'img' => 'https://cdn.tgdd.vn/Products/Images/42/87838/iphone-7-256gb-5-400x460.png'
];
$main = new ViewModel(['product' => $product]);
$main->setTemplate('application/partial/main');
$view->addChild($main, 'main');
$tintuc = new ViewModel();
$tintuc->setTemplate('application/partial/tintuc');
$view->addChild($tintuc, 'sidebar', true);
//Tạo viewmodel con $tintuc, và thiết lập capture kết quả
//vào placeholder có tên sidebar của ViewModel cha
$sanphamkhac = new ViewModel();
$sanphamkhac->setTemplate('application/partial/sanphamkhac');
//Chú ý giá trị true
$view->addChild($sanphamkhac, 'sidebar', true);
$view = new ViewModel(
['name' => $productname]
);
return $view;
}
//...
Tạo template application/partial/sanphamkhac
module/Application/view/application/partial/sanphamkhac.phtml
<h2 class="mt-4">Sản phẩm khác</h2> <div class="card mt-4"> <img class="card-img-top" src="https://cdn.tgdd.vn/Products/Images/42/114113/iphone-8-64gb-1-400x460.png" alt="Card image cap"> <div class="card-body"> <h4 class="card-title">Điện thoại iPhone 8 64GB</h4> <p class="card-text">iPhone 8 64GB nổi bật với điểm nhấn mặt lưng kính kết hợp cạnh viền và mặt trước giữ nguyên thiết kế như iPhone 7, cùng với đó là hàng loạt công nghệ đáng mong đợi như sạc nhanh, không dây hay hỗ trợ thực tế ảo AR.</p> <a href="#" class="btn btn-primary">Mua ngay</a> </div> </div>
Chạy thử với URL: http://localhost/zf3/product/dien-thoai/dien-thoai-iphonex.html
Với cách làm tương tự bạn hãy tạo ViewModel con, template để hiện thị nội dung quảng cáo và capture vào vị trí adstop