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ử

Trong bài viết này

  1. Ý tưởng chung
  2. Logic hoạt động của chức năng chỉnh sửa
  3. Bổ sung chức năng chỉnh sửa
  4. Chỉnh sửa tiêu đề của form nhập thông tin
  5. Chỉnh sửa mã của layout

Ý tưởng chung 🔝

Chỉnh sửa thông tin về một phần tử hiện có là một thao tác không thể thiếu trong công cụ quản lý. Phần này sẽ trình bày cách thức xây dựng chức năng cơ bản này.
Khi người dùng duyệt danh sách các phần tử (sinh viên), ta có thể cung cấp chức năng "chỉnh sửa" thông qua đường link đặt dưới tên của mỗi phần tử hoặc (và) nút "Edit" đặt bên cạnh mỗi phần tử.

Ở phần trước, để xây dựng chức năng "Thêm sinh viên", ta đã sử dụng lớp JControllerForm. Ở đó, ta cũng đã chỉ ra rằng trong số các phương thức mà lớp này hỗ trợ có cả phương thức edit(). Sẽ không khó để đoán được rằng đây chính là phương thức mà có thể giúp ta xây dựng chức năng chỉnh sửa thông tin một phần tử.

Logic hoạt động của chức năng chỉnh sửa 🔝

Khi chúng ta thừa kế lớp JControllerForm để xây dựng lớp StudentsControllerStudent (tức là controller 'student') để triển khai chức năng chỉnh sửa phần tử, logic hoạt động sẽ như sau:

  1. Người dùng truy vấn phương thức edit() của controller student. Trong truy vấn phải chỉ ra giá trị primary key của phần tử muốn chỉnh sửa. Trong trường hợp của chúng ta, primary key trong bảng '#__students' là cột 'id' nên trong truy vấn phải có tham số id.
  2. Server (xem mã của file \libraries\src\MVC\Controller\FormController.php từ dòng 416 đến dòng 424) sẽ lưu lại giá trị của primary key vào session rồi chuyển hướng client tới địa chỉ 'index.php?option=com_student&view=student&layout=edit'. Đây chính là địa chỉ (view và layout) chuyển hướng được sử dụng khi ta cài đặt chức năng add() ở phần trước. 
  3. Client thực hiện chuyển hướng; người dùng sẽ nhận được form để nhập (chỉnh sửa) thông tin về một phần tử (sinh viên) với thông tin cũ đã được điền sẵn. Thông tin cũ được điền vào form là nhờ mấy dòng lệnh nạp dữ liệu vào from trong mã của phương thức getForm() mà ta đã xây dựng trong model 'student'. 
  4. Người dùng nhập (chỉnh sửa) thông tin rồi thực hiện submit bằng cách bấm nút "Lưu" (save). Dữ liệu được gửi tới phương thức save() của controller student.
  5. Server kiểm tra tính hợp lệ của dữ liệu. Nếu hợp lệ thì lưu vào CSDL rồi chuyển hướng client tới view mặc định. Còn nếu không hợp lệ thì thực hiện bước 2.
