--------------- <> -----------------
--- KHOA HỌC - CÔNG NGHỆ - GIÁO DỤC - VIỆC LÀM ---
--- Học để đi cùng bà con trên thế giới ---

Tìm kiếm trong Blog

Hiển thị các bài đăng có nhãn CakePHP2. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn CakePHP2. Hiển thị tất cả bài đăng

Ngu ngơ học làm web (x31) - CakePHP2 - beforeFilter, lấy thông tin user, virtualFields

Tiếp theo của: Ngu ngơ học làm web (x30) - CakePHP2 – Auth, đăng nhập, đăng xuất
-----

Phần x31. CakePHP2 – beforeFilter, lấy thông tin user, virtualFields


Xem (clip số 33b – chickenrainshop):

- Lọc các action không yêu cầu đăng nhập

- Sử dụng Auth->allow, beforeFilter (3":21)

- Sử dụng parent để không ghi đè hàm cha (8":15)

Mặc định AuthComponent sẽ tác động lên mọi action trong mọi controller của project. Nghĩa là người dùng vào bất kì trang nào, hoặc sử dụng bất kì chức năng nào của website, nó cũng bắt phải đăng nhập; hay nói cách khác mọi thao tác đều phải đăng nhập để hệ thống kiểm soát.

Tuy nhiên, trong thực tế, sẽ có những trang, những action không nhất thiết phải đăng nhập mới sử dụng được. Vì vậy, cần phải khai báo để website bỏ qua yêu cầu chứng thực đối với các trường hợp này. Khai báo trong hàm beforeFilter.

Đọc thêm về hàm beforeFilter tại đây: https://book.cakephp.org/2.0/en/controllers.html

[This function is executed before every action in the controller. It’s a handy place to check for an active session or inspect user permissions.

The beforeFilter() method will be called for missing actions, and scaffolded actions.]

Hàm beforeFilter được thực thi trước mọi action trong controller.

Trong hàm beforeFilter, sử dụng hàm $this->Auth->allow(); để liệt kê các action mà người sử dụng có thể dùng mà không cần đăng nhập. Ví dụ,

[AppController.php]

public function beforeFilter() {
                        $this->Auth->allow('menu', 'index');
            }

Do action ‘menu’, ‘index’ được chỉ định trong AppController, nên chỉ định này sẽ áp dụng cho mọi action ‘menu’, ‘index’ của mọi controller là con của AppController.

Nếu chỉ muốn thiết lập cho một controller cụ thể thì viết thêm hàm beforeFilter trong chính controller đó, và nhớ thêm dòng mã:

parent::beforeFilter();

để thực thi các nội dung được thiết lập tại hàm beforeFilter trong AppController. Ví dụ,

[CouponsController.php]

            public function beforeFilter() {
                        parent::beforeFilter();
                        $this->Auth->allow('add');
            }

Xem (clip số 34a – chickenrainshop):

Sau khi người dùng đăng nhập thành công, thông tin người dùng sẽ được lưu trong AuthComponent.
Để kiểm tra người dùng đăng nhập hay chưa? Sử dụng hàm:

$this->Auth->login()

Để lấy thông tin người dùng, sử dụng hàm:

$this->Auth->user();

Nội dung hàm lấy thông tin người dùng, trong AppController.php:

public function getUser() {
                        // kiểm tra người dùng đăng nhập hay chưa?
                        if ($this->Auth->login()) {
                                    // trả về thông tin người dùng
                                    return $this->Auth->user();
                        }
            }

Để gửi thông tin người dùng đã đăng nhập tới tất cả các view, sẽ thực hiện trong hàm beforeFilter tại AppController.php:

public function beforeFilter() {
                        $this->Auth->allow('menu', 'index');
                        $this->set('userInfo', $this->getUser());
            }

Trong các view, để kiểm tra xem người dùng đã đăng nhập hay chưa, sẽ kiểm tra biến userInfo:

[View\Layouts\default.ctp]

<!-- sidebar -->
<div class="sidebar col col-lg-3">
<!-- user panel -->
<?php if (!empty($userInfo)): ?>
            <div class="panel panel-info">
                        <h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span><small> Xin chào <strong><?php echo $userInfo['username']; ?></strong></small></h4>
                        <ul>
                                    <li><a href="">Cập nhật thông tin</a></li>
                                    <li><a href="">Đổi mật khẩu</a></li>
                                    <li><a href="">Lịch sử mua hàng</a></li>
                                    <li><a href="">Đăng xuất</a></li>
                        </ul>
            </div>
<?php else: ?>
            <div class="panel panel-info">
                        <h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span><small> Xin chào <strong>khách</strong></small></h4>
                        Nhấn vào <?php echo $this->Html->link('đây','/login'); ?> để đăng nhập.
            </div>
<?php endif ?>                      
<!-- /user panel -->

Thiết lập virtualFields:

Đọc thêm về virtualFields tại đây: https://book.cakephp.org/2.0/en/models/virtual-fields.html

VirtualFields cho phép tạo một tên trường ảo cho Model, tuy nhiên chỉ có thể đọc (read) dữ liệu từ trường này chứ không thể lưu xuống cơ sở dữ liệu (save).

Khai báo virtualFields trong model User:

[Model\User.php]

class User extends AppModel {
            public $virtualFields = [
                        'fullname' => 'concat(User.lastname, " ", User.firstname)'
            ];

Hàm concat để nối chuỗi.

Gọi ra sử dụng trong view:

[default.ctp]

<h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span><small> Xin chào <strong><?php echo $userInfo['fullname']; ?></strong></small></h4>
            <ul>

                        <li><a href="">Cập nhật thông tin</a></li>
-----------
Cập nhật 22/6/2017
-----------
Xem thêm:
- Tổng hợp các bài viết về Ngu ngơ học làm web

Ngu ngơ học làm web (x30) - CakePHP2 - Auth, đăng nhập, đăng xuất

Tiếp theo của: Ngu ngơ học làm web (x29) - CakePHP2 – saveAll(), array->JSON
-----

Phần x30. CakePHP2 – Auth, đăng nhập, đăng xuất


Xem (clip số 32 – chickenrainshop):

Trong website, có một số trang ai cũng có thể truy cập vào, tuy nhiên có một số trang cần phải đăng nhập thì mới truy cập được. Để làm điều này, cần phải có cơ chế đăng nhập và xác thực người dùng.

Đọc thêm để phân biệt: identifying (anh tên gì?), authenticating (có đúng là anh không?) và authorizing (mời anh đọc báo?).

CakePHP sử dụng AuthComponent để thực hiện việc xác thực người dùng.

Quy trình xây dựng một chức năng bằng CakePHP:

Bước 1: Khởi tạo
Bước 2: Xử lý
Bước 3: Hiển thị
Một chức năng có thể cần một form để gửi dữ liệu về server, hoặc cần tạo một view, hoặc chỉ cần sử dụng link/postLink
Viết xử lý trong các function (hay còn gọi là action) của controller. Sử dụng Model, hàm find hoặc các component
Hiển thị lại dữ liệu do action trả về lên View (nếu cần), hoặc chỉ gửi thông báo lên View (sử dụng Flash)

Xem (clip số 33a – chickenrainshop):

Khai báo sử dụng authcomponent trong AppController.php

class AppController extends Controller {
            public $components = [
                        'Flash','Session','Tool',
                        'Auth' =>[
                                    'loginAction' => '/login',
                                    'authError' => 'Bạn cần phải đăng nhập để tiếp tục!',
                                    'flash' => [
                                                            'element' => 'error',
                                                            'key' => 'auth',
                                                            'params' => ['class' => 'alert alert-danger']
                                                ],
                                    'loginRedirect' => '/'
                                    ]
                        ];

Trong đó,

- loginAction: trỏ tới action xử lý cho việc đăng nhập, ví dụ: 'loginAction' => '/login'.

- authError: chuỗi thông báo khi người dùng chưa đăng nhập, nhưng lại muốn thực hiện một chức năng có yêu cầu đăng nhập, ví dụ: 'authError' => 'Bạn cần phải đăng nhập để tiếp tục!'. Bản chất của authError hoạt động giống như một thông báo bằng Flash. Có thể thiết lập thêm các tham số cho authError.

- flash: định dạng cho chuỗi thông báo ‘authError’

- loginRedirect: trỏ tới trang sẽ được hiển thị, nếu đăng nhập thành công

Cấu hình route cho việc đăng nhập:

[routes.php]

Router::connect('/', ['controller' => 'books', 'action' => 'index']);
Router::connect('/sach-moi', ['controller' => 'books', 'action' => 'latest_books']);
Router::connect('/login', ['controller' => 'users', 'action' => 'login']);

Tạo action login trong UsersController.php:

/**
 * login - đăng nhập
 */
            public function login() {
                        $this->set('title_for_layout', 'Đăng nhập');
                       
            }

Tạo view login.ctp trong View\Users,

<div class="panel panel-info">
            <h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span></h4>
            <?php echo $this->Flash->render('auth'); ?>
            <?php echo $this->Form->create('User', ['class' => 'form-horizontal', 'inputDefaults' => ['label' => false]]); ?>
                        <div class="control-group">
                                    <label class="control-label" for="inputUsername">Username</label>
                                    <div class="controls">
                                                <?php echo $this->Form->input('username', ['placeholder' => 'Tên đăng nhập']); ?>
                                    </div>
                        </div>
                        <div class="control-group">
                                    <label class="control-label" for="inputPassword">Password</label>
                                    <div class="controls">
                                                <?php echo $this->Form->input('password', ['placeholder' => 'Mật khẩu']); ?>
                                    </div>
                        </div>
                        <div class="control-group">
                                    <div class="controls">
                                                <?php echo $this->Form->button('Đăng nhập', ['type' => 'submit', 'class' => 'col-lg-2 btn btn-primary']); ?>
                                    </div>
                        </div>
            <?php echo $this->Form->end(); ?>
</div>

Do ở đây đang sử dụng phiên bản CakePHP 2.9.6, khác so với trong clip, nên làm theo clip sẽ bị một số vấn đề:

- sql_dump: sẽ không xuất ra mật khẩu đã được băm (hashed)

- nhập mật khẩu ở dạng rõ (plain-text), giống như trong cơ sở dữ liệu đang có, sẽ không vượt qua được quá trình chứng thực


Đọc thêm một ví dụ mẫu về chứng thực (authenticating) và phân quyền (authorizing) tại đây: https://book.cakephp.org/2.0/en/tutorials-and-examples/blog-auth-example/auth.html

Vấn đề đang gặp phải là:

- Khi lưu mật khẩu vào cơ sở dữ liệu, mật khẩu chưa được băm (hashing), vẫn ở dạng rõ (plain text)

- Tại form login, sau khi người dùng nhập mật khẩu,  bấm nút Đăng nhập, PHP sẽ băm mật khẩu này 
và so sánh với mật khẩu trong cơ sở dữ liệu

- Kết quả là mật khẩu đã được băm sẽ không thể khớp với mật khẩu đang ở dạng rõ trong cơ sở dữ liệu

Cách khắc phục:

Thực hiện băm mật khẩu của người dùng và lưu lại vào cơ sở dữ liệu. Sử dụng hàm 
Security::hash($yourPassword, NULL, true) để băm.

Ví dụ,

Giả sử mật khẩu dạng rõ là ‘1’, thực hiện băm mật khẩu này (trong view login.ctp):

<div class="panel panel-info">
<?php echo 'hashedPasswords = '.Security::hash(1, NULL, true); ?>
            <h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span></h4>

Kết quả băm là: 5059a8afd49865c1bf27881d9cfd5a22657ca1a9

Lưu kết quả băm này vào cơ sở dữ liệu, thay thế cho giá trị ‘1’ trước đó.

Bây giờ, kiểm tra lại bằng cách đăng nhập với mật khẩu ‘1’ là được.

Nội dung action login trong UsersController.php:

/**
 * login - đăng nhập
 */
            public function login() {
                        if ($this->request->is('post')) {
                                    if ($this->Auth->login()) { // thực thi quá trình chứng thực
                                                $this->redirect($this->Auth->redirect()); // nếu chứng thực thành công, chuyển tới trang được thiết lập trong biến Auth -> loginRedirect
                                    } else {
                                                $this->Flash->error('Sai tên đăng nhập hoặc mật khẩu', ['key' => 'auth', 'params' => ['class' => 'alert alert-danger']]);
                                    }
                        }
                        $this->set('title_for_layout', 'Đăng nhập');
                       
            }

Quá trình đăng xuất:

Sử dụng AuthComponent để logout, nó sẽ redirect về một action nào đó, mặc định AuthComponent sẽ redirect về view login.

Viết action logout trong UsersController.php:

/**
 * logout - đăng xuất
 */
            public function logout() {
                        $this->redirect($this->Auth->logout());
            }

Viết thêm đoạn mã để route (trong  routes.php):


Router::connect('/logout', ['controller' => 'users', 'action' => 'logout']);
-----------
Cập nhật 21/6/2017
-----------
Xem thêm:
- Tổng hợp các bài viết về Ngu ngơ học làm web

Ngu ngơ học làm web (x29) - CakePHP2 - saveAll(), array->JSON

Tiếp theo của: Ngu ngơ học làm web (x28) - CakePHP2 – findBy(), date(), between
-----

Phần x29. CakePHP2 – saveAll(), array->JSON


Xem (clip số 30b – chickenrainshop):

Đoạn mã tính lại giá trị của mục “Đã giảm” và “Tổng tiền phải trả” khi người dùng nhập mã giảm giá rồi nhưng lại nhập thêm sách:

[action addToCart(), BooksController.php]

$this->Session->write('payment.total', $total);
                                    // kiểm tra xem có mã giảm giá hay không
                                    if ($this->Session->check('payment.coupon')) {
                                                $pay = $total - $total*$this->Session->read('payment.discount')/100;
                                                $this->Session->write('payment.pay', $pay);
                                    }
$this->Flash->success('Đã thêm quyển sách vào trong giỏ hàng!', [

Đoạn mã tính lại giá trị của mục “Đã giảm” và “Tổng tiền phải trả” khi người dùng nhập mã giảm giá rồi nhưng lại cập nhật số lượng sách:

[action quantityUpdate(), BooksController.php]

$this->Session->write('payment.total', $total);
// kiểm tra xem có mã giảm giá hay không
            if ($this->Session->check('payment.coupon')) {
$pay = $total - $total*$this->Session->read('payment.discount')/100;
                        $this->Session->write('payment.pay', $pay);
            }          

Đoạn mã tính lại giá trị của mục “Đã giảm” và “Tổng tiền phải trả” khi người dùng nhập mã giảm giá rồi nhưng lại xóa quyển sách khỏi giỏ hàng:

[action remove(), BooksController.php]

            $total = $this->Tool->arraySum($cart, 'quantity', 'sale_price');
            $this->Session->write('payment.total', $total);
            // kiểm tra xem có mã giảm giá hay không
            if ($this->Session->check('payment.coupon')) {
                        $pay = $total - $total*$this->Session->read('payment.discount')/100;
                        $this->Session->write('payment.pay', $pay);
                        }          
            }
$this->redirect($this->referer());

Xem (clip số 31 – chickenrainshop):

- Label của helper (3":05)

- Sử dụng inputDefaults để bỏ label mặc định cho các input trong form (6":37)

- Tạo một button (7":02)

- Lưu dữ liệu vào database dùng saveAll (15":08) 

- Chuyển chuỗi thành đối tượng JSON (18":20) 

Đoạn mã cho mục “Thanh toán đơn hàng”:

[View\Books\view_cart.ctp]

<li>Mỗi mã giảm giá có mức giảm giá khác nhau và chỉ dùng trong khoảng thời gian quy định.</li>
                                                <li>Chỉ dùng một mã giảm giá khi thanh toán đơn hàng.</li>
                                                <li>Số tiền giảm giá được tính dựa trên <strong>số phần trăm giảm giá * tổng giá trị</strong> của đơn hàng</li>
                                    </ul>
                        <?php endif ?>
            </div>
            <!-- end coupon -->
            <!-- customer info -->
            <div class="panel panel-info col col-lg-7 col-lg-offset-1">
                        <h4 class="panel-heading"><span class="glyphicon glyphicon-user"></span>Thanh toán đơn hàng</h4>
                        <?php if (true): ?>
                                    <?php echo $this->Form->create('Order', ['url' => ['action' => 'checkout'], 'class' => 'form-horizontal', 'inputDefaults' => ['label' => false]]); ?>
                                    <div class="row">
                                                <?php echo $this->Form->label('name', 'Tên', ['class' => 'col col-lg-3 control-label']); ?>
                                                <div class="col col-lg-9">
                                                            <?php echo $this->Form->input('name', ['placeholder' => 'Nhập tên', 'value' =>'Lê Văn Tèo']); ?>
                                                </div>
                                    </div>
                                    <div class="row">
                                                <?php echo $this->Form->label('email', 'Email', ['class' => 'col col-lg-3 control-label']); ?>
                                                <div class="col col-lg-9">
                                                            <?php echo $this->Form->input('email', ['placeholder' => 'Nhập email', 'value' => 'levanteo@gmail.com']); ?>
                                                </div>
                                    </div>
                                    <div class="row">
                                                <?php echo $this->Form->label('address', 'Địa chỉ', ['class' => 'col col-lg-3 control-label']); ?>
                                                <div class="col col-lg-9">
                                                            <?php echo $this->Form->input('address', ['placeholder' => 'Nhập địa chỉ', 'value' => '123 đường abc, thành phố xyz']); ?>
                                                </div>
                                    </div>
                                    <div class="row">
                                                <?php echo $this->Form->label('phone', 'Điện thoại', ['class' => 'col col-lg-3 control-label']); ?>
                                                <div class="col col-lg-9">
                                                            <?php echo $this->Form->input('phone', ['placeholder' => 'Nhập số điện thoại', 'value' => '123456789']); ?>
                                                </div>
                                    </div>
                                    <div class="row">
                                                <div class="col col-lg-10 col-offset-2">
                                                            <?php echo $this->Form->button('Thực hiện thanh toán', ['type' => 'submit', 'class' => 'btn btn-primary pull-right']) ?>
                                                </div>
                                    </div>
                        <?php echo $this->Form->end(); ?>
                        <?php else: ?>
                                    Bạn phải đăng nhập trước khi thanh toán.
                        <?php endif ?>
            </div>
            <!-- end customer info -->

Để chuyển dữ liệu từ dạng mảng sang dạng đối tượng JSON (dạng text), sử dụng hàm json_encode, ví dụ:

$data = [
                                                'user_id' => 1,
                                                'orders_info' => json_encode($this->Session->read('cart')),
                                                'customer_info' => json_encode($this->request->data['Order']),
                                                'payment_info' => json_encode($this->Session->read('payment')),
                                                'status' => 0
                                    ];

[action checkout() trong OrdersController.php]

/**
 * thanh toán đơn hàng và lưu thông tin mua hàng
 *
 */
            public function checkout() {
                        if ($this->request->is('post')) {
                                    $data = [
                                                'user_id' => 1,
                                                'order_info' => json_encode($this->Session->read('cart')),
                                                'customer_info' => json_encode($this->request->data['Order']),
                                                'payment_info' => json_encode($this->Session->read('payment')),
                                                'status' => 0
                                    ];
                                    if ($this->Order->saveAll($data)) {
                                                $this->Session->delete('cart');
                                                $this->Session->delete('payment');
                                    } else {
                                                $this->Flash->error('Thanh toán không thực hiện được!', ['key' => 'order', 'params' => ['class' => 'alert alert-danger']]);
                                    }
                                    $this->redirect($this->referer());
                        }

            }
-----------
Cập nhật 19/6/2017
-----------
Xem thêm:
- Tổng hợp các bài viết về Ngu ngơ học làm web

Ngu ngơ học làm web (x28) - CakePHP2 - findBy(), date(), between

Tiếp theo của: Ngu ngơ học làm web (x27) - CakePHP2 – Cập nhật giỏ hàng (tt)
-----

Phần x28. CakePHP2 – findBy(), date(), between


Xem (clip số 30a – chickenrainshop):

- request->data() để lấy giá trị từ form post lên server (5":55)

- Sử dụng findBy(7":28)

- Lấy ngày hiện tại date()(9":43)

- Kiểm tra một ngày có nằm trong “ngày bắt đầu” và “ngày kết thúc” (9":59)

- Lưu session theo khóa (13":23)

- redirect về trang hiện tại (15":42)

Đoạn mã cho phần nhập mã giảm giá:

[View\Books\view_cart.ctp]

                        <?php echo $this->Form->postLink('Làm rỗng giỏ hàng', '/books/emptyCart', ['class' => 'col-lg-3 btn btn-default empty']); ?>
                        </div>
            </div>
            <!-- coupon -->
            <div class="panel panel-success col col-lg-4 coupon">
                        <h4 class="panel-heading"><span class="glyphicon glyphicon-barcode"></span> Mã giảm giá</h4>
                        <?php echo $this->Form->create('Coupon', ['url' => ['action' => 'add']], ['class' => 'form-inline']) ?>
                                    <?php echo $this->Form->input('code', ['class' => 'col-lg-9', 'placeholder' => 'Nhập mã giảm giá (coupon)', 'label' => false, 'div' => false]); ?>
                                    <?php echo $this->Form->button('Nhập', ['type' => 'submit', 'class' => 'col-lg-2 btn btn-primary']) ?>
                        <?php echo $this->Form->end(); ?>

                        <h4>Ghi chú:</h4>
                        <ul>
                                    <li>Mỗi mã giảm giá có mức giảm giá khác nhau và chỉ dùng trong khoảng thời gian quy định.</li>
                                    <li>Chỉ dùng một mã giảm giá khi thanh toán đơn hàng.</li>
                                    <li>Số tiền giảm giá được tính dựa trên <strong>số phần trăm giảm giá * tổng giá trị</strong> của đơn hàng</li>
                        </ul>
            </div>
            <!-- end coupon -->
<?php else: ?>
            <div class="panel">
                        Giỏ hàng đang rỗng.
                        Quay về <?php echo $this->Html->link('trang chủ', '/'); ?> để thêm quyển sách vào giỏ hàng.

Thêm mã css vào tập tin chickenrainshop.css:

.coupon {
    padding: 0px;
}

.coupon input {
    padding-left: 4px;
    border-radius: 4px;
    margin-left: 1px;
    padding-right: 1px;
    margin-top: 10px;
    margin-bottom: 10px;
}

.coupon button {
    width: 50px;
    margin-left: 6px;
    text-align: center;
    padding: 0;
    margin-top: 8px;
    margin-bottom: 10px;
    height: 30px;
}

Viết mã cho action add() trong CouponsController.php:

Để kiểm tra một giá trị có trong cơ sở dữ liệu hay không, sử dụng hàm findBy().


findBy<fieldName>(string $value);

Giá trị trả về của findBy() giống với find(‘first’).

Ví dụ, tìm hàng dữ liệu đầu tiên trong bảng coupons, có giá trị trường code = $code:

$coupon = $this->Coupon->findByCode($code);

Để lấy ngày hiện tại sử dụng hàm date(), với định dạng thời gian là: yyyy-mm-dd 00:00:00, ví dụ:
$today = date('Y-m-d H:i:s');

Kết quả sẽ xuất ra là: 2017-06-01 10:14:59

Hàm so sánh một mốc thời gian có nằm trong một khoảng thời gian cho trước hay không? Viết trong ToolComponent.php.

public function between($date, $start, $end, $timezone = 'Asia/Ho_Chi_Minh') {
                                    // thiết lập múi giờ cho server
                                    date_default_timezone_set($timezone);
                                    // chuyển chuỗi thời gian sang dạng Unix timestamp
                                    $date = strtotime($date);
                                    $start = strtotime($start);
                                    $end = strtotime($end);
                                    if ($date >= $start && $date <= $end) {
                                                return true;
                                    } else {
                                                return false;
                                    }

                        }

Mã cho action add() trong CouponsController.php:

public function add() {
                        if ($this->request->is('post')) {
                                    $code = $this->request->data['Coupon']['code'];
                                    $coupon = $this->Coupon->findByCode($code);
                                    if (!empty($coupon)) {
                                                $today = date('Y-m-d H:i:s');
                                                if ($this->Tool->between($today, $coupon['Coupon']['time_start'], $coupon['Coupon']['time_end'])) {
                                                            $this->Session->write('payment.coupon', $coupon['Coupon']['code']);
                                                            $this->Session->write('payment.discount', $coupon['Coupon']['percent']);
                                                            $total = $this->Session->read('payment.total');
                                                            $pay = $total - $total * $coupon['Coupon']['percent']/100;
                                                            $this->Session->write('payment.pay', $pay);
                                                } else {
                                                            $this->Flash->error('Mã giảm giá đã hết hạn!', ['key' => 'coupon', 'params' => ['class' => 'alert alert-danger']]);
                                                }
                                    } else {
                                                $this->Flash->error('Sai mã giảm giá!', ['key' => 'coupon', 'params' => ['class' => 'alert alert-danger']]);
                                    }
                                    $this->redirect($this->referer());
                        }
            }

Đoạn mã cho phần "nhập mã giảm giá", sửa lại như sau:

[View\Books\view_cart.ctp]

<!-- coupon -->
            <div class="panel panel-success col col-lg-4 coupon">
                        <h4 class="panel-heading"><span class="glyphicon glyphicon-barcode"></span> Mã giảm giá</h4>
                        <?php echo $this->Flash->render('coupon'); ?>
                        <!-- kiểm tra chỉ cho hiển thị form nhập coupon nến người dùng chưa nhập lần nào -->
                        <?php if ($this->Session->check('payment.coupon')): ?>
                                    Bạn đã nhập mã giảm giá!
                        <?php else: ?>
                                    <?php echo $this->Form->create('Coupon', ['url' => ['action' => 'add']], ['class' => 'form-inline']) ?>
                                    <?php echo $this->Form->input('code', ['class' => 'col-lg-9', 'placeholder' => 'Nhập mã giảm giá (coupon)', 'label' => false, 'div' => false]); ?>
                                    <?php echo $this->Form->button('Nhập', ['type' => 'submit', 'class' => 'col-lg-2 btn btn-primary']) ?>
                                    <?php echo $this->Form->end(); ?>
                                    <h4>Ghi chú:</h4>
                                    <ul>
                                                <li>Mỗi mã giảm giá có mức giảm giá khác nhau và chỉ dùng trong khoảng thời gian quy định.</li>
                                                <li>Chỉ dùng một mã giảm giá khi thanh toán đơn hàng.</li>
                                                <li>Số tiền giảm giá được tính dựa trên <strong>số phần trăm giảm giá * tổng giá trị</strong> của đơn hàng</li>
                                    </ul>
                        <?php endif ?>
            </div>
            <!-- end coupon -->

Phần mã về “giá tiền được giảm”, “tổng tiền” trong view_cart.ctp được sửa lại như sau:

<?php if ($this->Session->check('payment.coupon')): ?>
<td colspan="2"><strong>Đã giảm                                                                         <small>(Coupon: <?php echo $payment['coupon']; ?> - giảm <?php echo $payment['discount']; ?>%) </small>
            </strong></td>
<td colspan="2"><strong><?php echo $this->Number->currency($payment['total'] - $payment['pay'],' VND',['places' => 0,'wholePosition' => 'after']); ?></strong></td>
<?php else: ?>
            <td colspan="2"><strong>Đã giảm </strong></td>
            <td colspan="2"><strong>0 VND</strong></td>
<?php endif ?>
</tr>
<tr class="success">
            <td></td>
<?php if ($this->Session->check('payment.coupon')): ?>
            <td colspan="2"><h4><strong>Giá phải trả</strong></h4></td>
                                                                                                <td colspan="2"><h4><span class="label label-danger"><?php echo $this->Number->currency($payment['pay'],' VND',['places' => 0,'wholePosition' => 'after']); ?></span></h4></td>
<?php else: ?>
            <td colspan="2"><h4><strong>Giá phải trả</strong></h4></td>
            <td colspan="2"><h4><span class="label label-danger"><?php echo $this->Number->currency($payment['total'],' VND',['places' => 0,'wholePosition' => 'after']); ?></span></h4></td>
<?php endif ?>

</tr>
-----------
Cập nhật 4/6/2017
-----------
Xem thêm:
- Tổng hợp các bài viết về Ngu ngơ học làm web