From 5f544e812611ba44b02ce0d43cfa4a415e6edc00 Mon Sep 17 00:00:00 2001 From: Mario Gallegos Date: Sun, 14 Sep 2014 08:07:45 -0600 Subject: [PATCH] New features added --- README.md | 239 +---- README2.md | 45 - jqGrid.png | Bin 15751 -> 47935 bytes .../Encoders/JqGridJsonEncoder.php | 209 ++++- .../Encoders/RequestedDataInterface.php | 56 +- .../LaravelJqgrid/Facades/GridEncoder.php | 23 + .../LaravelJqgrid/Facades/GridRender.php | 12 +- .../LaravelJqgridServiceProvider.php | 194 ++-- .../LaravelJqgrid/Renders/JqGridRender.php | 853 ++++++++++++++---- .../LaravelJqgrid/Renders/RenderInterface.php | 356 +++++--- .../Validations/ColModel/NameValidation.php | 24 +- .../PropertyValidatorInterface.php | 12 +- .../EloquentRepositoryAbstract.php | 44 +- .../Repositories/RepositoryInterface.php | 105 ++- src/config/config.php | 330 ++++--- 15 files changed, 1571 insertions(+), 931 deletions(-) delete mode 100644 README2.md create mode 100644 src/Mgallegos/LaravelJqgrid/Facades/GridEncoder.php diff --git a/README.md b/README.md index 6513ed0..358a260 100644 --- a/README.md +++ b/README.md @@ -8,236 +8,37 @@ A Laravel 4 package implementation of the popular jQuery Grid Plugin (jqGrid). ## Requirements -* [Laravel 4 Framework](https://github.com/laravel/laravel) +* [Laravel 4 Framework](http://laravel.com/docs/installation) +* [jQuery v2.0.0 or later](http://jquery.com/) +* [Your choice of a jQuery UI theme](http://jqueryui.com/themeroller/#themeGallery) * [jQuery Grid Plugin v4.5.2 or later](http://www.trirand.com/blog/) ## Features +* Spreadsheet Exporter. +* CSV Exporter. * Config file with global properties to use in all grids of your application. -* PHP Render to handle javascript code. +* PHP Render to handle the jqGrid HTML and Javascript code. +* JSON Data Enconder to send the data to the grid in the correct format. * Datasource independent (you are able to create your own datasource implementation). -## Live Demo - -A live demo of Laravel 4 jqGrid package is available at the following address: http://goo.gl/s8uNBR - -The source code of the demo is available [here](https://github.com/mgallegos/laravel-jqgrid-demo). - -## Installation - -Require this package in your composer.json and run composer update: - - "mgallegos/laravel-jqgrid": "dev-master" - -After updating composer, add the ServiceProvider to the providers array in app/config/app.php - - 'Mgallegos\LaravelJqgrid\LaravelJqgridServiceProvider', - -Add the Render Facade to the aliases array in app/config/app.php: - - 'GridRender' => 'Mgallegos\LaravelJqgrid\Facades\GridRender', - -Optionally, run the following command if you wish to overwrite the default config properties: - - php artisan config:publish mgallegos/laravel-jqgrid/ - -## Usage - -### Step 1: Use the jqgrid render to create a grid in your application. - - Let's create the view myview.blade.php: -```php -{{ - GridRender::setGridId("myFirstGrid") - ->enablefilterToolbar() - ->setGridOption('url',URL::to('example/grid-data')) - ->setGridOption('rowNum',5) - ->setGridOption('shrinkToFit',false) - ->setGridOption('sortname','id') - ->setGridOption('caption','LaravelJqGrid example') - ->setNavigatorOptions('navigator', array('viewtext'=>'view')) - ->setNavigatorOptions('view',array('closeOnEscape'=>false)) - ->setFilterToolbarOptions(array('autosearch'=>true)) - ->setGridEvent('gridComplete', 'function(){alert("Grid complete event");}') - ->setNavigatorEvent('view', 'beforeShowForm', 'function(){alert("Before show form");}') - ->setFilterToolbarEvent('beforeSearch', 'function(){alert("Before search event");}') - ->addColumn(array('index'=>'id', 'width'=>55)) - ->addColumn(array('name'=>'product','width'=>100)) - ->addColumn(array('name'=>'amount','index'=>'amount', 'width'=>80, 'align'=>'right')) - ->addColumn(array('name'=>'total','index'=>'total', 'width'=>80)) - ->addColumn(array('name'=>'note','index'=>'note', 'width'=>55,'searchoptions'=>array('attr'=>array('title'=>'Note title')))) - ->renderGrid(); -}} -``` -You can see the documentation of each method in the [RenderInterface source code](src/Mgallegos/LaravelJqgrid/Renders/RenderInterface.php). -> Note: This package will **NOT** include the `jquery.jqGrid.min.js`, that is your work to do. - -### Step 2: Create a class that implements the "RepositoryInterface". - -The package includes an implementation of the "RepositoryInterface" that helps you handle your data if you are using [Query Builder](http://laravel.com/docs/queries) or [Eloquent ORM](http://laravel.com/docs/eloquent) (as explained below), I recommend you to use it as it will make your life easier however you can create your own datasource implementation if you need to, just remember to take into account all parameter received by both methods and the expected type of the return value. - -Let's create the class ExampleRepository: -```php -'column index/name 1','op'=>'operator','data'=>'searched string column 1'), array('field'=>'column index/name 2','op'=>'operator','data'=>'searched string column 2')) - * The 'field' key will contain the 'index' column property if is set, otherwise the 'name' column property. - * The 'op' key will contain one of the following operators: '=', '<', '>', '<=', '>=', '<>', '!=','like', 'not like', 'is in', 'is not in'. - * when the 'operator' is 'like' the 'data' already contains the '%' character in the appropiate position. - * The 'data' key will contain the string searched by the user. - * @return integer - * Total number of rows - */ - public function getTotalNumberOfRows(array $filters = array()) - { - return 5; - } - - - /** - * Get the rows data to be shown in the grid. - * - * @param integer $limit - * Number of rows to be shown into the grid - * @param integer $offset - * Start position - * @param string $orderBy - * Column name to order by. - * @param array $sord - * Sorting order - * @param array $filters - * An array of filters, example: array(array('field'=>'column index/name 1','op'=>'operator','data'=>'searched string column 1'), array('field'=>'column index/name 2','op'=>'operator','data'=>'searched string column 2')) - * The 'field' key will contain the 'index' column property if is set, otherwise the 'name' column property. - * The 'op' key will contain one of the following operators: '=', '<', '>', '<=', '>=', '<>', '!=','like', 'not like', 'is in', 'is not in'. - * when the 'operator' is 'like' the 'data' already contains the '%' character in the appropiate position. - * The 'data' key will contain the string searched by the user. - * @return array - * An array of array, each array will have the data of a row. - * Example: array(array('row 1 col 1','row 1 col 2'), array('row 2 col 1','row 2 col 2')) - */ - public function getRows($limit, $offset, $orderBy = null, $sord = null, array $filters = array()) - { - return array( - array('1-1', '1-2' , '1-3', '1-4', '1-5'), - array('2-1', '2-2' , '2-3', '2-4', '2-5'), - array('3-1', '3-2' , '3-3', '3-4', '3-5'), - array('4-1', '4-2' , '4-3', '4-4', '4-5'), - array('5-1', '5-2' , '5-3', '5-4', '5-5'), - ); - } - -} -``` -If you are using [Query Builder](http://laravel.com/docs/queries) or [Eloquent ORM](http://laravel.com/docs/eloquent) to implement your repository, your class can extends the [EloquentRepositoryAbstract](src/Mgallegos/LaravelJqgrid/Repositoies/EloquentRepositoryAbstract.php) class as it will do all the heavy lifting for you. - -If you are using [Query Builder](http://laravel.com/docs/queries), your repository class should look like this: -```php -Database = DB::table('table_1') - ->join('table_2', 'table_1.id', '=', 'table_2.id'); - - $this->visibleColumns = array('column_1','column_2','column_3'); - - $this->orderBy = array(array('table_1.id', 'asc'), array('table_1.name', 'desc')); - } - -} -``` -And if you are using [Eloquent ORM](http://laravel.com/docs/eloquent) (use Eloquent ORM only in those cases where your data comes from one table, if your data comes from more than one table use [Query Builder](http://laravel.com/docs/queries)), your repository class should look like this: -```php -Database = new YOUR_DATABASE_MODEL; - - $this->visibleColumns = array('column_1','column_2','column_3'); - - $this->orderBy = array(array('id', 'asc'), array('name','desc')); - } - -} -``` -> Note: I recommend you to see the [source code of the live example](https://github.com/mgallegos/laravel-jqgrid-demo/tree/master/app/Demo/Repository), to get a better understanding of how to implement a repository class using [Query Builder](http://laravel.com/docs/queries) and [Eloquent ORM](http://laravel.com/docs/eloquent). - -### Step 3: Create a controller to handle your grid data request. - -The package includes a data encoder to help you send the data to the grid in the correct format. An instance of a class that implements the interface [Mgallegos\LaravelJqgrid\Encoders\RequestedDataInterface](src/Mgallegos/LaravelJqgrid/Encoders/RequestedDataInterface.php) has already been bound in the package service provider, so all you have to do is declare it as an argument in you class constructor. - -Let's create the class AppController: -```php -GridEncoder = $GridEncoder; - } - - public function getIndex() - { - - return View::make('myview'); - - } - - public function postGridData() - { - $this->GridEncoder->encodeRequestedData(new ExampleRepository(), Input::all()); - } - -} -``` -### Step 4: Route all of the actions represented by our RESTful controller. - -Finally let's add the following line in the file app/routes.php -```php -Route::controller('example', 'Example\AppController'); -``` - -### Step 5: Edit package config file (optional) - -In the [package config file](src/config/config.php) you can set global properties to use in all grids of your application. +## Live Demos -## Aditional information +There are three demos available: -Any questions, feel free to [contact me](https://github.com/mgallegos) or ask [here](https://github.com/mgallegos/laravel-jqgrid/issues) using the "question" issue label. +* [Pivot Grid](http://goo.gl/LbOiI3) +* [CRUD Web App with jqGrid forms](http://goo.gl/RK49UB) +* [CRUD Web App with a custom form](http://goo.gl/f2IIQX) -## TODO +## Documentation + +The complete documentation can be found at: [mariogallegos.com](http://goo.gl/Krn7o7) + + +## Aditional information + +Any questions, problems or feature request feel free to open an [issue](https://github.com/mgallegos/laravel-jqgrid/issues). -* Pivot table implementation. -* Advanced Searching implementation. -* Tree-Grid implementation. -* Sub-Grid implementation. -* PDF Exporter. -* Spreadsheet Exporter. ## License diff --git a/README2.md b/README2.md deleted file mode 100644 index 358a260..0000000 --- a/README2.md +++ /dev/null @@ -1,45 +0,0 @@ -# Laravel 4 jqGrid package - -[![Latest Stable Version](https://poser.pugx.org/mgallegos/laravel-jqgrid/v/stable.png)](https://packagist.org/packages/mgallegos/laravel-jqgrid) [![Total Downloads](https://poser.pugx.org/mgallegos/laravel-jqgrid/downloads.png)](https://packagist.org/packages/mgallegos/laravel-jqgrid) - -A Laravel 4 package implementation of the popular jQuery Grid Plugin (jqGrid). - -![Image](https://raw.github.com/mgallegos/laravel-jqgrid/master/jqGrid.png) - -## Requirements - -* [Laravel 4 Framework](http://laravel.com/docs/installation) -* [jQuery v2.0.0 or later](http://jquery.com/) -* [Your choice of a jQuery UI theme](http://jqueryui.com/themeroller/#themeGallery) -* [jQuery Grid Plugin v4.5.2 or later](http://www.trirand.com/blog/) - -## Features - -* Spreadsheet Exporter. -* CSV Exporter. -* Config file with global properties to use in all grids of your application. -* PHP Render to handle the jqGrid HTML and Javascript code. -* JSON Data Enconder to send the data to the grid in the correct format. -* Datasource independent (you are able to create your own datasource implementation). - -## Live Demos - -There are three demos available: - -* [Pivot Grid](http://goo.gl/LbOiI3) -* [CRUD Web App with jqGrid forms](http://goo.gl/RK49UB) -* [CRUD Web App with a custom form](http://goo.gl/f2IIQX) - -## Documentation - -The complete documentation can be found at: [mariogallegos.com](http://goo.gl/Krn7o7) - - -## Aditional information - -Any questions, problems or feature request feel free to open an [issue](https://github.com/mgallegos/laravel-jqgrid/issues). - - -## License - -Laravel jqGrid package is open source software licensed under the MIT License. diff --git a/jqGrid.png b/jqGrid.png index d814a429bef938ebb28dc3ac66c23f4f28bbd48c..33893e1c841453314cca2a86fc37635278c42bc4 100644 GIT binary patch literal 47935 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 literal 15751 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 = $Excel; + } + /** * Echo in a jqGrid compatible format the data requested by a grid. * @@ -26,16 +42,48 @@ class JqGridJsonEncoder implements RequestedDataInterface { */ public function encodeRequestedData(RepositoryInterface $Repository, $postedData) { - $page = $postedData['page']; // get the requested page - $limit = $postedData['rows']; // get how many rows we want to have into the grid - $sidx = $postedData['sidx']; // get index row - i.e. user click to sort - $sord = $postedData['sord']; // get the direction - - if(isset($postedData['filters'])) + // $page = $postedData['page']; // get the requested page + // $limit = $postedData['rows']; // get how many rows we want to have into the grid + // $sidx = $postedData['sidx']; // get index row - i.e. user click to sort + // $sord = $postedData['sord']; // get the direction + + if(isset($postedData['page'])) + { + $page = $postedData['page']; // get the requested page + } + else + { + $page = 1; + } + + if(isset($postedData['rows'])) + { + $limit = $postedData['rows']; // get how many rows we want to have into the grid + } + else + { + $limit = null; + } + + if(isset($postedData['sidx'])) + { + $sidx = $postedData['sidx']; // get index row - i.e. user click to sort + } + else + { + $sidx = null; + } + + if(isset($postedData['sord'])) + { + $sord = $postedData['sord']; // get the direction + } + + if(isset($postedData['filters']) && !empty($postedData['filters'])) { $filters = json_decode(str_replace('\'','"',$postedData['filters']), true); } - + if(!$sidx || empty($sidx)) { $sidx = null; @@ -96,7 +144,7 @@ public function encodeRequestedData(RepositoryInterface $Repository, $postedDat $filter['op'] = 'not like'; $filter['data'] = '%' . $filter['data'] . '%'; break; - } + } } } else @@ -105,47 +153,154 @@ public function encodeRequestedData(RepositoryInterface $Repository, $postedDat } $count = $Repository->getTotalNumberOfRows($filters['rules']); - + + if(empty($limit)) + { + $limit = $count; + } + if(!is_int($count)) { - throw new Exception("The method getTotalNumberOfRows must return an integer"); + throw new Exception('The method getTotalNumberOfRows must return an integer'); } - + if( $count > 0 ) { $totalPages = ceil($count/$limit); } - else + else { $totalPages = 0; } - + if ($page > $totalPages) { $page = $totalPages; } - + if ($limit < 0 ) { $limit = 0; } - - $start = $limit * $page - $limit; - + + $start = $limit * $page - $limit; + if ($start < 0) { $start = 0; } - + $limit = $limit * $page; - - $rows = $Repository->getRows($limit, $start, $sidx, $sord, $filters['rules']); + + if(empty($postedData['pivotRows'])) + { + $rows = $Repository->getRows($limit, $start, $sidx, $sord, $filters['rules']); + } + else + { + $rows = json_decode($postedData['pivotRows'], true); + } if(!is_array($rows) || (isset($rows[0]) && !is_array($rows[0]))) { - throw new Exception("The method getRows must return an array of arrays, example: array(array('row 1 col 1','row 1 col 2'), array('row 2 col 1','row 2 col 2'))"); - } - - echo json_encode(array('page'=>$page, 'total'=>$totalPages, 'records'=>$count, 'rows'=>$rows)); - } + throw new Exception('The method getRows must return an array of arrays, example: array(array("column1" => "1-1", "column2" => "1-2"), array("column1" => "2-1", "column2" => "2-2"))'); + } + + if(isset($postedData['exportFormat'])) + { + $this->Excel->create($postedData['name'], function($Excel) use ($rows, $postedData) + { + foreach (json_decode($postedData['fileProperties'], true) as $key => $value) + { + $method = 'set' . ucfirst($key); + + $Excel->$method($value); + } + + $Excel->sheet($postedData['name'], function($Sheet) use ($rows, $postedData) + { + $columnCounter = 0; + + foreach (json_decode($postedData['model'], true) as $a => $model) + { + if(isset($model['hidden']) && $model['hidden'] !== true) + { + $columnCounter++; + } + + if(isset($model['hidedlg']) && $model['hidedlg'] === true) + { + continue; + } + + if(empty($postedData['pivot'])) + { + foreach ($rows as $b => &$row) + { + if(isset($model['hidden']) && $model['hidden'] === true) + { + unset($row[$model['index']]); + } + else + { + if(isset($model['label'])) + { + $row = array_add($row, $model['label'], $row[$model['index']]); + unset($row[$model['index']]); + } + else + { + $temp = $row[$model['index']]; + unset($row[$model['index']]); + $row = array_add($row, $model['index'], $temp); + } + } + } + } + + if(isset($model['align']) && isset($model['hidden']) && $model['hidden'] !== true) + { + $Sheet->getStyle($this->num_to_letter($columnCounter, true))->getAlignment()->applyFromArray( + array('horizontal' => $model['align']) + ); + } + } + + foreach (json_decode($postedData['sheetProperties'], true) as $key => $value) + { + $method = 'set' . ucfirst($key); + + $Sheet->$method($value); + } + + $Sheet->fromArray($rows); + + $Sheet->row(1, function($Row) { + $Row->setFontWeight('bold'); + }); + }); + })->export($postedData['exportFormat']); + } + else + { + echo json_encode(array('page' => $page, 'total' => $totalPages, 'records' => $count, 'rows' => $rows)); + } + } + + /** + * Takes a number and converts it to a-z,aa-zz,aaa-zzz, etc with uppercase option + * + * @access public + * @param int number to convert + * @param bool upper case the letter on return? + * @return string letters from number input + */ + protected function num_to_letter($num, $uppercase = FALSE) + { + $num -= 1; + + $letter = chr(($num % 26) + 97); + $letter .= (floor($num/26) > 0) ? str_repeat($letter, floor($num/26)) : ''; + return ($uppercase ? strtoupper($letter) : $letter); + } } diff --git a/src/Mgallegos/LaravelJqgrid/Encoders/RequestedDataInterface.php b/src/Mgallegos/LaravelJqgrid/Encoders/RequestedDataInterface.php index 4041e64..7cbd03a 100644 --- a/src/Mgallegos/LaravelJqgrid/Encoders/RequestedDataInterface.php +++ b/src/Mgallegos/LaravelJqgrid/Encoders/RequestedDataInterface.php @@ -1,28 +1,28 @@ -package('mgallegos/laravel-jqgrid'); - } - - /** - * Register the service provider. - * - * @return void - */ - public function register() - { - $this->registerRender(); - - $this->registerEncoder(); - } - - /** - * Register render service provider. - * - * @return void - */ - public function registerRender() - { - $this->app->bind('gridrender', function($app) - { - return new Renders\JqGridRender( array(), - array(new NameValidation()), - array(), - array(), - $app['config']->get('laravel-jqgrid::default_grid_options'), - $app['config']->get('laravel-jqgrid::default_col_model_properties'), - $app['config']->get('laravel-jqgrid::default_navigator_options'), - $app['config']->get('laravel-jqgrid::default_filter_toolbar_options'), - $app['config']->get('laravel-jqgrid::function_type_properties'), - $app['config']->get('laravel-jqgrid::default_filter_toolbar_buttons_options') - ); - }); - } - - /** - * Register encoder service provider. - * - * @return void - */ - public function registerEncoder() - { - $this->app->bind('Mgallegos\LaravelJqgrid\Encoders\RequestedDataInterface', function($app) - { - return new Encoders\JqGridJsonEncoder; - }); - } - - /** - * Get the services provided by the provider. - * - * @return array - */ - public function provides() - { - return array('gridrender'); - } - -} \ No newline at end of file + +use Illuminate\Support\ServiceProvider; + +class LaravelJqgridServiceProvider extends ServiceProvider { + + /** + * Indicates if loading of the provider is deferred. + * + * @var bool + */ + protected $defer = false; + + /** + * Bootstrap the application events. + * + * @return void + */ + public function boot() + { + $this->package('mgallegos/laravel-jqgrid'); + } + + /** + * Register the service provider. + * + * @return void + */ + public function register() + { + $this->app->register('Maatwebsite\Excel\ExcelServiceProvider'); + + $this->registerRender(); + + $this->registerEncoder(); + } + + /** + * Register render service provider. + * + * @return void + */ + public function registerRender() + { + $this->app->bind('gridrender', function($app) + { + return new Renders\JqGridRender( array(), + array(new NameValidation()), + array(), + array(), + $app['config']->get('laravel-jqgrid::default_grid_options'), + $app['config']->get('laravel-jqgrid::default_pivot_grid_options'), + $app['config']->get('laravel-jqgrid::default_group_header_options'), + $app['config']->get('laravel-jqgrid::default_col_model_properties'), + $app['config']->get('laravel-jqgrid::default_navigator_options'), + $app['config']->get('laravel-jqgrid::default_filter_toolbar_options'), + $app['config']->get('laravel-jqgrid::default_filter_toolbar_buttons_options'), + $app['config']->get('laravel-jqgrid::default_export_buttons_options'), + $app['config']->get('laravel-jqgrid::default_file_properties'), + $app['config']->get('laravel-jqgrid::default_sheet_properties'), + $app['config']->get('laravel-jqgrid::function_type_properties'), + $app['config']->get('laravel-jqgrid::pivot_options'), + $app['config']->get('laravel-jqgrid::group_header_options'), + $app['session']->token() + ); + }); + } + + /** + * Register encoder service provider. + * + * @return void + */ + public function registerEncoder() + { + $this->app->bind('Mgallegos\LaravelJqgrid\Encoders\RequestedDataInterface', function($app) + { + return new Encoders\JqGridJsonEncoder($app->make('excel')); + }); + } + + /** + * Get the services provided by the provider. + * + * @return array + */ + public function provides() + { + return array('gridrender', 'Mgallegos\LaravelJqgrid\Encoders\RequestedDataInterface'); + } + +} diff --git a/src/Mgallegos/LaravelJqgrid/Renders/JqGridRender.php b/src/Mgallegos/LaravelJqgrid/Renders/JqGridRender.php index 14d0a56..c249982 100644 --- a/src/Mgallegos/LaravelJqgrid/Renders/JqGridRender.php +++ b/src/Mgallegos/LaravelJqgrid/Renders/JqGridRender.php @@ -1,4 +1,4 @@ -gridId = str_random(10); - + + $this->jqPivot = false; + + $this->frozenColumn = false; + $this->colModelValidators = $colModelValidators; - + $this->optionValidators = $optionValidators; $this->navigatorValidators = $navigatorValidators; - + $this->filterToolbarValidators = $filterToolbarValidators; $this->colModel = array(); - + $this->options = $defaultGridOptions; - + + $this->pivotOptions = $defaultPivotGridOptions; + + $this->pivotOptionsNames = $pivotOptionsNames; + + $this->groupHeaderOptions = $defaultGroupHeaderOptions; + + $this->groupHeaderOptionsNames = $groupHeaderOptionsNames; + $this->navigatorOptions = $defaultNavigatorOptions; - + $this->navigatorEditOptions = array(); - + $this->navigatorAddOptions = array(); - + $this->navigatorDeleteOptions = array(); - + $this->navigatorSearchOptions = array(); - + $this->navigatorViewOptions = array(); - + $this->filterToolbarOptions = $defaultfilterToolbarOptions; - + + $this->filterToolbarButtonsOptions = $defaultFilterToolbarButtonsOptions; + + $this->exportButtonsOptions = $defaultExportButtonsOptions; + + $this->fileProperties = $defaultFileProperties; + + $this->sheetProperties = $defaultSheetProperties; + $this->defaultColModelProperties = $defaultColModelProperties; - + $this->defaultGridOptions = $defaultGridOptions; - + + $this->defaultPivotGridOptions = $defaultPivotGridOptions; + + $this->defaultGroupHeaderOptions = $defaultGroupHeaderOptions; + $this->defaultNavigatorOptions = $defaultNavigatorOptions; - + $this->defaultfilterToolbarOptions = $defaultfilterToolbarOptions; - + + $this->defaultFilterToolbarButtonsOptions = $defaultFilterToolbarButtonsOptions; + + $this->defaultExportButtonsOptions = $defaultExportButtonsOptions; + + $this->defaultFileProperties = $defaultFileProperties; + + $this->defaultSheetProperties = $defaultSheetProperties; + $this->functionTypeProperties = $functionTypeProperties; - - $this->filterToolbar = $defaultFilterToolbarButtonsOptions['filterToolbar']; - - $this->toggleButton = $defaultFilterToolbarButtonsOptions['toggleButton']; - - $this->clearButton = $defaultFilterToolbarButtonsOptions['clearButton']; - - $this->toggleButtonText = $defaultFilterToolbarButtonsOptions['toggleButtonText']; - - $this->clearButtonText = $defaultFilterToolbarButtonsOptions['clearButtonText']; + + $this->token = $token; } - + /** * Add a column at the last position in the columns model. * * @param string $id - * + * * @return $this * Returns an object, allowing the calls to be chained together in a single statement */ public function setGridId($id=null) - { + { $this->gridId = $id; - + return $this; } - + /** * Add a column at the last position in the columns model. * * @param array $properties - * An array of valid jqGrid column model property, the key of the array must correspond to a column model property. + * An array of valid jqGrid column model property, the index key of the array must correspond to a column model property. * Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options * @return $this * Returns an object, allowing the calls to be chained together in a single statement @@ -279,30 +423,156 @@ public function addColumn(array $properties = array()) { $validator->validate($properties); } - + + if (in_array('frozen', $properties)) + { + $this->frozenColumn = true; + } + if (!isset($properties['name']) && !isset($properties['index'])) { - $properties = array_add($properties, 'name', 'Col. ' . (count($this->colModel) + 1)); + $properties = array_add($properties, 'name', 'Col. ' . (count($this->colModel) + 1)); + $properties = array_add($properties, 'index', 'Col. ' . (count($this->colModel) + 1)); } - + if (!isset($properties['name']) && isset($properties['index'])) { $properties = array_add($properties, 'name', $properties['index']); } - + + if (isset($properties['name']) && !isset($properties['index'])) + { + $properties = array_add($properties, 'index', $properties['name']); + } + $this->markFunctionTypeProperty($properties); - + array_push($this->colModel, array_merge($this->defaultColModelProperties, $properties)); - + return $this; - + } - + /** - * Set an identifier to the grid. + * Add a group header. This are columns that can be added above the normal grid columns. + * This method has no effect when working with pivot grid. + * + * @param array $properties + * An array of valid group header options. + * Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:groupingheadar + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function addGroupHeader(array $properties = array()) + { + foreach ($this->optionValidators as $validator) + { + $validator->validate(array_add(array(), $option, $value)); + } + + $this->markFunctionTypeProperty($properties); + + if(!isset($this->groupHeaderOptions['groupHeaders'])) + { + $this->groupHeaderOptions['groupHeaders'] = array(); + } + + array_push($this->groupHeaderOptions['groupHeaders'], $properties); + + return $this; + } + + /** + * Add a X dimension. Use this method only when working with pivot grids. + * + * @param array $properties + * An array of valid xDimension options. + * Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pivotsettings + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function addXDimension(array $properties = array()) + { + foreach ($this->colModelValidators as $validator) + { + $validator->validate($properties); + } + + $this->markFunctionTypeProperty($properties); + + if(!isset($this->pivotOptions['xDimension'])) + { + $this->pivotOptions['xDimension'] = array(); + } + + array_push($this->pivotOptions['xDimension'], array_merge($this->defaultColModelProperties, $properties)); + + return $this; + } + + /** + * Add a Y dimension. Use this method only when working with pivot grids. + * + * @param array $properties + * An array of valid yDimension options. + * Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pivotsettings + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function addYDimension(array $properties = array()) + { + foreach ($this->colModelValidators as $validator) + { + $validator->validate($properties); + } + + $this->markFunctionTypeProperty($properties); + + if(!isset($this->pivotOptions['yDimension'])) + { + $this->pivotOptions['yDimension'] = array(); + } + + array_push($this->pivotOptions['yDimension'], array_merge($this->defaultColModelProperties, $properties)); + + return $this; + } + + /** + * Add an aggregate. Use this method only when working with pivot grids. + * + * @param array $properties + * An array of valid aggregate options (all jqGrid column model property can be used). + * Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pivotsettings + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function addAggregate(array $properties = array()) + { + foreach ($this->colModelValidators as $validator) + { + $validator->validate($properties); + } + + $this->markFunctionTypeProperty($properties); + + if(!isset($this->pivotOptions['aggregates'])) + { + $this->pivotOptions['aggregates'] = array(); + } + + array_push($this->pivotOptions['aggregates'], array_merge($this->defaultColModelProperties, $properties)); + + return $this; + } + + /** + * Set a jqGrid option. * * @param string $option - * A valid jqGrid option, online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options + * A valid jqGrid option, online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options or + * a valid pivot grid option, online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pivotsettings + * a valid group header option, online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:groupingheadar * @param mixed $option * A value of an option can be a string, boolean or array. * @return $this @@ -314,45 +584,79 @@ public function setGridOption($option, $value) { $validator->validate(array_add(array(), $option, $value)); } - + + if (in_array($option, array('xDimension', 'yDimension', 'aggregates'))) + { + foreach ($value as &$v) + { + $v = array_merge($this->defaultColModelProperties, $v); + $this->markFunctionTypeProperty($v); + } + } + $property = array_add(array(), $option, $value); - + $this->markFunctionTypeProperty($property); - - if(isset($this->options[$option])) + + if (in_array($option, $this->pivotOptionsNames)) { - $this->options[$option] = $property[$option]; + if(isset($this->pivotOptions[$option])) + { + $this->pivotOptions[$option] = $property[$option]; + } + else + { + $this->pivotOptions = array_add($this->pivotOptions, $option, $property[$option]); + } + } + else if (in_array($option, $this->groupHeaderOptionsNames)) + { + if(isset($this->groupHeaderOptions[$option])) + { + $this->groupHeaderOptions[$option] = $property[$option]; + } + else + { + $this->groupHeaderOptions = array_add($this->groupHeaderOptions, $option, $property[$option]); + } } else { - $this->options = array_add($this->options, $option, $property[$option]); + if(isset($this->options[$option])) + { + $this->options[$option] = $property[$option]; + } + else + { + $this->options = array_add($this->options, $option, $property[$option]); + } } return $this; } - + /** - * Set a jqGrid event. - * - * @param string $event - * Valid grid event, online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:events&s[]=event - * @param string $code - * Javascript code which will be executed when the event raises - * @return $this - * Returns an object, allowing the calls to be chained together in a single statement - */ + * Set a jqGrid event. + * + * @param string $event + * Valid grid event, online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:events&s[]=event + * @param string $code + * Javascript code which will be executed when the event raises + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ public function setGridEvent($event, $code) { foreach ($this->optionValidators as $validator) { - $validator->validate(array_add(array(), $option, $value)); + $validator->validate(array_add(array(), $event, $code)); } - + $this->options = array_add($this->options, $event, '###' . $code . '###'); - + return $this; } - + /** * Set options in the navigator or in any of the following modules add,edit,del,view, search. Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:navigator * @@ -369,9 +673,9 @@ public function setNavigatorOptions($module, array $options) { $validator->validate(array_add(array(), $module, $options)); } - + $this->markFunctionTypeProperty($options); - + switch ($module) { case 'navigator': @@ -393,10 +697,10 @@ public function setNavigatorOptions($module, array $options) $this->navigatorViewOptions = $options; break; } - - return $this; + + return $this; } - + /** * Set an event in the navigator or in the diffrent modules add,edit,del,view, search. Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:navigator * @@ -410,12 +714,12 @@ public function setNavigatorOptions($module, array $options) * Returns an object, allowing the calls to be chained together in a single statement */ public function setNavigatorEvent($module, $event, $code) - { + { foreach ($this->navigatorValidators as $validator) { - $validator->validate(array_add(array(), $module, $options)); + //$validator->validate(array_add(array(), $module, $options)); } - + switch ($module) { case 'navigator': @@ -437,10 +741,10 @@ public function setNavigatorEvent($module, $event, $code) $this->navigatorViewOptions = array_add($this->navigatorViewOptions, $event, '###' . $code . '###'); break; } - + return $this; } - + /** * Set options for the toolbar filter when enabled. Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:toolbar_searching * @@ -453,16 +757,40 @@ public function setFilterToolbarOptions(array $options) { foreach ($this->filterToolbarValidators as $validator) { - $validator->validate(array_add(array(), $module, $options)); + $validator->validate($options); } - + $this->markFunctionTypeProperty($options); - + $this->filterToolbarOptions = array_merge($this->filterToolbarOptions, $options); - + return $this; } - + + /** + * Set a Laravel Excel file property. + * + * @param string $option + * A valid Laravel Excel file property, online documentation available at http://www.maatwebsite.nl/laravel-excel/docs/reference-guide + * @param mixed $option + * A value of an option can be a string, boolean or array. + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function setFileProperty($option, $value) + { + if(isset($this->fileProperties[$option])) + { + $this->fileProperties[$option] = $value; + } + else + { + $this->fileProperties = array_add($this->fileProperties, $option, $value); + } + + return $this; + } + /** * Set a toolbar event. * @@ -479,12 +807,75 @@ public function setFilterToolbarEvent($event, $code) { $validator->validate(array_add(array(), $event, $code)); } - + $this->filterToolbarOptions = array_merge($this->filterToolbarOptions, array_add(array(), $event, '###' . $code . '###')); - + + return $this; + } + + /** + * When this method is called the grid will be treated as Pivot Grid (differents javascript methods are used to generate the grid) according to the official documentation. Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pivotdescription. + * + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function setGridAsPivot() + { + $this->jqPivot = true; + + return $this; + } + + /** + * Hide XLS Navigator button. + * + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function hideXlsExporter() + { + $this->exportButtonsOptions['xlsButtonVisible'] = false; + + return $this; + } + + /** + * Hide csv Navigator button. + * + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function hideCsvExporter() + { + $this->exportButtonsOptions['csvButtonVisible'] = false; + return $this; } - + + /** + * Set a Laravel Excel sheet property. + * + * @param string $option + * A valid Laravel Excel sheet property, online documentation available at http://www.maatwebsite.nl/laravel-excel/docs/reference-guide + * @param mixed $option + * A value of an option can be a string, boolean or array. + * @return $this + * Returns an object, allowing the calls to be chained together in a single statement + */ + public function setSheetProperty($option, $value) + { + if(isset($this->sheetProperties[$option])) + { + $this->sheetProperties[$option] = $value; + } + else + { + $this->sheetProperties = array_add($this->sheetProperties, $option, $value); + } + + return $this; + } + /** * Enable filter toolbar. * @@ -497,21 +888,21 @@ public function setFilterToolbarEvent($event, $code) */ public function enableFilterToolbar($createToggleButton = null, $createClearButton = null) { - $this->filterToolbar = true; - + $this->filterToolbarButtonsOptions['filterToolbar'] = true; + if(!is_null($createToggleButton)) { - $this->toggleButton = $createToggleButton; + $this->filterToolbarButtonsOptions['toggleButton'] = $createToggleButton; } - + if(!is_null($createClearButton)) { - $this->clearButton = $createClearButton; + $this->filterToolbarButtonsOptions['clearButton'] = $createClearButton; } - + return $this; } - + /** * Main method that construct the html and javascript code of the grid. * @@ -529,94 +920,171 @@ public function enableFilterToolbar($createToggleButton = null, $createClearButt public function renderGrid($script = true, $createTableElement = true, $createPagerElement = true, $echo = true) { $this->options = array_add($this->options, 'colModel', $this->colModel); - + if (!isset($this->options['pager'])) { $this->options = array_add($this->options, 'pager', $this->gridId . 'Pager'); } - + $html = ''; - + $html .= '

