Ngu ngơ học làm web (x49) - CakePHP2 - Lưu trên nhiều bảng

Tiếp theo của: Ngu ngơ học làm web (x48) - CakePHP2 – Hàm end(), upload và xóa tập tin
-----

Phần x49. CakePHP2 – Lưu trên nhiều bảng


Xem (clip số 47 – chickenrainshop):

Trong view ‘edit một cuốn sách’, thực hiện chuyển  ô Tác giả từ multiselect box thành một input kiểu text.

[View\Books\admin_edit.ctp]

<?php
                        echo $this->Form->input('id');
                        echo $this->Form->input('category_id');
                        echo $this->Form->input('title');
                        echo $this->Form->input('slug');
                        echo $this->Form->input('Writer', ['label' => 'Tác giả', 'type' => 'text', 'value' => $writers]);
                        echo $this->Html->image($this->request->data['Book']['image'], ['width'=>140, 'height' => 180]);
                        echo $this->Form->input('image',['label' => '', 'type' => 'file']);
                        echo $this->Form->input('info', ['label' => 'Nội dung', 'class' => 'ckeditor']);
                        echo $this->Form->input('price');
                        echo $this->Form->input('sale_price');
                        echo $this->Form->input('publisher');
                        echo $this->Form->input('publish_date');
                        echo $this->Form->input('link_download');
                        echo $this->Form->input('published');
            ?>

[BooksController.php]

            // xử lý thông tin tác giả trước khi thêm/cập nhật
            private function checkWriter($writerList = null) {
                        $writers = explode(',', $writerList);
                        $this->loadModel('Writer');
                        foreach ($writers as $writer) {
                                    $slug = $this->Tool->slug($writer);
                                    $writerInfo = $this->Writer->findBySlug($slug);
                                    if (empty($writerInfo)) {
                                                $data = [
                                                            'name' => ucwords(trim($writer)),
                                                            'slug' => $slug,
                                                            'biography' => 'Đang cập nhật'
                                                ];
                                                // cập nhật lại id để lưu mẩu tin mới
                                                $this->Writer->create();
                                                $this->Writer->save($data);
                                                $saveInfo[] = $this->Writer->id;
                                    } else {
                                                $saveInfo[] = $writerInfo['Writer']['id'];
                                    }
                        }
                        $this->request->data['Writer']['Writer'] = $saveInfo;
            }

            public function admin_edit($id = null) {
                        if (!$this->Book->exists($id)) {
                                    throw new NotFoundException(__('Invalid book'));
                        }
                        if ($this->request->is(array('post', 'put'))) {
                                    $this->checkSlug($this->request->data, 'Book', 'title');
                                    $this->loadModel('Category');
                                    $category = $this->Category->findById($this->request->data['Book']['category_id']);
                                    $save = true;
                                    if (!empty($this->request->data['Book']['image']['name'])) {
                                                $result = $this->uploadFile($category['Category']['slug']);
                                                if ($result['status']) {
                                                            $location = '/files/'.$category['Category']['slug'].'/'.$result['fileName'];
                                                            $this->request->data['Book']['image'] = $location;
                                                } else {
                                                            $this->Flash->error(__('Không upload được ảnh, vui lòng thử lại!'));
                                                            $save = false;
                                                }          
                                    } else {
                                                unset($this->request->data['Book']['image']);
                                    }
                                    if ($save) {
                                                $this->checkWriter($this->request->data['Writer']['Writer']);
                                                if ($this->Book->save($this->request->data)) {
                                                            $this->Flash->success(__('The book has been saved.'));
                                                            return $this->redirect(array('action' => 'index'));
                                                } else {
                                                            $this->Flash->error(__('The book could not be saved. Please, try again.'));
                                                }
                                    }                                  
                        } else {
                                    $options = array('conditions' => array('Book.' . $this->Book->primaryKey => $id));
                                    $this->request->data = $this->Book->find('first', $options);
                                    if (!empty($this->request->data['Writer'])) {
                                                foreach ($this->request->data['Writer'] as $writer) {
                                                            $writerList[] = $writer['name'];
                                                }
                                                $writers = implode(', ', $writerList);
                                    } else {
                                                $writers = null;
                                    }
                        }
                        $categories = $this->Book->Category->find('list');
                        $this->set(compact('categories', 'writers'));

            }
-----------
Cập nhật 30/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 (x48) - CakePHP2 - Hàm end(), upload và xóa tập tin

Tiếp theo của: Ngu ngơ học làm web (x47) - CakePHP2 – Xóa folder, upload tập tin, upload ảnh
-----

