Overview

Packages

  • Onion::Controllers
  • Onion::Core
  • Onion::UI
  • Onion::Utils

Classes

  • Form
  • Grid
  • Html
  • IconsGrid
  • Paginator
  • Template
  • Widget
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * Onion Framework - Zobrazenie dát v tabuľke s možnosťou radenia a filtrovania
  5:  *
  6:  * Copyright (c) 2011 Jano Gašpar (http://webstranky.net)
  7:  *
  8:  * @author    Jano Gašpar
  9:  * @copyright Copyright (c) 2011 Jano Gašpar
 10:  * @package   Onion::UI
 11:  **/
 12: class Grid
 13: {
 14:     private $auth;
 15: 
 16: 
 17:     /**
 18:      * @var string id na identifikáciu gridu
 19:      */
 20:     private $id;
 21: 
 22: 
 23:     /**
 24:      * @var string token na identifikáciu gridu
 25:      */
 26:     private $token;
 27: 
 28: 
 29:     /**
 30:      * @var string metóda na získavanie
 31:      */
 32:     private $model;
 33: 
 34: 
 35:     /**
 36:      * @var array stĺpce
 37:      */
 38:     private $columns = array();
 39: 
 40: 
 41:     /**
 42:      * @var array aktuálny sĺpec
 43:      */
 44:     private $current_column = array();
 45: 
 46: 
 47:     /**
 48:      * @var array akcie
 49:      */
 50:     private $actions = array();
 51: 
 52: 
 53:     /**
 54:      * @var array akcie na hromadné spracovanie dát
 55:      */
 56:     private $multi_actions = array();
 57: 
 58: 
 59:     /**
 60:      * @var int počet riadkov v gride, NULL zakáže stránkovanie
 61:      */
 62:     public $rows = 15;
 63: 
 64: 
 65:     /**
 66:      * @var int meno routy podľa ktorej sa generujú odkazy
 67:      */
 68:     public $route_name = NULL;
 69: 
 70: 
 71:     /**
 72:      * @var array stĺpec a smer radenia
 73:      */
 74:     public $default_order = array('id', 'ASC');
 75: 
 76: 
 77:     /**
 78:      * @var array texty
 79:      */
 80:     public $strings = array(
 81:         'page' => 'Page: ',
 82:         'selected items' => 'Selected items: ',
 83:         'search' => 'Search',
 84:         'show all' => 'Show all'
 85:         );
 86: 
 87: 
 88:     /**
 89:      * @var string CSS ID
 90:      */
 91:     public $css_id;
 92: 
 93: 
 94: 
 95:     /**
 96:      * Konštruktor
 97:      *
 98:      * @param  string $form_id identifikátor gridu
 99:      * @param  object $model   inštancia modelu na manipuláciu s dátami
100:      * @return object grid instance
101:      */
102:     public function __construct($app, $grid_id, $model)
103:     {
104:         $this->app = $app;
105: 
106:         $this->id = $grid_id;
107: 
108:         //token sa generuje nanovo pre každú reláciu (session)
109:         $token = md5(uniqid('', TRUE));
110:         if (isset($_SESSION['grid'][$grid_id]['token']) === TRUE) {
111:             $token = $_SESSION['grid'][$grid_id]['token'];
112:         }
113:         $this->token = $_SESSION['grid'][$grid_id]['token'] = $token;
114: 
115:         $this->model = $model;
116: 
117:         return $this;
118:     }
119: 
120: 
121:     /**
122:      * Nastavovanie atribútov stĺpcov gridu pomocuu preťažovania
123:      *
124:      * @param string            $atribute meno atribútu
125:      * @param string|array|bool $value    hodnota atribútu
126:      */
127:     final public function __call($attribute, $value)
128:     {
129:         if (isset($value[0]) === FALSE) {
130:             $value = '';
131: 
132:         } else {
133:             $value = $value[0];
134:         }
135: 
136:         $this->current_column[$attribute] = $value;
137: 
138:         return $this;
139:     }
140: 
141: 
142:     /**
143:      * Metóda volaná pri prístupe k objektu ako k reťazcu
144:      */
145:     final public function __tostring()
146:     {
147:         return $this->render();
148:     }
149: 
150: 
151:     /**
152:      * Metóda na pridanie stĺpca
153:      *
154:      * @param  string meno stĺpca v databáze
155:      * @param  string meno stĺpca zobrazené v gride
156:      * @param  int    orezať na počet znakov
157:      * @return object grid instance
158:      */
159:     public function add_column($column, $title)
160:     {
161:         $this->save_prev_column();
162: 
163:         $this->current_column = array(
164:             'column' => $column,
165:             'title' => $title,
166:             'strip_to' => NULL,
167:             'filter' => NULL,
168:             'format' => NULL
169:             );
170: 
171:         return $this;
172:     }
173: 
174: 
175:     /**
176:      * Metóda na uloženie stĺpca a vytvorenie nového
177:      *
178:      * @param  string identifikátor gridu
179:      * @return object grid instance
180:      */
181:     private function save_prev_column()
182:     {
183:         if (empty($this->current_column) === FALSE) {
184:             $alias = $this->current_column['column'];
185: 
186:             list($alias) = preg_split('/( as )/i', $alias);
187:             $alias = str_replace('.', '_', $alias);
188:             $this->current_column['alias'] = $alias;
189: 
190:             $this->columns[$alias] = $this->current_column;
191:             $this->current_column = array();
192:         }
193:     }
194: 
195: 
196:     /**
197:      * Metóda na pridanie akcie
198:      *
199:      * @param  string meno akcie
200:      * @param  string meno routy ktorá sa použije na vygenerovanie uri
201:      * @param  string cesta k ikone, ak je NULL použije sa iba text
202:      * @return object grid instance
203:      */
204:     public function add_action($title, $route_name, $class = NULL)
205:     {
206:         $this->actions[] = array(
207:             'title' => $title,
208:             'route_name' => $route_name,
209:             'class' => $class
210:             );
211: 
212:         return $this;
213:     }
214: 
215: 
216:     /**
217:      * Metóda na pridanie akcie na hromadné spracovanie
218:      *
219:      * @param  string meno akcie
220:      * @param  string meno funkcie ktorá má vykonať hromadnú akciu
221:      * @return object grid instance
222:      */
223:     public function add_multi_action($title, $callback, $class = NULL)
224:     {
225:         $this->multi_actions[] = array(
226:             'title' => $title,
227:             'callback' => $callback,
228:             'class' => $class
229:             );
230: 
231:         return $this;
232:     }
233: 
234: 
235:     /**
236:      * Metóda na priradenie filtra k stĺpcu
237:      *
238:      * @param  string       typ filtra
239:      * @param  string|array parametre filtra
240:      * @return object grid instance
241:      */
242:     public function add_filter($type = 'text', $parameters = NULL)
243:     {
244:         $this->current_column['filter'] = array(
245:             'type' => $type,
246:             'parameters' => $parameters
247:             );
248: 
249:         return $this;
250:     }
251: 
252: 
253:     /**
254:      * Metóda na priradenie formátovania k stĺpcu
255:      *
256:      * @param  string|array metóda, pole = externá metóda, reťazec = interná metóda
257:      * @param  string|array parametre formátovania
258:      * @return object grid instance
259:      */
260:     public function format()
261:     {
262:         $arguments = func_get_args();
263: 
264:         $this->current_column['format']['callback'] = array_shift($arguments);
265:         $this->current_column['format']['arguments'] = $arguments;
266: 
267:         return $this;
268:     }
269: 
270: 
271:     /**
272:      * Metóda na vykreslenie gridu
273:      */
274:     public function render()
275:     {
276:         $this->save_prev_column();
277: 
278:         $uri_data = $this->app->request->get;
279:         $uri = $this->app->create_uri($this->route_name, array(), $uri_data);
280: 
281:         $grid = Html::element('form')
282:             ->method('get')
283:             ->action($uri)
284:             ->id($this->css_id)
285:             ->class('grid');
286: 
287:         $hiddens = $grid->create('p')
288:             ->class('hiddens');
289: 
290:         $hiddens->create('input')
291:             ->type('hidden')
292:             ->name('token')
293:             ->value($this->token);
294: 
295:         $table  = $grid->create('table');
296:         $header = $table->create('thead');
297:         $body   = $table->create('tbody');
298: 
299:         $tr_titles  = $header->create('tr')
300:             ->class('headers');
301:         $tr_filters = Html::element('tr')
302:             ->class('filters');
303: 
304:         // Ak sú nadefinované akcie na hromadné spracovanie
305:         // budú pridané aj checkboxy, preto aby sedeli počty stĺpcov
306:         // je treba vložiť stĺpce
307:         if (empty($this->multi_actions) === FALSE) {
308:             // do hlavičiek
309:             $tr_titles->create('th')
310:                 ->set_text('&nbsp;', TRUE);
311: 
312:             // a do filtrov
313:             $tr_filters->create('th')
314:                 ->set_text('&nbsp;', TRUE);
315:         }
316: 
317:         $uri_data = array();
318:         $uri_data['filters'] = array();
319:         $uri_data['order'] = array($this->default_order[0] => $this->default_order[1]);
320:         $uri_data['page'] = 1;
321:         $uri_data['token'] = $this->token;
322: 
323:         if ($this->is_submitted() === TRUE) {
324:             if (isset($this->app->request->get['filters_reset']) === FALSE) {
325:                 if (isset($this->app->request->get['filters']) === TRUE) {
326:                     $uri_data['filters'] = $this->app->request->get['filters'];
327:                 }
328: 
329:                 if (isset($this->app->request->get['order']) === TRUE) {
330:                     $uri_data['order'] = $this->app->request->get['order'];
331:                 }
332: 
333:                 $uri_data['page'] = max(Arrays::get_value($this->app->request->get, 'page'), 1);
334: 
335:             } else {
336:                 $uri = $this->app->create_uri($this->route_name, array(), $uri_data);
337:                 $this->app->response->redirect($uri, 303);
338:             }
339: 
340:         } elseif (isset($_SESSION['grid'][$this->id]['uri_data']) === TRUE) {
341:             $this->app->keep_flash();
342: 
343:             $uri_data = $_SESSION['grid'][$this->id]['uri_data'];
344:             $uri = $this->app->create_uri($this->route_name, array(), $uri_data);
345:             $this->app->response->redirect($uri, 303);
346:         }
347: 
348:         $_SESSION['grid'][$this->id]['uri_data'] = $uri_data;
349: 
350:         if (empty($uri_data['filters']) === FALSE) {
351:             foreach ($uri_data['filters'] as $column_name => $value) {
352:                 if (Validate::is_empty($value) === TRUE) {
353:                     continue;
354:                 }
355: 
356:                 if ($value == -1) {
357:                     continue;
358:                 }
359: 
360:                 $db_column = $this->columns[$column_name]['column'];
361: 
362:                 if ($this->columns[$column_name]['filter']['type'] === 'text') {
363:                     $this->model->where($db_column, '%' . $value . '%', 'LIKE');
364: 
365:                 } else {
366:                     $this->model->where($db_column, $value);
367:                 }
368:             }
369:         }
370: 
371:         if (empty($uri_data['order']) === FALSE) {
372:             foreach ($uri_data['order'] as $col => $dir) {
373:                 $this->model->order($col, $dir);
374:             }
375:         }
376: 
377:         $columns = array();
378:         $db_columns = array($this->model->table . '.id as id');
379: 
380:         // Príznak na vloženie filtrov
381:         // zabezpečuje vykreslenie filtrov iba v prípade že sú definované
382:         $add_filters = FALSE;
383: 
384:         // Vykreslenie hlavičiek
385:         foreach ($this->columns as $alias => $column) {
386:             $columns[] = $column['column'];
387:             $db_columns[] = $column['column'] . ' AS ' . $alias;
388: 
389:             $uri_data_tmp = $uri_data;
390:             $uri_data_tmp['order'] = array($alias => 'ASC');
391:             $uri_data_tmp['page'] = 1;
392: 
393:             // Vytvorenie adresy na nastavenie radenia
394:             // Ak sa triedi podľa tohoto stĺpca, je treba v adrese prehodiť smer radenia
395:             if (isset($uri_data['order'][$alias]) === TRUE
396:                 AND $uri_data['order'][$alias] === 'ASC') {
397: 
398:                 $uri_data_tmp['order'] = array($alias => 'DESC');
399:             }
400: 
401:             $uri = $this->app->create_uri($this->route_name, array(), $uri_data_tmp);
402: 
403:             // Názov
404:             $title = $tr_titles->create('th');
405:             $title->class[] = $column['column'];
406:             $title->create('a')
407:                     ->href($uri)
408:                     ->set_text($column['title']);
409: 
410:             if (isset($order[$alias]) === TRUE) {
411:                 $title->class[] = 'order-column';
412: 
413:                 if ($order[$alias] === 'ASC') {
414:                     $title->class[] = 'order-asc';
415: 
416:                 } else {
417:                     $title->class[] = 'order-desc';
418:                 }
419:             }
420: 
421:             // Ak stĺpec nemá filter vloží sa iba &nbsp;
422:             if ($column['filter'] === NULL) {
423:                 $tr_filters->create('th')
424:                     ->class($column['column'])
425:                     ->set_text('&nbsp;', TRUE);
426: 
427:             } else {
428:             // v opačnom prípade sa vykreslí pole filtra
429:                 $value = NULL;
430: 
431:                 if (isset($uri_data['filters'][$column['alias']]) === TRUE) {
432:                     $value = $uri_data['filters'][$column['alias']];
433:                 }
434: 
435:                 $add_filters = TRUE;
436:                 $tr_filters->create('th')
437:                     ->class($column['alias'])
438:                     ->add($this->filter_item($column, $value));
439:             }
440:         }
441: 
442:         // Ak sú nadefinované akcie pre jednotlivé položky
443:         // je treba vložiť stĺpce aby sedeli počty stĺpcov
444:         if (empty($this->actions) === FALSE) {
445:             // do hlavičiek
446:             $tr_titles->create('th')
447:                 ->set_text('&nbsp;', TRUE);
448: 
449:             if ($add_filters === FALSE) {
450:                 // a do filtrov
451:                 $tr_filters->create('th')
452:                     ->set_text('&nbsp;', TRUE);
453: 
454:             } else {
455:                 $buttons = $tr_filters->create('th')
456:                     ->class('grid_actions');
457:             }
458:         }
459: 
460:         // Filtre sú definované, je treba ich vykresliť
461:         if ($add_filters === TRUE) {
462:             $header->add($tr_filters);
463: 
464:             if (isset($buttons) === FALSE) {
465:                 $colspan = $tr_filters->count();
466:                 $buttons = $header->create('tr')
467:                     ->create('td')
468:                         ->colspan($colspan);
469:             }
470: 
471:             // a pridať tlačidlá na odoslanie filtra
472:             $buttons->create('input')
473:                 ->type('submit')
474:                 ->name('filters_submit')
475:                 ->value($this->strings['search']);
476: 
477:             // a reset filtra
478:             $buttons->create('input')
479:                 ->type('submit')
480:                 ->name('filters_reset')
481:                 ->value($this->strings['show all']);
482:         }
483: 
484:         // Na vykreslenie je nutné vedieť aj ID položiek, preto sa pole ID pridá
485:         // do zoznamu polí ktoré sa berú z databázy
486:         //$db_columns = array_merge(array($this->model->table . '.id as id'), $columns);
487:         $db_columns = array_unique($db_columns);
488: 
489:         $count =  $this->model->count();
490:         if (is_numeric($count) === FALSE) {
491:             $count = $count->count();
492:         }
493: 
494:         // Vytvorenie stránkovača
495:         $paginator = new Paginator($this->app, $uri_data['page'], $count, $this->rows);
496:         $paginator->route_name = $this->app->route['name'];
497:         $paginator->uri_parameter_name = 'page';
498: 
499:         $hiddens->create('input')
500:             ->type('hidden')
501:             ->name('page')
502:             ->value($uri_data['page']);
503: 
504:         if ($this->rows !== NULL) {
505:             // Limit pre model sa berie zo stránkovača, kde sa vypočítajú správne čísla
506:             $this->model->limit($paginator->limit);
507:         }
508: 
509:         // Samotné vykreslenie riadkov
510:         foreach ($this->model->get_rows($db_columns) as $row) {
511:             $tr = $body->create('tr');
512: 
513:             // Ak sú definované hromadné akcie je treba pridať checkboxy
514:             if (empty($this->multi_actions) === FALSE) {
515:                 $tr->create('td')
516:                     ->create('input')
517:                         ->type('checkbox')
518:                         ->name('items[' . $row['id'] . ']');
519:             }
520: 
521:             // Vykreslenie a formátovanie údajov
522:             foreach ($columns as $column) {
523:                 //prispôsobenie názvu stĺpca ak je v dopyte table.column_name as new_column_name
524:                 $column_name = $column;
525: 
526:                 list($column_name) = preg_split('/( as )/i', $column_name);
527:                 $column_name = str_replace('.', '_', $column_name);
528: 
529:                 $text = $row[$column_name];
530: 
531:                 $strip_to = $this->columns[$column_name]['strip_to'];
532: 
533:                 if ($strip_to !== NULL) {
534:                     $text = mb_substr($text, 0, $strip_to, 'UTF-8');
535:                 }
536: 
537:                 if ($this->columns[$column_name]['format'] !== NULL) {
538:                     $callback = $this->columns[$column_name]['format']['callback'];
539:                     $arguments = $this->columns[$column_name]['format']['arguments'];
540:                     //$arguments = array_merge(array($text), $arguments, array($row));
541:                     $arguments = array_merge(array($text), $arguments, array($row));
542: 
543:                     if (is_array($callback) === TRUE) {
544:                         $text = call_user_func_array($callback, $arguments);
545: 
546:                     } else {
547:                         // Lokálne funkcie majú prefix "format_"
548:                         $callback = 'format_' . $callback;
549:                         $text = call_user_func_array(array($this, $callback), $arguments);
550:                     }
551:                 }
552: 
553:                 $tr->create('td')
554:                     ->class(Strings::webalize($column))
555:                     ->add($text);
556:             }
557: 
558:             // Vykreslenie akcií na jednotlivých položkách ak sú definované
559:             if (empty($this->actions) === FALSE) {
560:                 $uri_data = $this->app->request->get;
561: 
562:                 $actions = $tr->create('td')
563:                     ->class('grid_actions');
564: 
565:                 foreach ($this->actions as $action) {
566:                     $uri = $this->app->create_uri($action['route_name'], array('id' => $row['id']), $uri_data);
567: 
568:                     $actions->create('a')
569:                         ->href($uri)
570:                         ->set_text($action['title'])
571:                         ->class($action['class']);
572: 
573:                     $actions->add('&nbsp;', TRUE);
574:                 }
575:             }
576:         }
577: 
578:         // Vykreslenie tlačidiel hromadných akcií
579:         $footer = $grid->create('div')
580:             ->class('footer');
581: 
582:         if (empty($this->multi_actions) === FALSE) {
583:             $buttons = $footer->create('div');
584:             $buttons->add($this->strings['selected items']);
585: 
586:             foreach($this->multi_actions as $key => $multi_action) {
587:                 if ($this->is_submitted() === TRUE
588:                     AND isset($this->app->request->get['multi_action'][$key]) === TRUE
589:                     AND isset($this->app->request->get['items']) === TRUE) {
590: 
591:                     call_user_func($multi_action['callback'], array_keys($this->app->request->get['items']));
592:                 }
593: 
594:                 $buttons->create('input')
595:                     ->type('submit')
596:                     ->value($multi_action['title'])
597:                     ->class($multi_action['class'])
598:                     ->name('multi_action[' . $key . ']');
599:             }
600:         }
601: 
602:         // Vykreslenie stránkovačov
603:         $p = $footer->create('div')
604:             ->class('paginator')
605:             ->set_text($this->strings['page'])
606:             ->add($paginator);
607: 
608:         $grid->insert(0, $p);
609: 
610:         return $grid->render();
611:     }
612: 
613: 
614:     /**
615:      * Rozhodovacia metóda na rozhodnutie aké filtrovacie pole sa vykreslí
616:      *
617:      * @param array  stĺpec ktorému je treba vykresliť filtrovacie pole
618:      * @param string aktuálna hodnota vyhľadávacieho poľa
619:      */
620:     private function filter_item($column, $value)
621:     {
622:         if (is_string($column['filter']['type']) === TRUE) {
623:             if ($column['filter']['type'] === 'select') {
624:                 return $this->filter_item_select($column, $value);
625: 
626:             } elseif ($column['filter']['type'] === 'text') {
627:                 return $this->filter_item_text($column, $value);
628:             }
629:         }
630: 
631:         return call_user_func($column['filter']['type'], $column, $value);
632:     }
633: 
634: 
635:     /**
636:      * Metóda na vykreslenie textového filtra
637:      *
638:      * @param array  stĺpec ktorému je treba vykresliť filtrovacie pole
639:      * @param string aktuálna hodnota vyhľadávacieho poľa
640:      */
641:     private function filter_item_text($column, $value)
642:     {
643:         $filter = Html::element('input')
644:             ->name('filters[' . $column['alias'] . ']')
645:             ->type('text')
646:             ->value($value);
647: 
648:         return $filter;
649:     }
650: 
651: 
652:     /**
653:      * Metóda na vykreslenie výberového filtra
654:      *
655:      * @param array  stĺpec ktorému je treba vykresliť filtrovacie pole
656:      * @param string aktuálna hodnota vyhľadávacieho poľa
657:      */
658:     private function filter_item_select($column, $value)
659:     {
660:         if ($value === NULL) {
661:             $value = -1;
662:         }
663: 
664:         $options = $column['filter']['parameters'];
665: 
666:         if ($options === NULL) {
667:             preg_match('/(.*)\.([^\ ]*)/i', $column['column'], $matches);
668:             if (empty($matches) === FALSE) {
669:                 $table = $matches[1];
670:                 $column_name = $matches[2];
671: 
672:             } else {
673:                 $table = $this->model->table;
674:                 $column_name = $column['column'];
675:             }
676: 
677:             $relations = $this->model->current_table_relations;
678:             if (isset($relations[$table]) === TRUE) {
679:                 $table = $relations[$table];
680:             }
681: 
682:             $table = $this->model->prefix . $table;
683: 
684:             $result = Database::query('SELECT `' . $column_name . '`, `' . $column_name . '` as `value` FROM `' . $table . '` ORDER BY `' . $column_name . '`');
685:             $options = $result->fetch_pairs('value', $column_name);
686:             $options = array('-1' => $this->strings['show all']) + $options;
687:         }
688: 
689:         $name = 'filters[' . $column['alias'] . ']';
690:         $filter = Html::element('select')
691:             ->name($name);
692: 
693:         foreach ($options as $option_value => $text) {
694:             $option = $filter->create('option')
695:                 ->value($option_value)
696:                 ->set_text($text);
697: 
698:                 if ($value == $option_value) {
699:                     $option->selected('selected');
700:                 }
701:         }
702: 
703:         return $filter;
704:     }
705: 
706: 
707:     /**
708:      * Metóda na zistenie či prijaté dáta patria aktuálnemu gridu
709:      */
710:     public function is_submitted()
711:     {
712:         return (Arrays::get_value($this->app->request->get, 'token') === $this->token);
713:     }
714: 
715: 
716:     /**
717:      * Metóda na formátovanie hodnoty ako adresy - web, alebo mail
718:      *
719:      * @param string $uri adresa
720:      */
721:     private function format_uri($uri)
722:     {
723:         $uri_html = Html::element('a')
724:             ->set_text($uri);
725: 
726:         if (strpos($uri, '@') == TRUE) {
727:             $uri_html->href('mailto: ' . $uri);
728: 
729:         } else {
730:             if (substr($uri, 0, 4) !== 'http') {
731:                 $uri = 'http://' . $uri;
732:             }
733: 
734:             $uri_html->href($uri);
735:         }
736: 
737:         return $uri_html;
738:     }
739: 
740: 
741:     /**
742:      * Metóda na formátovanie hodnoty funkciou date
743:      *
744:      * @param string dátum a čas
745:      * @param string formátovací reťazec pre funkciu date
746:      */
747:     private function format_datetime($datetime, $format = 'd. m. Y / H:i')
748:     {
749:         return date($format, strtotime($datetime));
750:     }
751: 
752: 
753:     /**
754:      * Metóda na prevod boolean hodnoty na reťazec
755:      *
756:      * @param bool   dátum a čas
757:      * @param string reťazec ktorý sa má vrátiť pri FALSE
758:      * @param string reťazec ktorý sa má vrátiť pri TRUE
759:      */
760:     private function format_bool($value, $no = 'No', $yes = 'Yes')
761:     {
762:         if ($value == TRUE) {
763:             return $yes;
764:         }
765: 
766:         return $no;
767:     }
768: }
Onion API documentation generated by ApiGen.
Generated using the TokenReflection library.