--------------- <> -----------------
--- 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

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

Ngu ngơ học làm web (x35) - CakePHP2 - Đăng kí người dùng

Tiếp theo của: Ngu ngơ học làm web (x34) - CakePHP2 – Hàm ràng buộc tại model, hashing, callback
-----

Phần x35. CakePHP2 – Đăng kí người dùng


Xem (clip số 36bt – chickenrainshop):

Viết link để hiển thị form đăng kí người dùng:

[Layouts\default.ctp]

<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>

Vào UsersController.php để viết hàm register:

[UsersController.php]

            public function register() {
                        $this->set('title_for_layout', 'Đăng ký');
            }

Vào routes.php để thực hiện route cho chức năng ‘dang-ky’:

[routes.php]

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

Cho phép người dùng thực hiện chức năng đăng ký khi chưa đăng nhập:

[AppController.php action beforeFilter()]

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

Tạo view cho action register:

[View\Users\register.ctp]

<div class="panel panel-info">
            <h4 class="panel-heading"><span class="glyphicon glyphicon-user"> Đăng ký</span></h4>
                        <?php //echo $this->Flash->render('auth'); ?>
                        <?php echo $this->element('errors'); ?>
                        <?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="inputUserName">Username</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('username', ['placeholder' => 'Tên đăng nhập', '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="inputPassword">Password</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('password', ['placeholder' => 'Mật khẩu', 'type' => 'password']); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <label class="control-label" for="inputConfirmPassword">Confirm Password</label>
                                                <div class="controls">
                                                            <?php echo $this->Form->input('confirmPassword', ['placeholder' => 'Xác nhận mật khẩu', 'type' => 'password']); ?>
                                                </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', ['placeholder' => 'Số điện thoại', 'type' => 'number', 'error' => false]); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <div class="controls">
                                                            <?php echo $this->Form->input('agree', ['label' => false, 'type' => 'checkbox', 'div' => false, 'after' => '<label>Tôi đồng ý với các điều khoản đã đưa ra.</label>']); ?>
                                                </div>
                                    </div>
                                    <div class="control-group">
                                                <div class="controls">
                                                            <?php echo $this->Form->button('Đăng ký', ['type' => 'submit', 'class' => 'col-lg-2 btn btn-primary']); ?>
                                                </div>
                                    </div>
                        <?php echo $this->Form->end(); ?>
</div>

Viết action register() trong UsersController.php:

            public function register() {
                        $this->set('title_for_layout', 'Đăng ký');
                        if ($this->request->is('post')) {
                                    if ($this->request->data['User']['agree'] == 1) {
                                                $this->User->create();
                                                $this->request->data['User']['group_id'] = 2;
                                                if ($this->User->save($this->request->data)) {
                                                            $this->Flash->success('Đã đăng ký thành công', ['params' => ['class' => 'alert alert-success']]);
                                                            $this->Auth->login();
                                                            $this->redirect($this->Auth->redirect());
                                                } else {
                                                            $this->Flash->error('Đăng ký chưa được, vui lòng thử lại!', ['params' => ['class' => 'alert alert-error']]);
                                                }
                                    } else {
                                                $this->Flash->error('Bạn cần đồng ý với các thỏa thuận để tiếp tục!', ['params' => ['class' => 'alert alert-error']]);         
                                    }
                                   
                        }
            }

Chỉnh lại một số trường trong Model\User.php:

'username' => array(
                                    'notBlank' => array(
                                                'rule' => array('notBlank'),
                                                'message' => 'Tên đăng nhập không được để trống',
                                                //'allowEmpty' => false,
                                                //'required' => false,
                                                //'last' => false, // Stop validation after this rule
                                                //'on' => 'create', // Limit validation to 'create' or 'update' operations
                                    ),
                                    'unique' => array(
                                                'rule' => 'isUnique',
                                                'message' => 'Tên đăng nhập đã tồn tại',
                                    ),
                        ),
'email' => array(
                                    'email' => array(
                                                'rule' => array('email'),
                                                'message' => 'Địa chỉ email không hợp lệ.',
                                                //'allowEmpty' => false,
                                                //'required' => false,
                                                //'last' => false, // Stop validation after this rule
                                                //'on' => 'create', // Limit validation to 'create' or 'update' operations
                                    ),
                                    'unique' => array(
                                                'rule' => 'isUnique',
                                                'message' => 'Địa chỉ email đã tồn tại.'
                                    ),
                        ),
'firstname' => array(
                                    'notBlank' => array(
                                                'rule' => array('notBlank'),
                                                'message' => 'Tên không được để trống.',
                                                //'allowEmpty' => false,
                                                //'required' => false,
                                                //'last' => false, // Stop validation after this rule
                                                //'on' => 'create', // Limit validation to 'create' or 'update' operations
                                    ),
                        ),
                        'lastname' => array(
                                    'notBlank' => array(
                                                'rule' => array('notBlank'),
                                                'message' => 'Họ không được để trống.',
                                                //'allowEmpty' => false,
                                                //'required' => false,
                                                //'last' => false, // Stop validation after this rule
                                                //'on' => 'create', // Limit validation to 'create' or 'update' operations
                                    ),

                        ),
-----------
Cập nhật 5/7/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 (x34) - CakePHP2 - Hàm ràng buộc tại model, hashing, callback

Tiếp theo của: Ngu ngơ học làm web (x33) - CakePHP2 – Đổi mật khẩu, ràng buộc ở controller
-----

Phần x34. CakePHP2 – Hàm ràng buộc tại model, hashing, callback


Xem (clip số 35b – chickenrainshop):

- Viết hàm ràng buộc trong model (1":12)

- Sử dụng saveField để lưu một trường dữ liệu (6":18)

- Hàm callback (10":45)

- Gọi class :: (12":07)

Thay vì sử dụng hàm strcmp() trong controller để so sánh giữa password và confirmPassword, có thể viết hàm để so sánh hai đối tượng này trong model.

Đọc thêm về Adding your own Validation Methods tại đây: https://book.cakephp.org/2.0/en/models/data-validation.html

Ví dụ:

class User extends AppModel {

    public $validate = array(
        'promotion_code' => array(
            'rule' => array('limitDuplicates', 25),
            'message' => 'This code has been used too many times.'
        )
    );

    public function limitDuplicates($check, $limit) {
        // $check will have value: array('promotion_code' => 'some-value')
        // $limit will have value: 25
        $existingPromoCount = $this->find('count', array(
            'conditions' => $check,
            'recursive' => -1
        ));
        return $existingPromoCount < $limit;
    }
}

Viết một ràng buộc trong model:

[Model\User.php]

Hàm matchPassword(): sẽ thực hiện so sánh confirm password với password:

            public function matchPassword($check, $passwordField = 'password') {
                        $password = $this->data['User'][$passwordField];
                        $confirmPassword = $this->data['User']['confirmPassword'];
                        if (strcmp($password, $confirmPassword) == 0) {
                                    return true;
                        } else {
                                    return false;
                        }
            }

Gọi và sử dụng hàm matchPassword():

'confirmPassword' => array(
                                    'notBlank' => array(
                                                'rule' => array('notBlank'),
                                                'message' => 'Xác nhận mật khẩu không được để trống',
                                    ),
                                    'matchPassword' => array(
                                                'rule' => array('matchPassword', 'password'),
                                                'message' => 'Xác nhận mật khẩu không đúng!'
                                    ),
                        ),

Lưu mật khẩu mới vào cơ sở dữ liệu, sử dụng hàm saveField(‘tên trường’, ‘giá trị’). Hàm saveField sẽ lưu dữ liệu vào trường được chỉ định, tuy nhiên, mặc định, nó sẽ tạo một mẩu tin mới, để lưu vào một mẩu tin cũ cần chỉ ra id của mẩu tin đó.

Ví dụ:

if ($this->User->validates()) {
                                                $userInfo = $this->getUser();
                                                $this->User->id = $userInfo['id'];
                                                if ($this->User->saveField('password', $this->Auth->password($this->request->data['User']['password']))) {
                                                            $this->Flash->success('Đã lưu thành công', ['params' => ['class' => 'alert alert-success']]);
                                                } else {
                                                            $this->Flash->error('Đổi mật khẩu chưa thành công, vui lòng thử lại!', ['params' => ['class' => 'alert alert-error']]);
}

Để băm mật khẩu trước khi lưu, sử dụng hàm password của Auth, ví dụ:

if ($this->User->saveField('password', $this->Auth->password($this->request->data['User']['password']))) {

Để không phải viết lại $this->Auth->password, mỗi khi muốn băm mật khẩu, có thể viết một hàm callback trong model.

Hàm callback là gì?

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

Hàm callback là hàm chạy ngầm được gọi bởi thao tác khác trong model hoặc controller.

Ví dụ, hàm callback beforeSave(), sẽ được gọi và thực thi trước mỗi thao tác lưu dữ liệu vào cơ sở dữ liệu.

// âm thầm băm mật khẩu trước khi lưu
            public function beforeSave($options = array()) {
                        if (isset($this->data['User']['password'])) {
                                    $this->data['User']['password'] = AuthComponent::password($this->data['User']['password']);
                        }
                        return true;
            }          

Trong model không thể gọi $this->Auth->password mà sử dụng AuthComponent::password

Đã viết hàm callback beforeSave(), nên trong UsersController sẽ sửa lại một chút:

$this->User->id = $userInfo['id'];
            if ($this->User->saveField('password', $this->request->data['User']['password'])) {
            $this->Flash->success('Đã lưu thành công', ['params' => ['class' => 'alert alert-successs']]);

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