Trình bày trang và layout ZF

Trình bày trang web trong ứng dụng Skeleton liên quan đến các lớp đối tượng gồm mô hình MVC của ZF trong Zend\Mvc, các chức năng để truy cập biến truyền đến view, dựng HTML trong Zend\View

Với ứng dụng Skeleton bạn đã cài đặt, việc trình bày và điều khiển ứng sử trang với người dùng còn tích hợp sẵn thư viện CSS getBootstrapjQuery, ở đây coi như bạn đã biết về cách dùng hai thư viện này.

Ở mục Đổi template ZF và mục View Helper chúng ta đã biết cách ZF sử dụng template và layout để dựng trang, bạn hãy đọc qua lại để nhớ. Giờ ta sẽ tìm hiểu một số đặc điểm thường dùng khi dựng HTML với View của ZF.

Tạo một ứng dụng đơn giản ZF3

Để thực hành và hiểu các vấn đề ta sẽ tạo ra một Controller mới, hiện thị thông tin sản phẩm. Các kiến thức mà bạn dùng đến gồm: Tạo một Controller và các Action, Đăng ký Controller, Viết Route, Tạo template cho từng Action

Bước 1 : Tạo Controller: Product, có hai action là categorydetail

module/Application/src/Controller/ProductController.php

<?php
namespace Application\Controller;


use Zend\Mvc\Controller\AbstractActionController;

class ProductController extends AbstractActionController
{
    public function categoryAction() {

    }

    public function detailAction() {

    }
}

Bước 2 : Tiến hành đăng ký tên controller trên vào hệ thống, bằng cách cập nhật module.config.php

module/Application/config/module.config.php

//..
    'controllers' => [
        'factories' => [
           Controller\IndexController::class => Controller\IndexController::class,
           \Application\Controller\ProductController::class => InvokableFactory::class,
           //...
        ],
    ],
//..

Bước 3 : Đăng ký route vào hệ thống. Với các URL truy cập đến controller/action như sau

  • http://localhost/zf3/product/ sẽ gọi đến action category
  • http://localhost/zf3/product/[categoryname]/ sẽ gọi đến action category và lưu lại thông tin categoryname
  • http://localhost/zf3/product/[categoryname]/[productname].html sẽ gọi đến action detail và có lưu lại thông tin categoryname và productname

Để đáp ứng việc, khai báo hai route có tên showcategoryshowdetail như sau:

module/Application/config/module.config.php

//...
 'router' => [
    'routes' => [
        //...
        'showcategory' => [
            'type'    => Segment::class,
            'options' => [
                'route'    => '/product/[:categoryname/]',
                'defaults' => [
                    'controller'    => Controller\ProductController::class,
                    'action'        => 'category',
                ],
            ],
        ],

        'showdetail' => [
            'type'    => Segment::class,
            'options' => [
                'route'    => '/product/[:categoryname/:productname].html',
                'defaults' => [
                    'controller'    => Controller\ProductController::class,
                    'action'        => 'detail',
                ],
            ],
        ],
        //...


    ],
],
//...

Bước 5 : Tạo 2 file template tương ứng với 2 action của controller là category.phtmldetail.phtml với nội dung như sau:

module/Application/view/application/product/category.phtml

<h1>Thông tin về Category Sản phẩm</h1>

module/Application/view/application/product/detail.phtml

<h1>Thông tin chi tiết sản phẩm</h1>

Mời chạy thử với url như: http://localhost/zf3/product/do-gia-dung/http://localhost/zf3/product/do-gia-dung/ban-la-a123.html để xem action categorydetail đã hoạt động, tương ứng với template là category.phtmldetail.phtml

ketqua

Đổi layout ứng dụng

