Tentu, saya akan bantu membuatkan contoh CRUD (Create, Read, Update, Delete) dengan AJAX, Upload File, dan DataTables menggunakan CodeIgniter 3.
Ini akan menjadi panduan langkah demi langkah yang cukup panjang, jadi mari kita mulai.
Struktur Proyek yang Akan Kita Buat:
1.Database: crud_ajax_ci3 (dengan tabel items)
2.CodeIgniter 3: Instalasi dasar
3.Aset: jQuery, DataTables, Bootstrap (untuk tampilan)
Langkah 1: Persiapan Database
Buat database baru dengan nama crud_ajax_ci3. Kemudian, buat tabel items dengan struktur sebagai berikut:
1 2 3 4 5 6 7 8 9 | CREATE TABLE `items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `description` text, `image` varchar(255) DEFAULT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; |
Langkah 2: Instalasi CodeIgniter 3
- Unduh CodeIgniter 3 dari situs resminya: https://codeigniter.com/download
- Ekstrak file ke direktori server web Anda (misalnya
htdocs
jika Anda menggunakan XAMPP/WAMP, atauwww
jika Anda menggunakan Laragon). Beri nama foldernyacrud_ajax_ci3
.
Langkah 3: Konfigurasi CodeIgniter
Buka folder application/config/
dan edit file-file berikut:
1. config.php
Ubah base_url
sesuai dengan URL proyek Anda:
1 | $config['base_url'] = 'http://localhost/crud_ajax_ci3/'; // Sesuaikan dengan path Anda |
2. database.php
Konfigurasi koneksi database Anda:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | $db['default'] = array( 'dsn' => '', 'hostname' => 'localhost', 'username' => 'root', // Ganti dengan username database Anda 'password' => '', // Ganti dengan password database Anda 'database' => 'crud_ajax_ci3', 'dbdriver' => 'mysqli', 'dbprefix' => '', 'pconnect' => FALSE, 'db_debug' => (ENVIRONMENT !== 'production'), 'cache_on' => FALSE, 'cachedir' => '', 'char_set' => 'utf8', 'dbcollat' => 'utf8_general_ci', 'swap_pre' => '', 'encrypt' => FALSE, 'compress' => FALSE, 'stricton' => FALSE, 'failover' => array(), 'save_queries' => TRUE ); |
3. autoload.php
Muat library yang akan kita gunakan:
1 2 3 4 | // application/config/autoload.php $autoload['libraries'] = array('database', 'form_validation', 'session', 'upload'); // Tambahkan 'upload' $autoload['helper'] = array('url', 'form', 'file'); // Tambahkan 'file' |
Langkah 4: Membuat Model (Item_model.php
)
Buat file baru di application/models/Item_model.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Item_model extends CI_Model { var $table = 'items'; var $column_order = array(null, 'name', 'description', 'image', null); // Sesuaikan dengan kolom yang ditampilkan di DataTables var $column_search = array('name', 'description'); // Kolom yang bisa dicari var $order = array('id' => 'desc'); // Urutan default public function __construct() { parent::__construct(); $this->load->database(); } private function _get_datatables_query() { $this->db->from($this->table); $i = 0; foreach ($this->column_search as $item) // loop column { if($_POST['search']['value']) // if datatable send POST for search { if($i===0) // first loop { $this->db->group_start(); // open bracket. query Where with OR clause better with bracket. because multiple condition $this->db->like($item, $_POST['search']['value']); } else { $this->db->or_like($item, $_POST['search']['value']); } if(count($this->column_search) - 1 == $i) //last loop $this->db->group_end(); //close bracket } $i++; } if(isset($_POST['order'])) // here order processing { $this->db->order_by($this->column_order[$_POST['order']['0']['column']], $_POST['order']['0']['dir']); } else if(isset($this->order)) { $order = $this->order; $this->db->order_by(key($order), $order[key($order)]); } } function get_datatables() { $this->_get_datatables_query(); if($_POST['length'] != -1) $this->db->limit($_POST['length'], $_POST['start']); $query = $this->db->get(); return $query->result(); } function count_filtered() { $this->_get_datatables_query(); $query = $this->db->get(); return $query->num_rows(); } public function count_all() { $this->db->from($this->table); return $this->db->count_all_results(); } public function get_by_id($id) { $this->db->from($this->table); $this->db->where('id',$id); $query = $this->db->get(); return $query->row(); } public function save($data) { $this->db->insert($this->table, $data); return $this->db->insert_id(); } public function update($where, $data) { $this->db->update($this->table, $data, $where); return $this->db->affected_rows(); } public function delete_by_id($id) { $this->db->where('id', $id); $this->db->delete($this->table); } } |
Langkah 5: Membuat Controller (Items.php
)
Buat file baru di application/controllers/Items.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | <?php defined('BASEPATH') OR exit('No direct script access allowed'); class Items extends CI_Controller { public function __construct() { parent::__construct(); $this->load->model('item_model'); $this->load->helper(array('form', 'url')); } public function index() { $this->load->view('item_view'); } public function ajax_list() { $list = $this->item_model->get_datatables(); $data = array(); $no = $_POST['start']; foreach ($list as $item) { $no++; $row = array(); $row[] = $no; $row[] = $item->name; $row[] = $item->description; if($item->image) { $row[] = '<a href="'.base_url('uploads/'.$item->image).'" target="_blank"><img src="'.base_url('uploads/'.$item->image).'" class="img-responsive" style="max-width: 100px; max-height: 100px;" /></a>'; } else { $row[] = '(No image)'; } //add html for action $row[] = '<a class="btn btn-sm btn-primary" href="javascript:void(0)" title="Edit" onclick="edit_item('."'".$item->id."'".')"><i class="glyphicon glyphicon-pencil"></i> Edit</a> <a class="btn btn-sm btn-danger" href="javascript:void(0)" title="Hapus" onclick="delete_item('."'".$item->id."'".')"><i class="glyphicon glyphicon-trash"></i> Delete</a>'; $data[] = $row; } $output = array( "draw" => $_POST['draw'], "recordsTotal" => $this->item_model->count_all(), "recordsFiltered" => $this->item_model->count_filtered(), "data" => $data, ); echo json_encode($output); } public function ajax_add() { $this->_validate(); $data = array( 'name' => $this->input->post('name'), 'description' => $this->input->post('description'), ); if(!empty($_FILES['image']['name'])) { $upload = $this->_do_upload(); $data['image'] = $upload; } $insert = $this->item_model->save($data); echo json_encode(array("status" => TRUE)); } public function ajax_edit($id) { $data = $this->item_model->get_by_id($id); echo json_encode($data); } public function ajax_update() { $this->_validate(); $data = array( 'name' => $this->input->post('name'), 'description' => $this->input->post('description'), ); if($this->input->post('remove_image')) // if remove photo checked { if(file_exists('uploads/'.$this->input->post('remove_image')) && $this->input->post('remove_image')) unlink('uploads/'.$this->input->post('remove_image')); $data['image'] = null; } if(!empty($_FILES['image']['name'])) { $upload = $this->_do_upload(); //delete file if it exists $item = $this->item_model->get_by_id($this->input->post('id')); if(file_exists('uploads/'.$item->image) && $item->image) unlink('uploads/'.$item->image); $data['image'] = $upload; } $this->item_model->update(array('id' => $this->input->post('id')), $data); echo json_encode(array("status" => TRUE)); } public function ajax_delete($id) { //delete file $item = $this->item_model->get_by_id($id); if(file_exists('uploads/'.$item->image) && $item->image) unlink('uploads/'.$item->image); $this->item_model->delete_by_id($id); echo json_encode(array("status" => TRUE)); } private function _do_upload() { $config['upload_path'] = 'uploads/'; $config['allowed_types'] = 'gif|jpg|png|jpeg'; $config['max_size'] = 2048; // set max size allowed in Kilobyte $config['max_width'] = 1024; // set max width image allowed $config['max_height'] = 768; // set max height image allowed $config['file_name'] = round(microtime(true) * 1000); //just milisecond timestamp fot unique name $this->load->library('upload', $config); $this->upload->initialize($config); // Tambahkan baris ini if(!$this->upload->do_upload('image')) //upload and validate { $data['inputerror'][] = 'image'; $data['error_string'][] = 'Upload error: '.$this->upload->display_errors('',''); //show ajax error $data['status'] = FALSE; echo json_encode($data); exit(); } return $this->upload->data('file_name'); } private function _validate() { $data = array(); $data['error_string'] = array(); $data['inputerror'] = array(); $data['status'] = TRUE; if($this->input->post('name') == '') { $data['inputerror'][] = 'name'; $data['error_string'][] = 'Nama wajib diisi'; $data['status'] = FALSE; } if($this->input->post('description') == '') { $data['inputerror'][] = 'description'; $data['error_string'][] = 'Deskripsi wajib diisi'; $data['status'] = FALSE; } if($data['status'] === FALSE) { echo json_encode($data); exit(); } } } |
Langkah 6: Membuat View (item_view.php
)
Buat file baru di application/views/item_view.php
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>CRUD AJAX CodeIgniter 3 dengan Upload & DataTables</title> <link href="<?php echo base_url('assets/bootstrap/css/bootstrap.min.css')?>" rel="stylesheet"> <link href="<?php echo base_url('assets/datatables/css/dataTables.bootstrap.min.css')?>" rel="stylesheet"> <link href="<?php echo base_url('assets/bootstrap-datepicker/css/bootstrap-datepicker3.min.css')?>" rel="stylesheet"> </head> <body> <div class="container"> <h1 style="font-size:20pt">CRUD AJAX CodeIgniter 3 dengan Upload & DataTables</h1> <button class="btn btn-success" onclick="add_item()"><i class="glyphicon glyphicon-plus"></i> Tambah Item</button> <button class="btn btn-default" onclick="reload_table()"><i class="glyphicon glyphicon-refresh"></i> Refresh</button> <br /> <br /> <table id="table" class="table table-striped table-bordered" cellspacing="0" width="100%"> <thead> <tr> <th>No</th> <th>Nama</th> <th>Deskripsi</th> <th>Gambar</th> <th style="width:125px;">Aksi</th> </tr> </thead> <tbody> </tbody> <tfoot> <tr> <th>No</th> <th>Nama</th> <th>Deskripsi</th> <th>Gambar</th> <th>Aksi</th> </tr> </tfoot> </table> </div> <script src="<?php echo base_url('assets/jquery/jquery-2.2.3.min.js')?>"></script> <script src="<?php echo base_url('assets/bootstrap/js/bootstrap.min.js')?>"></script> <script src="<?php echo base_url('assets/datatables/js/jquery.dataTables.min.js')?>"></script> <script src="<?php echo base_url('assets/datatables/js/dataTables.bootstrap.min.js')?>"></script> <script src="<?php echo base_url('assets/bootstrap-datepicker/js/bootstrap-datepicker.min.js')?>"></script> <script type="text/javascript"> var save_method; // for save method string var table; var base_url = '<?php echo base_url();?>'; $(document).ready(function() { //datatables table = $('#table').DataTable({ "processing": true, //Feature control the processing indicator. "serverSide": true, //Feature control DataTables' server-side processing mode. "order": [], //Initial no order. // Load data for the table's content from an Ajax source "ajax": { "url": "<?php echo site_url('items/ajax_list')?>", "type": "POST" }, //Set column definition initialisation properties. "columnDefs": [ { "targets": [ 0 ], //first column / numbering column "orderable": false, //set not orderable }, { "targets": [ -1 ], //last column "orderable": false, //set not orderable }, { "targets": [ -2 ], //image column "orderable": false, //set not orderable }, ], }); //set input/textarea/select event when change value, remove error class and remove text help block $("input").change(function(){ $(this).parent().parent().removeClass('has-error'); $(this).next().empty(); }); $("textarea").change(function(){ $(this).parent().parent().removeClass('has-error'); $(this).next().empty(); }); $("select").change(function(){ $(this).parent().parent().removeClass('has-error'); $(this).next().empty(); }); }); function add_item() { save_method = 'add'; $('#form')[0].reset(); // reset form on modals $('.form-group').removeClass('has-error'); // clear error class $('.help-block').empty(); // clear error string $('#modal_form').modal('show'); // show bootstrap modal $('.modal-title').text('Tambah Item'); // Set Title to Bootstrap modal title $('#image-preview').hide(); // hide image preview modal $('#label-remove').hide(); // hide remove image checkbox } function edit_item(id) { save_method = 'update'; $('#form')[0].reset(); // reset form on modals $('.form-group').removeClass('has-error'); // clear error class $('.help-block').empty(); // clear error string //Ajax Load data from ajax $.ajax({ url : "<?php echo site_url('items/ajax_edit/')?>/" + id, type: "GET", dataType: "JSON", success: function(data) { $('[name="id"]').val(data.id); $('[name="name"]').val(data.name); $('[name="description"]').val(data.description); $('#modal_form').modal('show'); // show bootstrap modal when complete loaded $('.modal-title').text('Edit Item'); // Set title to Bootstrap modal title $('#image-preview').show(); // show image preview modal if(data.image) { $('#label-remove').show(); // show remove image checkbox $('#image-preview div').html('<img src="'+base_url+'uploads/'+data.image+'" class="img-responsive" style="max-width: 100px; max-height: 100px;">'); // show image $('#image-preview div').append('<input type="checkbox" name="remove_image" value="'+data.image+'"/> Hapus Gambar saat menyimpan'); // remove image } else { $('#label-remove').hide(); $('#image-preview div').empty(); } }, error: function (jqXHR, textStatus, errorThrown) { alert('Error get data from ajax'); } }); } function reload_table() { table.ajax.reload(null,false); //reload datatable ajax } function save() { $('#btnSave').text('saving...'); //change button text $('#btnSave').attr('disabled',true); //set button disable var url; if(save_method == 'add') { url = "<?php echo site_url('items/ajax_add')?>"; } else { url = "<?php echo site_url('items/ajax_update')?>"; } // ajax adding data to database var formData = new FormData($('#form')[0]); $.ajax({ url : url, type: "POST", data: formData, contentType: false, processData: false, dataType: "JSON", success: function(data) { if(data.status) //if success close modal and reload ajax table { $('#modal_form').modal('hide'); reload_table(); } else //if error { for (var i = 0; i < data.inputerror.length; i++) { $('[name="'+data.inputerror[i]+'"]').parent().parent().addClass('has-error'); //select parent twice to select div form-group class and add has-error class $('[name="'+data.inputerror[i]+'"]').next().text(data.error_string[i]); //select span help-block class set text error string } } $('#btnSave').text('save'); //change button text $('#btnSave').attr('disabled',false); //set button enable }, error: function (jqXHR, textStatus, errorThrown) { alert('Error adding / update data'); $('#btnSave').text('save'); //change button text $('#btnSave').attr('disabled',false); //set button enable } }); } function delete_item(id) { if(confirm('Are you sure delete this data?')) { // ajax delete data to database $.ajax({ url : "<?php echo site_url('items/ajax_delete')?>/"+id, type: "POST", dataType: "JSON", success: function(data) { //if success reload ajax table $('#modal_form').modal('hide'); reload_table(); }, error: function (jqXHR, textStatus, errorThrown) { alert('Error deleting data'); } }); } } </script> <div class="modal fade" id="modal_form" role="dialog"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h3 class="modal-title">Form Item</h3> </div> <div class="modal-body form"> <form action="#" id="form" class="form-horizontal"> <input type="hidden" value="" name="id"/> <div class="form-body"> <div class="form-group"> <label class="control-label col-md-3">Nama</label> <div class="col-md-9"> <input name="name" placeholder="Nama Item" class="form-control" type="text"> <span class="help-block"></span> </div> </div> <div class="form-group"> <label class="control-label col-md-3">Deskripsi</label> <div class="col-md-9"> <textarea name="description" placeholder="Deskripsi Item" class="form-control"></textarea> <span class="help-block"></span> </div> </div> <div class="form-group" id="image-preview"> <label class="control-label col-md-3">Gambar</label> <div class="col-md-9"> (Tidak ada gambar) <span class="help-block"></span> </div> </div> <div class="form-group"> <label class="control-label col-md-3" id="label-remove">Unggah Gambar</label> <div class="col-md-9"> <input name="image" type="file"> <span class="help-block"></span> </div> </div> </div> </form> </div> <div class="modal-footer"> <button type="button" id="btnSave" onclick="save()" class="btn btn-primary">Simpan</button> <button type="button" class="btn btn-danger" data-dismiss="modal">Batal</button> </div> </div></div></div></body> </html> |
Langkah 7: Buat Folder uploads
dan assets
- Buat folder baru di root proyek Anda (sejajar dengan
application
dansystem
) dengan namauploads
. Folder ini akan digunakan untuk menyimpan gambar yang diunggah. - Buat folder
assets
di root proyek Anda. Di dalamnya, buat subfolderbootstrap
,datatables
,jquery
, danbootstrap-datepicker
.assets/bootstrap/css/
: Letakkanbootstrap.min.css
di sini.assets/bootstrap/js/
: Letakkanbootstrap.min.js
di sini.assets/datatables/css/
: LetakkandataTables.bootstrap.min.css
di sini.assets/datatables/js/
: Letakkanjquery.dataTables.min.js
dandataTables.bootstrap.min.js
di sini.assets/jquery/
: Letakkanjquery-2.2.3.min.js
(atau versi jQuery lain) di sini.assets/bootstrap-datepicker/css/
: Letakkanbootstrap-datepicker3.min.css
di sini.assets/bootstrap-datepicker/js/
: Letakkanbootstrap-datepicker.min.js
di sini.
Anda bisa mendapatkan file-file ini dari sumber resmi masing-masing:
- Bootstrap: https://getbootstrap.com/docs/3.4/getting-started/ (Pilih v3.4.1 atau yang sesuai)
- DataTables: https://datatables.net/download/ (Pilih Bootstrap 3 Integration)
- jQuery: https://jquery.com/download/
- Bootstrap Datepicker: https://github.com/eternicode/bootstrap-datepicker
Langkah 8: Uji Coba Aplikasi
Buka browser Anda dan akses: http://localhost/crud_ajax_ci3/items
Anda seharusnya melihat halaman dengan tabel kosong dan tombol “Tambah Item”.
Cara Menggunakan:
- Tambah Item: Klik tombol “Tambah Item”, isi formulir, dan Anda bisa mengunggah gambar.
- Edit Item: Klik tombol “Edit” di baris tabel. Anda bisa mengubah data dan mengganti atau menghapus gambar yang sudah ada.
- Hapus Item: Klik tombol “Delete” di baris tabel. Akan ada konfirmasi sebelum menghapus data dan gambar terkait.
- Pencarian & Pagination: DataTables secara otomatis menyediakan fungsionalitas pencarian dan pagination.
Penjelasan Singkat Kode:
Item_model.php
: Mengelola interaksi dengan database untuk tabelitems
. Ini mencakup fungsi untuk mendapatkan data DataTables (dengan filtering dan ordering), serta fungsi CRUD dasar (save
,update
,get_by_id
,delete_by_id
).Items.php
(Controller):index()
: Memuat view utama.ajax_list()
: Endpoint AJAX untuk DataTables. Mengambil data dari model dan memformatnya menjadi JSON yang dapat dibaca oleh DataTables.ajax_add()
: Menangani penambahan item baru, termasuk proses upload gambar.ajax_edit()
: Mengambil data item berdasarkan ID untuk mengisi form edit.ajax_update()
: Menangani pembaruan item, termasuk logic untuk mengganti atau menghapus gambar.ajax_delete()
: Menghapus item dan gambar terkait dari server._do_upload()
: Fungsi private untuk menangani proses upload gambar menggunakan library Upload CodeIgniter._validate()
: Fungsi private untuk validasi input form.
item_view.php
(View):- Tampilan utama yang berisi tabel DataTables dan tombol-tombol aksi.
- Modal Bootstrap digunakan untuk form tambah/edit item.
- Kode JavaScript menggunakan jQuery dan DataTables API untuk:
- Menginisialisasi DataTables.
- Mengirim permintaan AJAX untuk operasi CRUD.
- Menampilkan/menyembunyikan modal.
- Menangani tampilan preview gambar dan opsi hapus gambar.
Penting:
- Pastikan Anda mengunduh dan menempatkan semua file aset (CSS/JS) di lokasi yang benar sesuai dengan struktur
assets
yang saya jelaskan. - Sesuaikan
base_url
diconfig.php
dan kredensial database didatabase.php
. - Hak akses folder
uploads
harus diatur agar server web bisa menulis file ke dalamnya. Di Linux/macOS, Anda mungkin perlu menjalankanchmod -R 777 uploads
. Di Windows, pastikan user IIS/Apache memiliki hak tulis. - Ini adalah contoh dasar. Untuk proyek produksi, Anda mungkin perlu menambahkan lebih banyak validasi, otentikasi, sanitasi input, dan penanganan error yang lebih robust.
Semoga panduan ini membantu Anda dalam membuat CRUD AJAX dengan Upload dan DataTables di CodeIgniter 3!