-
Notifications
You must be signed in to change notification settings - Fork 46
/
DataTable.php
310 lines (286 loc) · 12.3 KB
/
DataTable.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
<?php
/**
* @copyright Copyright (c) 2015 Serhiy Vinichuk
* @license MIT
* @author Serhiy Vinichuk <[email protected]>
*/
namespace nullref\datatable;
use nullref\datatable\assets\DataTableAsset;
use yii\base\Model;
use yii\base\Widget;
use yii\data\ActiveDataProvider;
use yii\data\ArrayDataProvider;
use yii\db\ActiveQueryInterface;
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\helpers\Json;
/**
* Class DataTable
* @package nullref\datatable
* Features
* @property bool $autoWidth Feature control DataTables' smart column width handling
* @property bool $deferRender Feature control deferred rendering for additional speed of initialisation.
* @property bool $info Feature control table information display field
* @property bool $jQueryUI Use markup and classes for the table to be themed by jQuery UI ThemeRoller.
* @property bool $lengthChange Feature control the end user's ability to change the paging display length of the table.
* @property bool $ordering Feature control ordering (sorting) abilities in DataTables.
* @property bool $paging Enable or disable table pagination.
* @property bool $processing Feature control the processing indicator.
* @property bool $scrollX Enable horizontal scrolling
* @property bool $scrollY Enable vertical scrolling
* @property bool $searching Feature control search (filtering) abilities
* @property bool $serverSide Enable server-side filtering, paging and sorting calculations
* @property bool $stateSave Restore table state on page reload
* @property array $language
* Options
* @property array $ajax Load data for the table's content from an Ajax source
* @property array $data Data to use as the display data for the table.
* @property array $columnDefs Set column definition initialisation properties.
* @property array $columns Set column specific initialisation properties.
* @property bool|int|array $deferLoading Delay the loading of server-side data until second draw
* @propert bool $destroy Destroy any existing table matching the selector and replace with the new options.
* @property int $displayStart Initial paging start point
* @property string $dom Define the table control elements to appear on the page and in what order
* @property array $lengthMenu Change the options in the page length `select` list.
* @property bool $orderCellsTop Control which cell the order event handler will be applied to in a column
* @property bool $orderClasses Highlight the columns being ordered in the table's body
* @property array $order Initial order (sort) to apply to the table
* @property array $orderFixed Ordering to always be applied to the table
* @property bool $orderMulti Multiple column ordering ability control.
* @property int $pageLength Change the initial page length (number of rows per page)
* @property string $pagingType Pagination button display options.
* @property string|array $renderer Display component renderer types
* @property bool $retrieve Retrieve an existing DataTables instance
* @property bool $scrollCollapse Allow the table to reduce in height when a limited number of rows are shown.
* @property array $search
* @property array $searchCols Define an initial search for individual columns.
* @property array $searchDelay Set a throttle frequency for searching
* @property int $stateDuration Saved state validity duration
* @property array $stripeClasses Set the zebra stripe class names for the rows in the table.
* @property int $tabIndex Tab index control for keyboard navigation
* Callbacks
* @property string $createdRow Callback for whenever a TR element is created for the table's body.
* @property string $drawCallback Function that is called every time DataTables performs a draw.
* @property string $footerCallback Footer display callback function.
* @property string $formatNumber Number formatting callback function.
* @property string $headerCallback Header display callback function.
* @property string $infoCallback Table summary information display callback.
* @property string $initComplete Initialisation complete callback.
* @property string $preDrawCallback Pre-draw callback.
* @property string $rowCallback Row draw callback.
* @property string $stateLoadCallback Callback that defines where and how a saved state should be loaded.
* @property string $stateLoaded State loaded callback.
* @property string $stateLoadParams State loaded - data manipulation callback
* @property string $stateSaveCallback Callback that defines how the table state is stored and where.
* @property string $stateSaveParams State save - data manipulation callback
*/
class DataTable extends Widget
{
const COLUMN_TYPE_DATE = 'date';
const COLUMN_TYPE_NUM = 'num';
const COLUMN_TYPE_NUM_FMT = 'num-fmt';
const COLUMN_TYPE_HTML_NUM = 'html-num';
const COLUMN_TYPE_HTML_NUM_FMT = 'html-num-fmt';
const COLUMN_TYPE_STRING = 'string';
const PAGING_SIMPLE = 'simple';
const PAGING_SIMPLE_NUMBERS = 'simple_numbers';
const PAGING_FULL = 'full';
const PAGING_FULL_NUMBERS = 'full_numbers';
public $id;
/**
* @var array Html options for table
*/
public $tableOptions = [];
/**
* @var array
*/
public $extraColumns = [];
public $withColumnFilter;
/** @var bool */
public $globalVariable = false;
protected $_options = [];
/**
* @var \yii\data\DataProviderInterface the data provider for the view.
*/
protected $_dataProvider;
protected $_extraColumns = [];
/**
* @throws \yii\base\InvalidConfigException
* @throws \Exception if ArrayHelper::getValue()
*/
public function init()
{
parent::init();
if ($this->data === null) {
$this->data = is_null($this->_dataProvider) ? [] : $this->_dataProvider->getModels();
}
DataTableAsset::register($this->getView());
$this->initColumns();
$this->initData();
}
/**
* @throws \yii\base\InvalidConfigException
*/
protected function initColumns()
{
$this->_extraColumns = $this->extraColumns;
if (isset($this->_options['columns'])) {
$demoObject = $this->getModel();
foreach ($this->_options['columns'] as $key => $value) {
if (!is_array($value)) {
$value = [
'class' => DataTableColumn::class,
'attribute' => $value,
'label' => $demoObject instanceof \yii\base\Model
? $demoObject->getAttributeLabel($value)
: Inflector::camel2words($value)
];
}
if (isset($value['type'])) {
if ($value['type'] == 'link') {
$value['class'] = LinkColumn::className();
unset($value['type']);
}
}
if (!isset($value['class'])) {
$value['class'] = DataTableColumn::className();
}
if (isset($value['class'])) {
$column = \Yii::createObject($value);
if ($column instanceof DataTableColumn) {
$this->_extraColumns = array_merge($this->_extraColumns, $column->getExtraColumns());
}
$this->_options['columns'][$key] = $column;
}
}
}
}
/**
* Detect a model class from `dataProvider` or `data` attributes
*
* @see \yii\grid\DataColumn::getHeaderCellLabel()
*
* return Model|null NULL is returned when only property $data is defined, and is either empty or first entry is not of type model
*/
protected function getModel()
{
$provider = $this->_dataProvider;
if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQueryInterface) {
/* @var $modelClass Model */
$modelClass = $provider->query->modelClass;
$model = $modelClass::instance();
} elseif ($provider instanceof ArrayDataProvider && $provider->modelClass !== null) {
/* @var $modelClass Model */
$modelClass = $provider->modelClass;
$model = $modelClass::instance();
} else {
$models = $this->data;
//$model = (count($models)) ? $models[0] : null;
$model = reset($models);
}
return $model instanceof Model ? $model : null;
}
/**
* @throws \Exception if ArrayHelper::getValue() throws
*/
private function initData()
{
$this->_extraColumns = array_unique($this->_extraColumns);
if (array_key_exists('data', $this->_options)) {
$data = [];
foreach ($this->_options['data'] as $obj) {
$row = [];
foreach ($this->_options['columns'] as $column) {
if ($column instanceof DataTableColumn) {
if ($column->data) {
$value = ArrayHelper::getValue($obj, $column->data);
if (($pos = strrpos($column->data, '.')) !== false) {
$keys = explode('.', $column->data);
$a = $value;
foreach (array_reverse($keys) as $key) {
$a = [$key => $a];
}
$row[$keys[0]] = $a[$keys[0]];
} else {
$row[$column->data] = $value;
}
}
}
}
foreach ($this->_extraColumns as $column) {
$row[$column] = ArrayHelper::getValue($obj, $column);
}
if ($row) {
$data[] = $row;
}
}
$this->_options['data'] = $data;
}
}
public function run()
{
$id = isset($this->id) ? $this->id : $this->getId();
echo Html::beginTag('table', ArrayHelper::merge(['id' => $id], $this->tableOptions));
echo Html::endTag('table');
$globalVariable = $this->globalVariable ? "window[$id] =" : '';
if ($this->withColumnFilter) {
$encodedParams = Json::encode($this->getParams());
$this->getView()->registerJs(
<<<JS
(function() {
var params = ${encodedParams};
var table;
${globalVariable} table = jQuery("#${id}").DataTable(params);
var filterRow = jQuery('<tr></tr>');
jQuery('#${id} thead tr th').each(function(i)
{
var cell = jQuery('<td></td>')
.attr('colspan', jQuery(this).attr('colspan'))
.attr('class', jQuery(this).attr('class'))
.attr('tabindex', jQuery(this).attr('tabindex'))
.attr('aria-controls', jQuery(this).attr('aria-controls'))
.removeClass('sorting sorting_disabled')
.appendTo(filterRow);
if (params.columns && params.columns[i] && params.columns[i].renderFilter) {
cell.html(jQuery.isFunction(params.columns[i].renderFilter) ? params.columns[i].renderFilter(table) : params.columns[i].renderFilter);
}
});
jQuery('#${id} thead').append(filterRow);
jQuery('#${id} thead tr:eq(1) td').each( function (i)
{
jQuery(':input', this).on('keyup change', function () {
if (table.column(i).search() !== jQuery(this).val()) {
table
.column(i)
.search(jQuery(this).val())
.draw();
}
} );
} );
})();
JS
);
} else {
$this->getView()->registerJs($globalVariable . 'jQuery("#' . $id . '").DataTable(' . Json::encode($this->getParams()) . ');');
}
}
protected function getParams()
{
return $this->_options;
}
public function __get($name)
{
if ($name == 'dataProvider') {
return $this->_dataProvider;
}
return isset($this->_options[$name]) ? $this->_options[$name] : null;
}
public function __set($name, $value)
{
if ($name == 'dataProvider') {
return $this->_dataProvider = $value;
}
return $this->_options[$name] = $value;
}
}