Bạn có thể sửa file template module/Application/view/layout/layout.phtml theo nhu cầu riêng của bạn. Ngoài ra bạn cũng có thể tạo layout mới, và thiết lập một số action, sử dụng layout riêng này. Ví dụ bạn copy module/Application/view/layout/layout.phtml thành module/Application/view/layout/layoutproduct.phtml và mở ra sử một số thông tin cho khác biệt như: chuyển sử dụng getBootstrap 4.x

module/Application/view/layout/layoutproduct.phtml

<?= $this->doctype() ?>

<html lang="en">
<head>
    <meta charset="utf-8">
    <?= $this->headTitle('Sản phẩm với ZF')->setSeparator(' - ')->setAutoEscape(false) ?>

    <?= $this->headMeta()
        ->appendName('viewport', 'width=device-width, initial-scale=1.0')
        ->appendHttpEquiv('X-UA-Compatible', 'IE=edge')
    ?>

    <!-- Le styles -->
    <?= $this->headLink(['rel' => 'shortcut icon', 'type' => 'image/vnd.microsoft.icon', 'href' => $this->basePath() . '/img/favicon.ico'])
        ->prependStylesheet($this->basePath('public/css/style.css'))
        ->prependStylesheet($this->basePath('public/css/bootstrap-theme.min.css'))
        ->prependStylesheet('https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css')
    ?>

    <!-- Scripts -->
    <?= $this->headScript()
        ->prependFile($this->basePath('public/js/bootstrap.min.js'))
        ->prependFile($this->basePath('public/js/jquery-3.1.0.min.js'))
    ?>
</head>
<body class="p-4">
<div class="container">
<nav class="navbar navbar-expand-lg navbar-dark bg-danger">
    <a class="navbar-brand" href="<?=$this->url("home")?>">Trang chủ</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarNav">
        <ul class="navbar-nav">
            <li class="nav-item active">
                <a class="nav-link" href="<?=$this->url('showcategory')?>"> Danh mục sản phẩm</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="#">Giới thiệu</a>
            </li>
        </ul>
    </div>
</nav>
</div>

<div class="container">
    <?= $this->content ?>
    <hr>
    <footer>
        <p>&copy; 2000 - <?= date('Y') ?> by Công ty của bạn.</p>
    </footer>
</div>
<?= $this->inlineScript() ?>
</body>
</html>

Sau đó đăng ký template layout này vào hệ thống bằng một cái tên ví dụ: layoutproduct

module/Application/config/module.config.php

'view_manager' => [
    //...
    'template_map' => [
        'layout/layout'           => __DIR__ . '/../view/layout/layout.phtml',
        'application/index/index' => __DIR__ . '/../view/application/index/index.phtml',
        'error/404'               => __DIR__ . '/../view/error/404.phtml',
        'error/index'             => __DIR__ . '/../view/error/index.phtml',
        'layoutproduct'           => __DIR__ . '/../view/layout/layoutproduct.phtml',
        //...
    ],
    //...

],
//..

Lúc này trong các action của controller mà muốn sử dụng layout mới tên layoutproduct thay cho layout mặc định thì thêm đoạn code

$this->layout()->setTemplate('layoutproduct');

Ví dụ:

module/Application/src/Controller/ProductController.php

<?php
namespace Application\Controller;


use Zend\Mvc\Controller\AbstractActionController;

class ProductController extends AbstractActionController
{
    public function categoryAction() {
        $this->layout()->setTemplate('layoutproduct');
    }

    public function detailAction() {
        $this->layout()->setTemplate('layoutproduct');
    }
}

Mời chạy thử với url như: http://localhost/zf3/product/do-gia-dung/http://localhost/zf3/product/do-gia-dung/ban-la-a123.html để xem nó đã dùng layout mới

ketqua

Thay layout tất cả các action

Trong các controller có phương thức onDispatch(MvcEvent $e), hàm này tự động gọi sau khi Action thi hành xong, bạn chỉ cần nạp chồng hàm này để thiết lập layout. Ví dụ sau kết quả tương đương với thiết lập trong từng Action