+ + + + + + + + fileProperties) . '\'> + sheetProperties) . '\'> +
'; + if($createTableElement) { $html .= '
'; } - + if($createTableElement) { $html .= '
'; } - - $script = 'jQuery("#' . $this->gridId . '").jqGrid(' . json_encode($this->options) . ')'; - $script .= '.navGrid("#'. $this->options['pager'] .'", '. json_encode($this->navigatorOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorEditOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorAddOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorDeleteOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorSearchOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorViewOptions, JSON_FORCE_OBJECT) .' );'; - - if($this->filterToolbar) + + if($this->jqPivot) + { + $mtype = $this->options['mtype']; + unset($this->options['colModel'], $this->options['mtype'], $this->options['datatype']); + $script = 'jQuery("#' . $this->gridId . '").jqGrid("jqPivot", "'. $this->options['url'] . '", ' . json_encode($this->pivotOptions) . ', ' . json_encode($this->options) . ', {async : false, type: "' . $mtype .'"});'; + $script .= 'jQuery("#' . $this->gridId . '").navGrid("#'. $this->options['pager'] .'", '. json_encode($this->navigatorOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorEditOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorAddOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorDeleteOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorSearchOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorViewOptions, JSON_FORCE_OBJECT) .' );'; + } + else + { + $script = 'jQuery("#' . $this->gridId . '").jqGrid(' . json_encode($this->options) . ')'; + $script .= '.navGrid("#'. $this->options['pager'] .'", '. json_encode($this->navigatorOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorEditOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorAddOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorDeleteOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorSearchOptions, JSON_FORCE_OBJECT) .', '. json_encode($this->navigatorViewOptions, JSON_FORCE_OBJECT) .' );'; + } + + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navButtonAdd", "#' . $this->options['pager'] . '",{"id": "' . $this->gridId . 'XlsButton", "caption":"' . $this->exportButtonsOptions['xlsButtonText'] . '", "buttonicon":"' . $this->exportButtonsOptions['xlsIcon'] . '", "onClickButton":function(){ ' . $this->getJavascriptExportFunctionCode() . ' jQuery("#' . $this->gridId . 'ExportFormat").val("xls"); jQuery("#' . $this->gridId . 'ExportForm").submit();} });'; + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navButtonAdd", "#' . $this->options['pager'] . '",{"id": "' . $this->gridId . 'CsvButton", "caption":"' . $this->exportButtonsOptions['csvButtonText'] . '", "buttonicon":"' . $this->exportButtonsOptions['csvIcon'] . '", "onClickButton":function(){ ' . $this->getJavascriptExportFunctionCode() . ' jQuery("#' . $this->gridId . 'ExportFormat").val("csv"); jQuery("#' . $this->gridId . 'ExportForm").submit();} });'; + + if($this->exportButtonsOptions['xlsButtonVisible'] || $this->exportButtonsOptions['csvButtonVisible']) + { + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navSeparatorAdd", "#' . $this->options['pager'] . '");'; + } + + if(!$this->exportButtonsOptions['xlsButtonVisible']) + { + $script .= 'jQuery("#' . $this->gridId . 'XlsButton").hide();'; + } + + if(!$this->exportButtonsOptions['csvButtonVisible']) + { + $script .= 'jQuery("#' . $this->gridId . 'CsvButton").hide();'; + } + + if($this->filterToolbarButtonsOptions['filterToolbar']) { $script .= 'jQuery("#' . $this->gridId . '").jqGrid("filterToolbar", ' . json_encode($this->filterToolbarOptions, JSON_FORCE_OBJECT) . ');'; - - if($this->toggleButton) + + if($this->filterToolbarButtonsOptions['toggleButton']) { - $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navButtonAdd", "#' . $this->options['pager'] . '",{"caption":"'. $this->toggleButtonText .'", "buttonicon":"ui-icon-pin-s", "onClickButton":function(){ jQuery("#' . $this->gridId . '")[0].toggleToolbar();} });'; + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navButtonAdd", "#' . $this->options['pager'] . '",{"caption":"'. $this->filterToolbarButtonsOptions['toggleButtonText'] .'", "buttonicon":"ui-icon-pin-s", "onClickButton":function(){ jQuery("#' . $this->gridId . '")[0].toggleToolbar();} });'; } - - if($this->clearButton) + + if($this->filterToolbarButtonsOptions['clearButton']) { - $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navButtonAdd", "#' . $this->options['pager'] . '",{"caption":"'. $this->clearButtonText .'", "buttonicon":"ui-icon-refresh", "onClickButton":function(){ jQuery("#' . $this->gridId . '")[0].clearToolbar();} });'; + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navButtonAdd", "#' . $this->options['pager'] . '",{"caption":"'. $this->filterToolbarButtonsOptions['clearButtonText'] .'", "buttonicon":"ui-icon-refresh", "onClickButton":function(){ jQuery("#' . $this->gridId . '")[0].clearToolbar();} });'; } + + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("navSeparatorAdd", "#' . $this->options['pager'] . '");'; + } + + if(!empty($this->groupHeaderOptions)) + { + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("setGroupHeaders", ' . json_encode($this->groupHeaderOptions) . ');'; + } + + if($this->frozenColumn) + { + $script .= 'jQuery("#' . $this->gridId . '").jqGrid("setFrozenColumns");'; } - + $script = str_replace(array('"###','###"','\"'), array('', '', '"'), $script); - + + $this->reset(); + + if($script) + { + $script = ''; + } + + if($echo) + { + echo $html . $script; + } + else + { + return $html.$script; + } + } + + /** + * Reset variables to their original state. + * + * @return void + */ + protected function reset() + { $this->gridId = str_random(10); - + + $this->jqPivot = false; + + $this->frozenColumn = false; + $this->options = $this->defaultGridOptions; - + + $this->pivotOptions = $this->defaultPivotGridOptions; + + $this->groupHeaderOptions = $this->defaultGroupHeaderOptions; + $this->colModel = array(); - + $this->navigatorOptions = $this->defaultNavigatorOptions; - + $this->navigatorEditOptions = array(); - + $this->navigatorAddOptions = array(); - + $this->navigatorDeleteOptions = array(); - + $this->navigatorSearchOptions = array(); - + $this->navigatorViewOptions = array(); - + $this->filterToolbarOptions = $this->defaultfilterToolbarOptions; - - if($script) - { - $script = ''; - } - - if($echo) - { - echo $html.$script; - } - else - { - return $html.$script; - } + + $this->filterToolbarButtonsOptions = $this->defaultFilterToolbarButtonsOptions; + + $this->exportButtonsOptions = $this->defaultExportButtonsOptions; + + $this->fileProperties = $this->defaultFileProperties; + + $this->sheetProperties = $this->defaultSheetProperties; } - + /** * Mark function type properties. * First and last quotes will be removed from the javascript code to all properties marked by this method * * @param array $properties * An array of valid jqGrid column model property, the key of the array must correspond to a column model property. - * + * * @return void */ protected function markFunctionTypeProperty(array &$properties) { - foreach ($properties as $key => &$value) - { - if (in_array($key,array_keys($this->functionTypeProperties))) - { + foreach ($properties as $key => &$value) + { + if (in_array($key, array_keys($this->functionTypeProperties))) + { if (!in_array($value, $this->functionTypeProperties[$key])) { $value = '###' . $value . '###'; @@ -627,10 +1095,55 @@ protected function markFunctionTypeProperty(array &$properties) if(is_array($value) && array_values($value) != $value) { $this->markFunctionTypeProperty($value); - } + } } - + } } - + + /** + * Get exporter's javascript code. + * + * @return void + */ + protected function getJavascriptExportFunctionCode() + { + + $code = ' + var headers = [], rows = [], row, cellCounter, postData; + jQuery("#' . $this->gridId . 'Model").val(JSON.stringify(jQuery("#' . $this->gridId . '").getGridParam("colModel"))); + postData = jQuery("#' . $this->gridId . '").getGridParam("postData"); + if(postData["filters"] != undefined) + { + jQuery("#' . $this->gridId . 'Filters").val(postData["filters"]); + } + '; + + $jqPivot = ' + jQuery.each($("#gbox_' . $this->gridId . '").find(".ui-jqgrid-sortable"), function( index, header ) + { + headers.push(jQuery(header).text()); + }); + jQuery.each($("#gview_' . $this->gridId . '").find(".ui-widget-content"), function( index, gridRows ) + { + row = {}, cellCounter = 0; + jQuery.each($(gridRows).find("td"), function( index, cell) + { + row[headers[cellCounter++]] = $(cell).text(); + }); + for (i = cellCounter; i < headers.length; i++) { + row[headers[i]] = ""; + } + rows.push(row); + }); + jQuery("#' . $this->gridId . 'Rows").val(JSON.stringify(rows)); + '; + + if($this->jqPivot) + { + $code .= $jqPivot; + } + + return $code; + } } diff --git a/src/Mgallegos/LaravelJqgrid/Renders/RenderInterface.php b/src/Mgallegos/LaravelJqgrid/Renders/RenderInterface.php index 48ffea9..e21eba7 100644 --- a/src/Mgallegos/LaravelJqgrid/Renders/RenderInterface.php +++ b/src/Mgallegos/LaravelJqgrid/Renders/RenderInterface.php @@ -1,136 +1,220 @@ -whereIn($filter['field'], explode(',',$filter['data'])); continue; } - + if($filter['op'] == 'is not in') { $query->whereNotIn($filter['field'], explode(',',$filter['data'])); continue; } - + $query->where($filter['field'], $filter['op'], $filter['data']); } }) @@ -93,7 +93,7 @@ public function getTotalNumberOfRows(array $filters = array()) * The 'data' key will contain the string searched by the user. * @return array * An array of array, each array will have the data of a row. - * Example: array(array('row 1 col 1','row 1 col 2'), array('row 2 col 1','row 2 col 2')) + * Example: array(array("column1" => "1-1", "column2" => "1-2"), array("column1" => "2-1", "column2" => "2-2")) */ public function getRows($limit, $offset, $orderBy = null, $sord = null, array $filters = array()) { @@ -101,21 +101,21 @@ public function getRows($limit, $offset, $orderBy = null, $sord = null, array $f { $this->orderBy = array(array($orderBy, $sord)); } - + if($limit == 0) { $limit = 1; } - + $orderByRaw = array(); foreach ($this->orderBy as $orderBy) { array_push($orderByRaw, implode(' ',$orderBy)); } - - $orderByRaw = implode(',',$orderByRaw); - + + $orderByRaw = implode(',',$orderByRaw); + $rows = $this->Database->whereNested(function($query) use ($filters) { foreach ($filters as $filter) @@ -125,31 +125,31 @@ public function getRows($limit, $offset, $orderBy = null, $sord = null, array $f $query->whereIn($filter['field'], explode(',',$filter['data'])); continue; } - + if($filter['op'] == 'is not in') { $query->whereNotIn($filter['field'], explode(',',$filter['data'])); continue; } - + $query->where($filter['field'], $filter['op'], $filter['data']); } }) ->take($limit) ->skip($offset) ->orderByRaw($orderByRaw) - ->get($this->visibleColumns); - + ->get($this->visibleColumns); + if(!is_array($rows)) { $rows = $rows->toArray(); } - + foreach ($rows as &$row) - { - $row = array_values((array) $row); + { + $row = (array) $row; } - + return $rows; } diff --git a/src/Mgallegos/LaravelJqgrid/Repositories/RepositoryInterface.php b/src/Mgallegos/LaravelJqgrid/Repositories/RepositoryInterface.php index c3c1641..176854c 100644 --- a/src/Mgallegos/LaravelJqgrid/Repositories/RepositoryInterface.php +++ b/src/Mgallegos/LaravelJqgrid/Repositories/RepositoryInterface.php @@ -1,53 +1,52 @@ -'column index/name 1','op'=>'operator','data'=>'searched string column 1'), array('field'=>'column index/name 2','op'=>'operator','data'=>'searched string column 2')) - * The 'field' key will contain the 'index' column property if is set, otherwise the 'name' column property. - * The 'op' key will contain one of the following operators: '=', '<', '>', '<=', '>=', '<>', '!=','like', 'not like', 'is in', 'is not in'. - * when the 'operator' is 'like' the 'data' already contains the '%' character in the appropiate position. - * The 'data' key will contain the string searched by the user. - * @return integer - * Total number of rows - */ - public function getTotalNumberOfRows(array $filters = array()); - - - /** - * Get the rows data to be shown in the grid. - * - * @param integer $limit - * Number of rows to be shown into the grid - * @param integer $offset - * Start position - * @param string $orderBy - * Column name to order by. - * @param array $sord - * Sorting order - * @param array $filters - * An array of filters, example: array(array('field'=>'column index/name 1','op'=>'operator','data'=>'searched string column 1'), array('field'=>'column index/name 2','op'=>'operator','data'=>'searched string column 2')) - * The 'field' key will contain the 'index' column property if is set, otherwise the 'name' column property. - * The 'op' key will contain one of the following operators: '=', '<', '>', '<=', '>=', '<>', '!=','like', 'not like', 'is in', 'is not in'. - * when the 'operator' is 'like' the 'data' already contains the '%' character in the appropiate position. - * The 'data' key will contain the string searched by the user. - * @return array - * An array of array, each array will have the data of a row. - * Example: array(array('row 1 col 1','row 1 col 2'), array('row 2 col 1','row 2 col 2')) - */ - public function getRows($limit, $offset, $orderBy = null, $sord = null, array $filters = array()); - - -} +'column index/name 1','op'=>'operator','data'=>'searched string column 1'), array('field'=>'column index/name 2','op'=>'operator','data'=>'searched string column 2')) + * The 'field' key will contain the 'index' column property if is set, otherwise the 'name' column property. + * The 'op' key will contain one of the following operators: '=', '<', '>', '<=', '>=', '<>', '!=','like', 'not like', 'is in', 'is not in'. + * when the 'operator' is 'like' the 'data' already contains the '%' character in the appropiate position. + * The 'data' key will contain the string searched by the user. + * @return integer + * Total number of rows + */ + public function getTotalNumberOfRows(array $filters = array()); + + + /** + * Get the rows data to be shown in the grid. + * + * @param integer $limit + * Number of rows to be shown into the grid + * @param integer $offset + * Start position + * @param string $orderBy + * Column name to order by. + * @param array $sord + * Sorting order + * @param array $filters + * An array of filters, example: array(array('field'=>'column index/name 1','op'=>'operator','data'=>'searched string column 1'), array('field'=>'column index/name 2','op'=>'operator','data'=>'searched string column 2')) + * The 'field' key will contain the 'index' column property if is set, otherwise the 'name' column property. + * The 'op' key will contain one of the following operators: '=', '<', '>', '<=', '>=', '<>', '!=','like', 'not like', 'is in', 'is not in'. + * when the 'operator' is 'like' the 'data' already contains the '%' character in the appropiate position. + * The 'data' key will contain the string searched by the user. + * @return array + * An array of array, each array will have the data of a row. + * Example: array(array("column1" => "1-1", "column2" => "1-2"), array("column1" => "2-1", "column2" => "2-2")) + */ + public function getRows($limit, $offset, $orderBy = null, $sord = null, array $filters = array()); + +} diff --git a/src/config/config.php b/src/config/config.php index 0cd68b6..91deb95 100644 --- a/src/config/config.php +++ b/src/config/config.php @@ -1,115 +1,215 @@ - array('datatype'=>'json', 'mtype' => 'POST'), - - /* - |-------------------------------------------------------------------------- - | Default column model properties - |-------------------------------------------------------------------------- - | - | An array of column model properties that will be set to all columns grid of your applications, - | the key of the array must correspond to a valid column model property. - | These column model properties can override by setting a different value in a specific grid. - | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options - | - */ - - 'default_col_model_properties' => array('searchoptions'=>array('sopt'=>array('cn'))), - - /* - |-------------------------------------------------------------------------- - | Default navigator options - |-------------------------------------------------------------------------- - | - | Default navigators options that will be set to all grid of your applications, - | the key of the array must correspond to a valid navigator option. - | These options can override by setting a different value in a specific grid. - | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:navigator - | - */ - - 'default_navigator_options' => array('add'=>false, 'edit'=>false, 'del'=>false, 'search'=>false, 'view'=>true, 'refresh'=>false), - - /* - |-------------------------------------------------------------------------- - | Default filter toolbar options - |-------------------------------------------------------------------------- - | - | Default navigators options that will be set to all grid of your applications, - | the key of the array must correspond to a valid filter toolbar option. - | These options can override by setting a different value in a specific grid. - | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:toolbar_searching - | - */ - - 'default_filter_toolbar_options' => array('stringResult'=>true), - - /* - |-------------------------------------------------------------------------- - | Default Filter Toolbar Buttons Options - |-------------------------------------------------------------------------- - | - | An array of toolbar button options that will be set to all grids of your applications. - | filterToolbar: enable o disabled the filter toolbar - | toggleButton: add a button to toggle the filter toolbar - | clearButton: add a button to clear the filter toolbar - | toggleButtonText: text of the toggle button - | clearButtonText: text of the clear button - | - */ - - 'default_filter_toolbar_buttons_options' => array('filterToolbar'=>false, 'toggleButton'=>true, 'clearButton'=>true, 'toggleButtonText'=>'', 'clearButtonText'=>''), - - /* - |-------------------------------------------------------------------------- - | Function type Grid options and column properties - |-------------------------------------------------------------------------- - | - | These options and properties can be set as a string by the programmer, but first and last quotes will be removed from the javascript code. - | In some cases there are predefined values that are exceptions, these can be included as an array. - | "JqGrid options" online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options - | "JqGrid column properties" online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options - | "JqGrid predefined format types" online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:predefined_formatter - | - | - */ - - 'function_type_properties' => array( - 'datatype' => array('xml', 'xmlstring', 'json', 'jsonstring', 'local', 'javascript', 'clientSide'), - 'cellattr' => array(), - 'formatter' => array('integer', 'number', 'currency', 'date', 'email', 'link', 'showlink', 'checkbox', 'select', 'actions'), - 'unformat' => array(), - 'sorttype' => array('int','integer', 'float','number', 'currency', 'date', 'text'), - 'unformat' => array(), - 'dataInit' => array(), - 'fn' => array(), - 'custom_element' => array(), - 'custom_value' => array(), - 'custom_func' => array(), - 'buildSelect' => array(), - 'dataInit' => array(), - 'summaryType' => array('sum','count','avg','min','max'), - 'rowattr' => array(), - ), -); + array('datatype' => 'json', 'mtype' => 'POST'), + + /* + |-------------------------------------------------------------------------- + | Default Pivot Grid Options + |-------------------------------------------------------------------------- + | + | An array of pivot grid options that will be set to all grids of your applications, + | the key of the array must correspond to a valid pivot grid option. + | These options can override by setting a different value in a specific grid. + | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pivotsettings + | + */ + + 'default_pivot_grid_options' => array('yDimension' => array()), + + /* + |-------------------------------------------------------------------------- + | Default Group Header Options + |-------------------------------------------------------------------------- + | + | An array of group header options that will be set to all grids of your applications, + | the key of the array must correspond to a valid pivot grid option. + | These options can override by setting a different value in a specific grid. + | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:groupingheadar + | + */ + + 'default_group_header_options' => array('useColSpanStyle' => true), + + /* + |-------------------------------------------------------------------------- + | Default column model properties + |-------------------------------------------------------------------------- + | + | An array of column model properties that will be set to all columns grid of your applications, + | the key of the array must correspond to a valid column model property. + | These column model properties can override by setting a different value in a specific grid. + | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options + | + */ + + 'default_col_model_properties' => array('searchoptions' => array('sopt'=>array('cn'))), + + /* + |-------------------------------------------------------------------------- + | Default navigator options + |-------------------------------------------------------------------------- + | + | Default navigators options that will be set to all grid of your applications, + | the key of the array must correspond to a valid navigator option. + | These options can override by setting a different value in a specific grid. + | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:navigator + | + */ + + 'default_navigator_options' => array('add' => false, 'edit' => false, 'del' => false, 'search' => false, 'view' => true, 'refresh' => false), + + /* + |-------------------------------------------------------------------------- + | Default filter toolbar options + |-------------------------------------------------------------------------- + | + | Default navigators options that will be set to all grid of your applications, + | the key of the array must correspond to a valid filter toolbar option. + | These options can override by setting a different value in a specific grid. + | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:toolbar_searching + | + */ + + 'default_filter_toolbar_options' => array('stringResult' => true), + + /* + |-------------------------------------------------------------------------- + | Default Filter Toolbar Buttons Options + |-------------------------------------------------------------------------- + | + | An array of toolbar button options that will be set to all grids of your applications. + | filterToolbar: enable o disabled the filter toolbar + | toggleButton: add a button to toggle the filter toolbar + | clearButton: add a button to clear the filter toolbar + | toggleButtonText: text of the toggle button + | clearButtonText: text of the clear button + | + */ + + 'default_filter_toolbar_buttons_options' => array('filterToolbar' => false, 'toggleButton' => true, 'clearButton' => true, 'toggleButtonText' => '', 'clearButtonText' => ''), + + /* + |-------------------------------------------------------------------------- + | Default Export Buttons Options + |-------------------------------------------------------------------------- + | + | An array of toolbar button options that will be set to all grids of your applications. + | xlsButtonVisible: show or hide the xls export button + | xlsButtonText: text of the xls export button + | xlsIcon: xls icon (jquery ui icon) + | csvButtonVisible: show or hide the csv export button + | csvButtonText: text of the csv export button + | csvIcon: csv icon (jquery ui icon) + | + */ + + 'default_export_buttons_options' => array('xlsButtonVisible' => true, 'xlsButtonText' => 'xls', 'xlsIcon' => 'ui-icon-arrowthickstop-1-s', 'csvButtonVisible' => true, 'csvButtonText' => 'csv', 'csvIcon' => 'ui-icon-arrowthickstop-1-s'), + + /* + |-------------------------------------------------------------------------- + | Default Laravel Excel File Properties + |-------------------------------------------------------------------------- + | + | Default Laravel Excel File Properties that will be set to all grid of your applications, + | the key of the array must correspond to a Laravel Excel File Property. + | These properties can override by setting a different value in a specific grid. + | Online documentation available at http://www.maatwebsite.nl/laravel-excel/docs/reference-guide + | + */ + + 'default_file_properties' => array(), + + /* + |-------------------------------------------------------------------------- + | Default Laravel Excel Sheet Properties + |-------------------------------------------------------------------------- + | + | Default Laravel Excel Sheet Properties that will be set to all grid of your applications, + | the key of the array must correspond to a Laravel Excel Sheet Property. + | These properties can override by setting a different value in a specific grid. + | Online documentation available at http://www.maatwebsite.nl/laravel-excel/docs/reference-guide + | + */ + + 'default_sheet_properties' => array(), + + /* + |-------------------------------------------------------------------------- + | Pivot options + |-------------------------------------------------------------------------- + | + | Pivots options defined by the jqGrid documentation. + | Warning: This options are vital when using the grid as a pivot grid, do not modify them unless you know what you are doing. + | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:pivotsettings + | + */ + + 'pivot_options' => array('aggregates', 'colTotals', 'frozenStaticCols', 'groupSummary', 'groupSummaryPos', 'rowTotals', 'rowTotalsText', 'xDimension', 'yDimension'), + + /* + |-------------------------------------------------------------------------- + | Group header options + |-------------------------------------------------------------------------- + | + | Group header options options defined by the jqGrid documentation. + | Warning: This options are vital when grouping header columns, do not modify them unless you know what you are doing. + | Online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:groupingheadar + | + */ + + 'group_header_options' => array('useColSpanStyle', 'groupHeaders'), + + /* + |-------------------------------------------------------------------------- + | Function type Grid options and column properties + |-------------------------------------------------------------------------- + | + | These options and properties can be set as a string by the programmer, but first and last quotes will be removed from the javascript code. + | In some cases there are predefined values that are exceptions, these can be included as an array. + | "JqGrid options" online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options + | "JqGrid column properties" online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:colmodel_options + | "JqGrid predefined format types" online documentation available at http://www.trirand.com/jqgridwiki/doku.php?id=wiki:predefined_formatter + | Warning: This options are vital for the functionaty of the package, do not modify them unless you know what you are doing. + | + */ + + 'function_type_properties' => array( + 'datatype' => array('xml', 'xmlstring', 'json', 'jsonstring', 'local', 'javascript', 'clientSide'), + 'cellattr' => array(), + 'formatter' => array('integer', 'number', 'currency', 'date', 'email', 'link', 'showlink', 'checkbox', 'select', 'actions'), + 'unformat' => array(), + 'sorttype' => array('int','integer', 'float','number', 'currency', 'date', 'text'), + 'unformat' => array(), + 'dataInit' => array(), + 'fn' => array(), + 'custom_element' => array(), + 'custom_value' => array(), + 'custom_func' => array(), + 'buildSelect' => array(), + 'dataInit' => array(), + 'summaryType' => array('sum','count','avg','min','max'), + 'rowattr' => array(), + 'converter' => array(), + ), +);