Phần x48. CakePHP2 – Hàm end(), upload và xóa tập tin


Xem (clip số 46b – chickenrainshop):

- Lấy phần mở rộng của một file, hàm explode (46b)(2":17)

- Hàm end (3":04)

Đổi tên tập tin hình ảnh khi upload lên server để tránh ghi đè vào các tập tin đã có sẵn trước đó, tên của tập tin hình ảnh chính là slug của cuốn sách.

Chỉnh sửa hàm uploadFile().

[BooksController.php]

private function uploadFile($folder = null) {
                        $file = new File($this->request->data['Book']['image']['tmp_name']);
                        $arr = explode('.', $this->request->data['Book']['image']['name']);
                        $ext = end($arr);
                        $fileName = $this->request->data['Book']['slug'].'.'.$ext;
                        if ($file->copy(APP.'webroot/files/'.$folder.'/'.$fileName)) {
                                    $result = [
                                                'status' => true,
                                                'fileName' => $fileName
                                    ];
                        } else {
                                    $result = [
                                                'status' => false,
                                                'fileName' => $fileName
                                    ];
                        }
                        return $result;
            }

Sửa lại action admin_edit()

[BooksController.php]

public function admin_edit($id = null) {
                        …
                                    if (!empty($this->request->data['Book']['image']['name'])) {
                                                $result = $this->uploadFile($category['Category']['slug']);
                                                if ($result['status']) {
                                                $location = '/files/'.$category['Category']['slug'].'/'.$result['fileName'];
                                                            $this->request->data['Book']['image'] = $location;
                                                …
            }

Xem (clip số 46c – chickenrainshop):

Cho phép upload tập tin hình ảnh trong view ‘thêm một cuốn sách’ (Add Book).

[View\Books\admin_add.ctp]
<div class="books form">
<?php echo $this->Form->create('Book', ['type' => 'file']); ?>
            <fieldset>
                        <legend><?php echo __('Add Book'); ?></legend>
            <?php
                        echo $this->Form->input('category_id');
                        echo $this->Form->input('title');
                        echo $this->Form->input('slug');
                        echo $this->Form->input('image', ['type' => 'file']);

[BooksController.php]

public function admin_add() {
                        if ($this->request->is('post')) {
                                    $this->checkSlug($this->request->data, 'Book', 'title');
                                    $this->loadModel('Category');
                                    $category = $this->Category->findById($this->request->data['Book']['category_id']);
                                    $result = $this->uploadFile($category['Category']['slug']);
                                    if ($result['status']) {
                                                $location = '/files/'.$category['Category']['slug'].'/'.$result['fileName'];
                                                $this->request->data['Book']['image'] = $location;
                                                $this->Book->create();
                                                if ($this->Book->save($this->request->data)) {
                                                            $this->Flash->success(__('The book has been saved.'));
                                                            return $this->redirect(array('action' => 'index'));
                                                } else {
                                                            $this->Flash->error(__('The book could not be saved. Please, try again.'));
                                                }
                                    } else {
                                                $this->Flash->error(__('Bạn chưa upload hình ảnh cho sách!'));
                                    }
                                   
                        }
                        $categories = $this->Book->Category->generateTreeList();
                        $writers = $this->Book->Writer->find('list');
                        $this->set(compact('categories', 'writers'));
            }

Thực hiện xóa tập tin hình ảnh:

[BooksController.php]

Thêm prefix admin vào trước action delete.

public function admin_delete($id = null) {

Thay đổi nội dung bên trong action admin_delete():

[BooksController.php]

public function admin_delete($id = null) {
                        $this->Book->id = $id;
                        if (!$this->Book->exists()) {
                                    throw new NotFoundException(__('Invalid book'));
                        }
                        $this->request->allowMethod('post', 'delete');
                        $book = $this->Book->findById($id);
                        $file = new File(APP.'/webroot/'.$book['Book']['image']);
                        if ($file->delete()) {
                                    if ($this->Book->delete()) {
                                                $this->Flash->success(__('The book has been deleted.'));
                                    } else {
                                                $this->Flash->error(__('The book could not be deleted. Please, try again.'));
                                    }
                        } else {
                                    $this->Flash->error(__('Không xóa được file hình ảnh, vui lòng thử lại sau!'));
                        }
                        return $this->redirect(array('action' => 'index'));

            }
-----------
Cập nhật 29/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 (x47) - CakePHP2 - Xóa folder, upload tập tin, upload ảnh

Tiếp theo của: Ngu ngơ học làm web (x46) - CakePHP2 – CKEditor, block, lọc dấu tiếng Việt, folder
-----

Phần x47. CakePHP2 – Xóa folder, upload tập tin, upload ảnh


Xem (clip số 45b – chickenrainshop):

- Xóa thư mục (45b)

- Cập nhật dữ liệu trong mySQL (2":21)

Trong clip có hướng dẫn tạo thêm trường folder trong bảng categories, tuy nhiên để ý thấy nội dung trường folder giống với trường slug, nên không tạo thêm trường folder nữa mà sử dụng luôn trường slug để làm việc.

Đưa câu khai báo sử dụng Folder lên đầu [CategoriesController.php]

<?php
App::uses('AppController', 'Controller');
App::uses('Folder', 'Utility');

Thực hiện xóa một Category, đồng thời xóa luôn thư mục chứa hình ảnh tương ứng:

[CategoriesController.php action admin_delete()]

public function admin_delete($id = null) {
                        $this->Category->id = $id;
                        if (!$this->Category->exists()) {
                                    throw new NotFoundException(__('Invalid category'));
                        }
                        $this->request->allowMethod('post', 'delete');
                        $category = $this->Category->findById($id);
                        $folder = new Folder(APP.'webroot/files/'.$category['Category']['slug']);
                        if ($folder->delete()) {
                                    if ($this->Category->removeFromTree($id, true)) {
                                                $this->Flash->success(__('The category has been deleted.'));
                                    } else {
                                                $this->Flash->error(__('The category could not be deleted. Please, try again.'));
                                    }
                        } else {
                                                $this->Flash->error(__('Không xóa được thư mục. Vui lòng thử lại sau.'));
                                    }
                        return $this->redirect(array('action' => 'index'));
            }

Xem (clip số 46a – chickenrainshop):

- upload ảnh (46a)

- Html->image (1":19)

- Khai báo type='file' trong form  (3":19)

- uploadFile (8":05)

Tải một tập tin ảnh lên server, thực hiện minh họa trên trang ‘edit một cuốn sách’.

Hiển thị ảnh lên view:

[View\Books\admin_edit.ctp]

<?php echo $this->Form->create('Book', ['novalidate' => true, 'type' => 'file']); ?>
            <fieldset>
                        <legend><?php echo __('Edit Book'); ?></legend>
            <?php
                        echo $this->Form->input('id');
                        echo $this->Form->input('category_id');
                        echo $this->Form->input('title');
                        echo $this->Form->input('slug');
                        echo $this->Html->image($this->request->data['Book']['image'], ['width'=>140, 'height' => 180]);
                        echo $this->Form->input('image',['label' => '', 'type' => 'file']);

Viết hàm uploadFile():

Khai báo sử dụng class File ở đầu tập tin BooksController.php:

<?php
App::uses('AppController', 'Controller');
App::uses('File', 'Utility');

[BooksController.php]

public function admin_edit($id = null) {
                        if (!$this->Book->exists($id)) {
                                    throw new NotFoundException(__('Invalid book'));
                        }
                        if ($this->request->is(array('post', 'put'))) {
                                    $this->checkSlug($this->request->data, 'Book', 'title');
                                    $this->loadModel('Category');
                                    $category = $this->Category->findById($this->request->data['Book']['category_id']);
                                    $save = true;
                                    if (!empty($this->request->data['Book']['image']['name'])) {
                                                if ($this->uploadFile($category['Category']['slug'])) {
                                                            $location = '/files/'.$category['Category']['slug'].'/'.$this->request->data['Book']['image']['name'];
                                                            $this->request->data['Book']['image'] = $location;
                                                } else {
                                                            $this->Flash->error(__('Không upload được ảnh, vui lòng thử lại!'));
                                                            $save = false;
                                                }          
                                    } else {
                                                unset($this->request->data['Book']['image']);
                                    }
                                    if ($save) {
                                                if ($this->Book->save($this->request->data)) {
                                                            $this->Flash->success(__('The book has been saved.'));
                                                            return $this->redirect(array('action' => 'index'));
                                                } else {
                                                            $this->Flash->error(__('The book could not be saved. Please, try again.'));
                                                }
                                    }                                  
                        } else {
                                    $options = array('conditions' => array('Book.' . $this->Book->primaryKey => $id));
                                    $this->request->data = $this->Book->find('first', $options);
                        }
                        $categories = $this->Book->Category->find('list');
                        $writers = $this->Book->Writer->find('list');
                        $this->set(compact('categories', 'writers'));

            }
-----------
Cập nhật 28/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 (x46) - CakePHP2 - CKEditor, block, lọc dấu tiếng Việt, folder

Tiếp theo của: Ngu ngơ học làm web (x45) - CakePHP2 – Tạo helper, Admin books
-----

Phần x46. CakePHP2 – CKEditor, block, lọc dấu tiếng Việt, folder


Tích hợp CKEditor vào trang ‘edit một cuốn sách’:

Vào trang http://ckeditor.com để tải mã nguồn của CKEditor về máy, chọn bản standard.

Sau khi tải về máy, giải nén và chép vào thư mục webroot\js của dự án.

Khai báo sử dụng CKEditor trong layout admin:

[Layouts\admin.ctp]

echo $this->fetch('script');
echo $this->Html->script('ckeditor/ckeditor');

Nhúng CKEditor vào input:

[View\Books\admin_edit.ctp]

echo $this->Form->input('image');
echo $this->Form->input('info', ['label' => 'Nội dung', 'class' => 'ckeditor']);
echo $this->Form->input('price');

Khắc phục lỗi hiển thị tiếng Việt khi xem ở chế độ source của CKEditor:

[js\ckeditor\config.js]

config.entities = false;
// The toolbar groups arrangement, optimized for two toolbar rows.
config.toolbarGroups = [

Sửa lỗi hiển thị thẻ html trong view View\Books\view.ctp:

[View\Books\view.ctp]

<h4>Giới thiệu:</h4>
<p>
<?php echo $book['Book']['info']; ?>
</p>

Thêm nút New Book cho trang [admin_index.ctp]

<h2><?php echo __('Sách'); ?></h2>
            <div class="actions">
            <ul>
                        <li><?php echo $this->Html->link(__('New Book'), array('controller' => 'books', 'action' => 'add')); ?> </li>
            </ul>

Đổi view [View\Books\add.ctp] thành [View\Books\admin_add.ctp].

Đổi action [ControllersBooks.php add()] thành [ControllersBooks.php admin_add()]

Thực hiện thêm CKEditor cho chức năng ‘thêm một quyển sách’:

[View\Books\admin_add.ctp]

echo $this->Form->input('image');
echo $this->Form->input('info', ['label' => 'Nội dung', 'class' => 'ckeditor']);
echo $this->Form->input('price');

Sửa lại một chút action admin_add() để hiển thị danh mục sách theo cấu trúc cây:

[BooksController.php action admin_add()]

$categories = $this->Book->Category->generateTreeList();
$writers = $this->Book->Writer->find('list');
$this->set(compact('categories', 'writers'));

Để gọi một khối lệnh (block) (ví dụ: đoạn mã javascript) trong view sử dụng cú pháp:

<?php
$this->start(‘script);
            // khối lệnh
$this->end();
?>

Ví dụ,

[admin_edit.ctp]

<?php
            $this->start('script');
                        echo $this->Html->script('ckeditor/ckeditor');
            $this->end();
?>

Và [admin_add.ctp]

<?php
            $this->start('script');
                        echo $this->Html->script('ckeditor/ckeditor');
            $this->end();
?>

Xem (clip số 45a – chickenrainshop):

- Tạo link thân thiện (slug) (45a) 

- Sử dụng novalidate (2":01)

- Chuyển kí tự có dấu thành không dấu stripUnicode, preg_replace, Inflector, slug (4":08)

- Tạo thư mục trong cakePHP sử dụng Folder (9":02)

- Lấy đường dẫn tuyệt đối - APP (10":51)

Tạo slug tự động, thực hiện trên view Categories\admin_add.ctp:

Sửa lại view admin_add.ctp, thiết lập hai thuộc tính required và novalidate:

[View\Categories\admin_add.ctp]

<div class="categories form">
<?php echo $this->Form->create('Category', ['novalidate' => true]); ?>
            …
                        echo $this->Form->input('name');
                        echo $this->Form->input('slug', ['required' => false, ]);
            …
</div>

[Controller\CategoriesController.php action admin_add()]

public function admin_add() {
                        if ($this->request->is('post')) {
                                    if (empty($this->request->data['Category']['slug'])) {
                                                $this->request->data['Category']['slug'] = $this->Tool->slug($this->request->data['Category']['name']);
                                    } else {
                                                $this->request->data['Category']['slug'] = $this->Tool->slug($this->request->data['Category']['slug']);
                                    }
                                    $this->Category->create();

[Component\ToolComponent.php]

// lọc dấu tiếng Việt
                        public function stripUnicode ($string) {
                                    if (!$string) return false;
                                    $unicode = array(
                                                'a'=>'á|à|ả|ã|ạ|ă|ắ|ặ|ằ|ẳ|ẵ|â|ấ|ầ|ẩ|ẫ|ậ|Á|À|Ả|Ã|Ạ|Ă|Ắ|Ặ|Ằ|Ẳ|Ẵ|Â|Ấ|Ầ|Ẩ|Ẫ|Ậ',
                                                'd'=>'đ|Đ',
                                                'e'=>'é|è|ẻ|ẽ|ẹ|ê|ế|ề|ể|ễ|ệ|É|È|Ẻ|Ẽ|Ẹ|Ê|Ế|Ề|Ể|Ễ|Ệ',
                                                'i'=>'í|ì|ỉ|ĩ|ị|Í|Ì|Ỉ|Ĩ|Ị',
                                                'o'=>'ó|ò|ỏ|õ|ọ|ô|ố|ồ|ổ|ỗ|ộ|ơ|ớ|ờ|ở|ỡ|ợ|Ó|Ò|Ỏ|Õ|Ọ|Ô|Ố|Ồ|Ổ|Ỗ|Ộ|Ơ|Ớ|Ờ|Ở|Ỡ|Ợ',
                                                'u'=>'ú|ù|ủ|ũ|ụ|ư|ứ|ừ|ử|ữ|ự|Ú|Ù|Ủ|Ũ|Ụ|Ư|Ứ|Ừ|Ử|Ữ|Ự',
                                                'y'=>'ý|ỳ|ỷ|ỹ|ỵ|Ý|Ỳ|Ỷ|Ỹ|Ỵ'
                                    );
                                    foreach ($unicode as $nonUnicode => $uni) {
                                                $string = preg_replace("/($uni)/", $nonUnicode, $string);
                                    }
                                    return $string;
                        }

                        public function slug($string, $character = '-') {
                                    // chuyển kí tự có dấu thành không dấu
                                    $string = $this->stripUnicode($string);
                                    App::uses('Inflector', 'Utility');
                                    $string = inflector::slug($string, $character);
                                    return strtolower($string);
                        }

Có thể tách đoạn mã tạo slug thành một hàm riêng, đặt tại AppController để sử dụng lại:

[AppController.php]

public function checkSlug($data, $model, $name, $slugField = 'slug') {
                        if (empty($this->request->data[$model][$slugField])) {
                                                $this->request->data[$model][$slugField] = $this->Tool->slug($this->request->data[$model][$name]);
                                    } else {
                                                $this->request->data[$model][$slugField] = $this->Tool->slug($this->request->data[$model][$slugField]);
                                    }
            }

Sửa lại một chút mã của admin_add():

[Controller\CategoriesController.php action admin_add()]

public function admin_add() {
                        if ($this->request->is('post')) {
                                    // if (empty($this->request->data['Category']['slug'])) {
                                    //          $this->request->data['Category']['slug'] = $this->Tool->slug($this->request->data['Category']['name']);
                                    // } else {
                                    //          $this->request->data['Category']['slug'] = $this->Tool->slug($this->request->data['Category']['slug']);
                                    // }
                                    $this->checkSlug($this->request->data, 'Category', 'name');
                                    $this->Category->create();

Tạo thêm một danh mục sách, đồng thời phải tạo luôn thư mục để lưu hình ảnh của sách thuộc danh mục này:

[Controller\CategoriesController.php action admin_add()]

public function admin_add() {
                        if ($this->request->is('post')) {
                                    // if (empty($this->request->data['Category']['slug'])) {
                                    //          $this->request->data['Category']['slug'] = $this->Tool->slug($this->request->data['Category']['name']);
                                    // } else {
                                    //          $this->request->data['Category']['slug'] = $this->Tool->slug($this->request->data['Category']['slug']);
                                    // }
                                    $this->checkSlug($this->request->data, 'Category', 'name');
                                    App::uses('Folder', 'Utility');
                                    $folder = new Folder();
                                    if ($folder->create(APP.'/webroot/files/'.$this->request->data['Category']['slug'])) {
                                                $this->Category->create();
                                                if ($this->Category->save($this->request->data)) {
                                                            $this->Flash->success(__('The category has been saved.'));
                                                            return $this->redirect(array('action' => 'index'));
                                                } else {
                                                            $this->Flash->error(__('The category could not be saved. Please, try again.'));
                                                }
                                    } else {
                                                $this->Flash->error(__('Không tạo được thư mục. Vui lòng thử lại sau.'));
                                    }
                                   

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