<?php
namespace Application\Controller;


use Zend\Mvc\Controller\AbstractActionController;
use Zend\Mvc\MvcEvent;

class ProductController extends AbstractActionController
{
    public function onDispatch(MvcEvent $e)
    {
        //Thi hành hàm onDispatch mặc định
        $response = parent::onDispatch($e);

        //Thiết lập layout mới
        $this->layout()->setTemplate('layoutproduct');

        return $response;
    }


    public function categoryAction() {

    }

    public function detailAction() {

    }
}

Sử dụng Partial trong View

Ví dụ sửa action category của Controller như sau

module/Application/src/Controller/ProductController.php

//...
public function categoryAction() {
    $category = [
        [
            'id' => 1,
            'name' => 'Máy ảnh số',
            'amout' => 90,
        ],
        [
            'id' => 2,
            'name' => 'Máy nghe nhạc',
            'amout' => 45,
        ],
        [
            'id' => 3,
            'name' => 'Vỏ điện thoại',
            'amout' => 45,
        ],
        [
            'id' => 4,
            'name' => 'Pin',
            'amout' => 39,
        ],
        [
            'id' => 5,
            'name' => 'Xác dự phòng',
            'amout' => 29,
        ],
    ];

    return new ViewModel(['category' => $category]);


}
//..

Với code trên, ViewModel đã chuyển biến dữ liệu $category vào template category.phtml

Giờ sửa category.phtml và lấy dữ liệu controller truyền đến để dựng HTML

<?php
$this->headTitle('Partial View Demo');
?>

<h1 class="text-primary">Thông tin danh mục sản phẩm</h1>
<p>
    Đây là dữ liệu do controller truyền đến
    trong biến có tên <strong>$this->category</strong>
</p>
<table class="table table-striped table-hover">
    <tr>
        <th>ID</th>
        <th>Category</th>
        <th>Số lượng</th>
    </tr>

    <?php
        foreach ($this->category as $cate) {
            ?>
                <tr>
                    <td> <?= $cate['id'] ?> </td>
                    <td> <?= $cate['name'] ?> </td>
                    <td> <?= $cate['amount'] ?> </td>
                </tr>
            <?
        }
    ?>
</table>

Mời chạy với URL: http://localhost/zf3/product/ kết quả:

ket qua

Bạn quan sát đoạn mã trong template

<tr>
    <td> <?= $cate['id'] ?> </td>
    <td> <?= $cate['name'] ?> </td>
    <td> <?= $cate['amount'] ?> </td>
</tr>

Đoạn mã xuất ra mã HTML từng dòng của bảng, lặp lại cho hết tất cả các mục. Một ý tưởng ở đây là nhóm các đoạn mã đó vào một script template khác sau đó dùng partial() để dựng với cú pháp như sau:

$this->partial('name_template', $value)

name_template là tên dẫn đến file .pthml, $value là mảng chứa dữ liệu truyền tới .phtml

Ở ví dụ trên, bạn lưu đoạn mã dựng dòng của bảng vào file template có tên rowcate.phtml

module/Application/view/application/partial/rowcate.phtml

<tr>
    <td> <?= $cate['id'] ?> </td>
    <td> <?= $cate['name'] ?> </td>
    <td> <?= $cate['amount'] ?> </td>
</tr>

Giờ sửa lại category.phtml

<?php
$this->headTitle('Partial View Demo');
?>

<h1 class="text-primary">Thông tin danh mục sản phẩm</h1>
<p>
    Đây là dữ liệu do controller truyền đến
    trong biến có tên <strong>$this->category</strong>
</p>
<table class="table table-striped table-hover">
    <tr>
        <th>ID</th>
        <th>Category</th>
        <th>Số lượng</th>
    </tr>

    <?php
        foreach ($this->category as $cate) {
            echo $this->partial('application/partial/rowcate', ['cate' => $cate]);
        }
    ?>
</table>