Nếu đã đọc phần trước (Thêm một phần tử) thì thấy rằng logic của việc thêm và chỉnh sửa là giống nhau. Đều gọi đến phương thức edit() rồi phương thức save() của controller. Câu hỏi đặt ra: làm sao controller phân biệt được đâu là yêu cầu chỉnh sửa và đâu là yêu cầu thêm mới? Điểm mấu chốt của câu trả lời cho câu hỏi này nằm trong phương thức save(). Ở phần trước, ta đã xem xét mã nguồn của phương thức này nhưng (tạm thời) bỏ qua một số dòng quan trọng. Hãy xem lại một lần nữa dưới một góc nhìn khác.
public function save($key = null, $urlVar = null)
{
    // Check for request forgeries.
    $this->checkToken();

    $app   = \JFactory::getApplication();
    $model = $this->getModel();
    $table = $model->getTable();
    $data  = $this->input->post->get('jform', array(), 'array');
    $checkin = property_exists($table, $table->getColumnAlias('checked_out'));
    $context = "$this->option.edit.$this->context";
    $task = $this->getTask();

    // Determine the name of the primary key for the data.
    if (empty($key))
    {
        $key = $table->getKeyName();
    }

    // To avoid data collisions the urlVar may be different from the primary key.
    if (empty($urlVar))
    {
        $urlVar = $key;
    }

    $recordId = $this->input->getInt($urlVar);

    // Populate the row id from the session.
    $data[$key] = $recordId;

    //...
}
Trước hết, cần làm rõ ý nghĩa của 2 tham số của phương thức save().
  • $key (string) là tên trường primary key trong $table; trong trường hợp của ta thì primary key là 'id'.
  • $urlVar (string) là tham số chứa Primary Key; tham số này (như tên gọi của biến) được truyền lên server qua URL. Nói chung, $urlVar nên trùng với $key, nhưng
Dòng lệnh số 26 và 29 sẽ xác định giá trị của primary key từ request rồi đưa vào mảng $data. Về sau, khi gọi phương thức $model->save(), nếu primary key là 0 (hoặc null) thì $data sẽ được lưu thành một record mới trong bảng (đây chính là trường hợp ở phần trước, khi trong request của ta không có tham số nào xác định primary key); còn ngược lại thì bản ghi tương ứng sẽ được cập nhật.
Như vậy, điều quan trọng ở phần này là khi truy vấn phương thức save(), ta phải đưa vào request tham số chứa primary key của phần tử cần chỉnh sửa; cụ thể là đưa tham số 'id' vào URL .

Bổ sung chức năng chỉnh sửa 🔝

Qua phân tích trên đây có thể thấy rằng để triển khai chức năng chỉnh sửa một phần tử thì ta chỉ cần cung cấp khả năng để người dùng thực hiện truy vấn mà thôi, các phần xử lý khác đã có sẵn. Ta sẽ thực hiện như trên hình ở phần "Ý tưởng chung" trên đây: khi hiển thị view mặc định (xem danh sách các phần tử), ta sẽ thêm đường link vào dưới tên mỗi sinh viên, và bổ sung cột "Hành động" với nút "Edit" trên mỗi dòng.
Để đạt được điều này, ta chỉ cần chỉnh sửa duy nhất tập tin layout 'default.php' của view students. Nội dung mới sẽ như sau:
<?php
defined('_JEXEC') or die;
?>
<table class="table table-striped">
    <thead>
        <tr>
            <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 '<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>

<form action="<?php echo JRoute::_('index.php?option=com_students'); ?>"
    name="adminForm" id="adminForm" method="POST">
    <input type="hidden" name="task" value="" />
</form>
Sự thay đổi (xét từ trên xuống) so với mã trước đó bao gồm:
  • Dòng 10: Trong phần tiêu đề của bảng, ta bổ sung cột "Hành động"
  • Dòng 17: Với mỗi phần tử (trong vòng lặp foreach), ta sử dụng biến $urlEdit để chứa đường link truy vấn chức năng chỉnh sửa (edit) phần tử đó. Tham số id được xác định qua trường $item->id. Lưu ý rằng layout này được dùng chung cho cả add()edit(); nếu nó được gọi bởi add() thì $item->id sẽ rỗng, còn nếu nó được gọi bởi edit() thì $item->id chính là giá trị primary key của phần tử mà ta đang chỉnh sửa.
  • Dòng 19: Với mỗi phần tử, họ và tên được hiển thị bên trong cặp thẻ <a> với href $urlEdit đã xác định như trên.
  • Dòng 22: Với mỗi phần tử, ta hiển thị thêm nút emoji 'icon-edit' được đặt bên trong cặp thẻ <a> với href $urlEdittitle="Chỉnh sửa".
