--------------- <> -----------------
--- 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 (x16) - CakePHP2 - Search trên một table

Tiếp theo của: Ngu ngơ học làm web (x15) - CakePHP2 – counterCache
-----

Phần x16. CakePHP2 – Search trên một table


Xem (clip số 22a – chickenrainshop):

- Tạo form tìm kiếm bằng html helper, liên kết đến một model

- Kiểm tra request có phải là post không? (2":56)

- Xem dữ liệu bằng hàm pr()(3":04)

- Truy vấn tìm kiếm tương đối (6":19)

- Kiểm tra một biến có rỗng hay không empty() (7":45)

- Kiểm tra biến có tồn tại hay không isset() (8":33)

Để ý thứ tự khi viết một chức năng mới:

- Vào controller viết hàm xử lý, chỉ cần tên hàm thôi, chưa xử lý gì bên trong

- Vào view viết mã cho giao diện

- Vào lại controller viết tiếp các xử lý khi người dùng gửi dữ liệu về server

Hai cách viết để sử dụng contain dưới đây là như nhau:

- Cách một:

'contain' => ['Writer.name', 'Writer.slug']

- Cách hai:

'contain' => ['Writer' => ['name', 'slug']]

Câu truy vấn tìm kiếm tương đối (like):

Ví dụ,

$books = $this->Book->find('all',
                                                [
                                                            'fields' => ['title', 'image', 'sale_price', 'slug'],
                                                            // 'contain' => ['Writer.name', 'Writer.slug'],
                                                            'contain' => ['Writer' => ['name', 'slug']],
                                                            'order' => ['Book.created' => 'desc'],
                                                            'conditions' => [
                                                                        'published' => 1,
                                                                        'title like' => '%'.$keyword.'%'
                                                                        ]
                                                ]);

Ghi lại một số đoạn mã để tham khảo:

[hàm search() trong BooksController.php]

public function search() {
                        $notFound = false;
                        if ($this->request->is('post')) {
                                    $keyword = $this->request->data['Book']['keyword'];
                                    $books = $this->Book->find('all',
                                                [
                                                            'fields' => ['title', 'image', 'sale_price', 'slug'],
                                                            // 'contain' => ['Writer.name', 'Writer.slug'],
                                                            'contain' => ['Writer' => ['name', 'slug']],
                                                            'order' => ['Book.created' => 'desc'],
                                                            'conditions' => [
                                                                        'published' => 1,
                                                                        'title like' => '%'.$keyword.'%'
                                                                        ]
                                                ]);
                                    if (!empty($books)) {
                                                $this->set('results', $books);
                                    } else {
                                                $notFound = true;
                                    }
                        }
                        $this->set('notFound', $notFound);
            }

[View\Books\search.ctp]

<?php echo $this->Form->create('Book'); ?>
            <?php echo $this->Form->input('keyword',
                        [
                                    'label' => '',
                                    'placeholder' => 'Nhập từ khóa tìm kiếm...'
                        ]);
            ?>
            <?php if(isset($results) && $notFound == false): ?>
                        <?php echo $this->element('book', ['books' => $results]); ?>
            <?php elseif($notFound == true): ?>
                        Không tìm thấy quyển sách này!
            <?php endif ?>

<?php echo $this->Form->end('Search'); ?>
-----------
Cập nhật 14/5/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 (x15) - CakePHP2 - counterCache

Tiếp theo của: Ngu ngơ học làm web (x14) - CakePHP2 – Validate
-----

Phần x15. CakePHP2 – counterCache


Xem (clip số 21 – chickenrainshop):


Tính năng counterCache, giúp đếm và cập nhật tự động các mẩu tin liên quan, thay thế cho việc đếm bằng hàm find(‘count’).

Cách làm:

- Vào bảng Books, tạo trường comment_count. Ở đây, Books có quan hệ hasMany với Comments, việc đặt tên comment_count được đặt theo quy ước chứ không được đặt tùy ý, cụ thể: model_count.

Trường comment_count: kiểu int, giá trị khởi tạo là 0, cho phép NULL.

- Kích hoạt tính năng counterCache: vào model Comment, trong phần định nghĩa quan hệ belongsTo, thêm mục ‘counterCache’ => true.

public $belongsTo = array(
                        'User' => array(
                                    'className' => 'User',
                                    'foreignKey' => 'user_id',
                                    'conditions' => '',
                                    'fields' => '',
                                    'order' => ''
                        ),
                        'Book' => array(
                                    'className' => 'Book',
                                    'foreignKey' => 'book_id',
                                    'conditions' => '',
                                    'fields' => '',
                                    'order' => '',
                                    'counterCache' => true
                        )
            );

Trong view Books\view.ctp, thêm dòng mã sau:

<dt><?php echo __('Comments'); ?></dt>
                        <dd>
                                    <?php echo h($book['Book']['comment_count']); ?> comments
                                    &nbsp;
                        </dd>

Hàm cập nhật giá trị comment_count cho tất cả cuốn sách, viết trong BooksController:

public function updateComments() {
                        $books = $this->Book->find('all', [
                                    'fields' => 'id',
                                    'contain' => 'Comment'
                                    ]);
                        // pr($books);
                        foreach ($books as $book) {
                                    if (count($book['Comment']) > 0) {
                                                $this->Book->updateAll(
                                                            ['comment_count' => count($book['Comment'])],
                                                            ['Book.id' => $book['Book']['id']]
                                                );
                                    }
                        }
            }


-----------
Cập nhật 12/5/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 (x14) - CakePHP2 - Validate

Tiếp theo của: Ngu ngơ học làm web (x13) - CakePHP2 – Hàm rand(), Form, Redirect
-----

Phần x14. CakePHP2 – Validate


Xem (clip số 20b – chickenrainshop):

- Kiểm tra dữ liệu (validate) ngay tại controller thay vì tại model (20b)

- Lưu biến thông báo lỗi vào session (6":16)

- Ẩn dấu * trên view khi có thuộc tính require (7":50)

- Tắt validate mặc định của trình duyệt (9":04)

- Đọc và kiểm tra session có tồn tại không (11":35)

- Kiểm tra biến có tồn tại hay không trên view (isset)(15":57)

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

Để kiểm tra dữ liệu đã hợp lệ hay chưa, bước đầu tiên là khai báo luật (rule) trong model như sau:

public $validate = array(
            'content' => array(
                        'notBlank' => array(
                                    'rule' => array('notBlank'),
                                    'message' => 'Nội dung nhận xét 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
                        ),
                        'minLength' => array(
                                    'rule' => array('minLength', 8),
                                    'message' => 'Nội dung nhận xét tối thiểu phải có 8 kí tự',
                        ),
            ),
);  

Biến $validate thuộc kiểu public, là một mảng,

- content: là tên của trường dữ liệu (trong database và trên view) cần kiểm tra tính hợp lệ

- notBlank: là tên luật áp dụng cho trường content

- Mỗi luật gồm các mục:

+ rule:  nội dung của luật

+ message: thông báo lỗi khi dữ liệu nhập vào không đúng theo yêu cầu

+ required: bắt buộc phải có trường này trong array khi ghi dữ liệu xuống cơ sở dữ liệu, mặc dù giá trị của trường này có thể là trống (blank). Đọc thêm ở đây: https://book.cakephp.org/2.0/en/models/data-validation.html#required

Bước thứ hai là thực hiện kiểm tra dữ liệu bên controller:

- Việc kiểm tra dữ liệu phải được thực hiện trước khi lưu dữ liệu xuống cơ sở dữ liệu, sử dụng lệnh sau:

$this->Comment->set($this->request->data);
if($this->Comment->validates()) {
            // xử lý nếu kiểm tra dữ liệu thành công                                             
} else {
            // xử lý nếu kiểm tra dữ liệu thất bại
}

Trong đó, $this->request->data là dữ liệu gửi từ client lên server

Bước thứ ba là lưu thông báo lỗi vào session:

$this->Session->write('comment_errors', $comment_errors);

Bước thứ tư là hiển thị thông báo lỗi tại view:

- Tại controller, kiểm tra xem có thông báo lỗi ghi trong session hay không:

$this->Session->check('comment_errors')

- Đọc thông báo lỗi từ session, lưu vào một biến:

$errors = $this->Session->read('comment_errors')

- Gửi biến lên view:

$this->set('errors', $errors);

- Xóa thông báo lỗi trong session, để nó không hiển thị ở các lần sau.

$this->Session->delete('comment_errors');

- Tại view, hiển thị thông báo lỗi trong biến $errors:

Nên đặt ngay dưới ô nhập liệu,

echo $this->Form->input('content');
            if (isset($errors)) {
                        foreach ($errors as $error) {
                                    echo $error[0];
                        }
            }

Để ẩn dấu * trên view, thêm thuộc tính require => false, ví dụ:

echo $this->Form->input('user_id',
                                    [
                                                'type' => 'text',
                                                'value' => 1,
                                                'hidden' => true,
                                                'label' => '',
                                                'required' => false
                                    ]);

Tắt validate mặc định của trình duyệt bằng cách thêm thuộc tính novalidate => true trong lệnh tạo form, ví dụ:

<?php echo $this->Form->create('Comment', ['url' => ['action' => 'add'], 'novalidate' => true]); ?>

Để không hiển thị thông báo lỗi validate mặc định ngay dưới một input, thêm thuộc tính 'error' => false, ví dụ:
<?php echo $this->Form->input('keyword',
                        [
                                    'label' => '',
                                    'placeholder' => 'Nhập từ khóa tìm kiếm...',
                                    'error' => false
                        ]);

            ?>
-----------
Cập nhật 11/5/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 (x13) - CakePHP2 - Hàm rand(), Form, Redirect

Tiếp theo của: Ngu ngơ học làm web (x12) - CakePHP2 – Sử dụng containable behavior
-----

Phần x13. CakePHP2 – Hàm rand(), Form, Redirect


Xem (clip số 19 – chickenrainshop):

Dùng hàm rand() trong tùy chọn order của câu truy vấn, kết hợp với tùy chọn limit để lấy ra các mẩu tin một cách ngẫu nhiên.

[BooksController.php action view]

Thêm các dòng mã sau vào cuối tập tin:

$relatedBooks = $this->Book->find('all',
                                    [
                                                'fields' => ['title', 'image', 'sale_price', 'slug'],
                                                'conditions' => [
                                                            'category_id' => $book['Book']['category_id'],
                                                            'Book.id <>' => $book['Book']['id'],
                                                            'published' => 1
                                                            ],
                                                'limit' => 5,
                                                'order' => 'rand()',
                                                'contain' => ['Writer' => ['name', 'slug']]
                                    ]);
                        $this->set('relatedBooks', $relatedBooks);

[View\Books\view.ctp] thêm các dòng mã sau:

<h3><?php echo __('Sách liên quan') ?></h3>
<?php echo $this->element('book', ['books' => $relatedBooks]); ?>

Xem (clip số 20a – chickenrainshop):

- Chuyển trang web sang action khác: redirect (6":57)

- Reload trang hiện tại: redirect(referer())(8":57)

- Ẩn label: (10":16)

Lấy form gửi comment từ View\Comments\add.ctp chuyển sang View\Books\view.ctp.

Câu hỏi đặt ra ở đây là:

Action và controller của form được xác định như thế nào?

Đọc thêm về FormHelper tại đây: https://book.cakephp.org/2.0/en/core-libraries/helpers/form.html

Nếu có tùy chọn url thì controller sẽ tương ứng TênModel và action là giá trị trong tùy chọn url

Ví dụ,

Tại view Books\view.ctp, câu lệnh tạo form là:

<?php echo $this->Form->create('Comment', ['url' => ['action' => 'add']]); ?>

Khi đó, controller sẽ là Comments, và action là add.

Kết quả render ra là:

<form action="/comments/add" id="CommentAddForm" method="post" accept-charset="utf-8">

Nếu không có tùy chọn url thì controller và action sẽ là controller và tên action tương ứng với nơi đặt đoạn mã tạo form.

Ví dụ,

Tại view Books\view.ctp, câu lệnh tạo form là:

<?php echo $this->Form->create('Comment'); ?>

Khi đó, controller sẽ là Books,và action là view.

Kết quả render ra là:

<form action="/mot-voi-mot-la-ba" id="CommentViewForm" method="post" accept-charset="utf-8">

(có kết quả trên là bởi lệnh này trong routes.php: Router::connect('/:book_title', ['controller' => 'books', 'action' => 'view'], ['pass' => ['book_title']]);)

- method mặc định là post

- id mặc định sau khi render sẽ là TênModelTênActionForm

- Các input của Form được ngầm định là tên của các field Model

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

Lệnh $this->Comment->create(): dùng để reset lại trạng thái của model trước khi thêm vào một mẩu tin mới vào cơ sở dữ liệu.


Đọc thêm về $this->Session->setFlash() tại đây: https://book.cakephp.org/2.0/en/core-libraries/helpers/session.html


Đọc thêm về $this->Flash() tại đây: https://book.cakephp.org/2.0/en/core-libraries/helpers/flash.html
-----------
Cập nhật 10/5/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 (x12) - CakePHP2 - Sử dụng containable behavior

Tiếp theo của: Ngu ngơ học làm web (x11) - CakePHP2 – Phân trang nâng cao
-----

Phần x12. CakePHP2 – Sử dụng containable behavior


Xem (clip số 18a – chickenrainshop):

Dùng containable behavior để hạn chế thông tin khi truy vấn cơ sở dữ liệu.

[BooksController.php action view]

public function view($slug = null) {
                        $options = [
                                    'conditions' => ['Book.slug' => $slug],
                                    'contain' => ['Writer' => ['name', 'slug']]
                                    ];
                        $book = $this->Book->find('first', $options);
                        if (empty($book)) {
                                    throw new NotFoundException(__('Không tìm thấy quyển sách này'));
                        }
                        $this->set('book', $book);
            }

[View\Books\view.ctp]

<div class="books view">
<h2><?php echo __('Book'); ?></h2>
            <dl>
                        <dt><?php echo __('Title'); ?></dt>
                        <dd>
                                    <?php echo h($book['Book']['title']); ?>
                                    &nbsp;
                        </dd>
                        <dt><?php echo __('Image'); ?></dt>
                        <dd>
                                    <?php echo $this->Html->image($book['Book']['image']); ?>
                                    &nbsp;
                        </dd>
                        <dt><?php echo __('Info'); ?></dt>
                        <dd>
                                    <?php echo h($book['Book']['info']); ?>
                                    &nbsp;
                        </dd>
                        <dt><?php echo __('Price'); ?></dt>
                        <dd>
                                    <?php echo h($book['Book']['price']); ?>
                                    &nbsp;
                        </dd>
                        <dt><?php echo __('Sale Price'); ?></dt>
                        <dd>
                                    <?php echo h($book['Book']['sale_price']); ?>
                                    &nbsp;
                        </dd>
                        <dt><?php echo __('Publisher'); ?></dt>
                        <dd>
                                    <?php echo h($book['Book']['publisher']); ?>
                                    &nbsp;
                        </dd>
                        <dt><?php echo __('Publish Date'); ?></dt>
                        <dd>
                                    <?php echo h($book['Book']['publish_date']); ?>
                                    &nbsp;
                        </dd>
            </dl>
</div>
<div class="related">
            <h3><?php echo __('Related Writers'); ?></h3>
            <?php if (!empty($book['Writer'])): ?>
                        <?php foreach ($book['Writer'] as $writer): ?>
                                    <?php echo $this->Html->link($writer['name'], '/tac-gia/'.$writer['slug']); ?>
                        <?php endforeach; ?>
            <?php endif; ?>
</div>
<div class="related">
            <h3><?php echo __('Related Comments'); ?></h3>
            <?php if (!empty($book['Comment'])): ?>
            <?php foreach ($book['Comment'] as $comment): ?>

            <?php endforeach; ?>
            </table>
<?php endif; ?>
</div>

Xem (clip số 18b – chickenrainshop):

Hiển thị thông tin bình luận của mỗi quyển sách.

Khai báo sẽ sử dụng containable behavior trong [Model\Comment.php]

class Comment extends AppModel {
            public $actsAs = ['Containable'];

[View\Books\view.ctp]

Thêm đoạn mã sau vào cuối tập tin:

<div class="related">
            <h3><?php echo __('Related Comments'); ?></h3>
            <?php if (!empty($comments)): ?>
            <?php foreach ($comments as $comment): ?>
                        <?php echo $comment['User']['username'].' đã gửi: '.'"'.$comment['Comment']['content'].'"'; ?> <br>
            <?php endforeach; ?>
            </table>
<?php endif; ?>

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