├── .gitignore ├── Blade.php ├── LICENSE ├── MY_Model.php ├── MY_Profiler.php ├── README.md └── TableAdmin.php /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .idea/ 3 | -------------------------------------------------------------------------------- /Blade.php: -------------------------------------------------------------------------------- 1 | _data[$name] = $value; 90 | } 91 | 92 | 93 | public function __unset($name) 94 | { 95 | unset($this->_data[$name]); 96 | } 97 | 98 | 99 | public function __get($name) 100 | { 101 | if (key_exists($name, $this->_data)) 102 | { 103 | return $this->_data[$name]; 104 | } 105 | 106 | $_CI = & get_instance(); 107 | return $_CI->$name; 108 | } 109 | 110 | 111 | public function __construct() 112 | { 113 | $this->load->driver('cache'); 114 | } 115 | 116 | 117 | /** 118 | * Sets global data for template 119 | * 120 | * @param string $name 121 | * @param mixed $value 122 | * @return Blade 123 | */ 124 | public function set($name, $value) 125 | { 126 | $this->_data[$name] = $value; 127 | return $this; 128 | } 129 | 130 | 131 | /** 132 | * Appends or concats a value to a template global data if type is array 133 | * or string respectively 134 | * 135 | * @param string $name 136 | * @param mixed $value 137 | * @return Blade 138 | */ 139 | public function append($name, $value) 140 | { 141 | if (is_array($this->_data[$name])) 142 | { 143 | $this->_data[$name][] = $value; 144 | } 145 | else 146 | { 147 | $this->_data[$name] .= $value; 148 | } 149 | 150 | return $this; 151 | } 152 | 153 | 154 | /** 155 | * Sets multiple global data in array format 156 | * 157 | * @param array $data 158 | * @return Blade 159 | */ 160 | public function set_data($data) 161 | { 162 | $this->_data = array_merge($this->_data, $data); 163 | return $this; 164 | } 165 | 166 | 167 | /** 168 | * Adds a custom compiler function 169 | * 170 | * @param mixed $compiler 171 | * @return Blade 172 | */ 173 | public function extend($compiler) 174 | { 175 | $this->_extensions[] = $compiler; 176 | return $this; 177 | } 178 | 179 | 180 | /** 181 | * Outputs template content. You can also pass an array of global data. 182 | * If $return is TRUE then returns the template as a string. 183 | * 184 | * @param string $template 185 | * @param array $data 186 | * @param bool $return 187 | * @return string 188 | */ 189 | public function render($template, $data = NULL, $return = FALSE) 190 | { 191 | if (isset($data)) 192 | { 193 | $this->set_data($data); 194 | } 195 | 196 | // Compile and run template 197 | $compiled = $this->_compile($template); 198 | $content = $this->_run($compiled, $this->_data); 199 | 200 | if ( ! $return) 201 | { 202 | $this->output->append_output($content); 203 | } 204 | 205 | return $content; 206 | } 207 | 208 | 209 | /** 210 | * Find the full path to a view file. Shows an error if file not found. 211 | * 212 | * @param string $view 213 | * @return string 214 | */ 215 | protected function _find_view($view) 216 | { 217 | //edit view string : example view1.view2.... 218 | $view = str_replace('.','/',$view); 219 | // Default location 220 | $full_path = APPPATH . 'views/' . $view . $this->blade_ext; 221 | 222 | // Modular Separation / Modular Extensions has been detected 223 | if (method_exists($this->router, 'fetch_module')) 224 | { 225 | $module = $this->router->fetch_module(); 226 | list($path, $_view) = Modules::find($view . $this->blade_ext, $module, 'views/'); 227 | 228 | if ($path) 229 | { 230 | $full_path = $path . $_view; 231 | } 232 | } 233 | 234 | // File not found 235 | if ( ! is_file($full_path)) 236 | { 237 | show_error('[Blade] Unable to find view: ' . $view); 238 | } 239 | 240 | return $full_path; 241 | } 242 | 243 | 244 | /** 245 | * Compiles a template and stores it in the cache. 246 | * 247 | * @param string $template 248 | * @return string 249 | */ 250 | protected function _compile($template) 251 | { 252 | // Prepare template info 253 | $view_path = $this->_find_view($template); 254 | $cache_id = 'blade-' . md5($view_path); 255 | 256 | // Test if a compiled version exists in the cache 257 | if ($compiled = $this->cache->file->get($cache_id)) 258 | { 259 | // In production, avoid to test if the template was updated 260 | if (ENVIRONMENT == 'production') 261 | { 262 | return $compiled; 263 | } 264 | 265 | // Return cache version if the template was not updated 266 | $meta = $this->cache->file->get_metadata($cache_id); 267 | if ($meta['mtime'] > filemtime($view_path)) 268 | { 269 | return $compiled; 270 | } 271 | } 272 | 273 | // Template content 274 | $template = file_get_contents($view_path); 275 | 276 | // Compilers 277 | foreach ($this->_compilers as $compiler) 278 | { 279 | $method = "_compile_{$compiler}"; 280 | $template = $this->$method($template); 281 | } 282 | 283 | // Store compiled version in the cache 284 | $this->cache->file->save($cache_id, $template, $this->cache_time); 285 | 286 | // Return compiled template 287 | return $template; 288 | } 289 | 290 | 291 | /** 292 | * Runs a compiled template with its variables 293 | * 294 | * @param string $template 295 | * @param array $data 296 | * @return string 297 | */ 298 | protected function _run($template, $data = NULL) 299 | { 300 | if (is_array($data)) 301 | { 302 | extract($data); 303 | } 304 | 305 | ob_start(); 306 | eval(' ?>' . $template . '_data, $data) : $this->_data; 325 | 326 | // Compile and run template 327 | $compiled = $this->_compile($template); 328 | return $this->_run($compiled, $data); 329 | } 330 | 331 | 332 | /** 333 | * Gets a section content 334 | * 335 | * @param string $section 336 | * @return string 337 | */ 338 | protected function _yield($section) 339 | { 340 | return isset($this->_sections[$section]) ? $this->_sections[$section] : ''; 341 | } 342 | 343 | 344 | /** 345 | * Starts buffering section content 346 | * 347 | * @param string $section 348 | */ 349 | protected function _section_start($section) 350 | { 351 | array_push($this->_last_section, $section); 352 | ob_start(); 353 | } 354 | 355 | 356 | /** 357 | * Stops buffering section content. Returns the current section name. 358 | * 359 | * @return string 360 | */ 361 | protected function _section_end() 362 | { 363 | $last = array_pop($this->_last_section); 364 | $this->_section_extend($last, ob_get_clean()); 365 | 366 | return $last; 367 | } 368 | 369 | /** 370 | * Alias section_end 371 | */ 372 | protected function _stop() 373 | { 374 | return $this->_section_end(); 375 | } 376 | 377 | 378 | /** 379 | * Stores section content, replacing '@parent' with the previous section 380 | * content if any. 381 | * 382 | * @param string $section 383 | * @param string $content 384 | */ 385 | protected function _section_extend($section, $content) 386 | { 387 | if (isset($this->_sections[$section])) 388 | { 389 | $this->_sections[$section] = str_replace('@parent', $content, $this->_sections[$section]); 390 | } 391 | else 392 | { 393 | $this->_sections[$section] = $content; 394 | } 395 | } 396 | 397 | 398 | // ------------------------------------------------------- 399 | // 400 | // COMPILERS 401 | // 402 | // ------------------------------------------------------- 403 | 404 | 405 | /** 406 | * Get the regular expression for a generic Blade function. 407 | * 408 | * @param string $function 409 | * @return string 410 | */ 411 | public function matcher($function) 412 | { 413 | return '/(\s*)@' . $function . '(\s*\(.*\))/'; 414 | } 415 | 416 | 417 | /** 418 | * Rewrites Blade comments into PHP comments. 419 | * 420 | * @param string $value 421 | * @return string 422 | */ 423 | protected function _compile_comments($value) 424 | { 425 | $value = preg_replace('/\{\{--(.+?)(--\}\})?\n/', "", $value); 426 | 427 | return preg_replace('/\{\{--((.|\s)*?)--\}\}/', "\n", $value); 428 | } 429 | 430 | 431 | /** 432 | * Rewrites Blade echo statements into PHP echo statements. 433 | * 434 | * @param string $value 435 | * @return string 436 | */ 437 | protected function _compile_echos($value) 438 | { 439 | return preg_replace('/\{\{(.+?)\}\}/', '', $value); 440 | } 441 | 442 | 443 | /** 444 | * Rewrites Blade "for else" statements into valid PHP. 445 | * 446 | * @param string $value 447 | * @return string 448 | */ 449 | protected function _compile_forelse($value) 450 | { 451 | preg_match_all('/(\s*)@forelse(\s*\(.*\))(\s*)/', $value, $matches); 452 | 453 | foreach ($matches[0] as $forelse) 454 | { 455 | preg_match('/\$[^\s]*/', $forelse, $variable); 456 | 457 | // Once we have extracted the variable being looped against, we can add 458 | // an if statement to the start of the loop that checks if the count 459 | // of the variable being looped against is greater than zero. 460 | $if = " 0): ?>"; 461 | 462 | $search = '/(\s*)@forelse(\s*\(.*\))/'; 463 | 464 | $replace = '$1' . $if . ''; 465 | 466 | $blade = preg_replace($search, $replace, $forelse); 467 | 468 | // Finally, once we have the check prepended to the loop we'll replace 469 | // all instances of this forelse syntax in the view content of the 470 | // view being compiled to Blade syntax with real PHP syntax. 471 | $value = str_replace($forelse, $blade, $value); 472 | } 473 | 474 | return $value; 475 | } 476 | 477 | 478 | /** 479 | * Rewrites Blade "empty" statements into valid PHP. 480 | * 481 | * @param string $value 482 | * @return string 483 | */ 484 | protected function _compile_empty($value) 485 | { 486 | return str_replace('@empty', '', $value); 487 | } 488 | 489 | 490 | /** 491 | * Rewrites Blade "forelse" endings into valid PHP. 492 | * 493 | * @param string $value 494 | * @return string 495 | */ 496 | protected function _compile_endforelse($value) 497 | { 498 | return str_replace('@endforelse', '', $value); 499 | } 500 | 501 | 502 | /** 503 | * Rewrites Blade structure openings into PHP structure openings. 504 | * 505 | * @param string $value 506 | * @return string 507 | */ 508 | protected function _compile_structure_openings($value) 509 | { 510 | $pattern = '/(\s*)@(if|elseif|foreach|for|while)(\s*\(.*\))/'; 511 | 512 | return preg_replace($pattern, '$1', $value); 513 | } 514 | 515 | 516 | /** 517 | * Rewrites Blade structure closings into PHP structure closings. 518 | * 519 | * @param string $value 520 | * @return string 521 | */ 522 | protected function _compile_structure_closings($value) 523 | { 524 | $pattern = '/(\s*)@(endif|endforeach|endfor|endwhile)(\s*)/'; 525 | 526 | return preg_replace($pattern, '$1$3', $value); 527 | } 528 | 529 | 530 | /** 531 | * Rewrites Blade else statements into PHP else statements. 532 | * 533 | * @param string $value 534 | * @return string 535 | */ 536 | protected function _compile_else($value) 537 | { 538 | return preg_replace('/(\s*)@(else)(\s*)/', '$1$3', $value); 539 | } 540 | 541 | 542 | /** 543 | * Rewrites Blade "unless" statements into valid PHP. 544 | * 545 | * @param string $value 546 | * @return string 547 | */ 548 | protected function _compile_unless($value) 549 | { 550 | $pattern = '/(\s*)@unless(\s*\(.*\))/'; 551 | 552 | return preg_replace($pattern, '$1', $value); 553 | } 554 | 555 | 556 | /** 557 | * Rewrites Blade "unless" endings into valid PHP. 558 | * 559 | * @param string $value 560 | * @return string 561 | */ 562 | protected function _compile_endunless($value) 563 | { 564 | return str_replace('@endunless', '', $value); 565 | } 566 | 567 | 568 | /** 569 | * Execute user defined compilers. 570 | * 571 | * @param string $value 572 | * @return string 573 | */ 574 | protected function _compile_extensions($value) 575 | { 576 | foreach ($this->_extensions as $compiler) 577 | { 578 | $value = call_user_func($compiler, $value); 579 | } 580 | 581 | return $value; 582 | } 583 | 584 | 585 | /** 586 | * Rewrites Blade @include statements into valid PHP. 587 | * 588 | * @param string $value 589 | * @return string 590 | */ 591 | protected function _compile_includes($value) 592 | { 593 | $pattern = static::matcher('include'); 594 | 595 | return preg_replace($pattern, '$1_include$2; ?>', $value); 596 | } 597 | 598 | 599 | /** 600 | * Rewrites Blade "@layout" expressions into valid PHP. 601 | * 602 | * @param string $value 603 | * @return string 604 | */ 605 | protected function _compile_layouts($value) 606 | { 607 | $pattern = $this->matcher('layout'); 608 | 609 | // Find "@layout" expressions 610 | if ( ! preg_match_all($pattern, $value, $matches, PREG_SET_ORDER)) 611 | { 612 | return $value; 613 | } 614 | 615 | // Delete "@layout" expressions 616 | $value = preg_replace($pattern, '', $value); 617 | 618 | // Include layouts at the end of template 619 | foreach ($matches as $set) 620 | { 621 | $value .= "\n" . $set[1] . '_include' . $set[2] . "; ?>\n"; 622 | } 623 | 624 | return $value; 625 | } 626 | 627 | /** 628 | * Edit Blade library - add @extends for Laravel Blade 5 629 | * @author : kiendt@hblab.vn 630 | * @date : 01/12/2015 631 | * Rewrites Blade "@extends" expressions into valid PHP. 632 | * 633 | * @param string $value 634 | * @return string 635 | */ 636 | protected function _compile_extends($value) 637 | { 638 | $pattern = $this->matcher('extends'); 639 | 640 | // Find "@extends" expressions 641 | if ( ! preg_match_all($pattern, $value, $matches, PREG_SET_ORDER)) 642 | { 643 | return $value; 644 | } 645 | 646 | // Delete "@extends" expressions 647 | $value = preg_replace($pattern, '', $value); 648 | 649 | // Include extends at the end of template 650 | foreach ($matches as $set) 651 | { 652 | $value .= "\n" . $set[1] . '_include' . $set[2] . "; ?>\n"; 653 | } 654 | 655 | return $value; 656 | } 657 | 658 | /** 659 | * Rewrites Blade @yield statements into Section statements. 660 | * 661 | * The Blade @yield statement is a shortcut to the Section::yield method. 662 | * 663 | * @param string $value 664 | * @return string 665 | */ 666 | protected function _compile_yields($value) 667 | { 668 | $pattern = $this->matcher('yield'); 669 | 670 | return preg_replace($pattern, '$1_yield$2; ?>', $value); 671 | } 672 | 673 | 674 | /** 675 | * Rewrites Blade @section statements into Section statements. 676 | * 677 | * @param string $value 678 | * @return string 679 | */ 680 | protected function _compile_section_start($value) 681 | { 682 | $pattern = $this->matcher('section'); 683 | 684 | return preg_replace($pattern, '$1_section_start$2; ?>', $value); 685 | } 686 | 687 | 688 | /** 689 | * Rewrites Blade @endsection statements into Section statements. 690 | * 691 | * @param string $value 692 | * @return string 693 | */ 694 | protected function _compile_section_end($value) 695 | { 696 | $replace = '_section_end(); ?>'; 697 | 698 | return str_replace('@endsection', $replace, $value); 699 | } 700 | 701 | /** 702 | * Add alias section end 703 | */ 704 | protected function _compile_stop($value) 705 | { 706 | $replace = '_section_end(); ?>'; 707 | 708 | return str_replace('@stop', $replace, $value); 709 | } 710 | 711 | 712 | /** 713 | * Rewrites Blade yield section statements into valid PHP. 714 | * 715 | * @return string 716 | */ 717 | protected function _compile_yield_sections($value) 718 | { 719 | $replace = '_yield($this->_section_end()); ?>'; 720 | 721 | return str_replace('@yield_section', $replace, $value); 722 | } 723 | 724 | 725 | } 726 | 727 | 728 | // END Blade class 729 | 730 | /* End of file Blade.php */ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /MY_Model.php: -------------------------------------------------------------------------------- 1 | _database_connection = group_name or array() | OPTIONAL 29 | * Sets the connection preferences (group name) set up in the database.php. If not trset, it will use the 30 | * 'default' (the $active_group) database connection. 31 | * $this->timestamps = TRUE | array('made_at','modified_at','removed_at') 32 | * If set to TRUE tells MY_Model that the table has 'created_at','updated_at' (and 'deleted_at' if $this->soft_delete is set to TRUE) 33 | * If given an array as parameter, it tells MY_Model, that the first element is a created_at field type, the second element is a updated_at field type (and the third element is a deleted_at field type) 34 | * $this->soft_deletes = FALSE; 35 | * Enables (TRUE) or disables (FALSE) the "soft delete" on records. Default is FALSE 36 | * $this->timestamps_format = 'Y-m-d H:i:s' 37 | * You can at any time change the way the timestamp is created (the default is the MySQL standard datetime format) by modifying this variable. You can choose between whatever format is acceptable by the php function date() (default is 'Y-m-d H:i:s'), or 'timestamp' (UNIX timestamp) 38 | * $this->return_as = 'object' | 'array' 39 | * Allows the model to return the results as object or as array 40 | * $this->has_one['phone'] = 'Phone_model' or $this->has_one['phone'] = array('Phone_model','foreign_key','local_key'); 41 | * $this->has_one['address'] = 'Address_model' or $this->has_one['address'] = array('Address_model','foreign_key','another_local_key'); 42 | * Allows establishing ONE TO ONE or more ONE TO ONE relationship(s) between models/tables 43 | * $this->has_many['posts'] = 'Post_model' or $this->has_many['posts'] = array('Posts_model','foreign_key','another_local_key'); 44 | * Allows establishing ONE TO MANY or more ONE TO MANY relationship(s) between models/tables 45 | * $this->has_many_pivot['posts'] = 'Post_model' or $this->has_many_pivot['posts'] = array('Posts_model','foreign_primary_key','local_primary_key'); 46 | * Allows establishing MANY TO MANY or more MANY TO MANY relationship(s) between models/tables with the use of a PIVOT TABLE 47 | * !ATTENTION: The pivot table name must be composed of the two table names separated by "_" the table names having to to be alphabetically ordered (NOT users_posts, but posts_users). 48 | * Also the pivot table must contain as identifying columns the columns named by convention as follows: table_name_singular + _ + foreign_table_primary_key. 49 | * For example: considering that a post can have multiple authors, a pivot table that connects two tables (users and posts) must be named posts_users and must have post_id and user_id as identifying columns for the posts.id and users.id tables. 50 | * $this->cache_driver = 'file' 51 | * $this->cache_prefix = 'mm' 52 | * If you know you will do some caching of results without the native caching solution, you can at any time use the MY_Model's caching. 53 | * By default, MY_Model uses the files to cache result. 54 | * If you want to change the way it stores the cache, you can change the $cache_driver property to whatever CodeIgniter cache driver you want to use. 55 | * Also, with $cache_prefix, you can prefix the name of the caches. by default any cache made by MY_Model starts with 'mm' + _ + "name chosen for cache" 56 | * $this->pagination_delimiters = array('',''); 57 | * If you know you will use the paginate() method, you can change the delimiters between the pages links 58 | * $this->pagination_arrows = array('<','>'); 59 | * You can also change the way the previous and next arrows look like. 60 | * 61 | * 62 | * parent::__construct(); 63 | * } 64 | * } 65 | * 66 | **/ 67 | class MY_Model extends CI_Model 68 | { 69 | /** 70 | * Select the database connection from the group names defined inside the database.php configuration file or an 71 | * array. 72 | */ 73 | protected $_database_connection = NULL; 74 | /** @var 75 | * This one will hold the database connection object 76 | */ 77 | protected $_database; 78 | /** @var null 79 | * Sets table name 80 | */ 81 | public $table = NULL; 82 | /** 83 | * @var null 84 | * Sets PRIMARY KEY 85 | */ 86 | public $primary_key = 'id'; 87 | /** 88 | * @var array 89 | * You can establish the fields of the table. If you won't these fields will be filled by MY_Model (with one query) 90 | */ 91 | public $table_fields = array(); 92 | /** 93 | * @var array 94 | * Sets fillable fields 95 | */ 96 | public $fillable = array(); 97 | /** 98 | * @var array 99 | * Sets protected fields 100 | */ 101 | public $protected = array(); 102 | private $_can_be_filled = NULL; 103 | /** @var bool | array 104 | * Enables created_at and updated_at fields 105 | */ 106 | protected $timestamps = TRUE; 107 | protected $timestamps_format = 'Y-m-d H:i:s'; 108 | protected $_created_at_field; 109 | protected $_updated_at_field; 110 | protected $_deleted_at_field; 111 | /** @var bool 112 | * Enables soft_deletes 113 | */ 114 | protected $soft_deletes = FALSE; 115 | /** relationships variables */ 116 | private $_relationships = array(); 117 | public $has_one = array(); 118 | public $has_many = array(); 119 | public $has_many_pivot = array(); 120 | public $separate_subqueries = TRUE; 121 | private $_requested = array(); 122 | /** end relationships variables */ 123 | /*caching*/ 124 | public $cache_driver = 'file'; 125 | public $cache_prefix = 'mm'; 126 | protected $_cache = array(); 127 | /*pagination*/ 128 | public $next_page; 129 | public $previous_page; 130 | public $all_pages; 131 | public $pagination_delimiters; 132 | public $pagination_arrows; 133 | /* validation */ 134 | private $validated = TRUE; 135 | private $row_fields_to_update = array(); 136 | /** 137 | * The various callbacks available to the model. Each are 138 | * simple lists of method names (methods will be run on $this). 139 | */ 140 | protected $before_create = array(); 141 | protected $after_create = array(); 142 | protected $before_update = array(); 143 | protected $after_update = array(); 144 | protected $before_get = array(); 145 | protected $after_get = array(); 146 | protected $before_delete = array(); 147 | protected $after_delete = array(); 148 | protected $before_soft_delete = array(); 149 | protected $after_soft_delete = array(); 150 | protected $callback_parameters = array(); 151 | protected $return_as = 'object'; 152 | protected $return_as_dropdown = NULL; 153 | protected $_dropdown_field = ''; 154 | private $_trashed = 'without'; 155 | private $_select = '*'; 156 | public function __construct() 157 | { 158 | parent::__construct(); 159 | $this->load->helper('inflector'); 160 | $this->_set_connection(); 161 | $this->_set_timestamps(); 162 | $this->_fetch_table(); 163 | $this->pagination_delimiters = (isset($this->pagination_delimiters)) ? $this->pagination_delimiters : array('',''); 164 | $this->pagination_arrows = (isset($this->pagination_arrows)) ? $this->pagination_arrows : array('<','>'); 165 | /* These below are implementation examples for before_create and before_update triggers. 166 | Their respective functions - add_creator() and add_updater() - can be found at the end of the model. 167 | They add user id on create and update. If you comment this out don't forget to do the same for the methods() 168 | $this->before_create[]='add_creator'; 169 | $this->before_create[]='add_updater'; 170 | */ 171 | } 172 | public function _get_table_fields() 173 | { 174 | if(empty($this->table_fields)) 175 | { 176 | $this->table_fields = $this->_database->list_fields($this->table); 177 | } 178 | return TRUE; 179 | } 180 | public function fillable_fields() 181 | { 182 | if(!isset($this->_can_be_filled)) 183 | { 184 | $this->_get_table_fields(); 185 | $no_protection = array(); 186 | foreach ($this->table_fields as $field) { 187 | if (!in_array($field, $this->protected)) { 188 | $no_protection[] = $field; 189 | } 190 | } 191 | if (!empty($this->fillable)) { 192 | $can_fill = array(); 193 | foreach ($this->fillable as $field) { 194 | if (in_array($field, $no_protection)) { 195 | $can_fill[] = $field; 196 | } 197 | } 198 | $this->_can_be_filled = $can_fill; 199 | } else { 200 | $this->_can_be_filled = $no_protection; 201 | } 202 | } 203 | return TRUE; 204 | } 205 | public function _prep_before_write($data) 206 | { 207 | $this->fillable_fields(); 208 | // We make sure we have the fields that can be filled 209 | $can_fill = $this->_can_be_filled; 210 | // Let's make sure we receive an array... 211 | $data_as_array = (is_object($data)) ? (array)$data : $data; 212 | $new_data = array(); 213 | $multi = FALSE; 214 | foreach($data as $element) 215 | { 216 | $multi = (is_array($element)) ? TRUE : FALSE; 217 | } 218 | if($multi===FALSE) 219 | { 220 | foreach ($data_as_array as $field => $value) 221 | { 222 | if (in_array($field, $can_fill)) { 223 | $new_data[$field] = $value; 224 | } 225 | } 226 | } 227 | else 228 | { 229 | foreach($data_as_array as $key => $row) 230 | { 231 | foreach ($row as $field => $value) 232 | { 233 | if (in_array($field, $can_fill)) { 234 | $new_data[$key][$field] = $value; 235 | } 236 | } 237 | } 238 | } 239 | return $new_data; 240 | } 241 | public function _prep_before_read() 242 | { 243 | } 244 | public function _prep_after_read($data, $multi = TRUE) 245 | { 246 | // let's join the subqueries... 247 | $data = $this->join_temporary_results($data); 248 | $this->_database->reset_query(); 249 | if(isset($this->return_as_dropdown) && $this->return_as_dropdown == 'dropdown') 250 | { 251 | foreach($data as $row) 252 | { 253 | $dropdown[$row[$this->primary_key]] = $row[$this->_dropdown_field]; 254 | } 255 | $data = $dropdown; 256 | $this->return_as_dropdown = NULL; 257 | } 258 | elseif($this->return_as == 'object') 259 | { 260 | $data = json_decode(json_encode($data), FALSE); 261 | } 262 | if(isset($this->_select)) 263 | { 264 | $this->_select = '*'; 265 | } 266 | return $data; 267 | } 268 | /** 269 | * public function from_form($rules = NULL,$additional_values = array(), $row_fields_to_update = array()) 270 | * Gets data from form, after validating it and waits for an insert() or update() method in the query chain 271 | * @param null $rules Gets the validation rules. If nothing is passed (NULL), will look for the validation rules 272 | * inside the model $rules public property 273 | * @param array $additional_values Accepts additional fields to be filled, fields that are not to be found inside 274 | * the form. The values are inserted as an array with "field_name" => "field_value" 275 | * @param array $row_fields_to_update You can mention the fields from the form that can be used to identify 276 | * the row when doing an update 277 | * @return $this 278 | */ 279 | public function from_form($rules = NULL,$additional_values = NULL, $row_fields_to_update = array()) 280 | { 281 | $this->_get_table_fields(); 282 | $this->load->library('form_validation'); 283 | if(!isset($rules)) 284 | { 285 | if(empty($row_fields_to_update)) 286 | { 287 | $rules = $this->rules['insert']; 288 | } 289 | else 290 | { 291 | $rules = $this->rules['update']; 292 | } 293 | } 294 | $this->form_validation->set_rules($rules); 295 | if($this->form_validation->run()) 296 | { 297 | $this->fillable_fields(); 298 | $this->validated = array(); 299 | foreach($rules as $rule) 300 | { 301 | if(in_array($rule['field'],$this->_can_be_filled)) 302 | { 303 | $this->validated[$rule['field']] = $this->input->post($rule['field']); 304 | } 305 | } 306 | if(isset($additional_values) && is_array($additional_values) && !empty($additional_values)) 307 | { 308 | foreach($additional_values as $field => $value) 309 | { 310 | if(in_array($field, $this->_can_be_filled)) 311 | { 312 | $this->validated[$field] = $value; 313 | } 314 | } 315 | } 316 | if(!empty($row_fields_to_update)) 317 | { 318 | foreach ($row_fields_to_update as $key => $field) { 319 | if (in_array($field, $this->table_fields)) { 320 | $this->row_fields_to_update[$field] = $this->input->post($field); 321 | } 322 | else if (in_array($key, $this->table_fields)){ 323 | $this->row_fields_to_update[$key] = $field; 324 | } 325 | else { 326 | continue; 327 | } 328 | } 329 | } 330 | return $this; 331 | } 332 | else 333 | { 334 | $this->validated = FALSE; 335 | return $this; 336 | } 337 | } 338 | /** 339 | * public function insert($data) 340 | * Inserts data into table. Can receive an array or a multidimensional array depending on what kind of insert we're talking about. 341 | * @param $data 342 | * @return int/array Returns id/ids of inserted rows 343 | */ 344 | public function insert($data = NULL) 345 | { 346 | if(!isset($data) && $this->validated!=FALSE) 347 | { 348 | $data = $this->validated; 349 | $this->validated = FALSE; 350 | } 351 | elseif(!isset($data)) 352 | { 353 | return FALSE; 354 | } 355 | $data = $this->_prep_before_write($data); 356 | //now let's see if the array is a multidimensional one (multiple rows insert) 357 | $multi = FALSE; 358 | foreach($data as $element) 359 | { 360 | $multi = (is_array($element)) ? TRUE : FALSE; 361 | } 362 | // if the array is not a multidimensional one... 363 | if($multi === FALSE) 364 | { 365 | if($this->timestamps !== FALSE) 366 | { 367 | $data[$this->_created_at_field] = $this->_the_timestamp(); 368 | $data[$this->_updated_at_field] = $this->_the_timestamp(); 369 | } 370 | $data = $this->trigger('before_create',$data); 371 | if($this->_database->insert($this->table, $data)) 372 | { 373 | $id = $this->_database->insert_id(); 374 | $return = $this->trigger('after_create',$id); 375 | return $return; 376 | } 377 | return FALSE; 378 | } 379 | // else... 380 | else 381 | { 382 | $return = array(); 383 | foreach($data as $row) 384 | { 385 | if($this->timestamps !== FALSE) 386 | { 387 | $row[$this->_created_at_field] = $this->_the_timestamp(); 388 | $row[$this->_updated_at_field] = $this->_the_timestamp(); 389 | } 390 | $row = $this->trigger('before_create',$row); 391 | if($this->_database->insert($this->table,$row)) 392 | { 393 | $return[] = $this->_database->insert_id(); 394 | } 395 | } 396 | $after_create = array(); 397 | foreach($return as $id) 398 | { 399 | $after_create[] = $this->trigger('after_create', $id); 400 | } 401 | return $after_create; 402 | } 403 | return FALSE; 404 | } 405 | /** 406 | * public function update($data) 407 | * Updates data into table. Can receive an array or a multidimensional array depending on what kind of update we're talking about. 408 | * @param array $data 409 | * @param array|int $column_name_where 410 | * @param bool $escape should the values be escaped or not - defaults to true 411 | * @return str/array Returns id/ids of inserted rows 412 | */ 413 | public function update($data = NULL, $column_name_where = NULL, $escape = TRUE) 414 | { 415 | if(!isset($data) && $this->validated!=FALSE) 416 | { 417 | $data = $this->validated; 418 | $this->validated = FALSE; 419 | } 420 | elseif(!isset($data)) 421 | { 422 | $this->_database->reset_query(); 423 | return FALSE; 424 | } 425 | // Prepare the data... 426 | $data = $this->_prep_before_write($data); 427 | //now let's see if the array is a multidimensional one (multiple rows insert) 428 | $multi = FALSE; 429 | foreach($data as $element) 430 | { 431 | $multi = (is_array($element)) ? TRUE : FALSE; 432 | } 433 | // if the array is not a multidimensional one... 434 | if($multi === FALSE) 435 | { 436 | if($this->timestamps !== FALSE) 437 | { 438 | $data[$this->_updated_at_field] = $this->_the_timestamp(); 439 | } 440 | $data = $this->trigger('before_update',$data); 441 | if($this->validated === FALSE && count($this->row_fields_to_update)) 442 | { 443 | $this->where($this->row_fields_to_update); 444 | $this->row_fields_to_update = array(); 445 | } 446 | if(isset($column_name_where)) 447 | { 448 | if (is_array($column_name_where)) 449 | { 450 | $update_where_in = FALSE; 451 | 452 | foreach ($column_name_where as $field=>$element) { 453 | $update_where_in = (is_array($element)) ? TRUE : FALSE; 454 | } 455 | if($update_where_in) { 456 | $this->where($field,$element); 457 | }else{ 458 | $this->where($column_name_where); 459 | } 460 | } elseif (is_numeric($column_name_where)) { 461 | $this->_database->where($this->primary_key, $column_name_where); 462 | } else { 463 | $column_value = (is_object($data)) ? $data->{$column_name_where} : $data[$column_name_where]; 464 | $this->_database->where($column_name_where, $column_value); 465 | } 466 | } 467 | if($escape) 468 | { 469 | if($this->_database->update($this->table, $data)) 470 | { 471 | $affected = $this->_database->affected_rows(); 472 | $return = $this->trigger('after_update',$affected); 473 | return $return; 474 | } 475 | } 476 | else 477 | { 478 | if($this->_database->set($data, null, FALSE)->update($this->table)) 479 | { 480 | $affected = $this->_database->affected_rows(); 481 | $return = $this->trigger('after_update',$affected); 482 | return $return; 483 | } 484 | } 485 | return FALSE; 486 | } 487 | // else... 488 | else 489 | { 490 | $rows = 0; 491 | foreach($data as $row) 492 | { 493 | if($this->timestamps !== FALSE) 494 | { 495 | $row[$this->_updated_at_field] = $this->_the_timestamp(); 496 | } 497 | $row = $this->trigger('before_update',$row); 498 | if(is_array($column_name_where)) 499 | { 500 | $this->_database->where($column_name_where[0], $column_name_where[1]); 501 | } 502 | else 503 | { 504 | $column_value = (is_object($row)) ? $row->{$column_name_where} : $row[$column_name_where]; 505 | $this->_database->where($column_name_where, $column_value); 506 | } 507 | if($escape) 508 | { 509 | if($this->_database->update($this->table,$row)) 510 | { 511 | $rows++; 512 | } 513 | } 514 | else 515 | { 516 | if($this->_database->set($row, null, FALSE)->update($this->table)) 517 | { 518 | $rows++; 519 | } 520 | } 521 | } 522 | $affected = $rows; 523 | $return = $this->trigger('after_update',$affected); 524 | return $return; 525 | } 526 | return FALSE; 527 | } 528 | /** 529 | * public function where($field_or_array = NULL, $operator_or_value = NULL, $value = NULL, $with_or = FALSE, $with_not = FALSE, $custom_string = FALSE) 530 | * Sets a where method for the $this object 531 | * @param null $field_or_array - can receive a field name or an array with more wheres... 532 | * @param null $operator_or_value - can receive a database operator or, if it has a field, the value to equal with 533 | * @param null $value - a value if it received a field name and an operator 534 | * @param bool $with_or - if set to true will create a or_where query type pr a or_like query type, depending on the operator 535 | * @param bool $with_not - if set to true will also add "NOT" in the where 536 | * @param bool $custom_string - if set to true, will simply assume that $field_or_array is actually a string and pass it to the where query 537 | * @return $this 538 | */ 539 | public function where($field_or_array = NULL, $operator_or_value = NULL, $value = NULL, $with_or = FALSE, $with_not = FALSE, $custom_string = FALSE) 540 | { 541 | if($this->soft_deletes===TRUE) 542 | { 543 | if(debug_backtrace()[1]['function']!='force_delete') 544 | { 545 | $this->_where_trashed(); 546 | } 547 | } 548 | if(is_array($field_or_array)) 549 | { 550 | $multi = FALSE; 551 | foreach($field_or_array as $element) { 552 | $multi = (is_array($element)) ? TRUE : FALSE; 553 | } 554 | if($multi === TRUE) 555 | { 556 | foreach ($field_or_array as $where) 557 | { 558 | $field = $where[0]; 559 | $operator_or_value = isset($where[1]) ? $where[1] : NULL; 560 | $value = isset($where[2]) ? $where[2] : NULL; 561 | $with_or = (isset($where[3])) ? TRUE : FALSE; 562 | $with_not = (isset($where[4])) ? TRUE : FALSE; 563 | $this->where($field, $operator_or_value, $value, $with_or,$with_not); 564 | } 565 | return $this; 566 | } 567 | } 568 | if($with_or === TRUE) 569 | { 570 | $where_or = 'or_where'; 571 | } 572 | else 573 | { 574 | $where_or = 'where'; 575 | } 576 | if($with_not === TRUE) 577 | { 578 | $not = '_not'; 579 | } 580 | else 581 | { 582 | $not = ''; 583 | } 584 | if($custom_string === TRUE) 585 | { 586 | $this->_database->{$where_or}($field_or_array, NULL, FALSE); 587 | } 588 | elseif(is_numeric($field_or_array)) 589 | { 590 | $this->_database->{$where_or}(array($this->table.'.'.$this->primary_key => $field_or_array)); 591 | } 592 | elseif(is_array($field_or_array) && !isset($operator_or_value)) 593 | { 594 | $this->_database->where($field_or_array); 595 | } 596 | elseif(!isset($value) && isset($field_or_array) && isset($operator_or_value) && !is_array($operator_or_value)) 597 | { 598 | $this->_database->{$where_or}(array($this->table.'.'.$field_or_array => $operator_or_value)); 599 | } 600 | elseif(!isset($value) && isset($field_or_array) && isset($operator_or_value) && is_array($operator_or_value) && !is_array($field_or_array)) 601 | { 602 | //echo $field_or_array; 603 | //exit; 604 | $this->_database->{$where_or.$not.'_in'}($this->table.'.'.$field_or_array, $operator_or_value); 605 | } 606 | elseif(isset($field_or_array) && isset($operator_or_value) && isset($value)) 607 | { 608 | if(strtolower($operator_or_value) == 'like') { 609 | if($with_not === TRUE) 610 | { 611 | $like = 'not_like'; 612 | } 613 | else 614 | { 615 | $like = 'like'; 616 | } 617 | if ($with_or === TRUE) 618 | { 619 | $like = 'or_'.$like; 620 | } 621 | $this->_database->{$like}($field_or_array, $value); 622 | } 623 | else 624 | { 625 | $this->_database->{$where_or}($field_or_array.' '.$operator_or_value, $value); 626 | } 627 | } 628 | return $this; 629 | } 630 | /** 631 | * public function limit($limit, $offset = 0) 632 | * Sets a rows limit to the query 633 | * @param $limit 634 | * @param int $offset 635 | * @return $this 636 | */ 637 | public function limit($limit, $offset = 0) 638 | { 639 | $this->_database->limit($limit, $offset); 640 | return $this; 641 | } 642 | /** 643 | * public function group_by($grouping_by) 644 | * A wrapper to $this->_database->group_by() 645 | * @param $grouping_by 646 | * @return $this 647 | */ 648 | public function group_by($grouping_by) 649 | { 650 | $this->_database->group_by($grouping_by); 651 | return $this; 652 | } 653 | /** 654 | * public function delete($where) 655 | * Deletes data from table. 656 | * @param $where primary_key(s) Can receive the primary key value or a list of primary keys as array() 657 | * @return Returns affected rows or false on failure 658 | */ 659 | public function delete($where = NULL) 660 | { 661 | if(!empty($this->before_delete) || !empty($this->before_soft_delete) || !empty($this->after_delete) || !empty($this->after_soft_delete) || ($this->soft_deletes === TRUE)) 662 | { 663 | $to_update = array(); 664 | if(isset($where)) 665 | { 666 | $this->where($where); 667 | } 668 | $query = $this->_database->get($this->table); 669 | foreach($query->result() as $row) 670 | { 671 | $to_update[] = array($this->primary_key => $row->{$this->primary_key}); 672 | } 673 | if(!empty($this->before_soft_delete)) 674 | { 675 | foreach($to_update as &$row) 676 | { 677 | $row = $this->trigger('before_soft_delete',$row); 678 | } 679 | } 680 | if(!empty($this->before_delete)) 681 | { 682 | foreach($to_update as &$row) 683 | { 684 | $row = $this->trigger('before_delete',$row); 685 | } 686 | } 687 | } 688 | if(isset($where)) 689 | { 690 | $this->where($where); 691 | } 692 | $affected_rows = 0; 693 | if($this->soft_deletes === TRUE) 694 | { 695 | if(isset($to_update)) 696 | { 697 | foreach($to_update as &$row) 698 | { 699 | //$row = $this->trigger('before_soft_delete',$row); 700 | $row[$this->_deleted_at_field] = $this->_the_timestamp(); 701 | } 702 | $affected_rows = $this->_database->update_batch($this->table, $to_update, $this->primary_key); 703 | $to_update['affected_rows'] = $affected_rows; 704 | $this->trigger('after_soft_delete',$to_update); 705 | } 706 | return $affected_rows; 707 | } 708 | else 709 | { 710 | if($this->_database->delete($this->table)) 711 | { 712 | $affected_rows = $this->_database->affected_rows(); 713 | if(!empty($this->after_delete)) 714 | { 715 | $to_update['affected_rows'] = $affected_rows; 716 | $to_update = $this->trigger('after_delete',$to_update); 717 | $affected_rows = $to_update; 718 | } 719 | return $affected_rows; 720 | } 721 | } 722 | return FALSE; 723 | } 724 | /** 725 | * public function force_delete($where = NULL) 726 | * Forces the delete of a row if soft_deletes is enabled 727 | * @param null $where 728 | * @return bool 729 | */ 730 | public function force_delete($where = NULL) 731 | { 732 | if(isset($where)) 733 | { 734 | $this->where($where); 735 | } 736 | if($this->_database->delete($this->table)) 737 | { 738 | return $this->_database->affected_rows(); 739 | } 740 | return FALSE; 741 | } 742 | /** 743 | * public function restore($where = NULL) 744 | * "Un-deletes" a row 745 | * @param null $where 746 | * @return bool 747 | */ 748 | public function restore($where = NULL) 749 | { 750 | $this->with_trashed(); 751 | if(isset($where)) 752 | { 753 | $this->where($where); 754 | } 755 | if($affected_rows = $this->_database->update($this->table,array($this->_deleted_at_field=>NULL))) 756 | { 757 | return $affected_rows; 758 | } 759 | return FALSE; 760 | } 761 | /** 762 | * public function trashed($where = NULL) 763 | * Verifies if a record (row) is soft_deleted or not 764 | * @param null $where 765 | * @return bool 766 | */ 767 | public function trashed($where = NULL) 768 | { 769 | $this->only_trashed(); 770 | if(isset($where)) 771 | { 772 | $this->where($where); 773 | } 774 | $this->limit(1); 775 | $query = $this->_database->get($this->table); 776 | if($query->num_rows() == 1) 777 | { 778 | return TRUE; 779 | } 780 | return FALSE; 781 | } 782 | /** 783 | * public function get() 784 | * Retrieves one row from table. 785 | * @param null $where 786 | * @return mixed 787 | */ 788 | public function get($where = NULL) 789 | { 790 | if(isset($this->_cache) && !empty($this->_cache)) 791 | { 792 | $this->load->driver('cache'); 793 | $cache_name = $this->_cache['cache_name']; 794 | $seconds = $this->_cache['seconds']; 795 | $data = $this->cache->{$this->cache_driver}->get($cache_name); 796 | } 797 | if(isset($data) && $data !== FALSE) 798 | { 799 | $this->_database->reset_query(); 800 | return $data; 801 | } 802 | else 803 | { 804 | $this->trigger('before_get'); 805 | if($this->_select) 806 | { 807 | $this->_database->select($this->_select); 808 | } 809 | if(!empty($this->_requested)) 810 | { 811 | foreach($this->_requested as $requested) 812 | { 813 | $this->_database->select($this->_relationships[$requested['request']]['local_key']); 814 | } 815 | } 816 | if(isset($where)) 817 | { 818 | $this->where($where); 819 | } 820 | $this->limit(1); 821 | $query = $this->_database->get($this->table); 822 | if ($query->num_rows() == 1) 823 | { 824 | $row = $query->row_array(); 825 | $row = $this->trigger('after_get', $row); 826 | $row = $this->_prep_after_read(array($row),FALSE); 827 | $row = $row[0]; 828 | if(isset($cache_name) && isset($seconds)) 829 | { 830 | $this->cache->{$this->cache_driver}->save($cache_name, $data, $seconds); 831 | $this->_reset_cache($cache_name); 832 | } 833 | return $row; 834 | } 835 | else 836 | { 837 | return FALSE; 838 | } 839 | } 840 | } 841 | /** 842 | * public function get_all() 843 | * Retrieves rows from table. 844 | * @param null $where 845 | * @return mixed 846 | */ 847 | public function get_all($where = NULL) 848 | { 849 | if(isset($this->_cache) && !empty($this->_cache)) 850 | { 851 | $this->load->driver('cache'); 852 | $cache_name = $this->_cache['cache_name']; 853 | $seconds = $this->_cache['seconds']; 854 | $data = $this->cache->{$this->cache_driver}->get($cache_name); 855 | } 856 | if(isset($data) && $data !== FALSE) 857 | { 858 | $this->_database->reset_query(); 859 | return $data; 860 | } 861 | else 862 | { 863 | $this->trigger('before_get'); 864 | if(isset($where)) 865 | { 866 | $this->where($where); 867 | } 868 | elseif($this->soft_deletes===TRUE) 869 | { 870 | $this->_where_trashed(); 871 | } 872 | if(isset($this->_select)) 873 | { 874 | $this->_database->select($this->_select); 875 | } 876 | if(!empty($this->_requested)) 877 | { 878 | foreach($this->_requested as $requested) 879 | { 880 | $this->_database->select($this->_relationships[$requested['request']]['local_key']); 881 | } 882 | } 883 | $query = $this->_database->get($this->table); 884 | if($query->num_rows() > 0) 885 | { 886 | $data = $query->result_array(); 887 | $data = $this->trigger('after_get', $data); 888 | $data = $this->_prep_after_read($data,TRUE); 889 | if(isset($cache_name) && isset($seconds)) 890 | { 891 | $this->cache->{$this->cache_driver}->save($cache_name, $data, $seconds); 892 | $this->_reset_cache($cache_name); 893 | } 894 | return $data; 895 | } 896 | else 897 | { 898 | return FALSE; 899 | } 900 | } 901 | } 902 | /** 903 | * public function count() 904 | * Retrieves number of rows from table. 905 | * @param null $where 906 | * @return integer 907 | */ 908 | public function count($where = NULL) 909 | { 910 | if(isset($where)) 911 | { 912 | $this->where($where); 913 | } 914 | $this->_database->from($this->table); 915 | $number_rows = $this->_database->count_all_results(); 916 | return $number_rows; 917 | } 918 | /** RELATIONSHIPS */ 919 | /** 920 | * public function with($requests) 921 | * allows the user to retrieve records from other interconnected tables depending on the relations defined before the constructor 922 | * @param string $request 923 | * @param array $arguments 924 | * @return $this 925 | */ 926 | public function with($request,$arguments = array()) 927 | { 928 | $this->_set_relationships(); 929 | if (array_key_exists($request, $this->_relationships)) 930 | { 931 | $this->_requested[$request] = array('request'=>$request); 932 | $parameters = array(); 933 | if(isset($arguments)) 934 | { 935 | foreach($arguments as $argument) 936 | { 937 | $requested_operations = explode('|',$argument); 938 | foreach($requested_operations as $operation) 939 | { 940 | $elements = explode(':', $operation, 2); 941 | if (sizeof($elements) == 2) { 942 | $parameters[$elements[0]] = $elements[1]; 943 | } else { 944 | show_error('MY_Model: Parameters for with_*() method must be of the form: "...->with_*(\'where:...|fields:...\')"'); 945 | } 946 | } 947 | } 948 | } 949 | $this->_requested[$request]['parameters'] = $parameters; 950 | } 951 | /* 952 | if($separate_subqueries === FALSE) 953 | { 954 | $this->separate_subqueries = FALSE; 955 | foreach($this->_requested as $request) 956 | { 957 | if($this->_relationships[$request]['relation'] == 'has_one') $this->_has_one($request); 958 | } 959 | } 960 | else 961 | { 962 | $this->after_get[] = 'join_temporary_results'; 963 | } 964 | */ 965 | return $this; 966 | } 967 | /** 968 | * protected function join_temporary_results($data) 969 | * Joins the subquery results to the main $data 970 | * @param $data 971 | * @return mixed 972 | */ 973 | protected function join_temporary_results($data) 974 | { 975 | $order_by = array(); 976 | foreach($this->_requested as $requested_key => $request) 977 | { 978 | $pivot_table = NULL; 979 | $relation = $this->_relationships[$request['request']]; 980 | $this->load->model($relation['foreign_model']); 981 | $foreign_key = $relation['foreign_key']; 982 | $local_key = $relation['local_key']; 983 | $foreign_table = $relation['foreign_table']; 984 | $type = $relation['relation']; 985 | $relation_key = $relation['relation_key']; 986 | if($type=='has_many_pivot') 987 | { 988 | $pivot_table = $relation['pivot_table']; 989 | $pivot_local_key = $relation['pivot_local_key']; 990 | $pivot_foreign_key = $relation['pivot_foreign_key']; 991 | $get_relate = $relation['get_relate']; 992 | } 993 | $local_key_values = array(); 994 | foreach($data as $key => $element) 995 | { 996 | if(isset($element[$local_key]) and !empty($element[$local_key])) 997 | { 998 | $id = $element[$local_key]; 999 | $local_key_values[$key] = $id; 1000 | } 1001 | } 1002 | if(!$local_key_values) 1003 | { 1004 | $data[$key][$relation_key] = NULL; 1005 | continue; 1006 | } 1007 | if(!isset($pivot_table)) 1008 | { 1009 | $sub_results = $this->{$relation['foreign_model']}; 1010 | $select = array(); 1011 | $select[] = '`'.$foreign_table.'`.`'.$foreign_key.'`'; 1012 | if(!empty($request['parameters'])) 1013 | { 1014 | if(array_key_exists('fields',$request['parameters'])) 1015 | { 1016 | if($request['parameters']['fields'] == '*count*') 1017 | { 1018 | $the_select = '*count*'; 1019 | $sub_results = (isset($the_select)) ? $sub_results->fields($the_select) : $sub_results; 1020 | $sub_results = $sub_results->fields($foreign_key); 1021 | } 1022 | else 1023 | { 1024 | $fields = explode(',', $request['parameters']['fields']); 1025 | foreach ($fields as $field) 1026 | { 1027 | $select[] = '`' . $foreign_table . '`.`' . trim($field) . '`'; 1028 | } 1029 | $the_select = implode(',', $select); 1030 | $sub_results = (isset($the_select)) ? $sub_results->fields($the_select) : $sub_results; 1031 | } 1032 | } 1033 | if(array_key_exists('fields',$request['parameters']) && ($request['parameters']['fields']=='*count*')) 1034 | { 1035 | $sub_results->group_by('`' . $foreign_table . '`.`' . $foreign_key . '`'); 1036 | } 1037 | if(array_key_exists('where',$request['parameters']) || array_key_exists('non_exclusive_where',$request['parameters'])) 1038 | { 1039 | $the_where = array_key_exists('where', $request['parameters']) ? 'where' : 'non_exclusive_where'; 1040 | } 1041 | $sub_results = isset($the_where) ? $sub_results->where($request['parameters'][$the_where],NULL,NULL,FALSE,FALSE,TRUE) : $sub_results; 1042 | } 1043 | $sub_results = $sub_results->where($foreign_key, $local_key_values)->get_all(); 1044 | } 1045 | else 1046 | { 1047 | $this->_database->join($pivot_table, $foreign_table.'.'.$foreign_key.' = '.$pivot_table.'.'.$pivot_foreign_key, 'left'); 1048 | $this->_database->join($this->table, $pivot_table.'.'.$pivot_local_key.' = '.$this->table.'.'.$local_key,'left'); 1049 | $this->_database->select($foreign_table.'.'.$foreign_key); 1050 | $this->_database->select($pivot_table.'.'.$pivot_local_key); 1051 | if(!empty($request['parameters'])) 1052 | { 1053 | if(array_key_exists('fields',$request['parameters'])) 1054 | { 1055 | if($request['parameters']['fields'] == '*count*') 1056 | { 1057 | $this->_database->select('COUNT(`'.$foreign_table.'`*) as counted_rows, `' . $foreign_table . '`.`' . $foreign_key . '`', FALSE); 1058 | } 1059 | else 1060 | { 1061 | $fields = explode(',', $request['parameters']['fields']); 1062 | $select = array(); 1063 | foreach ($fields as $field) { 1064 | $select[] = '`' . $foreign_table . '`.`' . trim($field) . '`'; 1065 | } 1066 | $the_select = implode(',', $select); 1067 | $this->_database->select($the_select); 1068 | } 1069 | } 1070 | if(array_key_exists('where',$request['parameters']) || array_key_exists('non_exclusive_where',$request['parameters'])) 1071 | { 1072 | $the_where = array_key_exists('where',$request['parameters']) ? 'where' : 'non_exclusive_where'; 1073 | $this->_database->where($request['parameters'][$the_where],NULL,NULL,FALSE,FALSE,TRUE); 1074 | } 1075 | } 1076 | $this->_database->where_in($pivot_table.'.'.$pivot_local_key,$local_key_values); 1077 | $sub_results = $this->_database->get($foreign_table)->result_array(); 1078 | $this->_database->reset_query(); 1079 | } 1080 | if(isset($sub_results) && !empty($sub_results)) { 1081 | $subs = array(); 1082 | foreach ($sub_results as $result) { 1083 | $result_array = (array)$result; 1084 | $the_foreign_key = $result_array[$foreign_key]; 1085 | if(isset($pivot_table)) 1086 | { 1087 | $the_local_key = $result_array[singular($this->table) . '_' . $local_key]; 1088 | if(isset($get_relate) and $get_relate === TRUE) 1089 | { 1090 | $subs[$the_local_key][$the_foreign_key] = $this->{$relation['foreign_model']}->where($local_key, $result[$local_key])->get(); 1091 | } 1092 | else 1093 | { 1094 | $subs[$the_local_key][$the_foreign_key] = $result; 1095 | } 1096 | } 1097 | else 1098 | { 1099 | if ($type == 'has_one') { 1100 | $subs[$the_foreign_key] = $result; 1101 | } else { 1102 | $subs[$the_foreign_key][] = $result; 1103 | } 1104 | } 1105 | } 1106 | $sub_results = $subs; 1107 | foreach($local_key_values as $key => $value) 1108 | { 1109 | if(array_key_exists($value,$sub_results)) 1110 | { 1111 | $data[$key][$relation_key] = $sub_results[$value]; 1112 | } 1113 | else 1114 | { 1115 | if(array_key_exists('where',$request['parameters'])) 1116 | { 1117 | unset($data[$key]); 1118 | } 1119 | } 1120 | } 1121 | } 1122 | else 1123 | { 1124 | $data[$key][$relation_key] = NULL; 1125 | } 1126 | if(array_key_exists('order_by',$request['parameters'])) 1127 | { 1128 | $elements = explode(',', $request['parameters']['order_by']); 1129 | if(sizeof($elements)==2) 1130 | { 1131 | $order_by[$relation_key] = array(trim($elements[0]), trim($elements[1])); 1132 | } 1133 | else 1134 | { 1135 | $order_by[$relation_key] = array(trim($elements[0]), 'desc'); 1136 | } 1137 | } 1138 | unset($this->_requested[$requested_key]); 1139 | } 1140 | if($order_by) 1141 | { 1142 | foreach($order_by as $field => $row) 1143 | { 1144 | list($key, $value) = $row; 1145 | $data = $this->_build_sorter($data, $field, $key, $value); 1146 | } 1147 | } 1148 | return $data; 1149 | } 1150 | /** 1151 | * private function _has_one($request) 1152 | * 1153 | * returns a joining of two tables depending on the $request relationship established in the constructor 1154 | * @param $request 1155 | * @return $this 1156 | */ 1157 | private function _has_one($request) 1158 | { 1159 | $relation = $this->_relationships[$request]; 1160 | $this->_database->join($relation['foreign_table'], $relation['foreign_table'].'.'.$relation['foreign_key'].' = '.$this->table.'.'.$relation['local_key'], 'left'); 1161 | return TRUE; 1162 | } 1163 | /** 1164 | * private function _set_relationships() 1165 | * 1166 | * Called by the public method with() it will set the relationships between the current model and other models 1167 | */ 1168 | private function _set_relationships() 1169 | { 1170 | if(empty($this->_relationships)) 1171 | { 1172 | $options = array('has_one','has_many','has_many_pivot'); 1173 | foreach($options as $option) 1174 | { 1175 | if(isset($this->{$option}) && !empty($this->{$option})) 1176 | { 1177 | foreach($this->{$option} as $key => $relation) 1178 | { 1179 | if(!is_array($relation)) 1180 | { 1181 | $foreign_model = $relation; 1182 | $foreign_model_name = strtolower($foreign_model); 1183 | $this->load->model($foreign_model_name); 1184 | $foreign_table = $this->{$foreign_model_name}->table; 1185 | $foreign_key = $this->{$foreign_model_name}->primary_key; 1186 | $local_key = $this->primary_key; 1187 | $pivot_local_key = $this->table.'_'.$local_key; 1188 | $pivot_foreign_key = $foreign_table.'_'.$foreign_key; 1189 | $get_relate = FALSE; 1190 | } 1191 | else 1192 | { 1193 | if($this->_is_assoc($relation)) 1194 | { 1195 | $foreign_model = $relation['foreign_model']; 1196 | if(array_key_exists('foreign_table',$relation)) 1197 | { 1198 | $foreign_table = $relation['foreign_table']; 1199 | } 1200 | else 1201 | { 1202 | $foreign_model_name = strtolower($foreign_model); 1203 | $this->load->model($foreign_model_name); 1204 | $foreign_table = $this->{$foreign_model_name}->table; 1205 | } 1206 | $foreign_key = $relation['foreign_key']; 1207 | $local_key = $relation['local_key']; 1208 | if($option=='has_many_pivot') 1209 | { 1210 | $pivot_table = $relation['pivot_table']; 1211 | $pivot_local_key = (array_key_exists('pivot_local_key',$relation)) ? $relation['pivot_local_key'] : $this->table.'_'.$this->primary_key; 1212 | $pivot_foreign_key = (array_key_exists('pivot_foreign_key',$relation)) ? $relation['pivot_foreign_key'] : $foreign_table.'_'.$foreign_key; 1213 | $get_relate = (array_key_exists('get_relate',$relation) && ($relation['get_relate']===TRUE)) ? TRUE : FALSE; 1214 | } 1215 | } 1216 | else 1217 | { 1218 | $foreign_model = $relation[0]; 1219 | $foreign_model_name = strtolower($foreign_model); 1220 | $this->load->model($foreign_model_name); 1221 | $foreign_table = $this->{$foreign_model_name}->table; 1222 | $foreign_key = $relation[1]; 1223 | $local_key = $relation[2]; 1224 | if($option=='has_many_pivot') 1225 | { 1226 | $pivot_local_key = $this->table.'_'.$this->primary_key; 1227 | $pivot_foreign_key = $foreign_table.'_'.$foreign_key; 1228 | $get_relate = (isset($relation[3]) && ($relation[3]===TRUE())) ? TRUE : FALSE; 1229 | } 1230 | } 1231 | } 1232 | if($option=='has_many_pivot' && !isset($pivot_table)) 1233 | { 1234 | $tables = array($this->table, $foreign_table); 1235 | sort($tables); 1236 | $pivot_table = $tables[0].'_'.$tables[1]; 1237 | } 1238 | $this->_relationships[$key] = array('relation' => $option, 'relation_key' => $key, 'foreign_model' => strtolower($foreign_model), 'foreign_table' => $foreign_table, 'foreign_key' => $foreign_key, 'local_key' => $local_key); 1239 | if($option == 'has_many_pivot') 1240 | { 1241 | $this->_relationships[$key]['pivot_table'] = $pivot_table; 1242 | $this->_relationships[$key]['pivot_local_key'] = $pivot_local_key; 1243 | $this->_relationships[$key]['pivot_foreign_key'] = $pivot_foreign_key; 1244 | $this->_relationships[$key]['get_relate'] = $get_relate; 1245 | } 1246 | } 1247 | } 1248 | } 1249 | } 1250 | } 1251 | /** END RELATIONSHIPS */ 1252 | /** 1253 | * public function on($connection_group = NULL) 1254 | * Sets a different connection to use for a query 1255 | * @param $connection_group = NULL - connection group in database setup 1256 | * @return obj 1257 | */ 1258 | public function on($connection_group = NULL) 1259 | { 1260 | if(isset($connection_group)) 1261 | { 1262 | $this->_database->close(); 1263 | $this->load->database($connection_group); 1264 | $this->_database = $this->db; 1265 | } 1266 | return $this; 1267 | } 1268 | /** 1269 | * public function reset($connection_group = NULL) 1270 | * Resets the connection to the default used for all the model 1271 | * @return obj 1272 | */ 1273 | public function reset() 1274 | { 1275 | if(isset($connection_group)) 1276 | { 1277 | $this->_database->close(); 1278 | $this->_set_connection(); 1279 | } 1280 | return $this; 1281 | } 1282 | /** 1283 | * Trigger an event and call its observers. Pass through the event name 1284 | * (which looks for an instance variable $this->event_name), an array of 1285 | * parameters to pass through and an optional 'last in interation' boolean 1286 | */ 1287 | public function trigger($event, $data = array(), $last = TRUE) 1288 | { 1289 | if (isset($this->$event) && is_array($this->$event)) 1290 | { 1291 | foreach ($this->$event as $method) 1292 | { 1293 | if (strpos($method, '(')) 1294 | { 1295 | preg_match('/([a-zA-Z0-9\_\-]+)(\(([a-zA-Z0-9\_\-\., ]+)\))?/', $method, $matches); 1296 | $method = $matches[1]; 1297 | $this->callback_parameters = explode(',', $matches[3]); 1298 | } 1299 | $data = call_user_func_array(array($this, $method), array($data, $last)); 1300 | } 1301 | } 1302 | return $data; 1303 | } 1304 | /** 1305 | * public function with_trashed() 1306 | * Sets $_trashed to TRUE 1307 | */ 1308 | public function with_trashed() 1309 | { 1310 | $this->_trashed = 'with'; 1311 | return $this; 1312 | } 1313 | /** 1314 | * public function with_trashed() 1315 | * Sets $_trashed to TRUE 1316 | */ 1317 | public function only_trashed() 1318 | { 1319 | $this->_trashed = 'only'; 1320 | return $this; 1321 | } 1322 | private function _where_trashed() 1323 | { 1324 | switch($this->_trashed) 1325 | { 1326 | case 'only' : 1327 | $this->_database->where($this->_deleted_at_field.' IS NOT NULL', NULL, FALSE); 1328 | break; 1329 | case 'without' : 1330 | $this->_database->where($this->_deleted_at_field.' IS NULL', NULL, FALSE); 1331 | break; 1332 | case 'with' : 1333 | break; 1334 | } 1335 | $this->_trashed = 'without'; 1336 | return $this; 1337 | } 1338 | /** 1339 | * public function fields($fields) 1340 | * does a select() of the $fields 1341 | * @param $fields the fields needed 1342 | * @return $this 1343 | */ 1344 | public function fields($fields = NULL) 1345 | { 1346 | if(isset($fields)) 1347 | { 1348 | if($fields == '*count*') 1349 | { 1350 | $this->_select = ''; 1351 | $this->_database->select('COUNT(*) AS counted_rows',FALSE); 1352 | } 1353 | else 1354 | { 1355 | $this->_select = array(); 1356 | $fields = (!is_array($fields)) ? explode(',', $fields) : $fields; 1357 | if (!empty($fields)) 1358 | { 1359 | foreach ($fields as &$field) 1360 | { 1361 | $exploded = explode('.', $field); 1362 | if (sizeof($exploded) < 2) 1363 | { 1364 | $field = $this->table . '.' . $field; 1365 | } 1366 | } 1367 | } 1368 | $this->_select = $fields; 1369 | } 1370 | } 1371 | else 1372 | { 1373 | $this->_select = NULL; 1374 | } 1375 | return $this; 1376 | } 1377 | /** 1378 | * public function order_by($criteria, $order = 'ASC' 1379 | * A wrapper to $this->_database->order_by() 1380 | * @param $criteria 1381 | * @param string $order 1382 | * @return $this 1383 | */ 1384 | public function order_by($criteria, $order = 'ASC') 1385 | { 1386 | if(is_array($criteria)) 1387 | { 1388 | foreach ($criteria as $key=>$value) 1389 | { 1390 | $this->_database->order_by($key, $value); 1391 | } 1392 | } 1393 | else 1394 | { 1395 | $this->_database->order_by($criteria, $order); 1396 | } 1397 | return $this; 1398 | } 1399 | /** 1400 | * Return the next call as an array rather than an object 1401 | */ 1402 | public function as_array() 1403 | { 1404 | $this->return_as = 'array'; 1405 | return $this; 1406 | } 1407 | /** 1408 | * Return the next call as an object rather than an array 1409 | */ 1410 | public function as_object() 1411 | { 1412 | $this->return_as = 'object'; 1413 | return $this; 1414 | } 1415 | public function as_dropdown($field = NULL) 1416 | { 1417 | if(!isset($field)) 1418 | { 1419 | show_error('MY_Model: You must set a field to be set as value for the key: ...->as_dropdown(\'field\')->...'); 1420 | exit; 1421 | } 1422 | $this->return_as_dropdown = 'dropdown'; 1423 | $this->_dropdown_field = $field; 1424 | $this->_select = array($this->primary_key, $field); 1425 | return $this; 1426 | } 1427 | public function set_cache($string, $seconds = 86400) 1428 | { 1429 | $prefix = (strlen($this->cache_prefix)>0) ? $this->cache_prefix.'_' : ''; 1430 | $this->_cache = array('cache_name' => $prefix.$string,'seconds'=>$seconds); 1431 | return $this; 1432 | } 1433 | private function _reset_cache($string) 1434 | { 1435 | if(isset($string)) 1436 | { 1437 | $this->_cache = array(); 1438 | } 1439 | return $this; 1440 | } 1441 | public function delete_cache($string = NULL) 1442 | { 1443 | $this->load->driver('cache'); 1444 | $prefix = (strlen($this->cache_prefix)>0) ? $this->cache_prefix.'_' : ''; 1445 | if(isset($string) && (strpos($string,'*') === FALSE)) 1446 | { 1447 | $this->cache->{$this->cache_driver}->delete($prefix . $string); 1448 | } 1449 | else 1450 | { 1451 | $cached = $this->cache->file->cache_info(); 1452 | foreach($cached as $file) 1453 | { 1454 | if(array_key_exists('relative_path',$file)) 1455 | { 1456 | $path = $file['relative_path']; 1457 | break; 1458 | } 1459 | } 1460 | $mask = (isset($string)) ? $path.$prefix.$string : $path.$prefix.'*'; 1461 | array_map('unlink', glob($mask)); 1462 | } 1463 | return $this; 1464 | } 1465 | /** 1466 | * private function _set_timestamps() 1467 | * 1468 | * Sets the fields for the created_at, updated_at and deleted_at timestamps 1469 | * @return bool 1470 | */ 1471 | private function _set_timestamps() 1472 | { 1473 | if($this->timestamps !== FALSE) 1474 | { 1475 | $this->_created_at_field = (is_array($this->timestamps) && isset($this->timestamps[0])) ? $this->timestamps[0] : 'created_at'; 1476 | $this->_updated_at_field = (is_array($this->timestamps) && isset($this->timestamps[1])) ? $this->timestamps[1] : 'updated_at'; 1477 | $this->_deleted_at_field = (is_array($this->timestamps) && isset($this->timestamps[2])) ? $this->timestamps[2] : 'deleted_at'; 1478 | } 1479 | return TRUE; 1480 | } 1481 | /** 1482 | * private function _the_timestamp() 1483 | * 1484 | * returns a value representing the date/time depending on the timestamp format choosed 1485 | * @return string 1486 | */ 1487 | private function _the_timestamp() 1488 | { 1489 | if($this->timestamps_format=='timestamp') 1490 | { 1491 | return time(); 1492 | } 1493 | else 1494 | { 1495 | return date($this->timestamps_format); 1496 | } 1497 | } 1498 | /** 1499 | * private function _set_connection() 1500 | * 1501 | * Sets the connection to database 1502 | */ 1503 | private function _set_connection() 1504 | { 1505 | //unset($this->db); 1506 | isset($this->_database_connection) ? $this->load->database($this->_database_connection) : $this->load->database(); 1507 | $this->_database = $this->db; 1508 | } 1509 | /* 1510 | * HELPER FUNCTIONS 1511 | */ 1512 | public function paginate($rows_per_page, $total_rows = NULL, $page_number = 1) 1513 | { 1514 | $this->load->helper('url'); 1515 | $segments = $this->uri->total_segments(); 1516 | $uri_array = $this->uri->segment_array(); 1517 | $page = $this->uri->segment($segments); 1518 | if(is_numeric($page)) 1519 | { 1520 | $page_number = $page; 1521 | } 1522 | else 1523 | { 1524 | $page_number = $page_number; 1525 | $uri_array[] = $page_number; 1526 | ++$segments; 1527 | } 1528 | $next_page = $page_number+1; 1529 | $previous_page = $page_number-1; 1530 | if($page_number == 1) 1531 | { 1532 | $this->previous_page = $this->pagination_delimiters[0].$this->pagination_arrows[0].$this->pagination_delimiters[1]; 1533 | } 1534 | else 1535 | { 1536 | $uri_array[$segments] = $previous_page; 1537 | $uri_string = implode('/',$uri_array); 1538 | $this->previous_page = $this->pagination_delimiters[0].anchor($uri_string,$this->pagination_arrows[0]).$this->pagination_delimiters[1]; 1539 | } 1540 | $uri_array[$segments] = $next_page; 1541 | $uri_string = implode('/',$uri_array); 1542 | if(isset($total_rows) && (ceil($total_rows/$rows_per_page) == $page_number)) 1543 | { 1544 | $this->next_page = $this->pagination_delimiters[0].$this->pagination_arrows[1].$this->pagination_delimiters[1]; 1545 | } 1546 | else 1547 | { 1548 | $this->next_page = $this->pagination_delimiters[0].anchor($uri_string, $this->pagination_arrows[1]).$this->pagination_delimiters[1]; 1549 | } 1550 | $rows_per_page = (is_numeric($rows_per_page)) ? $rows_per_page : 10; 1551 | if(isset($total_rows)) 1552 | { 1553 | if($total_rows!=0) 1554 | { 1555 | $number_of_pages = ceil($total_rows / $rows_per_page); 1556 | $links = $this->previous_page; 1557 | for ($i = 1; $i <= $number_of_pages; $i++) { 1558 | unset($uri_array[$segments]); 1559 | $uri_string = implode('/', $uri_array); 1560 | $links .= $this->pagination_delimiters[0]; 1561 | $links .= (($page_number == $i) ? anchor($uri_string, $i) : anchor($uri_string . '/' . $i, $i)); 1562 | $links .= $this->pagination_delimiters[1]; 1563 | } 1564 | $links .= $this->next_page; 1565 | $this->all_pages = $links; 1566 | } 1567 | else 1568 | { 1569 | $this->all_pages = $this->pagination_delimiters[0].$this->pagination_delimiters[1]; 1570 | } 1571 | } 1572 | if(isset($this->_cache) && !empty($this->_cache)) 1573 | { 1574 | $this->load->driver('cache'); 1575 | $cache_name = $this->_cache['cache_name'].'_'.$page_number; 1576 | $seconds = $this->_cache['seconds']; 1577 | $data = $this->cache->{$this->cache_driver}->get($cache_name); 1578 | } 1579 | if(isset($data) && $data !== FALSE) 1580 | { 1581 | return $data; 1582 | } 1583 | else 1584 | { 1585 | $this->trigger('before_get'); 1586 | $this->where(); 1587 | $this->limit($rows_per_page, (($page_number-1)*$rows_per_page)); 1588 | $data = $this->get_all(); 1589 | if($data) 1590 | { 1591 | if(isset($cache_name) && isset($seconds)) 1592 | { 1593 | $this->cache->{$this->cache_driver}->save($cache_name, $data, $seconds); 1594 | $this->_reset_cache($cache_name); 1595 | } 1596 | return $data; 1597 | } 1598 | else 1599 | { 1600 | return FALSE; 1601 | } 1602 | } 1603 | } 1604 | public function set_pagination_delimiters($delimiters) 1605 | { 1606 | if(is_array($delimiters) && sizeof($delimiters)==2) 1607 | { 1608 | $this->pagination_delimiters = $delimiters; 1609 | } 1610 | return $this; 1611 | } 1612 | public function set_pagination_arrows($arrows) 1613 | { 1614 | if(is_array($arrows) && sizeof($arrows)==2) 1615 | { 1616 | $this->pagination_arrows = $arrows; 1617 | } 1618 | return $this; 1619 | } 1620 | /** 1621 | * private function _fetch_table() 1622 | * 1623 | * Sets the table name when called by the constructor 1624 | * 1625 | */ 1626 | private function _fetch_table() 1627 | { 1628 | if (!isset($this->table)) 1629 | { 1630 | $this->table = $this->_get_table_name(get_class($this)); 1631 | } 1632 | return TRUE; 1633 | } 1634 | private function _get_table_name($model_name) 1635 | { 1636 | $table_name = plural(preg_replace('/(_m|_model)?$/', '', strtolower($model_name))); 1637 | return $table_name; 1638 | } 1639 | public function __call($method, $arguments) 1640 | { 1641 | if(substr($method,0,6) == 'where_') 1642 | { 1643 | $column = substr($method,6); 1644 | $this->where($column, $arguments); 1645 | return $this; 1646 | } 1647 | if(($method!='with_trashed') && (substr($method,0,5) == 'with_')) 1648 | { 1649 | $relation = substr($method,5); 1650 | $this->with($relation,$arguments); 1651 | return $this; 1652 | } 1653 | $parent_class = get_parent_class($this); 1654 | if ($parent_class !== FALSE && !method_exists($parent_class, $method) && !method_exists($this,$method)) 1655 | { 1656 | echo 'No method with that name ('.$method.') in MY_Model or CI_Model.'; 1657 | } 1658 | } 1659 | private function _build_sorter($data, $field, $order_by, $sort_by = 'DESC') 1660 | { 1661 | usort($data, function($a, $b) use ($field, $order_by, $sort_by) { 1662 | return strtoupper($sort_by) == "DESC" ? ($a[$field][$order_by] < $b[$field][$order_by]) : ($a[$field][$order_by] > $b[$field][$order_by]); 1663 | }); 1664 | return $data; 1665 | } 1666 | /** 1667 | * Verifies if an array is associative or not 1668 | * @param array $array 1669 | * @return bool 1670 | */ 1671 | private function _is_assoc(array $array) { 1672 | return (bool)count(array_filter(array_keys($array), 'is_string')); 1673 | } 1674 | 1675 | /* 1676 | public function add_creator($data) 1677 | { 1678 | $data['created_by'] = $_SESSION['user_id']; 1679 | return $data; 1680 | } 1681 | */ 1682 | 1683 | /* 1684 | public function add_updater($data) 1685 | { 1686 | $data['updated_by'] = $_SESSION['user_id']; 1687 | return $data; 1688 | } 1689 | */ 1690 | } -------------------------------------------------------------------------------- /MY_Profiler.php: -------------------------------------------------------------------------------- 1 | 4 | * - PX Webdesign 5 | * - Khairul Anwar 6 | * 7 | * Base on : - https://github.com/virutmath/codeigniter-custom/blob/master/MY_Profiler.php 8 | * - http://lab.clearpixel.com.au/2008/06/list-duplicate-database-queries-in-codeigniter/ 9 | * 10 | * Link : https://github.com/iruwl/simple-ci-profiler 11 | */ 12 | defined('BASEPATH') OR exit('No direct script access allowed'); 13 | class MY_Profiler extends CI_Profiler 14 | { 15 | protected $_available_sections = array( 16 | 'benchmarks', 17 | 'memory_usage', 18 | 'controller_info', 19 | 'uri_string', 20 | 'get', 21 | 'post', 22 | 'queries', 23 | 'duplicate_queries', 24 | 'http_headers', 25 | 'session_data', 26 | 'config' 27 | ); 28 | // http://php.net/manual/de/function.filesize.php 29 | private function human_filesize($bytes, $decimals = 2) { 30 | $sz = 'BKMGTP'; 31 | $factor = floor((strlen($bytes) - 1) / 3); 32 | return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor]; 33 | } 34 | protected function _compile_memory_usage() 35 | { 36 | return "\n\n" 37 | .'
' 38 | ."\n" 39 | .'  '.$this->CI->lang->line('profiler_memory_usage')."  \n" 40 | .'
' 41 | // .(($usage = memory_get_usage()) != '' ? number_format($usage).' bytes' : $this->CI->lang->line('profiler_no_memory')) 42 | .(($usage = memory_get_usage()) != '' ? $this->human_filesize($usage).' bytes' : $this->CI->lang->line('profiler_no_memory')) 43 | .'
'; 44 | } 45 | //fix complete queries fieldset 46 | protected function _compile_queries() 47 | { 48 | $dbs = array(); 49 | // Let's determine which databases are currently connected to 50 | foreach (get_object_vars($this->CI) as $name => $cobject) 51 | { 52 | if (is_object($cobject)) 53 | { 54 | if ($cobject instanceof CI_DB) 55 | { 56 | $dbs[get_class($this->CI).':$'.$name] = $cobject; 57 | } 58 | elseif ($cobject instanceof CI_Model) 59 | { 60 | foreach (get_object_vars($cobject) as $mname => $mobject) 61 | { 62 | if ($mobject instanceof CI_DB) 63 | { 64 | $dbs[get_class($cobject).':$'.$mname] = $mobject; 65 | } 66 | } 67 | } 68 | } 69 | } 70 | if (count($dbs) === 0) 71 | { 72 | return "\n\n" 73 | .'
' 74 | ."\n" 75 | .'  '.$this->CI->lang->line('profiler_queries').'  ' 76 | ."\n\n\n\n" 77 | .'\n
' 78 | .$this->CI->lang->line('profiler_no_db') 79 | ."
\n
"; 80 | } 81 | // Load the text helper so we can highlight the SQL 82 | $this->CI->load->helper('text'); 83 | // Key words we want bolded 84 | $highlight = array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'AND', 'LEFT JOIN', 'ORDER BY', 'GROUP BY', 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR ', 'HAVING', 'OFFSET', 'NOT IN', 'IN', 'LIKE', 'NOT LIKE', 'COUNT', 'MAX', 'MIN', 'ON', 'AS', 'AVG', 'SUM', '(', ')'); 85 | $output = "\n\n"; 86 | $count = 0; 87 | foreach ($dbs as $name => $db) 88 | { 89 | $hide_queries = (count($db->queries) > $this->_query_toggle_count) ? ' display:none' : ''; 90 | $total_time = number_format(array_sum($db->query_times), 4).' '.$this->CI->lang->line('profiler_seconds'); 91 | $show_hide_js = '('.$this->CI->lang->line('profiler_section_hide').')'; 92 | if ($hide_queries !== '') 93 | { 94 | $show_hide_js = '('.$this->CI->lang->line('profiler_section_show').')'; 95 | } 96 | $output .= '
' 97 | ."\n" 98 | .'  '.$this->CI->lang->line('profiler_database') 99 | .':  '.$db->database.' ('.$name.')   '.$this->CI->lang->line('profiler_queries') 100 | .': '.count($db->queries).' ('.$total_time.')  '.$show_hide_js."\n\n\n" 101 | .'\n"; 102 | if (count($db->queries) === 0) 103 | { 104 | $output .= '\n"; 106 | } 107 | else 108 | { 109 | foreach ($db->queries as $key => $val) 110 | { 111 | $time = number_format($db->query_times[$key], 4); 112 | $val = highlight_code($val); 113 | foreach ($highlight as $bold) 114 | { 115 | $val = str_replace($bold, ''.$bold.'', $val); 116 | } 117 | $output .= '\n"; 120 | } 121 | } 122 | $output .= "
' 105 | .$this->CI->lang->line('profiler_no_queries')."
' 118 | .$time.'  ' 119 | .$val."
\n
"; 123 | $count++; 124 | } 125 | return $output; 126 | } 127 | protected function _compile_duplicate_queries() { 128 | $output = ''; 129 | if ( ! class_exists('CI_DB_driver')) 130 | { 131 | $output .= "\n\n"; 132 | $output .= '
'; 133 | $output .= "\n"; 134 | $output .= '  DUPLICATE QUERIES  '; 135 | $output .= "\n"; 136 | $output .= "\n\n\n"; 137 | $output .="\n"; 138 | $output .= "
".$this->CI->lang->line('profiler_no_db')."
\n"; 139 | $output .= "
"; 140 | } 141 | else 142 | { 143 | $dbs = array(); 144 | // Let's determine which databases are currently connected to 145 | foreach (get_object_vars($this->CI) as $name => $cobject) 146 | { 147 | if (is_object($cobject)) 148 | { 149 | if ($cobject instanceof CI_DB) 150 | { 151 | $dbs[get_class($this->CI).':$'.$name] = $cobject; 152 | } 153 | elseif ($cobject instanceof CI_Model) 154 | { 155 | foreach (get_object_vars($cobject) as $mname => $mobject) 156 | { 157 | if ($mobject instanceof CI_DB) 158 | { 159 | $dbs[get_class($cobject).':$'.$mname] = $mobject; 160 | } 161 | } 162 | } 163 | } 164 | } 165 | foreach ($dbs as $name => $db) 166 | { 167 | $queries['original'] = $db->queries; 168 | $queries['unique'] = array_unique($db->queries); 169 | $queries['duplicates'] = array_diff_assoc($queries['original'],$queries['unique']); 170 | $duplicateOutput = ''; 171 | $duplicates = array(); 172 | $duplicatesCount = array(); 173 | // Append number of dupes 174 | if ($queries['duplicates']) { 175 | $highlight = array('SELECT', 'FROM', 'WHERE', 'AND', 'LEFT JOIN', 'ORDER BY', 'LIMIT', 'INSERT', 'INTO', 'VALUES', 'UPDATE', 'OR'); 176 | // Build the duplicates array 177 | $i = 0; 178 | foreach ($queries['duplicates'] as $duplicateQuery) { 179 | if (is_numeric($key = array_search($duplicateQuery,$duplicates))) { 180 | // Found query so just increment 181 | $duplicatesCount[$key]++; 182 | } else { 183 | // Query not found so add and increment 184 | $duplicates[$i] = $duplicateQuery; 185 | $duplicatesCount[$i] = 2; 186 | $i++; 187 | } 188 | } 189 | foreach ($duplicates as $key => $val) 190 | { 191 | $val = htmlspecialchars($val, ENT_QUOTES); 192 | foreach ($highlight as $bold) 193 | { 194 | $val = str_replace($bold, ''.$bold.'', $val); 195 | } 196 | $duplicateOutput .= "[".$duplicatesCount[$key]."]  ".$val."\n"; 197 | } 198 | } 199 | // Calculate number of dupes 200 | $duplicateNum = count($duplicates); 201 | if (count($db->queries) > 1 && $duplicateNum > 0) { 202 | $output .= "\n\n"; 203 | $output .= '
'; 204 | $output .= "\n"; 205 | $output .= '  DUPLICATE QUERIES: '.$db->database.' ('.$name.')  '; 206 | $output .= "\n"; 207 | $output .= "\n\n\n"; 208 | $output .= $duplicateOutput; 209 | $output .= "
\n"; 210 | $output .= "
"; 211 | } 212 | // If no dupes then don't output 213 | if (!count($queries['duplicates'])) { 214 | $output .= "\n\n"; 215 | $output .= '
'; 216 | $output .= "\n"; 217 | $output .= '  DUPLICATE QUERIES: '.$db->database.' ('.$name.')  '; 218 | $output .= "\n"; 219 | $output .= "No duplicate queries"; 220 | $output .= "
"; 221 | } 222 | } 223 | } 224 | return $output; 225 | } 226 | public function run() 227 | { 228 | $output = ''; 229 | $output .= ' 230 | '; 337 | $output .= '
'; 338 | $output .= $this->addDebugBar(); 339 | $output .= parent::run(); 340 | $output .= '
'; 341 | //add script 342 | $output .= ' 343 | '; 449 | return $output; 450 | } 451 | protected function addDebugBar() { 452 | $debugBar = ''; 453 | $debugBar .= '
'; 454 | $debugBar .= $this->debugBarTab(); 455 | $debugBar .= '
456 | × 457 |
'; 458 | $debugBar .= '
459 | 460 |
'; 461 | $debugBar .= '
'; 462 | return $debugBar; 463 | } 464 | protected function debugBarTab() { 465 | $tab = $this->debugIcon(); 466 | foreach ($this->_available_sections as $section) 467 | if ($this->_compile_{$section} !== FALSE) 468 | $tab .= $this->debugTabs($section); 469 | return $tab; 470 | } 471 | protected function debugIcon() { 472 | $tab = '
'; 473 | $tab .= ''; 474 | $tab .= '
'; 475 | return $tab; 476 | } 477 | protected function debugTabs($section) { 478 | $tab = '
'; 479 | // $tab .= ''.strtoupper($section).''; 480 | // $tab .= ''.$section.''; 481 | // $tab .= ''.$section.''; 482 | $tab .= ''.$section.''; 483 | $tab .= '
'; 484 | return $tab; 485 | } 486 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # codeigniter-custom 2 | CodeIgniter custom with cli, blade template, my_model... 3 | Save file to folder application/libraries 4 | 5 | ## Blade 6 | This is a port standalone of Laravel blade template engine. 7 | Docs: https://laravel.com/docs/5.2/blade 8 | 9 | ## MY_Model 10 | This model was forked from https://github.com/avenirer/CodeIgniter-MY_Model. Support CRUD and trigger function 11 | 12 | ## MY_Profiler 13 | If you want see profiler like a bottom-float bar, you can try it. 14 | Save file to libraries then enable profiler in config 15 | ![Demo](https://kiendt_vn.tinytake.com/sf/NjUzOTYyXzMxNjE1OTM) 16 | 17 | ## TableAdmin 18 | Generate table listing record in admin 19 | Example: 20 | 21 | ``` 22 | class CategoryController extends MY_Controller { 23 | ... 24 | public function index() { 25 | $allCat = $this->categoryRepository->getAllCategory(0); 26 | $this->dataView['list'] = $this->parseMultiLevelData($allCat); 27 | $listIcon = \Solid\Collections\Category::ICON; 28 | $tableConfig = [ 29 | 'module'=>$this->adminModule 30 | ]; 31 | $table = new TableAdmin($this->dataView['list'],$tableConfig); 32 | $table->column('id','ID'); 33 | $table->columnDropdown('icon','Icon',$listIcon); 34 | $table->column('active','Active','checkbox'); 35 | $table->column('id','Edit','edit'); 36 | $table->column('id','Delete','delete'); 37 | 38 | $this->dataView['tableAdmin'] = $table->render(); 39 | $this->blade->render('admin.category.index',$this->dataView); 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /TableAdmin.php: -------------------------------------------------------------------------------- 1 | array( 9 | 'id' => false 10 | ), 11 | 'formatDate' => 'd/m/Y', 12 | 'defaultOptionLabel' => '', 13 | 'pageSize' => 30, 14 | 'module' => '', 15 | 'idField' => 'id', 16 | 'colorScheme' => 'primary'//bootstrap 4.0 color scheme 17 | ); 18 | private $editLink; 19 | private $deleteLink; 20 | private $arrayFieldShow = array(); 21 | private $arrayFieldType = array(); 22 | private $arrayLabel = array(); 23 | private $arraySortable = array(); 24 | private $arrayFieldSearch = array(); 25 | private $arrayFieldSearchLike = array(); 26 | private $arrayDropdownOption = array(); 27 | private $arrayCustomSearch = array(); 28 | private $i; 29 | private $total; 30 | private $pageSize = 30; 31 | private $currentPage = 1; 32 | const SORT_BY_FIELD = 'sort_by'; 33 | const SORT_TYPE_FIELD = 'sort_type'; 34 | const SEARCH_FIELD_PREFIX = 'tableAdmin_search_'; 35 | const SEARCH_LIKE_FIELD_PREFIX = 'tableAdmin_like_'; 36 | 37 | public function __construct($list = array(), $config = array()) 38 | { 39 | if ($list) { 40 | $this->listRecord = $list; 41 | $this->i = 0; 42 | } 43 | 44 | if ($config) { 45 | $this->extendAttributes($config, $this->config);//TODO must add config 46 | $this->setDefaultLink(); 47 | } 48 | } 49 | 50 | public static function initialize($list, $config = array()) 51 | { 52 | return new Table_admin($list, $config); 53 | } 54 | 55 | 56 | private function setDefaultLink() 57 | { 58 | if ($this->config['module']) { 59 | $this->editLink = '/' . $this->config['module'] . '/edit/$' . $this->config['idField']; 60 | $this->deleteLink = '/' . $this->config['module'] . '/delete'; 61 | } 62 | } 63 | 64 | public function setEditLink($pattern = '') 65 | { 66 | if (!$pattern) { 67 | $pattern = '/' . $this->config['module'] . '/edit/$' . $this->config['idField']; 68 | } 69 | $this->editLink = $pattern; 70 | } 71 | 72 | public function setDeleteLink($pattern = '') 73 | { 74 | if (!$pattern) { 75 | $pattern = '/' . $this->config['module'] . '/delete'; 76 | } 77 | $this->deleteLink = $pattern; 78 | } 79 | 80 | public function paging($totalItems, $pageSize) 81 | { 82 | $this->total = $totalItems; 83 | $this->pageSize = $pageSize; 84 | } 85 | 86 | /** 87 | * @param string $field 88 | * @param string $label 89 | * @param string $type 90 | * @param bool $sortable 91 | * @param bool $searchable 92 | * @param bool $search_like 93 | */ 94 | public function column($field = '', $label = '', $type = '', $sortable = false, $searchable = false, $search_like = false) 95 | { 96 | $i = $this->i++; 97 | //add label of column 98 | $this->arrayLabel[$i] = $label; 99 | $this->arraySortable[$i] = $sortable; 100 | $this->arrayFieldSearch[$i] = (bool)$searchable; 101 | $this->arrayFieldSearchLike[$i] = (bool)$search_like; 102 | $this->arrayFieldShow[$i] = $field; 103 | $this->arrayFieldType[$i] = $type; 104 | } 105 | 106 | public function columnDropdown($field = '', $label = '', $option = array(), $sortable = false, $searchable = false, $search_like = false) 107 | { 108 | $i = $this->i++; 109 | //add label of column 110 | $this->arrayLabel[$i] = $label; 111 | $this->arraySortable[$i] = $sortable; 112 | $this->arrayFieldSearch[$i] = !!($searchable); 113 | $this->arrayFieldSearchLike[$i] = !!($search_like); 114 | $this->arrayFieldShow[$i] = $field; 115 | $this->arrayFieldType[$i] = 'dropdown'; 116 | $this->arrayDropdownOption[$i] = $option; 117 | } 118 | 119 | public function columnSearchDropdown($field = '', $label = '', $option = array(), $sortable = false, $searchable = false, $search_like = false) 120 | { 121 | $i = $this->i++; 122 | //add label of column 123 | $this->arrayLabel[$i] = $label; 124 | $this->arraySortable[$i] = $sortable; 125 | $this->arrayFieldSearch[$i] = !!($searchable); 126 | $this->arrayFieldSearchLike[$i] = !!($search_like); 127 | $this->arrayFieldShow[$i] = $field; 128 | $this->arrayFieldType[$i] = 'search_dropdown'; 129 | $this->arrayDropdownOption[$i] = $option; 130 | } 131 | 132 | public function render() 133 | { 134 | $this->renderTable(); 135 | return $this->table; 136 | } 137 | 138 | public static function getSort() 139 | { 140 | if (get_instance()->input->get(self::SORT_BY_FIELD, true)) { 141 | return array(snake_case(get_instance()->input->get(self::SORT_BY_FIELD, true)) => get_instance()->input->get(self::SORT_TYPE_FIELD, true)); 142 | } else 143 | return array(); 144 | } 145 | 146 | public static function getSearch() 147 | { 148 | $arr = array(); 149 | foreach ($_GET as $field => $item) { 150 | if ($item == '0') { 151 | $field = snake_case(str_replace(self::SEARCH_FIELD_PREFIX, '', $field)); 152 | $arr[$field] = $item; 153 | } 154 | if ($item && strpos($field, self::SEARCH_FIELD_PREFIX) !== FALSE) { 155 | $field = snake_case(str_replace(self::SEARCH_FIELD_PREFIX, '', $field)); 156 | $arr[$field] = $item; 157 | } 158 | if ($item && strpos($field, self::SEARCH_LIKE_FIELD_PREFIX) !== FALSE) { 159 | $field = snake_case(str_replace(self::SEARCH_LIKE_FIELD_PREFIX, '', $field)); 160 | $arr[$field] = array($field, 'like', $item); 161 | } 162 | } 163 | 164 | foreach ($arr as $field => &$value) { 165 | if (is_string($value)) { 166 | $value = array($field, '=', $value); 167 | } 168 | } 169 | return $arr; 170 | } 171 | 172 | /** 173 | * @param $field 174 | * @param null $defaultValue | option value 175 | * @param $type : type 1: input text, type 2: option 176 | * @param string $label placeholder 177 | * @param bool $searchLike 178 | * @return $this 179 | */ 180 | public function addSearch($field, $type, $label = '',$defaultValue = null, $searchLike = false) 181 | { 182 | $this->arrayCustomSearch[$field] = array('value' => $defaultValue, 'type' => $type, 'like' => $searchLike, 'label'=>$label); 183 | return $this; 184 | } 185 | 186 | private function generateCustomSearch() 187 | { 188 | if (!$this->arrayCustomSearch) { 189 | return ''; 190 | } 191 | $str = ''; 192 | foreach ($this->arrayCustomSearch as $field => $search) { 193 | $controlName = $search['like'] ? Table_Admin::SEARCH_LIKE_FIELD_PREFIX . $field : Table_Admin::SEARCH_FIELD_PREFIX . $field; 194 | $value = get_instance()->input->get($controlName, true) ?: ''; 195 | switch ($search['type']) { 196 | case 1: 197 | $str .= '
198 | 199 |
'; 200 | break; 201 | case 2: 202 | if (is_array($search['value'])) { 203 | $options = ''; 204 | foreach ($search['value'] as $k => $v) { 205 | $options .= ''; 206 | } 207 | $str .= '
208 | 210 |
'; 211 | } 212 | break; 213 | } 214 | } 215 | return $str; 216 | } 217 | 218 | private function showHeader() 219 | { 220 | $arrayFieldSearch = $this->arrayFieldSearch; 221 | $arrayFieldSearch = array_filter($arrayFieldSearch); 222 | if (empty($arrayFieldSearch) && empty($this->arrayCustomSearch)) { 223 | return ''; 224 | } 225 | $str = '
'; 226 | $str .= '
'; 227 | foreach ($this->arrayFieldShow as $i => $field) { 228 | if ($this->arrayFieldSearch[$i]) { 229 | $input_name = $this->arrayFieldSearchLike[$i] ? self::SEARCH_LIKE_FIELD_PREFIX . $field : self::SEARCH_FIELD_PREFIX . $field; 230 | $value = get_instance()->input->get($input_name, true); 231 | $value = $value ?: ''; 232 | $str .= '
'; 233 | if ($this->arrayFieldType[$i] == 'dropdown' || $this->arrayFieldType[$i] == 'search_dropdown') { 234 | $str .= ''; 241 | } else { 242 | $str .= ''; 246 | } 247 | $str .= '
'; 248 | } 249 | } 250 | $str .= $this->generateCustomSearch(); 251 | $str .= '
252 | 253 |
254 |
255 |
'; 256 | return $str; 257 | } 258 | 259 | private function showFooter() 260 | { 261 | if (!$this->total) { 262 | $this->total = count($this->listRecord); 263 | } 264 | $pagination = $this->getPaginationString(); 265 | $from = !$this->listRecord ? 0 : ($this->currentPage - 1) * $this->pageSize + 1; 266 | $to = $this->currentPage * $this->pageSize > $this->total ? $this->total : $this->currentPage * $this->pageSize; 267 | $footer = '
'; 268 | $footer .= '
269 |
270 | Showing ' . $from 271 | . ' to ' . $to . ' of ' . $this->total . ' entries 272 |
273 |
'; 274 | $footer .= '
'; 275 | $footer .= '
'; 276 | $footer .= $pagination; 277 | $footer .= '
'; 278 | $footer .= '
'; 279 | $footer .= '
'; 280 | return $footer; 281 | } 282 | 283 | private function uriString() 284 | { 285 | return strtok($_SERVER['REQUEST_URI'], '?'); 286 | } 287 | 288 | private function queryString() 289 | { 290 | return $_SERVER['QUERY_STRING']; 291 | } 292 | 293 | private function parseQueryParams() 294 | { 295 | $query = $this->queryString(); 296 | parse_str($query, $params); 297 | return $params; 298 | } 299 | 300 | private function addQueryParams($arrayParams) 301 | { 302 | $targetPage = $this->uriString(); 303 | $queryParams = $this->parseQueryParams(); 304 | foreach ($arrayParams as $param => $value) { 305 | $queryParams[$param] = $value; 306 | } 307 | return $targetPage . '?' . http_build_query($queryParams); 308 | } 309 | 310 | //function to return the pagination string 311 | private function getPaginationString($adjacents = 1, $pageParam = 'page') 312 | { 313 | //defaults 314 | if (!$adjacents) $adjacents = 1; 315 | $limit = $this->pageSize; 316 | $totalItems = $this->total; 317 | if (!$pageParam) $pageParam = 'page'; 318 | $targetPage = $this->uriString(); 319 | $queryParams = $this->parseQueryParams(); 320 | 321 | if (isset($queryParams[$pageParam])) { 322 | $this->currentPage = $queryParams[$pageParam]; 323 | unset($queryParams[$pageParam]); 324 | } else { 325 | $this->currentPage = 1; 326 | } 327 | $targetPage .= '?' . http_build_query($queryParams); 328 | $pageString = $queryParams ? '&page=' : 'page='; 329 | //other vars 330 | $prev = $this->currentPage - 1; //previous page is page - 1 331 | $next = $this->currentPage + 1; //next page is page + 1 332 | $lastPage = ceil($totalItems / $limit); //lastpage is = total items / items per page, rounded up. 333 | $lpm1 = $lastPage - 1; //last page minus 1 334 | 335 | /* 336 | Now we apply our rules and draw the pagination object. 337 | We're actually saving the code to a variable in case we want to draw it more than once. 338 | */ 339 | $pagination = ''; 340 | if ($lastPage > 1) { 341 | $pagination .= '"; 405 | } 406 | 407 | return $pagination; 408 | 409 | } 410 | 411 | private function parseLink($link, $dataObject) 412 | { 413 | $exp = explode('/', $link); 414 | $params = array(); 415 | if ($exp) { 416 | foreach ($exp as $section) { 417 | if (starts_with($section, '$')) { 418 | $params[] = $section; 419 | } 420 | } 421 | } 422 | foreach ($params as $param) { 423 | $param_name = substr($param, 1); 424 | $link = str_replace($param, $this->prepareFieldName($dataObject, $param_name), $link); 425 | } 426 | return $link; 427 | } 428 | 429 | 430 | private function th() 431 | { 432 | $str = ''; 433 | if ($this->config['show']['id']) { 434 | $str .= ''; 435 | } 436 | foreach ($this->arrayLabel as $key => $label) { 437 | $str .= '' . $label . ' ' . $this->addSort($key) . ''; 438 | } 439 | return $str . ''; 440 | } 441 | 442 | private function addSort($column_key) 443 | { 444 | if ($this->arraySortable[$column_key]) { 445 | $sort_by = get_instance()->input->get(self::SORT_BY_FIELD, true); 446 | $current_sort_type = get_instance()->input->get(self::SORT_TYPE_FIELD, true); 447 | $current_sort_type = strtolower($current_sort_type); 448 | $current_sort_type = $current_sort_type == 'asc' ? 'desc' : 'asc'; 449 | $sortField = is_string($this->arraySortable[$column_key]) 450 | ? $this->arraySortable[$column_key] 451 | : $this->arrayFieldShow[$column_key]; 452 | $url = $this->addQueryParams(array( 453 | self::SORT_BY_FIELD => $sortField, 454 | self::SORT_TYPE_FIELD => $current_sort_type 455 | )); 456 | $class_icon = $current_sort_type == 'asc' && ($this->arrayFieldShow[$column_key] == $sort_by || $this->arraySortable[$column_key] == $sort_by) 457 | ? 'fa-sort-amount-desc' 458 | : 'fa-sort-amount-asc'; 459 | return ''; 460 | } 461 | return ''; 462 | } 463 | 464 | private function start_tr($record_id) 465 | { 466 | if (!$this->config['show']['id']) 467 | return ''; 468 | $str = '' . $this->generateCheckbox(array( 469 | 'name' => 'row-checkbox-' . $record_id, 470 | 'id' => 'row-checkbox-' . $record_id, 471 | 'data-id' => $record_id, 472 | 'class' => 'form-control iCheck row-checkbox', 473 | 'value' => 0 474 | )) . ''; 475 | return $str; 476 | } 477 | 478 | private function tr($row_data, $record_id) 479 | { 480 | $str = ''; 481 | $str .= $this->start_tr($record_id); 482 | foreach ($this->arrayFieldShow as $key => $fieldName) { 483 | $type = $this->arrayFieldType[$key]; 484 | $type = explode(':', $type); 485 | $align = isset($type[1]) ? $type[1] : ''; 486 | $type = $type[0]; 487 | $value = !in_array($type, array('value', 'edit', 'delete')) ? $this->prepareFieldName($row_data, $fieldName) : $fieldName; 488 | 489 | switch ($type) { 490 | case 'checkbox': 491 | $align_default = $align ? $align : 'center'; 492 | $str .= '' . $this->generateCheckbox(array( 493 | 'name' => 'control-' . $fieldName . '-' . $record_id, 494 | 'id' => 'control-' . $fieldName . '-' . $record_id, 495 | 'data-id' => $record_id, 496 | 'class' => 'form-control iCheck control-' . $fieldName, 497 | 'value' => $value 498 | )) . ''; 499 | break; 500 | case 'image': 501 | $align_default = $align ? $align : 'center'; 502 | $str .= '' . 503 | $this->generateImage(array( 504 | 'name' => 'control-' . $fieldName . '-' . $record_id, 505 | 'id' => 'control-' . $fieldName . '-' . $record_id, 506 | 'data-id' => $record_id, 507 | 'class' => 'control-' . $fieldName, 508 | 'imagePath' => $value 509 | )) 510 | . ''; 511 | break; 512 | case 'dateint': 513 | $align_default = $align ? $align : 'center'; 514 | $str .= '' . date($this->config['formatDate'], $value) . ''; 515 | break; 516 | case 'date': 517 | case 'datetime': 518 | $align_default = $align ? $align : 'center'; 519 | $str .= '' . $value . ''; 520 | break; 521 | case 'url': 522 | $align_default = $align ? $align : 'center'; 523 | $str .= 'Xem'; 524 | break; 525 | case 'dropdown': 526 | $str .= '' . $this->generateDropdown(array( 527 | 'name' => 'control-' . $fieldName . '-' . $record_id, 528 | 'id' => 'control-' . $fieldName . '-' . $record_id, 529 | 'data-id' => $record_id, 530 | 'class' => 'form-control select2 dropdown control-' . $fieldName, 531 | 'value' => $value, 532 | 'option' => $this->arrayDropdownOption[$key] 533 | )) . ''; 534 | break; 535 | case 'edit': 536 | $align_default = $align ? $align : 'center'; 537 | $str .= ' 538 | 539 | 540 | 541 | '; 542 | break; 543 | case 'delete': 544 | $align_default = $align ? $align : 'center'; 545 | $str .= ' 546 | 549 | 550 | '; 551 | break; 552 | case 'text': 553 | $align_default = $align ? $align : 'left'; 554 | $str .= '' . $value . ''; 555 | break; 556 | case 'number': 557 | $align_default = $align ? $align : 'right'; 558 | $str .= '' . number_format(intval($value)) . ''; 559 | break; 560 | case 'activebox': 561 | $align_default = $align ? $align : 'center'; 562 | $str .= '' . $this->generateActive(array( 563 | 'name' => 'control-' . $fieldName . '-' . $record_id, 564 | 'id' => 'control-' . $fieldName . '-' . $record_id, 565 | 'data-id' => $record_id, 566 | 'class' => 'form-control iCheck control-' . $fieldName, 567 | 'value' => $value 568 | )) . ''; 569 | break; 570 | default: 571 | $align_default = $align ? $align : 'left'; 572 | $str .= '' . $value . ''; 573 | break; 574 | } 575 | } 576 | return $str . ''; 577 | } 578 | 579 | private function prepareFieldName($dataObject, $string = '') 580 | { 581 | //check if $string include callable function 582 | $fn = ''; 583 | if ($string) { 584 | $arr = explode('|', $string); 585 | if (count($arr) == 2) { 586 | $fn = $arr[1]; 587 | $string = $arr[0]; 588 | } 589 | } 590 | $arr = explode('.', $string); 591 | if ($arr) { 592 | foreach ($arr as $property) { 593 | $propertyArr = explode('*', $property); 594 | if (is_object($dataObject)) { 595 | $dataObject = property_exists($dataObject, $propertyArr[0]) ? $dataObject->{$propertyArr[0]} : ''; 596 | 597 | } elseif (is_array($dataObject)) { 598 | $dataObject = isset($dataObject[$propertyArr[0]]) ? $dataObject[$propertyArr[0]] : ''; 599 | } 600 | 601 | for ($i = 1; $i <= count($propertyArr) - 1; $i++) { 602 | if (isset($propertyArr[$i])) { 603 | $dataObject = isset($dataObject[$propertyArr[$i]]) ? $dataObject[$propertyArr[$i]] : ''; 604 | } 605 | } 606 | 607 | continue; 608 | } 609 | } 610 | 611 | if ($fn && is_callable($fn)) { 612 | $dataObject = $fn($dataObject); 613 | } 614 | return $dataObject; 615 | } 616 | 617 | private function renderTable() 618 | { 619 | $this->table = $this->showHeader(); 620 | $this->table .= '
'; 621 | $this->table .= $this->th(); 622 | if ($this->listRecord) { 623 | foreach ($this->listRecord as $i => $record) { 624 | if (property_exists($record, 'id')) { 625 | $id = $record->id; 626 | } else { 627 | $id = $i; 628 | } 629 | $this->table .= $this->tr($record, $id); 630 | } 631 | } 632 | $this->table .= '
'; 633 | $this->table .= '
'; 634 | $this->table .= $this->showFooter(); 635 | } 636 | 637 | private function generateActive($attribute = array()) 638 | { 639 | $default = $this->controlDefaultValue(); 640 | $this->extendAttributes($attribute, $default); 641 | $default['checked'] = $default['value'] ? 'checked' : ''; 642 | $default['data-id'] = isset($attribute['data-id']) ? ' data-id="' . $attribute['data-id'] . '" ' : ''; 643 | return '
644 |
645 |
'; 646 | } 647 | 648 | private function generateCheckbox($attribute = array()) 649 | { 650 | $default = $this->controlDefaultValue(); 651 | $this->extendAttributes($attribute, $default); 652 | $default['checked'] = $default['value'] ? 'checked' : ''; 653 | $default['data-id'] = isset($attribute['data-id']) ? ' data-id="' . $attribute['data-id'] . '" ' : ''; 654 | return '
655 | defaultControlAttribute($default) . ' 657 | ' . $default['data-id'] . ' 658 | value="1" 659 | ' . $default['checked'] . '/> 660 |
'; 661 | } 662 | 663 | private function generateImage($attribute = array()) 664 | { 665 | $default = $this->controlDefaultValue(); 666 | $this->extendAttributes($attribute, $default); 667 | return '
'; 668 | } 669 | 670 | private function defaultOption($label = '', $value = '') 671 | { 672 | $label = $label ?: $this->config['defaultOptionLabel']; 673 | return ''; 674 | } 675 | 676 | private function generateDropdown($attribute = array()) 677 | { 678 | $default = $this->controlDefaultValue(); 679 | $this->extendAttributes($attribute, $default); 680 | $default['defaultValue'] = isset($default['defaultValue']) ? $default['defaultValue'] : ''; 681 | $opts = $this->defaultOption($default['label'], $default['defaultValue']); 682 | $default['data-id'] = isset($attribute['data-id']) ? ' data-id="' . $attribute['data-id'] . '" ' : ''; 683 | foreach ($default['option'] as $key => $val) { 684 | $selected = $default['value'] == $key ? 'selected' : ''; 685 | $opts .= ''; 686 | } 687 | // echo $default['value']; 688 | return '
689 | 692 |
'; 693 | } 694 | 695 | private function defaultControlAttribute($default) 696 | { 697 | $default['id'] = str_replace(array('#', '.', '*', '|'), '-', $default['id']); 698 | $default['class'] = str_replace(array('#', '.', '*', '|'), '-', $default['class']); 699 | return ' name="' . $default['name'] . '" id="' . $default['id'] . '" class="' . $default['class'] . '" '; 700 | } 701 | 702 | private function extendAttributes($attributes, &$default) 703 | { 704 | if (is_array($attributes)) { 705 | foreach ($default as $key => $val) { 706 | if (isset($attributes[$key])) { 707 | $default[$key] = $attributes[$key]; 708 | unset($attributes[$key]); 709 | } 710 | } 711 | if (count($attributes) > 0) { 712 | $default = array_merge($default, $attributes); 713 | } 714 | } 715 | return $default; 716 | } 717 | 718 | private function controlDefaultValue() 719 | { 720 | return array( 721 | 'name' => '', 722 | 'label' => '', 723 | 'id' => '', 724 | 'value' => '', 725 | 'title' => '', 726 | 'class' => '' 727 | ); 728 | } 729 | } 730 | --------------------------------------------------------------------------------