Tất cả chỉ có thế. Đến đây, khi truy cập vào component, ta sẽ nhận được giao diện như dự kiến. Và khi ta nhấn chuột vào tên của sinh viên, hoặc vào nút emoji 'icon-edit' tương ứng thì chức năng chinh sửa sẽ được kích hoạt.

Chỉnh sửa tiêu đề của form nhập thông tin 🔝

Có thể thấy rằng khi ta sử dụng chức năng chỉnh sửa thông tin, tiêu đề của form nhập thông tin (view=sutdent, layout=edit) vẫn giống hệt như khi ta sử dụng chức năng thêm sinh viên (đều là "Nhập thông tin sinh viên").

Có thể ta muốn thay đổi điều này. Ví dụ, nếu là thêm mới thì ta đặt tiêu đề là "Nhập thông tin về sinh viên mới", còn nếu là chỉnh sửa thì ta đặt tiêu đề là "Chỉnh sửa thông tin sinh viên". Ngoài ra, ta cũng có thể muốn thay đổi nút "Lưu": nếu là thêm mới thì đặt là "Lưu mới", nhưng nếu chỉnh sửa thì đặt lại là "Cập nhật". Để đạt điều này, ta chỉnh sửa view 'student' như sau:
<?php
defined("_JEXEC") or die;
class StudentsViewStudent extends JViewLegacy
{
    protected $form;
    protected $item;
    public function display($tpl = null)
    {
        $this->form = $this->get('Form');
        $this->item = $this->get('Item');
        $this->addToolbar();
        return parent::display($tpl);
    }

    public function addToolbar()
    {
        JFactory::getApplication()->input->set('hidemainmenu', true);
        if(empty($this->item->id)) {
            JToolbarHelper::title("Nhập thông tin sinh viên mới");
            JToolbarHelper::save("student.save", "Lưu mới");
        }
        else
        {
            JToolbarHelper::title("Chỉnh sửa thông tin sinh viên");
            JToolbarHelper::save("student.save", "Cập nhật");
        }
        JToolbarHelper::cancel("student.cancel","Hủy");
    }
}

Ở đây, ta khai báo thêm một trường $item (dòng 6) và gọi đến getItem() của model (dòng 10) để xác định giá trị cho nó. Giá trị này một mặt được sử dụng trong phương thức addToolbar() để quyết định caption của nút save; mặt khác được sử dụng trong layout edit để truyền tham số 'id' tới phương thức save() của controller.
Phương thức addToolbar() được viết  lại để kiểm tra: nếu $item->id có giá trị null thì chứng tỏ người dùng đang thực hiện add(), còn nếu không phải null thì chứng tỏ người dùng đang thực hiện edit().
Sau khi chỉnh sửa, layout edit của view student sẽ hiển thị khác nhau cho 2 trường hợp add() edit(). Nếu là edit() thì sẽ như sau:

Chỉnh sửa mã của layout🔝

Ta cần chỉnh sửa lại mã của layout edit một chút để nó đáp ứng được cả 2 trường hợp: thêm phần tử và chỉnh sửa phần tử.
<?php
defined("_JEXEC") or die;
?>

<form id="adminForm" method="POST" action="<?php echo JRoute::_('index.php?option=com_students&id='.$this->item->id); ?>">
    <?php echo $this->form->renderField('name'); ?>
    <?php echo $this->form->renderField('year'); ?>
    <?php echo $this->form->renderField('avg'); ?>
    <?php echo JHtml::_('form.token'); ?>
    <input type="hidden" name="task" />
</form>
Ở dòng thứ 5, ta thêm tham số id vào URL trong thuộc tính action của form; giá trị của nó được xác định bởi thuộc tính $item->id. Nếu id bằng 0 hoặc null thì việc submit sẽ dẫn tới việc thêm phần tử mới; còn ngược lại thì chỉnh sửa một phần tử đã có, được xác định bởi id.
Đến đây, ta đã hoàn tất việc xây dựng chức năng chỉnh sửa thông tin một 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 1. Khởi tạo component