Lập trình tạo một MVC Component đơn giản cho Joomla. Phần 5. Xóa phần tử

Trong bài viết này

  1. Mô tả chức năng
  2. Xây dựng controller
  3. Logic hoạt động của chức năng xóa
  4. Chỉnh sửa view và layout mặc định
  5. Chỉnh sửa model

Mô tả chức năng 🔝

Chức năng tiếp theo mà chúng ta cần phát triển là xóa một phần tử khỏi danh sách. Người dùng có thể muốn xóa 1 phần tử, hoặc cũng có thể muốn đánh dấu nhiều phần tử rồi xóa đồng loạt.
Khi người dùng duyệt danh sách các phần tử, ta sẽ hiển thị tương ứng với mỗi phần tử một checkbox. Trên toolbar ta đặt nút "Xóa" để cho phép người dùng xóa những phần tử đã chọn.

Xây dựng controller 🔝

Xóa một hoặc nhiều phần tử là một thao tác thực hiện trên danh sách phần tử. Vì thế, để thực thi tao tác này thì người ta thường xây dựng controller có tên là dạng số nhiều của đối được quản lý. Trong trường hợp của ta, đối tượng là sinh viên nên tên controller sẽ là 'students'.
Cũng như add và edit, delete là một thao tác phổ quát trong quản lý, vì thế Joomla cũng đã cung cấp sẵn lớp controller hỗ trợ thao tác này, đó là lớp JControllerAdmin (tức AdminController). Do vậy, ta sẽ thừa kế lớp này để xây dựng controller 'students'. Trong thư mục \com_students\controllers ta tạo file 'students.php' với nội dung như sau:
<?php
defined('_JEXEC') or die;

class StudentsControllerStudents extends JControllerAdmin
{
}
Phương thức delete() mà ta cần đã được xây dựng sẵn trong JControllerAdmin nên mã của file trên chỉ cần có thế.

Logic hoạt động của chức năng xóa 🔝

Mã nguồn của phương thức JControllerAdmin::delete() (trong file \libraries\src\MVC\Controller\AdminController.php) như sau:
public function delete()
{
    // Check for request forgeries
    $this->checkToken();

    // Get items to remove from the request.
    $cid = $this->input->get('cid', array(), 'array');

    // Get the model.
    $model = $this->getModel();

    // Remove the items.
    $model->delete($cid);
    $this->setRedirect(\JRoute::_('index.php?option=' .
        $this->option .
        '&view=' . $this->view_list,
        false));
}
Logic hoạt động của chức năng xóa phần tử khá đơn giản
  1. Trong giao diện mặc định (còn gọi là list view), người dùng lựa chọn một hoặc một số phần tử rồi nhấn nút "Xóa". Mã javascript dưới nút "Xóa" sẽ dẫn đến việc submit form với task="students.delete".
  2. Phương thức delete() của controller students sẽ được gọi. Sau khi kiểm tra form token, nó lấy giá trị của tham số dạng mảng là 'cid' trong truy vấn để biết có những phần tử nào đã được chọn để xóa (giá trị của mỗi phần tử của mảng cid là primary key của một phần tử nào đó). Tiếp đó, phương thức $model->delete($cid) sẽ được gọi để thực hiện việc xóa. Sau cùng, server sẽ chuyển hướng client tới view mặc định (còn gọi là list view).
Ở đây có 2 vấn đề. Thứ nhất, trong truy vấn tới phương thức delete() phải có tham số dạng mảng với tên là 'cid' để cho biết phần tử nào cần xóa. Để thực hiện điều này thì ta cần tạo checkbox bằng công cụ thích hợp mà Joomla hỗ trợ. Thứ hai là model (tên model trùng với tên controller, là 'students') phải hỗ trợ phương thức delete(). Tuy nhiên, hiện tại, model 'students' của chúng ta được xây dựng từ lớp JModelList và chưa hỗ trợ phương thức này, do đó ta phải điều chỉnh lại model.

Chỉnh sửa view và layout mặc định 🔝

Ta cần chỉnh sửa lại view và layout mặc định để thêm các thành phần giao diện cần thiết để người dùng có thể thực hiện chức năng xóa phần tử.
Chỉnh sửa lại mã của phương thức addToolbar() của view students trong file \com_students\views\students\view.html.php như sau:
protected function addToolbar()
{
    JToolbarHelper::title("Danh sách sinh viên");
    JToolbarHelper::addNew("student.add","Thêm một sinh viên");
    JToolbarHelper::deleteList(
        "Bạn có chắc là muốn xóa các sinh viên đã chọn?",
        "students.delete",
        "Xóa");
}
Sự thay đổi chỉ là thêm một nút "Xóa" ứng với task là "students.delete".
Layout 'default.php' của view này phải thay đổi đáng kể. Mã mới sẽ như sau:
<?php
defined('_JEXEC') or die;
?>

