From f21ba4ddfc12ad5050ea54a741e6a66ea3cfd5b6 Mon Sep 17 00:00:00 2001 From: Kamil Tunkiewicz Date: Tue, 22 Jul 2014 15:01:33 +0200 Subject: [PATCH 1/7] Datatables 1.10+ update, backwards compatibile --- src/Bllim/Datatables/Datatables.php | 151 +++++++++++++++++++--------- 1 file changed, 101 insertions(+), 50 deletions(-) diff --git a/src/Bllim/Datatables/Datatables.php b/src/Bllim/Datatables/Datatables.php index bf0d585e..25c291fb 100644 --- a/src/Bllim/Datatables/Datatables.php +++ b/src/Bllim/Datatables/Datatables.php @@ -39,11 +39,60 @@ class Datatables protected $result_array = array(); protected $result_array_r = array(); + protected $input = array(); protected $mDataSupport; protected $index_column; + /** + * Read Input into $this->input according to jquery.dataTables.js version + */ + public function __construct() { + + if (Input::has('draw')) { + + // version 1.10+ + $this->input = Input::get(); + + } else { + + // version < 1.10 + + $this->input['draw'] = Input::get('sEcho',''); + $this->input['start'] = Input::get('iDisplayStart',''); + $this->input['length'] = Input::get('iDisplayLength',''); + $this->input['search'] = array( + 'value' => Input::get('sSearch',''), + 'regex' => Input::get('bRegex',''), + ); + $this->input['_'] = Input::get('_',''); + + $columns = explode(',',Input::get('sColumns','')); + $this->input['columns'] = array(); + for($i=0;$iinput['columns'][] = $arr; + } + + $this->input['order'] = array(); + for($i=0;$iinput['order'][] = $arr; + } + } + + return $this; + } + /** * Gets query and returns instance of class * @@ -377,9 +426,9 @@ protected function include_in_array($item,$array) */ protected function paging() { - if(!is_null(Input::get('iDisplayStart')) && Input::get('iDisplayLength') != -1) + if(!is_null($this->input['start']) && $this->input['start'] != -1) { - $this->query->skip(Input::get('iDisplayStart'))->take(Input::get('iDisplayLength',10)); + $this->query->skip($this->input['start'])->take((int)$this->input['length']>0?$this->input['length']:10); } } @@ -390,18 +439,18 @@ protected function paging() */ protected function ordering() { - - - if(!is_null(Input::get('iSortCol_0'))) + if(count($this->input['order'])>0) { $columns = $this->clean_columns( $this->last_columns ); - for ( $i=0, $c=intval(Input::get('iSortingCols')); $i<$c ; $i++ ) + for ( $i=0, $c=count($this->input['order']); $i<$c ; $i++ ) { - if ( Input::get('bSortable_'.intval(Input::get('iSortCol_'.$i))) == "true" ) - { - if(isset($columns[intval(Input::get('iSortCol_'.$i))])) - $this->query->orderBy($columns[intval(Input::get('iSortCol_'.$i))],Input::get('sSortDir_'.$i)); + $order_col = (int)$this->input['order'][$i]['column']; + if (isset($columns[$order_col])) { + if ( $this->input['columns'][$order_col]['orderable'] == "true" ) + { + $this->query->orderBy($columns[$order_col],$this->input['order'][$i]['dir']); + } } } @@ -419,7 +468,7 @@ protected function clean_columns( $cols, $use_alias = true) foreach ( $cols as $i=> $col ) { preg_match('#^(.*?)\s+as\s+(\S*?)$#si',$col,$matches); - $return[$i] = empty($matches) ? $col : $matches[$use_alias?2:1]; + $return[$i] = empty($matches) ? ($use_alias?$this->getColumnName($col):$col) : $matches[$use_alias?2:1]; } return $return; @@ -442,40 +491,34 @@ protected function filtering() unset($columns_copy[$i]); } } - $columns_copy = array_values($columns_copy); // copy of $this->columns cleaned for database queries $columns_clean = $this->clean_columns( $columns_copy, false ); + $columns_copy = $this->clean_columns( $columns_copy, true ); // global search - if (Input::get('sSearch','') != '') + if ($this->input['search']['value'] != '') { $copy_this = $this; $this->query->where(function($query) use ($copy_this, $columns_copy, $columns_clean) { $db_prefix = $copy_this->database_prefix(); - - for ($i=0,$c=count($columns_copy);$i<$c;$i++) + + for ($i=0,$c=count($columns_copy);$i<$c;$i++) { - if (Input::get('bSearchable_'.$i) == "true") + if ($this->input['columns'][$i]['orderable'] == "true") { - $column = $columns_copy[$i]; - if (stripos($column, ' AS ') !== false){ - $column = substr($column, stripos($column, ' AS ')+4); - } - $column = $copy_this->getColumnName($column); - // if filter column exists for this columns then use user defined method - if (isset($this->filter_columns[$column])) + if (isset($this->filter_columns[$columns_copy[$i]])) { // check if "or" equivalent exists for given function // and if the number of parameters given is not excess // than call the "or" equivalent - $method_name = 'or' . ucfirst($this->filter_columns[$column]['method']); + $method_name = 'or' . ucfirst($this->filter_columns[$columns_copy[$i]]['method']); - if ( method_exists($query->getQuery(), $method_name) && count($this->filter_columns[$column]['parameters']) <= with(new \ReflectionMethod($query->getQuery(),$method_name))->getNumberOfParameters() ) + if ( method_exists($query->getQuery(), $method_name) && count($this->filter_columns[$columns_copy[$i]]['parameters']) <= with(new \ReflectionMethod($query->getQuery(),$method_name))->getNumberOfParameters() ) { call_user_func_array( array( @@ -483,8 +526,8 @@ protected function filtering() $method_name ), $this->inject_variable( - $this->filter_columns[$column]['parameters'], - Input::get('sSearch') + $this->filter_columns[$columns_copy[$i]]['parameters'], + $this->input['search']['value'] ) ); } @@ -492,10 +535,10 @@ protected function filtering() // otherwise do simple LIKE search { - $keyword = '%'.Input::get('sSearch').'%'; + $keyword = '%'.$this->input['search']['value'].'%'; if(Config::get('datatables.search.use_wildcards', false)) { - $keyword = $copy_this->wildcard_like_string(Input::get('sSearch')); + $keyword = $copy_this->wildcard_like_string($this->input['search']['value']); } // Check if the database driver is PostgreSQL @@ -508,6 +551,7 @@ protected function filtering() } $column = $db_prefix . $columns_clean[$i]; + if(Config::get('datatables.search.case_insensitive', false)) { $query->orwhere(DB::raw('LOWER('.$cast_begin.$column.$cast_end.')'), 'LIKE', strtolower($keyword)); } else { @@ -525,35 +569,29 @@ protected function filtering() // column search for ($i=0,$c=count($columns_clean);$i<$c;$i++) { - if (Input::get('bSearchable_'.$i) == "true" && Input::get('sSearch_'.$i) != '') + if ($this->input['columns'][$i]['orderable'] == "true" && $this->input['columns'][$i]['search']['value'] != '') { - $column = $columns_copy[$i]; - if (stripos($column, ' AS ') !== false){ - $column = substr($column, stripos($column, ' AS ')+4); - } - $column = $this->getColumnName($column); - // if filter column exists for this columns then use user defined method - if (isset($this->filter_columns[$column])) + if (isset($this->filter_columns[$columns_copy[$i]])) { call_user_func_array( array( $this->query, - $this->filter_columns[$column]['method'] + $this->filter_columns[$columns_copy[$i]]['method'] ), $this->inject_variable( - $this->filter_columns[$column]['parameters'], - Input::get('sSearch_'.$i) + $this->filter_columns[$columns_copy[$i]]['parameters'], + $this->input['columns'][$i]['search']['value'] ) ); } else // otherwise do simple LIKE search { - $keyword = '%'.Input::get('sSearch_'.$i).'%'; + $keyword = '%'.$this->input['columns'][$i]['search']['value'].'%'; if(Config::get('datatables.search.use_wildcards', false)) { - $keyword = $copy_this->wildcard_like_string(Input::get('sSearch_'.$i)); + $keyword = $copy_this->wildcard_like_string($this->input['columns'][$i]['search']['value']); } if(Config::get('datatables.search.case_insensitive', false)) { @@ -686,16 +724,28 @@ protected function getColumnName($str) */ protected function output($raw=false) { - $sColumns = array_merge_recursive($this->columns,$this->sColumns); + if (Input::has('draw')) { + + $output = array( + "draw" => intval($this->input['draw']), + "recordsTotal" => $this->count_all, + "recordsFiltered" => $this->display_all, + "data" => $this->result_array_r, + ); + + } else { + + $sColumns = array_merge_recursive($this->columns,$this->sColumns); - $output = array( - "sEcho" => intval(Input::get('sEcho')), - "iTotalRecords" => $this->count_all, - "iTotalDisplayRecords" => $this->display_all, - "aaData" => $this->result_array_r, - "sColumns" => $sColumns - ); + $output = array( + "sEcho" => intval($this->input['draw']), + "iTotalRecords" => $this->count_all, + "iTotalDisplayRecords" => $this->display_all, + "aaData" => $this->result_array_r, + "sColumns" => $sColumns + ); + } if(Config::get('app.debug', false)) { $output['aQueries'] = DB::getQueryLog(); } @@ -706,4 +756,5 @@ protected function output($raw=false) return Response::json($output); } } + } From bd70e8ae75cc3d8c7d8e979689e6212f229bd727 Mon Sep 17 00:00:00 2001 From: Kamil Tunkiewicz Date: Wed, 23 Jul 2014 09:10:34 +0200 Subject: [PATCH 2/7] Update Datatables.php --- src/Bllim/Datatables/Datatables.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Bllim/Datatables/Datatables.php b/src/Bllim/Datatables/Datatables.php index 25c291fb..bda9dbc9 100644 --- a/src/Bllim/Datatables/Datatables.php +++ b/src/Bllim/Datatables/Datatables.php @@ -491,6 +491,7 @@ protected function filtering() unset($columns_copy[$i]); } } + $columns_copy = array_values($columns_copy); // copy of $this->columns cleaned for database queries $columns_clean = $this->clean_columns( $columns_copy, false ); From 556624347aab59ca1c970e1036f0bb03b7e617b1 Mon Sep 17 00:00:00 2001 From: Kamil Tunkiewicz Date: Wed, 23 Jul 2014 10:26:47 +0200 Subject: [PATCH 3/7] . --- src/Bllim/Datatables/Datatables.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bllim/Datatables/Datatables.php b/src/Bllim/Datatables/Datatables.php index f1352ff7..6a178f9d 100644 --- a/src/Bllim/Datatables/Datatables.php +++ b/src/Bllim/Datatables/Datatables.php @@ -512,7 +512,7 @@ protected function filtering() $db_prefix = $copy_this->database_prefix(); - for ($i=0,$c=count($columns_copy);$i<$c;$i++) + for ($i=0,$c=count($this->input['columns']);$i<$c;$i++) { if ($this->input['columns'][$i]['orderable'] == "true") { @@ -574,7 +574,7 @@ protected function filtering() $db_prefix = $this->database_prefix(); // column search - for ($i=0,$c=count($columns_clean);$i<$c;$i++) + for ($i=0,$c=count($this->input['columns']);$i<$c;$i++) { if ($this->input['columns'][$i]['orderable'] == "true" && $this->input['columns'][$i]['search']['value'] != '') { From f9049878a208a7a70c28e0869697c96613a76c40 Mon Sep 17 00:00:00 2001 From: Kamil Tunkiewicz Date: Wed, 23 Jul 2014 11:01:52 +0200 Subject: [PATCH 4/7] Added camelCase to snake_case magic method --- src/Bllim/Datatables/Datatables.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Bllim/Datatables/Datatables.php b/src/Bllim/Datatables/Datatables.php index 6a178f9d..fece2872 100644 --- a/src/Bllim/Datatables/Datatables.php +++ b/src/Bllim/Datatables/Datatables.php @@ -763,5 +763,18 @@ protected function output($raw=false) return Response::json($output); } } - + + /** + * PR #93 + * camelCase to snake_case magic method + */ + public function __call($name, $arguments) + { + $name = strtolower(preg_replace('/([^A-Z])([A-Z])/', "$1_$2", $name)); + if (method_exists($this, $name)) { + return call_user_func_array(array($this, $name),$arguments); + } else { + trigger_error('Call to undefined method '.__CLASS__.'::'.$name.'()', E_USER_ERROR); + } + } } From 6adf1e64a639245fcc35f10f35e67c565bf28919 Mon Sep 17 00:00:00 2001 From: Prateem Shrestha Date: Wed, 23 Jul 2014 05:30:54 -0400 Subject: [PATCH 5/7] Update README.md --- README.md | 78 +++++++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index ee076176..74bab8da 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,18 @@ **About** -This bundle is created to handle server-side works of DataTables Jquery Plugin (http://datatables.net) by using Eloquent ORM or Fluent Query Builder. +This bundle is created to handle the server-side processing of the DataTables Jquery Plugin (http://datatables.net) by using Eloquent ORM or Fluent Query Builder. ### Feature Overview - Supporting Eloquent ORM and Fluent Query Builder -- Adding or editing content of columns and removing columns +- Adding or editing the contents of columns and removing columns - Templating new or current columns via Blade Template Engine - Customizable search in columns ### Installation -Add the `bllim/datatables` under the `require` key after that run the `composer update`. +Require `bllim/datatables` in composer.json and run `composer update`. { "require": { @@ -24,7 +24,7 @@ Add the `bllim/datatables` under the `require` key after that run the `composer ... } -Composer will download the package. After package downloaded, open "app/config/app.php" and edit like below: +Composer will download the package. After the package is downloaded, open `app/config/app.php` and add the service provider and alias as below: 'providers' => array( ... @@ -49,53 +49,52 @@ $ php artisan config:publish bllim/datatables It is very simple to use this bundle. Just create your own fluent query object or eloquent object without getting results (that means don't use get(), all() or similar methods) and give it to Datatables. You are free to use all Eloquent ORM and Fluent Query Builder features. -It is better, you know these: -- When you use select method on Eloquent or Fluent Query, you choose columns -- You can easily edit columns by using edit_column($column,$content) -- You can remove any column by using remove_column($column) method -- You can add columns by using add_column($column_name, $content, $order) -- You can use Blade Template Engine in your $content values -- The name of columns is set by returned array. - - That means, for 'posts.id' it is 'id' and also for 'owner.name as ownername' it is 'ownername' -- You can set the "index" column (http://datatables.net/reference/api/row%28%29.index%28%29) using set_index_column($name) -- You can add search filters for each column to override default search functionality - +Some things you should know: +- When you call the `select` method on Eloquent or Fluenty Query, you choose columns. +- Modifying columns + - You can easily edit columns by using `edit_column($column, $content)` + - You can remove any column by using `remove_column($column)` + - You can add columns by using `add_column($column_name, $content, $order) + - You **can** use the Blade Template Engine in your `$content` values. + - You may pass a function as the $content to the `add_column` or `edit_column` calls. The function receives a single parameter: the query row/model record. (see Example 2) +- The column identifiers are set by the returned array. + - That means, for `posts.id` the relevant identifier is `id`, and for `owner.name as ownername` it is `ownername` +- You can set the "index" column (http://datatables.net/reference/api/row().index()) using `set_index_column($name)` +- You can add customized search filters for each column to override the default search functionality +- You can call `make(true)` to return an array of objects instead of an array of arrays. (see Example 4) ### Examples -**Example 1:** +**Example 1: Simple use** $posts = Post::select(array('posts.id','posts.name','posts.created_at','posts.status')); return Datatables::of($posts)->make(); -**Example 2:** +**Example 2: Adding and editing columns** $place = Place::left_join('owner','places.author_id','=','owner.id') - ->select(array('places.id','places.name','places.created_at','owner.name as ownername','places.status')); + ->select(array('places.id','places.name','places.created_at','owner.name as ownername','places.status')); return Datatables::of($place) - ->add_column('operations','edit - delete - ') - ->edit_column('status','@if($status) - Active - @else - Passive - @endif') - // you can also give a function as parameter to edit_column and add_column instead of blade string - ->edit_column('ownername','Author of this post is {{ $ownername }}') - ->remove_column('id') - ->make(); + ->add_column('operations', 'edit + delete + ') + ->edit_column('status', '{{ \$status ? 'Active' : 'Passive' }}') + ->edit_column('ownername', function($row) { + return "The author of this post is {$row->ownername}"; + }) + ->remove_column('id') + ->make(); -**Notice:** If you use double quotes while giving content of add_column or edit_column, you should escape variables with backslash (\) else you get error. For example: +**Notice:** If you use double quotes while assigning the $content in an `add_column` or `edit_column` call, you should escape variables with a backslash (\\) to prevent an error. For example: - edit_column('id',"- {{ \$id }}") . + edit_column('id', "{{ \$id }}") . -**Example 3:** +**Example 3: Using filter_column** $clients = Client::select(array( 'Client.id', @@ -119,17 +118,18 @@ It is better, you know these: **Notes on filter_column:** -Usage: `filter_column ( $column_name, $method, $param_1, $param_2 ... $param_n )` +Usage: `filter_column ( $column_name, $method, $param_1, $param_2, ..., $param_n )` * `$column_name` - the column name that search filter is be applied to * `$method` - can be any of QueryBuilder methods (where, whereIn, whereBetween, having etc.). * Note: For global search these methods are automaticaly converted to their "or" equivalents (if applicable, if not applicable, the column is not searched). - * If you do not want some column to be searchable in global search set the last where's parameter to "and" (see line 17 in example above). Doing this way the filter cannot be switched into its "or" equivalent therefore will not be searched in global search . -* `$param_1 ... $param_n` - these are parameters that will be passed to the selected where function. Possible types: + * If you do not want some column to be searchable in global search, set the last parameter to "and" (see line 17 in example above). Doing this, the filter cannot be switched into its "or" equivalent and therefore will not be searched in global search . +* `$param_1 ... $param_n` - these are parameters that will be passed to the selected where function (`$method`). Possible types: * `string` - * `DB::raw()` - The DB::raw() can output literaly everything into the query, for example subqueries or branching if you need some really sophisticated wheres. + * `DB::raw()` - The DB::raw() can output literaly everything into the query. For example, subqueries or branching if you need some really sophisticated wheres. * `function` - or any other callable - * `array` of any above -* the search value is passed to the query by `$1` string placed anywhere in parameters. If callable (function) is used the searched value is passed to callable as first parameter. The callable must returns value that will be passed to the QueryBuilder's function. + * `array` of any of the above +* The search value is passed to the query by the string `$1` placed anywhere in parameters. If a callable (function) is used the searched value is passed to callable as first parameter the first parameter (again see line 17). + * The callable must return a value that will be passed to the QueryBuilder's function. From 849d3550c069070d8e5212cb2df856fbabe59345 Mon Sep 17 00:00:00 2001 From: Prateem Shrestha Date: Wed, 23 Jul 2014 05:37:23 -0400 Subject: [PATCH 6/7] Update README.md --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 74bab8da..a3d19a0a 100644 --- a/README.md +++ b/README.md @@ -132,5 +132,27 @@ Usage: `filter_column ( $column_name, $method, $param_1, $param_2, ..., $p * The callable must return a value that will be passed to the QueryBuilder's function. +**Example 4: Returning an array of objects* + $posts = Post::select(array('posts.id','posts.name','posts.created_at','posts.status')); + + return Datatables::of($posts)->make(true); + +This returns a JSON array with data like below: + + data: { + { + id: 12, + name: 'Dummy Post', + created_at: '1974-06-20 13:09:51' + status: true + } + { + id: 15, + name: 'Test post please ignore', + created_at: '1974-06-20 13:15:51', + status: true + } + } + **License:** Licensed under the MIT License From 2846b9f2a758b129bcfdcce4aaa1cf183d748140 Mon Sep 17 00:00:00 2001 From: Kamil Tunkiewicz Date: Wed, 23 Jul 2014 12:56:37 +0200 Subject: [PATCH 7/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a3d19a0a..a0142933 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Some things you should know: ->add_column('operations', 'edit delete ') - ->edit_column('status', '{{ \$status ? 'Active' : 'Passive' }}') + ->edit_column('status', '{{ $status ? 'Active' : 'Passive' }}') ->edit_column('ownername', function($row) { return "The author of this post is {$row->ownername}"; })