Kết quả là đã dùng template rowcate để dựng từng dòng dữ liệu bảng. Kết quả cuối cùng như trên

Sử dụng Placeholder

Đây là kỹ thuật mà mã HTML phát sinh được lưu vào session sau đó đọc lại để sử dụng chứ không xuất trực tiếp. Một khối HTML muốn lưu lại thì đầu tiên chọn một cái tên gọi là keyholder.

Cú pháp sử dụng như sau:

<!--Bắt đầu khối HTML -->
<?php $this->placeholder('keyholder')->captureStart(); ?>

<!--Các mã HTML sẽ lưu vào keyholder -->

<?php $this->placeholder('keyholder')->captureEnd(); ?>

<!--Lấy nội dung trong keyholder -->
<? $noidung = $this->placeholder('keyholder');?>

Ví dụ sửa lại category.phtml với kỹ thuật sử dụng placeholder

module/Application/view/application/product/category.phtml

<?php
$this->headTitle('Partial View Demo');
?>

<h1 class="text-primary">Thông tin danh mục sản phẩm</h1>
<p>
    Đây là dữ liệu do controller truyền đến
    trong biến có tên <strong>$this->category</strong>
</p>


<?php $this->placeholder('listcategory')->captureStart(); ?>
<table class="table table-striped table-hover">
    <tr>
        <th>ID</th>
        <th>Category</th>
        <th>Số lượng</th>
    </tr>

    <?php
        foreach ($this->category as $cate) {
            echo $this->partial('application/partial/rowcate', ['cate' => $cate]);
        }
    ?>
</table>
<? $this->placeholder('listcategory')->captureEnd();?>


<?php $this->placeholder('rightaside')->captureStart(); ?>
<div class="panel panel-default">
    <div class="panel-heading">
        <h3 class="panel-title">Quảng cáo</h3>
    </div>
    <div class="panel-body">
        <strong>Học lập trình Zend Framework 3</strong>
        <p>Các thông tin về lập trình PHP và các Framework hay dùng.
        Học lập trình, học lập trình, học lập trình</p>
        <a target="_blank"
           href="https://xuanthulab.net/">
            Chi tiết
        </a>
    </div>
</div>
<? $this->placeholder('rightaside')->captureEnd();?>






<div class="row">
    <div class="col-md-8">
        <?=$this->placeholder('listcategory') ?>
    </div>
    <div class="col-md-4">
        <?=$this->placeholder('rightaside')?>
    </div>
</div>

Kết quả chạy thử

Thêm JS và CSS

Trong .phtml có thể thêm vào CSS và JS từ file hoặc nhúng mã.

Thêm JS

Cú pháp thêm bằng cách gọi các phương thức thích hợp từ headScript() như: appendFile(), prependFile(), appendScript(), prependScript()

$this->headScript()->appendFile('/js/yourscript.js');

Thêm InlineScript JS

$script = "alert('Hi')";
$this->inlineScript()->appendScript($script);

Thêm CSS

Thêm file CSS

$this->headLink()->appendStylesheet('/css/style.css');
//Hoặc prependStylesheet

Têm CSS inline

$css = "body {background:red;}";
$this->HeadStyle()->appendStyle($css);
//Hoặc $this->HeadStyle()->prependStyle($css);
<?php $this->placeholder('rightaside')->captureStart(); ?>
<div class="panel panel-default">
    <div class="panel-heading">
        <h3 class="panel-title">Quảng cáo</h3>
    </div>
    <div class="panel-body">
        <strong>Học lập trình Zend Framework 3</strong>
        <p>Các thông tin về lập trình PHP và các Framework hay dùng.
        Học lập trình, học lập trình, học lập trình</p>
        <a target="_blank"
           href="https://xuanthulab.net/">
            Chi tiết
        </a>
    </div>
</div>
<? $this->placeholder('rightaside')->captureEnd();?>


Đăng ký nhận bài viết mới