PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Grido/Grid.php

https://github.com/Kryspin/grido
PHP | 963 lines | 717 code | 65 blank | 181 comment | 10 complexity | 95e1d1b0391d330f62ae003a0e69ac26 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * This file is part of the Grido (http://grido.bugyik.cz)
  4. *
  5. * Copyright (c) 2011 Petr BugyĂ­k (http://petr.bugyik.cz)
  6. *
  7. * For the full copyright and license information, please view
  8. * the file license.md that was distributed with this source code.
  9. */
  10. namespace Grido;
  11. use Grido\Components\Columns\Column,
  12. Grido\Components\Filters\Filter,
  13. Grido\Components\Actions\Action,
  14. Grido\Components\Operation,
  15. Grido\Components\Export,
  16. Grido\Components\Paginator;
  17. /**
  18. * Grido - DataGrid for Nette Framework.
  19. *
  20. * @package Grido
  21. * @author Petr BugyĂ­k
  22. *
  23. * @property-read int $count
  24. * @property-read mixed $data
  25. * @property-read callback $rowCallback
  26. * @property-write bool $rememberState
  27. * @property-write array $defaultPerPage
  28. * @property-write string $templateFile
  29. * @property array $defaultFilter
  30. * @property array $defaultSort
  31. * @property array $perPageList
  32. * @property \Nette\Localization\ITranslator $translator
  33. * @property Paginator $paginator
  34. * @property string $primaryKey
  35. * @property string $filterRenderType
  36. * @property DataSources\IDataSource $model
  37. */
  38. class Grid extends \Nette\Application\UI\Control
  39. {
  40. /***** DEFAULTS ****/
  41. const BUTTONS = 'buttons';
  42. /** @var int @persistent */
  43. public $page = 1;
  44. /** @var int @persistent */
  45. public $perPage;
  46. /** @var array @persistent */
  47. public $sort = array();
  48. /** @var array @persistent */
  49. public $filter = array();
  50. /** @var array event on render */
  51. public $onRender;
  52. /** @var array event for modifying data */
  53. public $onFetchData;
  54. /** @var callback $rowCallback - callback returns tr html element; function($row, Html $tr) */
  55. protected $rowCallback;
  56. /** @var bool */
  57. protected $rememberState = FALSE;
  58. /** @var string */
  59. protected $primaryKey = 'id';
  60. /** @var string */
  61. protected $filterRenderType;
  62. /** @var array */
  63. protected $perPageList = array(10, 20, 30, 50, 100);
  64. /** @var int */
  65. protected $defaultPerPage = 20;
  66. /** @var array */
  67. protected $defaultFilter = array();
  68. /** @var array */
  69. protected $defaultSort = array();
  70. /** @var DataSources\IDataSource */
  71. protected $model;
  72. /** @var int total count of items */
  73. protected $count;
  74. /** @var mixed */
  75. protected $data;
  76. /** @var Paginator */
  77. protected $paginator;
  78. /** @var \Nette\Localization\ITranslator */
  79. protected $translator;
  80. /** @var bool cache */
  81. protected $hasFilters, $hasActions, $hasOperations, $hasExporting;
  82. /**
  83. * Sets a model that implements the interface Grido\DataSources\IDataSource
  84. * or data-source object DibiFluent, Nette\Database\Table\Selection.
  85. * @param mixed $model
  86. * @throws \InvalidArgumentException
  87. * @return Grid
  88. */
  89. public function setModel($model)
  90. {
  91. if ($model instanceof \DibiFluent) {
  92. $model = new DataSources\DibiFluent($model);
  93. } elseif ($model instanceof \Nette\Database\Table\Selection) {
  94. $model = new DataSources\NetteDatabase($model);
  95. } elseif (!$model instanceof DataSources\IDataSource) {
  96. throw new \InvalidArgumentException('Model must be implemented \Grido\DataSources\IDataSource.');
  97. }
  98. $this->model = $model;
  99. return $this;
  100. }
  101. /**
  102. * Sets default per page.
  103. * @param int $perPage
  104. * @return Grid
  105. */
  106. public function setDefaultPerPage($perPage)
  107. {
  108. $this->defaultPerPage = (int) $perPage;
  109. if (!in_array($perPage, $this->perPageList)) {
  110. $this->perPageList[] = $perPage;
  111. sort($this->perPageList);
  112. }
  113. return $this;
  114. }
  115. /**
  116. * Sets default filtering.
  117. * @param array $filter
  118. * @return Grid
  119. */
  120. public function setDefaultFilter(array $filter)
  121. {
  122. $this->defaultFilter = $this->defaultFilter
  123. ? array_merge($this->defaultFilter, $filter)
  124. : $filter;
  125. return $this;
  126. }
  127. /**
  128. * Sets default sorting.
  129. * @param array $sort
  130. * @return Grid
  131. */
  132. public function setDefaultSort(array $sort)
  133. {
  134. static $replace = array('asc' => Column::ASC, 'desc' => Column::DESC);
  135. foreach ($sort as $column => $dir) {
  136. $this->defaultSort[$column] = strtr(strtolower($dir), $replace);
  137. }
  138. return $this;
  139. }
  140. /**
  141. * Sets items to per-page select.
  142. * @param array $perPageList
  143. * @return Grid
  144. */
  145. public function setPerPageList(array $perPageList)
  146. {
  147. $this->perPageList = $perPageList;
  148. return $this;
  149. }
  150. /**
  151. * Sets translator.
  152. * @param \Nette\Localization\ITranslator $translator
  153. * @return Grid
  154. */
  155. public function setTranslator(\Nette\Localization\ITranslator $translator)
  156. {
  157. $this->translator = $translator;
  158. return $this;
  159. }
  160. /**
  161. * Sets type of filter rendering.
  162. * Defaults inner (Filter::RENDER_INNER) if column does not exist then outer filter (Filter::RENDER_OUTER).
  163. * @param string $type
  164. * @throws \InvalidArgumentException
  165. * @return Grid
  166. */
  167. public function setFilterRenderType($type)
  168. {
  169. if (!in_array($type, array(Filter::RENDER_INNER, Filter::RENDER_OUTER))) {
  170. throw new \InvalidArgumentException('Type must be Filter::RENDER_INNER or Filter::RENDER_OUTER.');
  171. }
  172. $this->filterRenderType = $type;
  173. return $this;
  174. }
  175. /**
  176. * Sets custom paginator.
  177. * @param Paginator $paginator
  178. * @return Grid
  179. */
  180. public function setPaginator(Paginator $paginator)
  181. {
  182. $this->paginator = $paginator;
  183. return $this;
  184. }
  185. /**
  186. * Sets grid primary key.
  187. * Defaults is "id".
  188. * @param string $key
  189. */
  190. public function setPrimaryKey($key)
  191. {
  192. $this->primaryKey = $key;
  193. return $this;
  194. }
  195. /**
  196. * Sets file name of custom template.
  197. * @param string $file
  198. * @return Grid
  199. */
  200. public function setTemplateFile($file)
  201. {
  202. $this->getTemplate()->setFile($file);
  203. return $this;
  204. }
  205. /**
  206. * Sets saving state to session.
  207. * @param bool $states
  208. * @return Grid
  209. */
  210. public function setRememberState($state = TRUE)
  211. {
  212. $this->rememberState = (bool) $state;
  213. return $this;
  214. }
  215. /**
  216. * Sets callback for customizing tr html object.
  217. * Callback returns tr html element; function($row, Html $tr).
  218. * @param $callback
  219. * @return Grid
  220. */
  221. public function setRowCallback($callback)
  222. {
  223. $this->rowCallback = $callback;
  224. return $this;
  225. }
  226. /**********************************************************************************************/
  227. /**
  228. * Returns total count of data.
  229. * @return int
  230. */
  231. public function getCount()
  232. {
  233. if ($this->count === NULL) {
  234. $this->count = $this->model->call('getCount');
  235. }
  236. return $this->count;
  237. }
  238. /**
  239. * Returns default filter.
  240. * @return array
  241. */
  242. public function getDefaultFilter()
  243. {
  244. return $this->defaultFilter;
  245. }
  246. /**
  247. * Returns default sort.
  248. * @return array
  249. */
  250. public function getDefaultSort()
  251. {
  252. return $this->defaultSort;
  253. }
  254. /**
  255. * Returns list of possible items per page.
  256. * @return array
  257. */
  258. public function getPerPageList()
  259. {
  260. return $this->perPageList;
  261. }
  262. /**
  263. * Returns primary key.
  264. * @return string
  265. */
  266. public function getPrimaryKey()
  267. {
  268. return $this->primaryKey;
  269. }
  270. /**
  271. * Returns items per page.
  272. * @return int
  273. */
  274. public function getPerPage()
  275. {
  276. return $this->perPage === NULL ? $this->defaultPerPage : $this->perPage;
  277. }
  278. /**
  279. * Returns column component.
  280. * @param string $name
  281. * @param bool $need
  282. * @return Column
  283. */
  284. public function getColumn($name, $need = TRUE)
  285. {
  286. return $this[Column::ID]->getComponent($name, $need);
  287. }
  288. /**
  289. * Returns filter component.
  290. * @param string $name
  291. * @param bool $need
  292. * @return Filter
  293. */
  294. public function getFilter($name, $need = TRUE)
  295. {
  296. return $this[Filter::ID]->getComponent($name, $need);
  297. }
  298. /**
  299. * Returns action component.
  300. * @param string $name
  301. * @param bool $need
  302. * @return Action
  303. */
  304. public function getAction($name, $need = TRUE)
  305. {
  306. return $this[Action::ID]->getComponent($name, $need);
  307. }
  308. /**
  309. * Returns operations component.
  310. * @param bool $need
  311. * @return Operation
  312. */
  313. public function getOperations($need = TRUE)
  314. {
  315. return $this->getComponent(Operation::ID, $need);
  316. }
  317. /**
  318. * Returns actual filter values.
  319. * @param string $key
  320. * @return mixed
  321. */
  322. public function getActualFilter($key = NULL)
  323. {
  324. $filter = $this->filter ? $this->filter : $this->defaultFilter;
  325. return $key && isset($filter[$key]) ? $filter[$key] : $filter;
  326. }
  327. /**
  328. * Returns fetched data.
  329. * @throws \Exception
  330. * @return array
  331. */
  332. public function getData($applyPaging = TRUE)
  333. {
  334. if ($this->model === NULL) {
  335. throw new \Exception('Model cannot be empty, please use method $grid->setModel().');
  336. }
  337. if ($this->data === NULL) {
  338. $this->applyFiltering();
  339. $this->applySorting();
  340. if ($applyPaging) {
  341. $this->applyPaging();
  342. }
  343. $this->data = $this->model->call('getData');
  344. if ($this->onFetchData) {
  345. $this->onFetchData($this);
  346. }
  347. }
  348. return $this->data;
  349. }
  350. /**
  351. * Returns translator.
  352. * @return Translations\FileTranslator
  353. */
  354. public function getTranslator()
  355. {
  356. if ($this->translator === NULL) {
  357. $this->setTranslator(new Translations\FileTranslator);
  358. }
  359. return $this->translator;
  360. }
  361. /**
  362. * Returns remember session for set expiration, etc.
  363. * @return \Nette\Http\Session
  364. */
  365. public function getRememberSession()
  366. {
  367. return $this->presenter->getSession($this->presenter->name . '\\' . ucfirst($this->name));
  368. }
  369. /**
  370. * @internal
  371. * @return string
  372. */
  373. public function getFilterRenderType()
  374. {
  375. if ($this->filterRenderType !== NULL) {
  376. return $this->filterRenderType;
  377. }
  378. if ($this->hasFilters()) {
  379. $this->filterRenderType = $this->hasActions()
  380. ? Filter::RENDER_INNER
  381. : Filter::RENDER_OUTER;
  382. $filters = $this[Filter::ID]->getComponents();
  383. foreach ($filters as $filter) {
  384. if (!$this[Column::ID]->getComponent($filter->name, FALSE)) {
  385. $this->filterRenderType = Filter::RENDER_OUTER;
  386. break;
  387. }
  388. }
  389. }
  390. return $this->filterRenderType;
  391. }
  392. /**
  393. * @internal
  394. * @return IModel
  395. */
  396. public function getModel()
  397. {
  398. return $this->model;
  399. }
  400. /**
  401. * @internal
  402. * @return Paginator
  403. */
  404. public function getPaginator()
  405. {
  406. if ($this->paginator === NULL) {
  407. $this->paginator = new Paginator;
  408. $this->paginator->setItemsPerPage($this->getPerPage())
  409. ->setGrid($this);
  410. }
  411. return $this->paginator;
  412. }
  413. /**
  414. * @internal
  415. * @param mixed $row item from db
  416. * @return \Nette\Utils\Html
  417. */
  418. public function getRowPrototype($row)
  419. {
  420. $tr = \Nette\Utils\Html::el('tr');
  421. if ($this->rowCallback) {
  422. $tr = callback($this->rowCallback)->invokeArgs(array($row, $tr));
  423. }
  424. return $tr;
  425. }
  426. /**********************************************************************************************/
  427. /**
  428. * Loads state informations.
  429. * @internal
  430. * @param array
  431. * @return void
  432. */
  433. public function loadState(array $params)
  434. {
  435. $this->loadRememberState($params);
  436. parent::loadState($params);
  437. if($this->perPage !== NULL && !in_array($this->perPage, $this->perPageList)) {
  438. $this->perPage = NULL;
  439. $this->reload();
  440. }
  441. }
  442. /**
  443. * Loads state informations from session.
  444. * @param array $params
  445. */
  446. protected function loadRememberState(array &$params)
  447. {
  448. $session = $this->getRememberSession();
  449. if ($this->presenter->signal) {
  450. $session->remove();
  451. } elseif (!$params && $session->params) {
  452. $params = (array) $session->params;
  453. }
  454. }
  455. /**
  456. * Ajax method.
  457. * @internal
  458. */
  459. public function handleRefresh()
  460. {
  461. $this->reload();
  462. }
  463. /**
  464. * @internal
  465. * @param int $page
  466. */
  467. public function handlePage($page)
  468. {
  469. $this->reload();
  470. }
  471. /**
  472. * @internal
  473. * @param array $sort
  474. */
  475. public function handleSort(array $sort)
  476. {
  477. $this->page = 1;
  478. $this->reload();
  479. }
  480. /**
  481. * @internal
  482. * @param \Nette\Application\UI\Form $form
  483. */
  484. public function handleForm(\Nette\Application\UI\Form $form)
  485. {
  486. //filter handling
  487. if ($form[self::BUTTONS]['search']->isSubmittedBy()) {
  488. $values = $form->values;
  489. foreach ($values[Filter::ID] as $name => $value) {
  490. $filter = $this->getFilter($name);
  491. $clearDefault = isset($this->defaultFilter[$name]);
  492. if ($value != '' || $clearDefault) {
  493. $this->filter[$name] = $filter->changeValue($value);
  494. } elseif (isset($this->filter[$name])) {
  495. unset($this->filter[$name]);
  496. }
  497. }
  498. //reset button handling
  499. } elseif ($form[self::BUTTONS]['reset']->isSubmittedBy()) {
  500. $this->sort = array();
  501. $this->filter = array();
  502. $this->perPage = NULL;
  503. $form->setValues(array(Filter::ID => $this->defaultFilter), TRUE);
  504. //operations handling
  505. } elseif ($this->hasOperations() && $form[self::BUTTONS][Operation::ID]->isSubmittedBy()) {
  506. $this->addCheckers($this->getData());
  507. $values = $form[Operation::ID]->values;
  508. if (empty($values[Operation::ID])) {
  509. $this->reload();
  510. }
  511. $ids = array();
  512. $operation = $values[Operation::ID];
  513. unset($values[Operation::ID]);
  514. foreach ($values as $key => $val) {
  515. if ($val) {
  516. $ids[] = $key;
  517. }
  518. }
  519. $this[Operation::ID]->onSubmit($operation, $ids);
  520. //change items per page handling
  521. } elseif ($form[self::BUTTONS]['perPage']->isSubmittedBy()) {
  522. $perPage = (int) $form['count']->value;
  523. $this->perPage = $perPage == $this->defaultPerPage ? NULL : $perPage;
  524. }
  525. $this->page = 1;
  526. $this->reload();
  527. }
  528. /**
  529. * @internal
  530. * @param string $name - filter name
  531. * @param string $query - value from input
  532. * @throws \InvalidArgumentException
  533. */
  534. public function handleSuggest($name, $query)
  535. {
  536. $filter = $this->getFilter($name, FALSE);
  537. if (!$this->presenter->isAjax() || !$filter || $filter->type != Filter::TYPE_TEXT) {
  538. $this->presenter->terminate();
  539. }
  540. $actualFilter = $this->getActualFilter();
  541. if (isset($actualFilter[$name])) {
  542. unset($actualFilter[$name]);
  543. }
  544. $conditions = $this->_applyFiltering($actualFilter);
  545. if ($filter->suggestsCallback) {
  546. $items = callback($this->suggestsCallback)->invokeArgs(array($query, $conditions));
  547. } elseif (method_exists($this->model, 'suggest')) {
  548. $conditions[] = $filter->makeFilter($query);
  549. $items = $this->model->call('suggest', key($filter->getColumns()), $conditions);
  550. } else {
  551. throw new \InvalidArgumentException('Set suggest callback or implement method in model.');
  552. }
  553. print \Nette\Utils\Json::encode($items);
  554. $this->presenter->terminate();
  555. }
  556. /**
  557. * @internal
  558. * @param string $type
  559. */
  560. public function handleExport($type)
  561. {
  562. if ($export = $this->getComponent(Export::ID, FALSE)) {
  563. $this->presenter->sendResponse($export);
  564. $this->presenter->terminate();
  565. } else {
  566. trigger_error("Exporting is not allowed.", E_USER_NOTICE);
  567. }
  568. }
  569. /**
  570. * Refresh wrapper.
  571. * @return void
  572. */
  573. protected function reload()
  574. {
  575. if ($this->presenter->isAjax()) {
  576. $this->invalidateControl();
  577. } else {
  578. $this->redirect('this');
  579. }
  580. }
  581. /**********************************************************************************************/
  582. /**
  583. * @internal
  584. * @return bool
  585. */
  586. public function hasActions()
  587. {
  588. if ($this->hasActions === NULL) {
  589. $container = $this->getComponent(Action::ID, FALSE);
  590. $this->hasActions = $container && count($container->getComponents()) > 0;
  591. }
  592. return $this->hasActions;
  593. }
  594. /**
  595. * @internal
  596. * @return bool
  597. */
  598. public function hasFilters()
  599. {
  600. if ($this->hasFilters === NULL) {
  601. $container = $this->getComponent(Filter::ID, FALSE);
  602. $this->hasFilters = $container && count($container->getComponents()) > 0;
  603. }
  604. return $this->hasFilters;
  605. }
  606. /**
  607. * @internal
  608. * @return bool
  609. */
  610. public function hasOperations()
  611. {
  612. if ($this->hasOperations === NULL) {
  613. $this->hasOperations = $this->getComponent(Operation::ID, FALSE);
  614. }
  615. return $this->hasOperations;
  616. }
  617. /**
  618. * @internal
  619. * @return bool
  620. */
  621. public function hasExporting()
  622. {
  623. if ($this->hasExporting === NULL) {
  624. $this->hasExporting = $this->getComponent(Export::ID, FALSE);
  625. }
  626. return $this->hasExporting;
  627. }
  628. /**********************************************************************************************/
  629. /**
  630. * @internal
  631. * @param string $class
  632. * @return \Nette\Templating\FileTemplate
  633. */
  634. public function createTemplate($class = NULL)
  635. {
  636. $template = parent::createTemplate($class);
  637. $template->setFile(__DIR__ . '/Grid.latte');
  638. $template->registerHelper('translate', callback($this->getTranslator(), 'translate'));
  639. return $template;
  640. }
  641. /**
  642. * @internal
  643. */
  644. public function render()
  645. {
  646. $data = $this->getData();
  647. $this->addCheckers($data);
  648. $this->template->paginator = $this->paginator;
  649. $this->template->data = $data;
  650. $this->onRender($this);
  651. $this->saveRememberState();
  652. $this->template->render();
  653. }
  654. protected function saveRememberState()
  655. {
  656. if ($this->rememberState) {
  657. $session = $this->getRememberSession();
  658. $session->params = $this->params;
  659. }
  660. }
  661. protected function applyFiltering()
  662. {
  663. $conditions = $this->_applyFiltering($this->getActualFilter());
  664. foreach ($conditions as $condition) {
  665. $this->model->call('filter', $condition);
  666. }
  667. }
  668. /**
  669. * @param array $filter
  670. * @return array
  671. */
  672. protected function _applyFiltering(array $filter)
  673. {
  674. $conditions = array();
  675. if ($filter && $this->hasFilters()) {
  676. $this['form']->setDefaults(array(Filter::ID => $filter));
  677. foreach ($filter as $column => $value) {
  678. $component = $this->getFilter($column, FALSE);
  679. if ($component) {
  680. if ($condition = $component->makeFilter($value)) {
  681. $conditions[] = $condition;
  682. } else {
  683. $conditions[] = array('0 = 1'); //result data must be null
  684. }
  685. } else {
  686. trigger_error("Filter with name '$column' does not exist.", E_USER_NOTICE);
  687. }
  688. }
  689. }
  690. return $conditions;
  691. }
  692. protected function applySorting()
  693. {
  694. $sort = array();
  695. $this->sort = $this->sort ? $this->sort : $this->defaultSort;
  696. foreach ($this->sort as $column => $dir) {
  697. $component = $this->getColumn($column, FALSE);
  698. if (!$component) {
  699. trigger_error("Column with name '$column' does not exist.", E_USER_NOTICE);
  700. break;
  701. } elseif (!$component->isSortable()) {
  702. trigger_error("Column with name '$column' is not sortable.", E_USER_NOTICE);
  703. break;
  704. } elseif (!in_array($dir, array(Column::ASC, Column::DESC))) {
  705. trigger_error("Dir '$dir' is not allowed.", E_USER_NOTICE);
  706. break;
  707. }
  708. $sort[$component->column] = $dir == Column::ASC ? 'ASC' : 'DESC';
  709. }
  710. if ($sort) {
  711. $this->model->call('sort', $sort);
  712. }
  713. }
  714. protected function applyPaging()
  715. {
  716. $paginator = $this->getPaginator()
  717. ->setItemCount($this->getCount())
  718. ->setPage($this->page);
  719. $this['form']['count']->setValue($this->getPerPage());
  720. $this->model->call('limit', $paginator->getOffset(), $paginator->getLength());
  721. }
  722. /**
  723. * @param array $data
  724. */
  725. protected function addCheckers($data)
  726. {
  727. if ($this->hasOperations()) {
  728. $operation = $this['form'][Operation::ID];
  729. if (count($operation->getComponents()) == 1) {
  730. $pk = $this[Operation::ID]->getPrimaryKey();
  731. foreach ($data as $item) {
  732. $operation->addCheckbox($item[$pk]);
  733. }
  734. }
  735. }
  736. }
  737. /**********************************************************************************************/
  738. /**
  739. * @param string $name
  740. * @param string $label
  741. * @param string $type starting constants with Column::TYPE_
  742. * @throws \InvalidArgumentException
  743. * @return Column
  744. */
  745. public function addColumn($name, $label, $type = Column::TYPE_TEXT)
  746. {
  747. $column = new $type($this, $name, $label);
  748. if (!$column instanceof Column) {
  749. throw new \InvalidArgumentException('Column must be inherited from \Grido\Components\Columns\Column.');
  750. }
  751. return $column;
  752. }
  753. /**
  754. * @param string $name
  755. * @param string $label
  756. * @param string $type starting constants with Filter::TYPE_
  757. * @param mixed $optional if type is select, then this it items for select
  758. * @throws \InvalidArgumentException
  759. * @return Filter
  760. */
  761. public function addFilter($name, $label, $type = Filter::TYPE_TEXT, $optional = NULL)
  762. {
  763. $filter = new $type($this, $name, $label, $optional);
  764. if (!$filter instanceof Filter) {
  765. throw new \InvalidArgumentException('Filter must be inherited from \Grido\Components\Filters\Filter.');
  766. }
  767. return $filter;
  768. }
  769. /**
  770. * @param string $name
  771. * @param string $label
  772. * @param string $type starting constants with Action::TYPE_
  773. * @param string $destination - first param for method $presenter->link()
  774. * @param array $args - second param for method $presenter->link()
  775. * @throws \InvalidArgumentException
  776. * @return Action
  777. */
  778. public function addAction($name, $label, $type = Action::TYPE_HREF, $destination = NULL, $args = array())
  779. {
  780. $action = new $type($this, $name, $label, $destination, $args);
  781. if (!$action instanceof Action) {
  782. throw new \InvalidArgumentException('Action must be inherited from \Grido\Components\Actions\Action.');
  783. }
  784. return $action;
  785. }
  786. /**
  787. * @param array $operations
  788. * @param callback $onSubmit - callback after operation submit
  789. * @param string $type operation class
  790. * @throws \InvalidArgumentException
  791. * @return Operation
  792. */
  793. public function setOperations($operations, $onSubmit, $type = '\Grido\Components\Operation')
  794. {
  795. $operation = new $type($this, $operations, $onSubmit);
  796. if (!$operation instanceof Components\Operation) {
  797. throw new \InvalidArgumentException('Operation must be inherited from \Grido\Components\Operation.');
  798. }
  799. return $operation;
  800. }
  801. /**
  802. * @param string $name of exporting file
  803. * @param string $type export class
  804. * @throws \InvalidArgumentException
  805. * @return Export
  806. */
  807. public function setExporting($name = NULL, $type = '\Grido\Components\Export')
  808. {
  809. $export = new $type($this, $name ? $name : ucfirst($this->name));
  810. if (!$export instanceof Components\Export) {
  811. throw new \InvalidArgumentException('Export must be inherited from \Grido\Components\Export.');
  812. }
  813. return $export;
  814. }
  815. protected function createComponentForm()
  816. {
  817. $form = new \Nette\Application\UI\Form;
  818. $form->setTranslator($this->getTranslator());
  819. $form->setMethod(\Nette\Application\UI\Form::GET);
  820. $buttons = $form->addContainer(self::BUTTONS);
  821. $buttons->addSubmit('search', 'Search');
  822. $buttons->addSubmit('reset', 'Reset');
  823. $buttons->addSubmit('perPage', 'Items per page');
  824. $form->addSelect('count', 'Count', array_combine($this->perPageList, $this->perPageList))
  825. ->controlPrototype->attrs['title'] = $this->getTranslator()->translate('Items per page');
  826. $form->onSuccess[] = callback($this, 'handleForm');
  827. return $form;
  828. }
  829. }