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