├── LICENSE
├── README.md
└── application
└── libraries
└── Datatables.php
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Cahya DSN
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ci-datatables
2 | Library for server side datatables implementation on codeigniter. CI Datatables is a wrapper class/library based on the native Datatables server-side implementation by Allan Jardine
3 | found at http://datatables.net/examples/data_sources/server_side.html for CodeIgniter
4 |
5 | [](LICENSE)
6 |
7 | ## Features
8 | 1. Easy to use. Generates json using only a few lines of code.
9 | 2. Support for table joins (left, right, outer, inner, left outer, right outer).
10 | 3. Able to define custom columns, and filters.
11 | 4. Editable custom variables with callback function support.
12 | 5. Supports generation of tables using non utf-8 charsets.
13 |
14 | ## Requirements
15 | jQuery 1.5+
16 | DataTables 1.10+
17 | CodeIgniter 3.*
18 |
19 | ## Installation
20 | To install the library, copy the libraries/datatables.php file into your application/libraries folder.
21 |
22 |
23 | ## Function Reference
24 |
25 | ### $this->setdb($database = 'default')
26 | Sets the name of the database configuration, if need another apart of default,
27 | The name of the database its optional.
28 |
29 | Note: you can use method chaining for more compact syntax.
30 | ### $this->datatables->select($columns);
31 | Sets which columns to fetch from the specified table.
32 |
33 | ```
34 | $this->datatables->select('id, name, age, gender');
35 | ```
36 | *This function also accepts an optional second parameter.
37 | ### $this->datatables->from($table);
38 | Sets the primary table in which data will be fetched from.
39 |
40 | ```
41 | $this->datatables->from('mytable');
42 | ```
43 | ### $this->datatables->join($table, $fk, [$type]);
44 | Sets join statement variables. If you want DataTables to include data from another table, you can define an a table along with its columns and foreign key relationship.
45 |
46 | ```
47 | $this->datatables->join('mystates', 'mycountries.state_id = mystates.id', 'left');
48 | ```
49 | *This function also accepts an optional third parameter for specifying the join type.
50 | ### $this->datatables->where();
51 | Sets filtering variable to facilitate custom filters.
52 | For additional filtering conditions, the WHERE clause works like CodeIgniter's. So you can do statements like:
53 |
54 | ```
55 | $this->datatables->where('id', '5');
56 | ```
57 |
58 | ```
59 | $this->datatables->where('age >', '25');
60 | ```
61 |
62 | ```
63 | $this->datatables->where('position != "admin"');
64 | ```
65 |
66 | ```
67 | $array = array('name' => 'J%', 'status' => 'Single');
68 | $this->datatables->where($array);
69 | ```
70 |
71 | ### $this->datatables->filter();
72 | Sets filtering variable to facilitate custom filters. Adds "(filtered from xxx total entries)" to datatables. Same usage with 'where()' method.
73 | ### $this->datatables->add_column($column, $content, [$match_replacement]);
74 | Sets additional column variables to facilitate custom columns. You can also make your own custom column definitions via the following syntax:
75 | *match_replacement is optional only needed if you have $1 to $n matches in your content pattern
76 |
77 | ```
78 | $this->datatables->add_column('edit', 'EDIT', 'id');
79 | ```
80 | ### $this->datatables->edit_column($column, $content, $match_replacement);
81 | Sets additional column variables for editing columns. You can also make your own custom column definitions via the following syntax:
82 | *match_replacement is needed in order to replace $1 to $n matches in your content pattern
83 |
84 | ```
85 | $this->datatables->edit_column('username', '$2', 'id, username');
86 | ```
87 | ### $this->datatables->unset_column($column);
88 | ### $this->datatables->generate([$output], [$charset]);
89 | Builds all the necessary query segments and performs the main query based on results set from chained statements.
90 | *optional parameter output (default is json) can be set to 'raw' to return a non-paginated array of the aaData and sColumns
91 | *optional parameter charset (default is UTF-8) is used when working with non utf-8 characters for json outputs (may decrease performance and be unstable)
92 |
93 | ```
94 | $this->datatables->generate();
95 | ```
96 |
97 | ```
98 | $this->datatables->generate('json', 'ISO-8859-1');
99 | ```
100 |
101 | ```
102 | $results = $this->datatables->generate('raw');
103 | $data['aaData'] = $results['aaData'];
104 | $data['sColumns'] = $results['sColumns'];
105 | $this->load->view('downloads/csv', $data);
106 | ```
107 |
108 | ## Method Chaining
109 |
110 | Method chaining allows you to simplify your syntax by connecting multiple functions. Consider this example:
111 | ```
112 | function list_all()
113 | {
114 | $this->datatables
115 | ->select('id, name, age, gender')
116 | ->from('tbl_profile')
117 | ->join('tbl_local', 'tbl_profile.local_id = tbl_local.id')
118 | ->select('country')
119 | ->join('tbl_states', 'tbl_profile.state_id = tbl_states.id')
120 | ->select('state')
121 | ->add_column('view', '
', 'id')
122 | ->add_column('edit', '
', 'id')
123 | ->add_column('delete', '
', 'id');
124 |
125 | $data['result'] = $this->datatables->generate();
126 | $this->load->view('ajax', $data);
127 | }
128 | ```
129 |
--------------------------------------------------------------------------------
/application/libraries/Datatables.php:
--------------------------------------------------------------------------------
1 |
13 | * @link http://github.com/cahyadsn/ci-datatables
14 | */
15 |
16 | class Datatables
17 | {
18 | /**
19 | * Global container variables for chained argument results
20 | *
21 | */
22 | protected $ci;
23 | protected $table;
24 | protected $select = array();
25 | protected $joins = array();
26 | protected $columns = array();
27 | protected $where = array();
28 | protected $filter = array();
29 | protected $add_columns = array();
30 | protected $edit_columns = array();
31 | protected $unset_columns = array();
32 |
33 | /**
34 | * Copies an instance of CI
35 | */
36 | public function __construct()
37 | {
38 | $this->ci =& get_instance();
39 | }
40 |
41 | /**
42 | * Set a specific database event the default, if vopid reconnect the default
43 | *
44 | * @param string $databasename
45 | * @return void
46 | */
47 | public function setdb($databasename = NULL)
48 | {
49 | if($databasename == NULL) $databasename = 'default';
50 | $this->ci->load->database($databasename);
51 | }
52 |
53 |
54 | /**
55 | * Generates the SELECT portion of the query
56 | *
57 | * @param string $columns
58 | * @param bool $backtick_protect
59 | * @return mixed
60 | */
61 | public function select($columns, $backtick_protect = TRUE)
62 | {
63 | foreach ($this->explode(',', $columns) as $val){
64 | $column = trim(preg_replace('/(.*)\s+as\s+(\w*)/i', '$2', $val));
65 | $this->columns[] = $column;
66 | $this->select[$column] = trim(preg_replace('/(.*)\s+as\s+(\w*)/i', '$1', $val));
67 | }
68 | $this->ci->db->select($columns, $backtick_protect);
69 | return $this;
70 | }
71 |
72 | /**
73 | * Generates the FROM portion of the query
74 | *
75 | * @param string $table
76 | * @return mixed
77 | */
78 | public function from($table)
79 | {
80 | $this->table = $table;
81 | $this->ci->db->from($table);
82 | return $this;
83 | }
84 |
85 | /**
86 | * Generates the JOIN portion of the query
87 | *
88 | * @param string $table
89 | * @param string $fk
90 | * @param string $type
91 | * @return mixed
92 | */
93 | public function join($table, $fk, $type = NULL)
94 | {
95 | $this->joins[] = array($table, $fk, $type);
96 | $this->ci->db->join($table, $fk, $type);
97 | return $this;
98 | }
99 |
100 | /**
101 | * Generates the WHERE portion of the query
102 | *
103 | * @param mixed $key_condition
104 | * @param string $val
105 | * @param bool $backtick_protect
106 | * @return mixed
107 | */
108 | public function where($key_condition, $val = NULL, $backtick_protect = TRUE)
109 | {
110 | $this->where[] = array($key_condition, $val, $backtick_protect);
111 | $this->ci->db->where($key_condition, $val, $backtick_protect);
112 | return $this;
113 | }
114 |
115 | /**
116 | * Generates the WHERE portion of the query
117 | *
118 | * @param mixed $key_condition
119 | * @param string $val
120 | * @param bool $backtick_protect
121 | * @return mixed
122 | */
123 | public function filter($key_condition, $val = NULL, $backtick_protect = TRUE)
124 | {
125 | $this->filter[] = array($key_condition, $val, $backtick_protect);
126 | return $this;
127 | }
128 |
129 | /**
130 | * Sets additional column variables for adding custom columns
131 | *
132 | * @param string $column
133 | * @param string $content
134 | * @param string $match_replacement
135 | * @return mixed
136 | */
137 | public function addColumn($column, $content, $match_replacement = NULL)
138 | {
139 | $this->add_columns[$column] = array('content' => $content, 'replacement' => $this->explode(',', $match_replacement));
140 | return $this;
141 | }
142 |
143 | /**
144 | * Sets additional column variables for editing columns
145 | *
146 | * @param string $column
147 | * @param string $content
148 | * @param string $match_replacement
149 | * @return mixed
150 | */
151 | public function editColumn($column, $content, $match_replacement)
152 | {
153 | $this->edit_columns[$column][] = array('content' => $content, 'replacement' => $this->explode(',', $match_replacement));
154 | return $this;
155 | }
156 |
157 | /**
158 | * Unset column
159 | *
160 | * @param string $column
161 | * @return mixed
162 | */
163 | public function unsetColumn($column)
164 | {
165 | $this->unset_columns[] = $column;
166 | return $this;
167 | }
168 |
169 | /**
170 | * Builds all the necessary query segments and performs the main query based on results set from chained statements
171 | *
172 | * @param string charset
173 | * @return string
174 | */
175 | public function generate($charset = 'UTF-8')
176 | {
177 | $this->getPaging();
178 | $this->getOrdering();
179 | $this->getFiltering();
180 | return $this->produceOutput($charset);
181 | }
182 |
183 | /**
184 | * Generates the LIMIT portion of the query
185 | *
186 | * @return mixed
187 | */
188 | protected function getPaging()
189 | {
190 | $iStart = $this->ci->input->post('iDisplayStart');
191 | $iLength = $this->ci->input->post('iDisplayLength');
192 | $this->ci->db->limit(($iLength != '' && $iLength != '-1')? $iLength : 10, ($iStart)? $iStart : 0);
193 | }
194 |
195 | /**
196 | * Generates the ORDER BY portion of the query
197 | *
198 | * @return mixed
199 | */
200 | protected function getOrdering()
201 | {
202 | if ($this->checkMDataprop()) {
203 | $mColArray = $this->getMDataprop();
204 | } elseif ($this->ci->input->post('sColumns')) {
205 | $mColArray = explode(',', $this->ci->input->post('sColumns'));
206 | } else {
207 | $mColArray = $this->columns;
208 | }
209 | $mColArray = array_values(array_diff($mColArray, $this->unset_columns));
210 | $columns = array_values(array_diff($this->columns, $this->unset_columns));
211 | for ($i = 0; $i < intval($this->ci->input->post('iSortingCols')); $i++) {
212 | if (
213 | isset($mColArray[intval($this->ci->input->post('iSortCol_' . $i))])
214 | && in_array($mColArray[intval($this->ci->input->post('iSortCol_' . $i))], $columns)
215 | && $this->ci->input->post('bSortable_'.intval($this->ci->input->post('iSortCol_' . $i))) == 'true'
216 | ) {
217 | $this->ci->db->order_by($mColArray[intval($this->ci->input->post('iSortCol_' . $i))], $this->ci->input->post('sSortDir_' . $i));
218 | }
219 | }
220 | }
221 |
222 | /**
223 | * Generates the LIKE portion of the query
224 | *
225 | * @return mixed
226 | */
227 | protected function getFiltering()
228 | {
229 | if ($this->checkMDataprop()) {
230 | $mColArray = $this->getMDataprop();
231 | } elseif ($this->ci->input->post('sColumns')) {
232 | $mColArray = explode(',', $this->ci->input->post('sColumns'));
233 | } else {
234 | $mColArray = $this->columns;
235 | }
236 | $sWhere = '';
237 | $sSearch = $this->ci->db->escape($this->ci->input->post('sSearch'));
238 | $mColArray = array_values(array_diff($mColArray, $this->unset_columns));
239 | $columns = array_values(array_diff($this->columns, $this->unset_columns));
240 | if ($sSearch != ''){
241 | for ($i = 0; $i < count($mColArray); $i++) {
242 | if ($this->ci->input->post('bSearchable_' . $i) == 'true' && in_array($mColArray[$i], $columns)) {
243 | $sWhere .= $this->select[$mColArray[$i]] . " LIKE '%" . $sSearch . "%' OR ";
244 | }
245 | }
246 | }
247 | $sWhere = substr_replace($sWhere, '', -3);
248 | if ($sWhere != '') {
249 | $this->ci->db->where('(' . $sWhere . ')');
250 | }
251 | for ($i = 0; $i < intval($this->ci->input->post('iColumns')); $i++) {
252 | if (isset($_POST['sSearch_' . $i]) && $this->ci->input->post('sSearch_' . $i) != '' && in_array($mColArray[$i], $columns)) {
253 | $miSearch = explode(',', $this->ci->input->post('sSearch_' . $i));
254 | foreach ($miSearch as $val) {
255 | if (preg_match("/(<=|>=|=|<|>)(\s*)(.+)/i", trim($val), $matches)) {
256 | $this->ci->db->where($this->select[$mColArray[$i]].' '.$matches[1], $matches[3]);
257 | } else {
258 | $this->ci->db->where($this->select[$mColArray[$i]].' LIKE', '%'.$val.'%');
259 | }
260 | }
261 | }
262 | }
263 | foreach ($this->filter as $val) {
264 | $this->ci->db->where($val[0], $val[1], $val[2]);
265 | }
266 | }
267 |
268 | /**
269 | * Compiles the select statement based on the other functions called and runs the query
270 | *
271 | * @return mixed
272 | */
273 | protected function getDisplayResult()
274 | {
275 | return $this->ci->db->get();
276 | }
277 |
278 | /**
279 | * Builds a JSON encoded string data
280 | *
281 | * @param string charset
282 | * @return string
283 | */
284 | protected function produceOutput($charset)
285 | {
286 | $aaData = array();
287 | $rResult = $this->getDisplayResult();
288 | $iTotal = $this->getTotalResults();
289 | $iFilteredTotal = $this->getTotalResults(true);
290 | foreach ($rResult->result_array() as $row_key => $row_val) {
291 | $aaData[$row_key] = ($this->checkMDataprop())? $row_val : array_values($row_val);
292 | foreach ($this->add_columns as $field => $val) {
293 | if ($this->checkMDataprop()) {
294 | $aaData[$row_key][$field] = $this->execReplace($val, $aaData[$row_key]);
295 | } else {
296 | $aaData[$row_key][] = $this->execReplace($val, $aaData[$row_key]);
297 | }
298 | }
299 | foreach ($this->edit_columns as $modkey => $modval) {
300 | foreach ($modval as $val) {
301 | $aaData[$row_key][($this->checkMDataprop())? $modkey : array_search($modkey, $this->columns)] = $this->execReplace($val, $aaData[$row_key]);
302 | }
303 | $aaData[$row_key] = array_diff_key($aaData[$row_key], ($this->checkMDataprop())? $this->unset_columns : array_intersect($this->columns, $this->unset_columns));
304 | }
305 | if (!$this->checkMDataprop()) {
306 | $aaData[$row_key] = array_values($aaData[$row_key]);
307 | }
308 | }
309 | $sColumns = array_diff($this->columns, $this->unset_columns);
310 | $sColumns = array_merge_recursive($sColumns, array_keys($this->add_columns));
311 | $sOutput = array
312 | (
313 | 'sEcho' => intval($this->ci->input->post('sEcho')),
314 | 'iTotalRecords' => $iTotal,
315 | 'iTotalDisplayRecords' => $iFilteredTotal,
316 | 'aaData' => $aaData,
317 | 'sColumns' => implode(',', $sColumns)
318 | );
319 | if (strtolower($charset) == 'utf-8') {
320 | return json_encode($sOutput);
321 | } else {
322 | return $this->jsonify($sOutput);
323 | }
324 | }
325 |
326 | /**
327 | * Get result count
328 | *
329 | * @return integer
330 | */
331 | protected function getTotalResults($filtering = FALSE)
332 | {
333 | if ($filtering) {
334 | $this->getFiltering();
335 | }
336 | foreach ($this->joins as $val) {
337 | $this->ci->db->join($val[0], $val[1], $val[2]);
338 | }
339 | foreach ($this->where as $val) {
340 | $this->ci->db->where($val[0], $val[1], $val[2]);
341 | }
342 | return $this->ci->db->count_all_results($this->table);
343 | }
344 |
345 | /**
346 | * Runs callback functions and makes replacements
347 | *
348 | * @param mixed $custom_val
349 | * @param mixed $row_data
350 | * @return string $custom_val['content']
351 | */
352 | protected function execReplace($custom_val, $row_data)
353 | {
354 | $replace_string = '';
355 | if (isset($custom_val['replacement']) && is_array($custom_val['replacement'])){
356 | foreach ($custom_val['replacement'] as $key => $val) {
357 | $sval = preg_replace("/(? $args_val) {
362 | $args_val = preg_replace("/(?columns))? ($row_data[($this->checkMDataprop())? $args_val : array_search($args_val, $this->columns)]) : $args_val;
364 | }
365 | $replace_string = call_user_func_array($func, $args);
366 | } elseif(in_array($sval, $this->columns)) {
367 | $replace_string = $row_data[($this->checkMDataprop())? $sval : array_search($sval, $this->columns)];
368 | } else {
369 | $replace_string = $sval;
370 | }
371 | $custom_val['content'] = str_ireplace('$' . ($key + 1), $replace_string, $custom_val['content']);
372 | }
373 | }
374 | return $custom_val['content'];
375 | }
376 |
377 | /**
378 | * Check mDataprop
379 | *
380 | * @return bool
381 | */
382 | protected function checkMDataprop()
383 | {
384 | if (!$this->ci->input->post('mDataProp_0')) return false;
385 | for ($i = 0; $i < intval($this->ci->input->post('iColumns')); $i++) {
386 | if (!is_numeric($this->ci->input->post('mDataProp_' . $i))) {
387 | return true;
388 | }
389 | }
390 | return false;
391 | }
392 |
393 | /**
394 | * Get mDataprop order
395 | *
396 | * @return mixed
397 | */
398 | protected function getMDataprop()
399 | {
400 | $mDataProp = array();
401 | for ($i = 0; $i < intval($this->ci->input->post('iColumns')); $i++) {
402 | $mDataProp[] = $this->ci->input->post('mDataProp_' . $i);
403 | }
404 | return $mDataProp;
405 | }
406 |
407 | /**
408 | * Return the difference of open and close characters
409 | *
410 | * @param string $str
411 | * @param string $open
412 | * @param string $close
413 | * @return string $retval
414 | */
415 | protected function balanceChars($str, $open, $close)
416 | {
417 | $openCount = substr_count($str, $open);
418 | $closeCount = substr_count($str, $close);
419 | $retval = $openCount - $closeCount;
420 | return $retval;
421 | }
422 |
423 | /**
424 | * Explode, but ignore delimiter until closing characters are found
425 | *
426 | * @param string $delimiter
427 | * @param string $str
428 | * @param string $open
429 | * @param string $close
430 | * @return mixed $retval
431 | */
432 | protected function explode($delimiter, $str, $open='(', $close=')')
433 | {
434 | $retval = array();
435 | $hold = array();
436 | $balance = 0;
437 | $parts = explode($delimiter, $str);
438 | foreach ($parts as $part) {
439 | $hold[] = $part;
440 | $balance += $this->balanceChars($part, $open, $close);
441 | if ($balance < 1) {
442 | $retval[] = implode($delimiter, $hold);
443 | $hold = array();
444 | $balance = 0;
445 | }
446 | }
447 | if (count($hold) > 0) {
448 | $retval[] = implode($delimiter, $hold);
449 | }
450 | return $retval;
451 | }
452 |
453 | /**
454 | * Workaround for json_encode's UTF-8 encoding if a different charset needs to be used
455 | *
456 | * @param mixed result
457 | * @return string
458 | */
459 | protected function jsonify($result = FALSE)
460 | {
461 | if(is_null($result)) return 'null';
462 | if($result === FALSE) return 'false';
463 | if($result === TRUE) return 'true';
464 | if (is_scalar($result)) {
465 | if (is_float($result)) {
466 | return floatval(str_replace(',', '.', strval($result)));
467 | }
468 | if (is_string($result)) {
469 | static $jsonReplaces = array(array('\\', '/', '\n', '\t', '\r', '\b', '\f', '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"'));
470 | return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $result) . '"';
471 | } else {
472 | return $result;
473 | }
474 | }
475 | $isList = TRUE;
476 | for ($i = 0, reset($result); $i < count($result); $i++, next($result)) {
477 | if (key($result) !== $i) {
478 | $isList = FALSE;
479 | break;
480 | }
481 | }
482 | $json = array();
483 | if ($isList) {
484 | foreach ($result as $value) {
485 | $json[] = $this->jsonify($value);
486 | }
487 | return '[' . join(',', $json) . ']';
488 | } else {
489 | foreach ($result as $key => $value) {
490 | $json[] = $this->jsonify($key) . ':' . $this->jsonify($value);
491 | }
492 | return '{' . join(',', $json) . '}';
493 | }
494 | }
495 | }
496 | /* End of file Datatables.php */
497 | /* Location: ./application/libraries/Datatables.php */
498 |
--------------------------------------------------------------------------------