Ngu ngơ học làm web (x36) - CakePHP2 - beforeValidate, unset, cập nhật thông tin user

Tiếp theo của: Ngu ngơ học làm web (x35) - CakePHP2 – Đăng kí người dùng
-----

Phần x36. CakePHP2 – beforeValidate, unset, cập nhật thông tin user


Xem (clip số 37 – chickenrainshop):

Hàm callback là các hàm được gọi để xử lý một số tác vụ cụ thể trên model hoặc controller. Hàm callback là hàm chạy ngầm (chạy trước hoặc sau một hàm/thao tác khác) được gọi bởi thao tác khác trong model hoặc controller.

Một số hàm callback:

Trên model
Trên controller
Hàm callback được gọi trước hoặc sau các thao tác: find, save, update, delete, validate.
Hàm callback được gọi trước hoặc sau các action.
Ví dụ: beforeFind(), afterFind(), beforeSave(), afterSave(), beforeDelete(), afterDelete(), beforeValidate()
Ví dụ: beforeFilter(), afterFilter(), beforeRender(), afterRender(), beforeRedirect()

Xem (clip số 38 – chickenrainshop):
- Cập nhật thông tin người dùng đã đăng nhập (38)

- findById (4":39)

- Hàm unset (12":13) 

Cập nhật thông tin người dùng:

Tạo link cho mục ‘Cập nhật thông tin’ trong layout default.ctp:

userInfo['fullname']; ?></strong></small></h4>
<ul>
<li><?php echo $this->Html->link('Cập nhật thông tin', '/cap-nhat-thong-tin'); ?></li>
            <li><?php echo $this->Html->link('Đổi mật khẩu', '/doi-mat-khau'); ?></li>

Thực hiện cắt đoạn mã hiển thị các chức năng liên quan đến user trong default.ctp, đưa vào element có tên là user_panel.ctp.

[Elements\user_panel.ctp]

<?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['fullname']; ?></strong></small></h4>
                        <ul>
                                    <li><?php echo $this->Html->link('Cập nhật thông tin', '/cap-nhat-thong-tin'); ?></li>
                                    <li><?php echo $this->Html->link('Đổi mật khẩu', '/doi-mat-khau'); ?></li>
                                    <li><a href="">Lịch sử mua hàng</a></li>
                                    <li><?php echo $this->Html->link('Đăng xuất', '/logout'); ?></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.<br>
                        Nếu bạn chưa có tài khoản hãy đăng ký tại <?php echo $this->Html->link('đây', '/dang-ky') ?>.
            </div>
<?php endif ?>

Trong layout default.ctp, thêm đoạn mã gọi element user_panel.ctp:

<div class="sidebar col col-lg-3">
            <!-- user panel -->
                        <?php echo $this->element('user_panel'); ?>                                  
            <!-- /user panel -->
            <div class="panel panel-info">
            <h4 class="panel-heading"><span class="glyphicon glyphicon-shopping-cart"></span>Giỏ hàng</h4>

Trong UsersController.php, viết action changeInfo():

[UserController.php]

public function changeInfo() {
                        $this->set('title_for_layout', 'Cập nhật thông tin');
            }

Trong routes.php, thực hiện route cho link /cap-nhat-thong-tin

[routes.php]

Router::connect('/doi-mat-khau', ['controller' => 'users', 'action' => 'changePassword']);
Router::connect('/cap-nhat-thong-tin', ['controller' => 'users', 'action' => 'changeInfo']);
Router::connect('/dang-ky', ['controller' => 'users', 'action' => 'register']);

Trong View\Users, tạo view có tên change_info.ctp:

<div class="panel panel-info">
            <h4 class="panel-heading"><span class="glyphicon glyphicon-user"> Cập nhật thông tin</span></h4>
                        <?php echo $this->Flash->render(); ?>
                        <?php echo $this->Form->create('User', ['class' => 'form-horizontal', 'novalidate' => true, 'inputDefaults' => ['label' => false]]); ?>
                                    <div class="control-group">
                                                <label class="control-label" for="inputLastName">Last Name</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('lastname', ['placeholder' => 'Họ']); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <label class="control-label" for="inputFirstName">First Name</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('firstname', ['placeholder' => 'Tên', 'type' => 'text']); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <label class="control-label" for="inputEmail">Email</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('email', ['placeholder' => 'Địa chỉ email', 'type' => 'text']); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <label class="control-label" for="inputAddress">Address</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('address', ['placeholder' => 'Địa chỉ', 'type' => 'text', 'error' => false]); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <label class="control-label" for="inputPhone">Phone</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('phone_number', ['placeholder' => 'Số điện thoại', 'type' => 'number', 'error' => false]); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <div class="controls">
                                                            <?php echo $this->Form->button('Cập nhật', ['type' => 'submit', 'class' => 'col-lg-2 btn btn-primary']); ?>
                                                </div>
                                    </div>
                        <?php echo $this->Form->end(); ?>
</div>

Để lấy thông tin người dùng dựa vào id, sử dụng hàm findById(‘id’). Ví dụ:

// lấy thông tin người dùng, hiển thị lên form
$this->request->data = $this->User->findById($userInfo['id']);

Viết tiếp action changeInfo():

/**
 * thay đổi thông tin người dùng
 */

            public function changeInfo() {
                        $userInfo = $this->getUser();
                        if ($this->request->is('put') || $this->request->is('post')) {
                                    $this->User->set($this->request->data);
                                    if ($this->User->Validates()) {
                                                $data = [
                                                            'lastname' => $this->request->data['User']['lastname'],
                                                            'firstname' => $this->request->data['User']['firstname'],
                                                            'email' => $this->request->data['User']['email'],
                                                            'email' => $this->request->data['User']['email'],
                                                            'phone_number' => $this->request->data['User']['phone_number'],
                                                            'address' => $this->request->data['User']['address']
                                                ];
                                                $this->User->id = $userInfo['id'];
                                                if ($this->User->save($data)) {
                                                            $this->Flash->success('Đã lưu thành công!', ['params' => ['class' => 'alert alert-success']]);
                                                } else {
                                                            $this->Flash->error('Có lỗi xảy ra, vui lòng thử lại!', ['params' => ['class' => 'alert alert-error']]);
                                                }
                                    }
                        } else {
                                    // lấy thông tin người dùng, hiển thị lên form
                                    $this->request->data = $this->User->findById($userInfo['id']);
                        }
                        $this->set('title_for_layout', 'Cập nhật thông tin');
            }

Khắc phục lỗi liên quan đến trường email, khi người dùng bấm nút ‘Cập nhật’ lần đầu thành công rồi, lại tiếp tục bấm thêm nữa, sẽ sinh ra lỗi bị trùng email. Lỗi này xảy ra do trong ràng buộc dữ liệu có yêu cầu email phải là duy nhất.

Viết hàm callback beforeValidate để xử lý lỗi này, ý tưởng là kiểm tra để đảm bảo dữ liệu người dùng muốn cập nhật và dữ liệu có sẵn trong cơ sở dữ liệu là như nhau, khi đó sử dụng hàm unset để gỡ bỏ trường dữ liệu tương ứng khi người dùng gửi lên (ví dụ email), kết quả là trường này bị rỗng và không thực hiện xác thực dữ liệu cho nó.

[Model\User.php]

// gỡ bỏ trường email trước khi validate
public function beforeValidate($options = array()) {
            if (isset($this->data['User']['email'])) {
                        // lấy thông tin người dùng đang đăng nhập
                        $userInfo = AuthComponent::user();
                        // lấy thông tin của người dùng hiện tại từ cơ sở dữ liệu
                        $user = $this->findById($userInfo['id']);
                        if (!empty($userInfo) && ($this->data['User']['email'] == $user['User']['email'])) {
                                    // gỡ bỏ trường email
                                    unset($this->data['User']['email']);
                        }
            }
            return true;

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