<form action="<?php echo JRoute::_('index.php?option=com_students'); ?>"
      name="adminForm" id="adminForm" method="POST">
    <table class="table table-striped">
        <thead>
            <tr>
                <th><input type="checkbox" onclick="Joomla.checkAll(this)"/></th>
                <th>Họ và tên</th>
                <th>Năm sinh</th>
                <th>Điểm trung bình</th>
                <th>Hành động</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($this->items as $i=>$item)
            {?>
                <?php
                    $urlEdit = JRoute::_('index.php?option=com_students&task=student.edit&id='.$item->id);
                ?>
                <tr class="row<?php echo $i%2;?>">
                    <td><?php echo JHtml::_('grid.id', $i, $item->id); ?></td>
                    <td><?php echo '<a href="'.$urlEdit.'">'.$item->name.'</a>';?></td>
                    <td><?php echo $item->year;?></td>
                    <td><?php echo $item->avg;?></td>
                    <td>
                        <a href="<?php echo $urlEdit;?>" title="Chỉnh sửa"><span class="icon-edit"></span></a>
                    </td>
                </tr>
            <?php } ?>
        </tbody>
    </table>
    <input type="hidden" name="task" value="" />
    <input type="hidden" name="boxchecked" value="0" />
    <?php echo JHtml::_('form.token'); ?>
</form>
Sự điều chỉnh bao gồm:
  • Trong bảng bổ sung cột đầu tiên chứa các checkbox, cho phép lựa chọn những phần tử muốn xóa. Ở phần header có checkbox để cho phép lựa chọn hoặc bỏ chọn tất cả các checkbox khác. Các checkbox ở phần body phải tạo ra bằng lời gọi JHtmlGrid::id($i, $item->id). Cách này giúp tạo ra các trường checkbox với tên dạng mảng là 'cid' và thuộc tính 'value' của mỗi checkbox là primary key của phần tử tương ứng.
  • Toàn bộ bảng được đưa vào trong form để các checkbox đều là thành phần của form.
  • Thêm  vào form trường ẩn có name="boxchecked" để sử dụng trong mã scirpt ứng với nút "Xóa", ý nghĩa của trường này là số lượng checkbox đã được tích chọn.
  • Thêm vào form trường chứa token vì phương thức delete() có gọi đến checkToken() để chống tấn công CSRF
Sau khi điều chỉnh view và layout mặc định như trên thì ta sẽ có được giao diện như trên hình ở đầu bài viết này.

Chỉnh sửa model 🔝

Như đã phân tích ở trên, phương thức $controller->delete() sẽ gọi đến phương thức $model->delete(). Và vì tên model trùng với tên controller nên ta cần bổ sung phương thức delete() cho model students như sau:
public function delete($primaryKeys)
{
    $table = $this->getTable('student');
    foreach ($primaryKeys as $pk)
    {
        $table->delete($pk);
    }

    JFactory::getApplication()->enqueueMessage(
        'Có '.count($primaryKeys).' sinh viên đã bị xóa!',
        'message'
    );
}
Tham số truyền vào cho phương thức delete() của model là mảng chứa primary key của các phần tử cần xóa. Để thực hiện việc xóa thì ta gọi đến phương thức delete() của lớp JTable. Ở phần trước ta đã xây dựng lớp TableStudent trong file (\com_students\tables\student.php) để thao tác trên bảng '#__students' nên ở đây ta sẽ gọi phương thức getTable() với tham số truyền vào là 'student' để tạo đối tượng của lớp bảng. Sau khi xóa xong, ta đưa một thông điệp vào session với nội dung cho biết số lượng sinh viên đã bị xóa. Thông điệp này sẽ được hiển thị ở view kế tiếp (tham khảo https://docs.joomla.org/Display_error_messages_and_notices)

Đến đây, ta đã hoàn tất việc xây dựng chức năng xóa phần tử.

Comments

Popular posts from this blog

Cài đặt Xdebug cho VSCode trên Windows

Lập trình tạo một MVC Component đơn giản cho Joomla. Phần 4. Chỉnh sửa một phần tử

Lập trình tạo một MVC Component đơn giản cho Joomla. Phần 1. Khởi tạo component