Complex classes like Grid often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Grid, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
51 | class Grid extends Components\Container |
||
52 | 1 | { |
|
53 | /***** DEFAULTS ****/ |
||
54 | const BUTTONS = 'buttons'; |
||
55 | |||
56 | const CLIENT_SIDE_OPTIONS = 'grido-options'; |
||
57 | |||
58 | /** @var int @persistent */ |
||
59 | public $page = 1; |
||
60 | |||
61 | /** @var int @persistent */ |
||
62 | public $perPage; |
||
63 | |||
64 | /** @var array @persistent */ |
||
65 | public $sort = []; |
||
66 | |||
67 | /** @var array @persistent */ |
||
68 | public $filter = []; |
||
69 | |||
70 | /** @var array event on all grid's components registered */ |
||
71 | public $onRegistered; |
||
72 | |||
73 | /** @var array event on render */ |
||
74 | public $onRender; |
||
75 | |||
76 | /** @var array event for modifying data */ |
||
77 | public $onFetchData; |
||
78 | |||
79 | /** @var callback returns tr html element; function($row, Html $tr) */ |
||
80 | protected $rowCallback; |
||
81 | |||
82 | /** @var \Nette\Utils\Html */ |
||
83 | protected $tablePrototype; |
||
84 | |||
85 | /** @var bool */ |
||
86 | protected $rememberState = FALSE; |
||
87 | |||
88 | /** @var string */ |
||
89 | protected $rememberStateSectionName; |
||
90 | |||
91 | /** @var string */ |
||
92 | protected $primaryKey = 'id'; |
||
93 | |||
94 | /** @var string */ |
||
95 | protected $filterRenderType; |
||
96 | |||
97 | /** @var array */ |
||
98 | protected $perPageList = [10, 20, 30, 50, 100]; |
||
99 | |||
100 | /** @var int */ |
||
101 | protected $defaultPerPage = 20; |
||
102 | |||
103 | /** @var array */ |
||
104 | protected $defaultFilter = []; |
||
105 | |||
106 | /** @var array */ |
||
107 | protected $defaultSort = []; |
||
108 | |||
109 | /** @var DataSources\IDataSource */ |
||
110 | protected $model; |
||
111 | |||
112 | /** @var int total count of items */ |
||
113 | protected $count; |
||
114 | |||
115 | /** @var mixed */ |
||
116 | protected $data; |
||
117 | |||
118 | /** @var Paginator */ |
||
119 | protected $paginator; |
||
120 | |||
121 | /** @var \Nette\Localization\ITranslator */ |
||
122 | protected $translator; |
||
123 | |||
124 | /** @var PropertyAccessor */ |
||
125 | protected $propertyAccessor; |
||
126 | |||
127 | /** @var bool */ |
||
128 | protected $strictMode = TRUE; |
||
129 | |||
130 | /** @var array */ |
||
131 | protected $options = [ |
||
132 | self::CLIENT_SIDE_OPTIONS => [] |
||
133 | ]; |
||
134 | |||
135 | /** @var Customization */ |
||
136 | protected $customization; |
||
137 | |||
138 | /** |
||
139 | * Sets a model that implements the interface Grido\DataSources\IDataSource or data-source object. |
||
140 | * @param mixed $model |
||
141 | * @param bool $forceWrapper |
||
142 | 1 | * @throws Exception |
|
143 | * @return Grid |
||
144 | */ |
||
145 | public function setModel($model, $forceWrapper = FALSE) |
||
146 | { |
||
147 | $this->model = $model instanceof DataSources\IDataSource && $forceWrapper === FALSE |
||
148 | ? $model |
||
149 | : new DataSources\Model($model); |
||
150 | |||
151 | return $this; |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Sets the default number of items per page. |
||
156 | * @param int $perPage |
||
157 | * @return Grid |
||
158 | */ |
||
159 | public function setDefaultPerPage($perPage) |
||
160 | { |
||
161 | $perPage = (int) $perPage; |
||
162 | $this->defaultPerPage = $perPage; |
||
163 | |||
164 | if (!in_array($perPage, $this->perPageList)) { |
||
165 | $this->perPageList[] = $perPage; |
||
166 | sort($this->perPageList); |
||
167 | } |
||
168 | |||
169 | return $this; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Sets default filtering. |
||
174 | * @param array $filter |
||
175 | * @return Grid |
||
176 | */ |
||
177 | public function setDefaultFilter(array $filter) |
||
182 | |||
183 | /** |
||
184 | * Sets default sorting. |
||
185 | * @param array $sort |
||
186 | * @return Grid |
||
187 | * @throws Exception |
||
188 | */ |
||
189 | public function setDefaultSort(array $sort) |
||
190 | { |
||
191 | 1 | static $replace = ['asc' => Column::ORDER_ASC, 'desc' => Column::ORDER_DESC]; |
|
192 | |||
193 | 1 | foreach ($sort as $column => $dir) { |
|
194 | 1 | $dir = strtr(strtolower($dir), $replace); |
|
195 | 1 | if (!in_array($dir, $replace)) { |
|
196 | throw new Exception("Dir '$dir' for column '$column' is not allowed."); |
||
197 | } |
||
198 | |||
199 | 1 | $this->defaultSort[$column] = $dir; |
|
200 | 1 | } |
|
201 | |||
202 | 1 | return $this; |
|
203 | } |
||
204 | |||
205 | /** |
||
206 | * Sets items to per-page select. |
||
207 | * @param array $perPageList |
||
208 | * @return Grid |
||
209 | */ |
||
210 | public function setPerPageList(array $perPageList) |
||
211 | { |
||
212 | $this->perPageList = $perPageList; |
||
213 | |||
214 | if ($this->hasFilters(FALSE) || $this->hasOperation(FALSE)) { |
||
215 | $this['form']['count']->setItems($this->getItemsForCountSelect()); |
||
216 | } |
||
217 | |||
218 | return $this; |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * Sets translator. |
||
223 | * @param \Nette\Localization\ITranslator $translator |
||
224 | * @return Grid |
||
225 | */ |
||
226 | public function setTranslator(\Nette\Localization\ITranslator $translator) |
||
231 | |||
232 | /** |
||
233 | * Sets type of filter rendering. |
||
234 | * Defaults inner (Filter::RENDER_INNER) if column does not exist then outer filter (Filter::RENDER_OUTER). |
||
235 | * @param string $type |
||
236 | * @throws Exception |
||
237 | * @return Grid |
||
238 | */ |
||
239 | public function setFilterRenderType($type) |
||
240 | { |
||
241 | $type = strtolower($type); |
||
242 | if (!in_array($type, [Filter::RENDER_INNER, Filter::RENDER_OUTER])) { |
||
243 | throw new Exception('Type must be Filter::RENDER_INNER or Filter::RENDER_OUTER.'); |
||
244 | } |
||
245 | |||
246 | $this->filterRenderType = $type; |
||
247 | return $this; |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Sets custom paginator. |
||
252 | * @param Paginator $paginator |
||
253 | * @return Grid |
||
254 | */ |
||
255 | public function setPaginator(Paginator $paginator) |
||
256 | { |
||
257 | $this->paginator = $paginator; |
||
258 | return $this; |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * Sets grid primary key. |
||
263 | * Defaults is "id". |
||
264 | * @param string $key |
||
265 | * @return Grid |
||
266 | */ |
||
267 | public function setPrimaryKey($key) |
||
268 | { |
||
269 | $this->primaryKey = $key; |
||
270 | return $this; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Sets file name of custom template. |
||
275 | * @param string $file |
||
276 | * @return Grid |
||
277 | */ |
||
278 | public function setTemplateFile($file) |
||
279 | { |
||
280 | $this->onRender[] = function() use ($file) { |
||
281 | $this->template->gridoTemplate = $this->getTemplate()->getFile(); |
||
|
|||
282 | $this->getTemplate()->setFile($file); |
||
283 | }; |
||
284 | |||
285 | return $this; |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Sets saving state to session. |
||
290 | * @param bool $state |
||
291 | * @param string $sectionName |
||
292 | * @return Grid |
||
293 | */ |
||
294 | public function setRememberState($state = TRUE, $sectionName = NULL) |
||
295 | { |
||
296 | $this->getPresenter(); //component must be attached to presenter |
||
297 | $this->getRememberSession(TRUE); //start session if not |
||
298 | $this->rememberState = (bool) $state; |
||
299 | $this->rememberStateSectionName = $sectionName; |
||
300 | |||
301 | return $this; |
||
302 | } |
||
303 | |||
304 | /** |
||
305 | * Sets callback for customizing tr html object. |
||
306 | * Callback returns tr html element; function($row, Html $tr). |
||
307 | * @param $callback |
||
308 | * @return Grid |
||
309 | */ |
||
310 | public function setRowCallback($callback) |
||
311 | { |
||
312 | $this->rowCallback = $callback; |
||
313 | return $this; |
||
314 | } |
||
315 | |||
316 | /** |
||
317 | * Sets client-side options. |
||
318 | * @param array $options |
||
319 | * @return Grid |
||
320 | */ |
||
321 | public function setClientSideOptions(array $options) |
||
326 | |||
327 | /** |
||
328 | * Determines whether any user error will cause a notice. |
||
329 | * @param bool $mode |
||
330 | * @return \Grido\Grid |
||
331 | */ |
||
332 | public function setStrictMode($mode) |
||
333 | { |
||
334 | $this->strictMode = (bool) $mode; |
||
335 | return $this; |
||
336 | } |
||
337 | |||
338 | /** |
||
339 | * @param \Grido\Customization $customization |
||
340 | */ |
||
341 | public function setCustomization(Customization $customization) |
||
342 | { |
||
343 | $this->customization = $customization; |
||
344 | } |
||
345 | |||
346 | /**********************************************************************************************/ |
||
347 | |||
348 | /** |
||
349 | * Returns total count of data. |
||
350 | * @return int |
||
351 | */ |
||
352 | public function getCount() |
||
353 | { |
||
354 | if ($this->count === NULL) { |
||
355 | $this->count = $this->getModel()->getCount(); |
||
356 | } |
||
357 | |||
358 | return $this->count; |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Returns default per page. |
||
363 | * @return int |
||
364 | */ |
||
365 | public function getDefaultPerPage() |
||
366 | 1 | { |
|
367 | if (!in_array($this->defaultPerPage, $this->perPageList)) { |
||
368 | $this->defaultPerPage = $this->perPageList[0]; |
||
369 | } |
||
370 | |||
371 | return $this->defaultPerPage; |
||
372 | } |
||
373 | |||
374 | /** |
||
375 | * Returns default filter. |
||
376 | * @return array |
||
377 | */ |
||
378 | public function getDefaultFilter() |
||
382 | |||
383 | /** |
||
384 | * Returns default sort. |
||
385 | * @return array |
||
386 | */ |
||
387 | public function getDefaultSort() |
||
391 | |||
392 | /** |
||
393 | * Returns list of possible items per page. |
||
394 | * @return array |
||
395 | */ |
||
396 | 1 | public function getPerPageList() |
|
397 | { |
||
398 | return $this->perPageList; |
||
399 | } |
||
400 | |||
401 | /** |
||
402 | * Returns primary key. |
||
403 | * @return string |
||
404 | */ |
||
405 | public function getPrimaryKey() |
||
406 | { |
||
407 | return $this->primaryKey; |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * Returns remember state. |
||
412 | * @return bool |
||
413 | */ |
||
414 | public function getRememberState() |
||
415 | { |
||
416 | return $this->rememberState; |
||
417 | } |
||
418 | |||
419 | /** |
||
420 | * Returns row callback. |
||
421 | * @return callback |
||
422 | */ |
||
423 | public function getRowCallback() |
||
424 | { |
||
425 | return $this->rowCallback; |
||
426 | } |
||
427 | |||
428 | /** |
||
429 | * Returns items per page. |
||
430 | * @return int |
||
431 | */ |
||
432 | public function getPerPage() |
||
433 | { |
||
434 | 1 | return $this->perPage === NULL |
|
435 | ? $this->getDefaultPerPage() |
||
436 | : $this->perPage; |
||
437 | } |
||
438 | |||
439 | /** |
||
440 | * Returns actual filter values. |
||
441 | * @param string $key |
||
442 | * @return mixed |
||
443 | */ |
||
444 | public function getActualFilter($key = NULL) |
||
445 | { |
||
446 | $filter = $this->filter ? $this->filter : $this->defaultFilter; |
||
447 | return $key !== NULL && isset($filter[$key]) ? $filter[$key] : $filter; |
||
448 | } |
||
449 | |||
450 | /** |
||
451 | * Returns fetched data. |
||
452 | * @param bool $applyPaging |
||
453 | * @param bool $useCache |
||
454 | * @param bool $fetch |
||
455 | * @throws Exception |
||
456 | * @return array|DataSources\IDataSource|\Nette\Database\Table\Selection |
||
457 | */ |
||
458 | public function getData($applyPaging = TRUE, $useCache = TRUE, $fetch = TRUE) |
||
459 | { |
||
460 | if ($this->getModel() === NULL) { |
||
461 | throw new Exception('Model cannot be empty, please use method $grid->setModel().'); |
||
462 | } |
||
463 | |||
464 | $data = $this->data; |
||
465 | if ($data === NULL || $useCache === FALSE) { |
||
466 | $this->applyFiltering(); |
||
467 | $this->applySorting(); |
||
468 | |||
469 | if ($applyPaging) { |
||
470 | $this->applyPaging(); |
||
471 | } |
||
472 | |||
473 | if ($fetch === FALSE) { |
||
474 | return $this->getModel(); |
||
475 | } |
||
476 | |||
477 | $data = $this->getModel()->getData(); |
||
478 | |||
479 | if ($useCache === TRUE) { |
||
480 | $this->data = $data; |
||
481 | } |
||
482 | |||
483 | if ($applyPaging && !empty($data) && !in_array($this->page, range(1, $this->getPaginator()->pageCount))) { |
||
484 | $this->__triggerUserNotice("Page is out of range."); |
||
485 | $this->page = 1; |
||
486 | } |
||
487 | |||
488 | if (!empty($this->onFetchData)) { |
||
489 | $this->onFetchData($this); |
||
490 | } |
||
491 | } |
||
492 | |||
493 | return $data; |
||
494 | } |
||
495 | |||
496 | /** |
||
497 | * Returns translator. |
||
498 | * @return Translations\FileTranslator |
||
499 | */ |
||
500 | public function getTranslator() |
||
508 | |||
509 | /** |
||
510 | * Returns remember session for set expiration, etc. |
||
511 | * @param bool $forceStart - if TRUE, session will be started if not |
||
512 | * @return \Nette\Http\SessionSection|NULL |
||
513 | */ |
||
514 | public function getRememberSession($forceStart = FALSE) |
||
515 | { |
||
516 | $presenter = $this->getPresenter(); |
||
517 | $session = $presenter->getSession(); |
||
518 | |||
519 | if (!$session->isStarted() && $forceStart) { |
||
520 | $session->start(); |
||
521 | } |
||
522 | |||
523 | return $session->isStarted() |
||
524 | ? ($session->getSection($this->rememberStateSectionName ?: ($presenter->name . ':' . $this->getUniqueId()))) |
||
525 | : NULL; |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * Returns table html element of grid. |
||
530 | * @return \Nette\Utils\Html |
||
531 | */ |
||
532 | public function getTablePrototype() |
||
533 | { |
||
534 | if ($this->tablePrototype === NULL) { |
||
535 | $this->tablePrototype = \Nette\Utils\Html::el('table'); |
||
536 | $this->tablePrototype->id($this->getName()); |
||
537 | } |
||
538 | |||
539 | return $this->tablePrototype; |
||
540 | } |
||
541 | |||
542 | /** |
||
543 | * @return string |
||
544 | * @internal |
||
545 | */ |
||
546 | public function getFilterRenderType() |
||
547 | { |
||
548 | if ($this->filterRenderType !== NULL) { |
||
549 | return $this->filterRenderType; |
||
550 | } |
||
551 | |||
552 | $this->filterRenderType = Filter::RENDER_OUTER; |
||
553 | if ($this->hasColumns() && $this->hasFilters() && $this->hasActions()) { |
||
554 | $this->filterRenderType = Filter::RENDER_INNER; |
||
555 | |||
556 | $filters = $this[Filter::ID]->getComponents(); |
||
557 | foreach ($filters as $filter) { |
||
558 | if (!$this[Column::ID]->getComponent($filter->name, FALSE)) { |
||
559 | $this->filterRenderType = Filter::RENDER_OUTER; |
||
560 | break; |
||
561 | } |
||
562 | } |
||
563 | } |
||
564 | |||
565 | return $this->filterRenderType; |
||
566 | } |
||
567 | |||
568 | /** |
||
569 | * @return DataSources\IDataSource |
||
570 | */ |
||
571 | public function getModel() |
||
572 | { |
||
573 | return $this->model; |
||
574 | } |
||
575 | |||
576 | /** |
||
577 | * @return Paginator |
||
578 | * @internal |
||
579 | */ |
||
580 | public function getPaginator() |
||
581 | { |
||
582 | if ($this->paginator === NULL) { |
||
583 | $this->paginator = new Paginator; |
||
584 | $this->paginator->setItemsPerPage($this->getPerPage()) |
||
585 | ->setGrid($this); |
||
586 | } |
||
587 | |||
588 | return $this->paginator; |
||
589 | } |
||
590 | |||
591 | /** |
||
592 | * A simple wrapper around symfony/property-access with Nette Database dot notation support. |
||
593 | * @param array|object $object |
||
594 | * @param string $name |
||
595 | * @return mixed |
||
596 | * @internal |
||
597 | */ |
||
598 | public function getProperty($object, $name) |
||
599 | { |
||
600 | 1 | if ($object instanceof \Nette\Database\Table\IRow && \Nette\Utils\Strings::contains($name, '.')) { |
|
601 | $parts = explode('.', $name); |
||
602 | foreach ($parts as $item) { |
||
603 | if (is_object($object)) { |
||
604 | $object = $object->$item; |
||
605 | } |
||
606 | } |
||
607 | |||
608 | return $object; |
||
609 | } |
||
610 | |||
611 | 1 | if (is_array($object)) { |
|
612 | 1 | $name = "[$name]"; |
|
613 | 1 | } |
|
614 | |||
615 | 1 | return $this->getPropertyAccessor()->getValue($object, $name); |
|
616 | } |
||
617 | |||
618 | /** |
||
619 | * @return PropertyAccessor |
||
620 | * @internal |
||
621 | */ |
||
622 | public function getPropertyAccessor() |
||
630 | |||
631 | /** |
||
632 | * @param mixed $row item from db |
||
633 | * @return \Nette\Utils\Html |
||
634 | * @internal |
||
635 | */ |
||
636 | public function getRowPrototype($row) |
||
637 | { |
||
638 | try { |
||
639 | $primaryValue = $this->getProperty($row, $this->getPrimaryKey()); |
||
640 | } catch (\Exception $e) { |
||
641 | $primaryValue = NULL; |
||
642 | } |
||
643 | |||
644 | $tr = \Nette\Utils\Html::el('tr'); |
||
645 | $primaryValue ? $tr->class[] = "grid-row-$primaryValue" : NULL; |
||
646 | |||
647 | if ($this->rowCallback) { |
||
648 | $tr = call_user_func_array($this->rowCallback, [$row, $tr]); |
||
649 | } |
||
650 | |||
651 | return $tr; |
||
652 | } |
||
653 | |||
654 | /** |
||
655 | * Returns client-side options. |
||
656 | * @return array |
||
657 | */ |
||
658 | public function getClientSideOptions() |
||
662 | |||
663 | /** |
||
664 | * @return bool |
||
665 | */ |
||
666 | public function isStrictMode() |
||
667 | { |
||
668 | return $this->strictMode; |
||
669 | } |
||
670 | |||
671 | /** |
||
672 | * @return Customization |
||
673 | */ |
||
674 | public function getCustomization() |
||
675 | { |
||
676 | if ($this->customization === NULL) { |
||
677 | $this->customization = new Customization($this); |
||
678 | } |
||
679 | |||
680 | return $this->customization; |
||
681 | } |
||
682 | |||
683 | /**********************************************************************************************/ |
||
684 | |||
685 | /** |
||
686 | * Loads state informations. |
||
687 | * @param array $params |
||
688 | * @internal |
||
689 | */ |
||
690 | public function loadState(array $params) |
||
691 | { |
||
692 | //loads state from session |
||
693 | $session = $this->getRememberSession(); |
||
694 | if ($session && $this->getPresenter()->isSignalReceiver($this)) { |
||
695 | $session->remove(); |
||
696 | } elseif ($session && empty($params) && $session->params) { |
||
697 | $params = (array) $session->params; |
||
698 | } |
||
699 | |||
700 | parent::loadState($params); |
||
701 | } |
||
702 | |||
703 | /** |
||
704 | * Saves state informations for next request. |
||
705 | * @param array $params |
||
706 | * @param \Nette\Application\UI\PresenterComponentReflection $reflection (internal, used by Presenter) |
||
707 | * @internal |
||
708 | */ |
||
709 | public function saveState(array &$params, $reflection = NULL) |
||
710 | { |
||
711 | !empty($this->onRegistered) && $this->onRegistered($this); |
||
712 | return parent::saveState($params, $reflection); |
||
713 | } |
||
714 | |||
715 | /** |
||
716 | * Ajax method. |
||
717 | * @internal |
||
718 | */ |
||
719 | public function handleRefresh() |
||
723 | |||
724 | /** |
||
725 | * @param int $page |
||
726 | * @internal |
||
727 | */ |
||
728 | public function handlePage($page) |
||
729 | { |
||
730 | $this->reload(); |
||
731 | } |
||
732 | |||
733 | /** |
||
734 | * @param array $sort |
||
735 | * @internal |
||
736 | */ |
||
737 | public function handleSort(array $sort) |
||
738 | { |
||
739 | $this->page = 1; |
||
740 | $this->reload(); |
||
741 | } |
||
742 | |||
743 | /** |
||
744 | * @param \Nette\Forms\Controls\SubmitButton $button |
||
745 | * @internal |
||
746 | */ |
||
747 | public function handleFilter(\Nette\Forms\Controls\SubmitButton $button) |
||
748 | { |
||
749 | $values = $button->form->values[Filter::ID]; |
||
750 | $session = $this->rememberState //session filter |
||
751 | ? isset($this->getRememberSession(TRUE)->params['filter']) |
||
752 | ? $this->getRememberSession(TRUE)->params['filter'] |
||
753 | : [] |
||
754 | : []; |
||
755 | |||
756 | foreach ($values as $name => $value) { |
||
757 | if (is_numeric($value) || !empty($value) || isset($this->defaultFilter[$name]) || isset($session[$name])) { |
||
758 | $this->filter[$name] = $this->getFilter($name)->changeValue($value); |
||
759 | } elseif (isset($this->filter[$name])) { |
||
760 | unset($this->filter[$name]); |
||
761 | } |
||
762 | } |
||
763 | |||
764 | $this->page = 1; |
||
765 | $this->reload(); |
||
766 | } |
||
767 | |||
768 | /** |
||
769 | * @param \Nette\Forms\Controls\SubmitButton $button |
||
770 | * @internal |
||
771 | */ |
||
772 | public function handleReset(\Nette\Forms\Controls\SubmitButton $button) |
||
773 | { |
||
774 | $this->sort = []; |
||
775 | $this->filter = []; |
||
776 | $this->perPage = NULL; |
||
777 | |||
778 | if ($session = $this->getRememberSession()) { |
||
779 | $session->remove(); |
||
780 | } |
||
781 | |||
782 | $button->form->setValues([Filter::ID => $this->defaultFilter], TRUE); |
||
783 | |||
784 | $this->page = 1; |
||
785 | $this->reload(); |
||
786 | } |
||
787 | |||
788 | /** |
||
789 | * @param \Nette\Forms\Controls\SubmitButton $button |
||
790 | * @internal |
||
791 | */ |
||
792 | public function handlePerPage(\Nette\Forms\Controls\SubmitButton $button) |
||
793 | { |
||
794 | $perPage = (int) $button->form['count']->value; |
||
795 | $this->perPage = $perPage == $this->defaultPerPage |
||
796 | ? NULL |
||
797 | : $perPage; |
||
798 | |||
799 | $this->page = 1; |
||
800 | $this->reload(); |
||
801 | } |
||
802 | |||
803 | /** |
||
804 | * Refresh wrapper. |
||
805 | * @return void |
||
806 | * @internal |
||
807 | */ |
||
808 | public function reload() |
||
809 | { |
||
810 | if ($this->presenter->isAjax()) { |
||
811 | $this->presenter->payload->grido = TRUE; |
||
812 | $this->redrawControl(); |
||
813 | } else { |
||
814 | $this->redirect('this'); |
||
815 | } |
||
816 | } |
||
817 | |||
818 | /**********************************************************************************************/ |
||
819 | |||
820 | /** |
||
821 | * @return \Nette\Templating\FileTemplate |
||
822 | * @internal |
||
823 | */ |
||
824 | public function createTemplate() |
||
825 | { |
||
826 | $template = parent::createTemplate(); |
||
827 | $template->setFile($this->getCustomization()->getTemplateFiles()[Customization::TEMPLATE_DEFAULT]); |
||
828 | $template->registerHelper('translate', [$this->getTranslator(), 'translate']); |
||
829 | |||
830 | return $template; |
||
831 | } |
||
832 | |||
833 | /** |
||
834 | * @internal |
||
835 | * @throws Exception |
||
836 | */ |
||
837 | public function render() |
||
838 | { |
||
839 | if (!$this->hasColumns()) { |
||
840 | throw new Exception('Grid must have defined a column, please use method $grid->addColumn*().'); |
||
841 | } |
||
842 | |||
843 | $this->saveRememberState(); |
||
844 | $data = $this->getData(); |
||
845 | |||
846 | if (!empty($this->onRender)) { |
||
847 | $this->onRender($this); |
||
848 | } |
||
849 | |||
850 | $this->template->data = $data; |
||
851 | $this->template->form = $form = $this['form']; |
||
852 | $this->template->paginator = $this->getPaginator(); |
||
853 | |||
854 | $this->template->columns = $this->getComponent(Column::ID)->getComponents(); |
||
855 | $this->template->actions = $this->hasActions() ? $this->getComponent(Action::ID)->getComponents() : []; |
||
856 | $this->template->buttons = $this->hasButtons() ? $this->getComponent(Button::ID)->getComponents() : []; |
||
857 | $this->template->formFilters = $this->hasFilters() ? $form->getComponent(Filter::ID)->getComponents() : []; |
||
858 | $this->template->customization = $this->getCustomization(); |
||
859 | |||
860 | $form['count']->setValue($this->getPerPage()); |
||
861 | |||
862 | if ($options = $this->options[self::CLIENT_SIDE_OPTIONS]) { |
||
863 | $this->getTablePrototype()->data[self::CLIENT_SIDE_OPTIONS] = json_encode($options); |
||
864 | } |
||
865 | |||
866 | $this->template->render(); |
||
867 | } |
||
868 | |||
869 | protected function saveRememberState() |
||
870 | { |
||
871 | if ($this->rememberState) { |
||
872 | $session = $this->getRememberSession(TRUE); |
||
873 | $params = array_keys($this->getReflection()->getPersistentParams()); |
||
874 | foreach ($params as $param) { |
||
875 | $session->params[$param] = $this->$param; |
||
876 | } |
||
877 | } |
||
878 | } |
||
879 | |||
880 | protected function applyFiltering() |
||
881 | { |
||
882 | $conditions = $this->__getConditions($this->getActualFilter()); |
||
883 | $this->getModel()->filter($conditions); |
||
884 | } |
||
885 | |||
886 | /** |
||
887 | * @param array $filter |
||
888 | * @return array |
||
889 | * @internal |
||
890 | */ |
||
891 | public function __getConditions(array $filter) |
||
892 | { |
||
893 | $conditions = []; |
||
894 | if (!empty($filter)) { |
||
895 | try { |
||
896 | $this['form']->setDefaults([Filter::ID => $filter]); |
||
897 | } catch (\Nette\InvalidArgumentException $e) { |
||
898 | $this->__triggerUserNotice($e->getMessage()); |
||
899 | $filter = []; |
||
900 | if ($session = $this->getRememberSession()) { |
||
901 | $session->remove(); |
||
902 | } |
||
903 | } |
||
904 | |||
905 | foreach ($filter as $column => $value) { |
||
906 | if ($component = $this->getFilter($column, FALSE)) { |
||
907 | if ($condition = $component->__getCondition($value)) { |
||
908 | $conditions[] = $condition; |
||
909 | } |
||
910 | } else { |
||
911 | $this->__triggerUserNotice("Filter with name '$column' does not exist."); |
||
912 | } |
||
913 | } |
||
914 | } |
||
915 | |||
916 | return $conditions; |
||
917 | } |
||
918 | |||
919 | protected function applySorting() |
||
920 | { |
||
921 | $sort = []; |
||
922 | $this->sort = $this->sort ? $this->sort : $this->defaultSort; |
||
923 | |||
924 | foreach ($this->sort as $column => $dir) { |
||
925 | $component = $this->getColumn($column, FALSE); |
||
926 | if (!$component) { |
||
927 | if (!isset($this->defaultSort[$column])) { |
||
928 | $this->__triggerUserNotice("Column with name '$column' does not exist."); |
||
929 | break; |
||
930 | } |
||
931 | |||
932 | } elseif (!$component->isSortable()) { |
||
933 | if (isset($this->defaultSort[$column])) { |
||
934 | $component->setSortable(); |
||
935 | } else { |
||
936 | $this->__triggerUserNotice("Column with name '$column' is not sortable."); |
||
937 | break; |
||
938 | } |
||
939 | } |
||
940 | |||
941 | if (!in_array($dir, [Column::ORDER_ASC, Column::ORDER_DESC])) { |
||
942 | if ($dir == '' && isset($this->defaultSort[$column])) { |
||
943 | unset($this->sort[$column]); |
||
944 | break; |
||
945 | } |
||
946 | |||
947 | $this->__triggerUserNotice("Dir '$dir' is not allowed."); |
||
948 | break; |
||
949 | } |
||
950 | |||
951 | $sort[$component ? $component->column : $column] = $dir == Column::ORDER_ASC ? 'ASC' : 'DESC'; |
||
952 | } |
||
953 | |||
954 | if (!empty($sort)) { |
||
955 | $this->getModel()->sort($sort); |
||
956 | } |
||
957 | } |
||
958 | |||
959 | protected function applyPaging() |
||
960 | { |
||
961 | $paginator = $this->getPaginator() |
||
962 | ->setItemCount($this->getCount()) |
||
963 | ->setPage($this->page); |
||
964 | |||
965 | $perPage = $this->getPerPage(); |
||
966 | if ($perPage !== NULL && !in_array($perPage, $this->perPageList)) { |
||
967 | $this->__triggerUserNotice("The number '$perPage' of items per page is out of range."); |
||
968 | } |
||
969 | |||
970 | $this->getModel()->limit($paginator->getOffset(), $paginator->getLength()); |
||
971 | } |
||
972 | |||
973 | protected function createComponentForm($name) |
||
991 | |||
992 | /** |
||
993 | * @return array |
||
994 | */ |
||
995 | protected function getItemsForCountSelect() |
||
999 | |||
1000 | /** |
||
1001 | * @internal |
||
1002 | * @param string $message |
||
1003 | */ |
||
1004 | public function __triggerUserNotice($message) |
||
1005 | { |
||
1006 | if ($this->getPresenter(FALSE) && $session = $this->getRememberSession()) { |
||
1007 | $session->remove(); |
||
1008 | } |
||
1009 | |||
1010 | $this->strictMode && trigger_error($message, E_USER_NOTICE); |
||
1011 | } |
||
1012 | } |
||
1013 |
If you access a property on an interface, you most likely code against a concrete implementation of the interface.
Available Fixes
Adding an additional type check:
Changing the type hint: