├── .gitignore ├── .travis.yml ├── AUTHORS.md ├── DataGridException.php ├── DependencyInjection ├── Configuration.php └── KitpagesDataGridExtension.php ├── Event ├── AbstractEvent.php └── DataGridEvent.php ├── Grid ├── Field.php ├── Grid.php ├── GridConfig.php ├── GridManager.php ├── Item.php └── ItemListNormalizer │ ├── LegacyNormalizer.php │ ├── NormalizerInterface.php │ └── StandardNormalizer.php ├── Hydrators └── DataGridHydrator.php ├── KitpagesDataGridBundle.php ├── KitpagesDataGridEvents.php ├── LICENSE ├── Paginator ├── Paginator.php ├── PaginatorConfig.php └── PaginatorManager.php ├── README.md ├── Resources ├── config │ └── services.xml ├── doc │ ├── 10-GridExtendedUse.md │ ├── 20-StandalonePaginator.md │ ├── 30-Events.md │ └── 40-CookBook.md ├── public │ └── css │ │ └── base.css ├── translations │ ├── messages.cs.yml │ ├── messages.en.yml │ ├── messages.fr.yml │ ├── messages.it.yml │ ├── messages.nl.yml │ └── messages.sk.yml └── views │ ├── Grid │ ├── bootstrap-grid.html.twig │ ├── bootstrap3-grid.html.twig │ ├── grid.html.twig │ ├── javascript.html.twig │ └── javascript_content.html.twig │ └── Paginator │ ├── bootstrap-paginator.html.twig │ ├── bootstrap3-paginator.html.twig │ └── paginator.html.twig ├── Tests ├── AppKernel.php ├── BundleOrmTestCase.php ├── Fonctionnal │ ├── ConfigTest.php │ └── TwigExtensionTest.php ├── Grid │ ├── ConversionSubscriber.php │ ├── CustomGrid.php │ ├── FieldTest.php │ ├── GridConfigTest.php │ ├── GridManagerTest.php │ └── GridTest.php ├── Hydrators │ └── DataGridHydratorTest.php ├── Mocks │ └── HydratorMockStatement.php ├── Paginator │ └── PaginatorManagerTest.php ├── PhpunitTest.php ├── TestEntities │ ├── Node.php │ └── NodeAssoc.php ├── Tool │ └── UrlToolTest.php ├── app │ ├── Resources │ │ ├── config │ │ │ └── doctrine │ │ │ │ ├── Node.orm.xml │ │ │ │ └── NodeAssoc.orm.xml │ │ └── views │ │ │ └── globals.html.twig │ └── config │ │ └── config_test.yml └── bootstrap.php ├── Tool └── UrlTool.php ├── Twig └── GlobalsTwigExtension.php ├── VERSIONS.md ├── composer.json └── phpunit.xml.dist /.gitignore: -------------------------------------------------------------------------------- 1 | phpunit.xml 2 | Tests/autoload.php 3 | Tests/app/cache 4 | Tests/cache 5 | vendor/* 6 | composer.lock 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 7.1 5 | 6 | before_script: 7 | - composer install --dev --prefer-source 8 | 9 | script: ./vendor/bin/phpunit --coverage-text 10 | 11 | notifications: 12 | email: 13 | - travis-ci@kitpages.fr 14 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | Authors 2 | ======= 3 | 4 | Core team 5 | --------- 6 | Philippe Le Van (twitter : @plv, github: philippe-levan) 7 | 8 | Contributors 9 | ------------ 10 | Valentin Ferriere (twitter : @choomz, github: choomz) 11 | Sébastien Lefebre (github : kitpagesSeb) 12 | Benjamin Dulau (twitter: @Delendial , github : benjamindulau) 13 | Thimothée Barray (twitter : @timbarray, github: tyx ) 14 | Marcin Radziwoński (github: radmar) 15 | -------------------------------------------------------------------------------- /DataGridException.php: -------------------------------------------------------------------------------- 1 | root('kitpages_data_grid'); 23 | 24 | $this->addGridConfiguration($rootNode); 25 | $this->addPaginatorConfiguration($rootNode); 26 | 27 | // Here you should define the parameters that are allowed to 28 | // configure your bundle. See the documentation linked above for 29 | // more information on that topic. 30 | 31 | return $treeBuilder; 32 | } 33 | 34 | private function addGridConfiguration(ArrayNodeDefinition $node) 35 | { 36 | $node 37 | ->addDefaultsIfNotSet() 38 | ->children() 39 | ->arrayNode('grid') 40 | ->addDefaultsIfNotSet() 41 | ->children() 42 | ->scalarNode('hydrator_class') 43 | ->defaultValue('\Kitpages\DataGridBundle\Hydrators\DataGridHydrator') 44 | ->end() 45 | ->scalarNode('default_twig') 46 | ->cannotBeEmpty() 47 | ->defaultValue('KitpagesDataGridBundle:Grid:grid.html.twig') 48 | ->end() 49 | ->end() 50 | ->end() 51 | ->end() 52 | ; 53 | 54 | } 55 | private function addPaginatorConfiguration(ArrayNodeDefinition $node) 56 | { 57 | $node 58 | ->addDefaultsIfNotSet() 59 | ->children() 60 | ->arrayNode('paginator') 61 | ->addDefaultsIfNotSet() 62 | ->children() 63 | ->scalarNode('default_twig') 64 | ->cannotBeEmpty() 65 | ->defaultValue('KitpagesDataGridBundle:Paginator:paginator.html.twig') 66 | ->end() 67 | ->scalarNode('item_count_in_page') 68 | ->cannotBeEmpty() 69 | ->defaultValue(50) 70 | ->end() 71 | ->scalarNode('visible_page_count_in_paginator') 72 | ->cannotBeEmpty() 73 | ->defaultValue(5) 74 | ->end() 75 | ->end() 76 | ->end() 77 | ->end() 78 | ; 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /DependencyInjection/KitpagesDataGridExtension.php: -------------------------------------------------------------------------------- 1 | processConfiguration($configuration, $configs); 24 | 25 | $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); 26 | $loader->load('services.xml'); 27 | 28 | $this->remapParameters($config, $container, array( 29 | 'grid' => 'kitpages_data_grid.grid', 30 | 'paginator' => 'kitpages_data_grid.paginator', 31 | )); 32 | 33 | $container->setParameter('kitpages_data_grid.grid.hydrator_class', $config['grid']['hydrator_class']); 34 | 35 | } 36 | 37 | /** 38 | * 39 | * @param array $config 40 | * @param ContainerBuilder $container 41 | * @param array $map 42 | * @return void 43 | */ 44 | protected function remapParameters(array $config, ContainerBuilder $container, array $map) 45 | { 46 | foreach ($map as $name => $paramName) { 47 | if (isset($config[$name])) { 48 | $container->setParameter($paramName, $config[$name]); 49 | } 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Event/AbstractEvent.php: -------------------------------------------------------------------------------- 1 | isDefaultPrevented = true; 18 | } 19 | 20 | public function isDefaultPrevented() 21 | { 22 | return $this->isDefaultPrevented; 23 | } 24 | 25 | public function stopPropagation() 26 | { 27 | $this->isPropagationStopped = true; 28 | } 29 | 30 | public function isPropagationStopped() 31 | { 32 | return $this->isPropagationStopped; 33 | } 34 | 35 | public function set($key, $val) 36 | { 37 | $this->data[$key] = $val; 38 | } 39 | 40 | public function get($key) 41 | { 42 | if (!array_key_exists($key, $this->data)) { 43 | return null; 44 | } 45 | 46 | return $this->data[$key]; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Event/DataGridEvent.php: -------------------------------------------------------------------------------- 1 | fieldName = $fieldName; 45 | $this->label = $fieldName; 46 | foreach ($optionList as $key => $val) { 47 | if (\in_array($key, array( 48 | 'label', 49 | 'sortable', 50 | 'filterable', 51 | 'visible', 52 | 'formatValueCallback', 53 | 'autoEscape', 54 | 'translatable', 55 | 'category', 56 | 'nullIfNotExists', 57 | 'dataList', 58 | 'uniqueId' 59 | ), true)) { 60 | $this->$key = $val; 61 | } else { 62 | throw new \InvalidArgumentException("key $key doesn't exist in option list"); 63 | } 64 | } 65 | $this->tagList = $tagList; 66 | } 67 | 68 | /** 69 | * @param string $fieldName 70 | */ 71 | public function setFieldName($fieldName) 72 | { 73 | $this->fieldName = $fieldName; 74 | } 75 | 76 | /** 77 | * @return string 78 | */ 79 | public function getFieldName() 80 | { 81 | return $this->fieldName; 82 | } 83 | 84 | /** 85 | * @param boolean $filterable 86 | */ 87 | public function setFilterable($filterable) 88 | { 89 | $this->filterable = $filterable; 90 | } 91 | 92 | /** 93 | * @return boolean 94 | */ 95 | public function getFilterable() 96 | { 97 | return $this->filterable; 98 | } 99 | 100 | /** 101 | * @param callable $formatValueCallback 102 | */ 103 | public function setFormatValueCallback($formatValueCallback) 104 | { 105 | $this->formatValueCallback = $formatValueCallback; 106 | } 107 | 108 | /** 109 | * @return callable 110 | */ 111 | public function getFormatValueCallback() 112 | { 113 | return $this->formatValueCallback; 114 | } 115 | 116 | /** 117 | * @param string $label 118 | */ 119 | public function setLabel($label) 120 | { 121 | $this->label = $label; 122 | } 123 | 124 | /** 125 | * @return string 126 | */ 127 | public function getLabel() 128 | { 129 | return $this->label; 130 | } 131 | 132 | /** 133 | * @param boolean $sortable 134 | */ 135 | public function setSortable($sortable) 136 | { 137 | $this->sortable = $sortable; 138 | } 139 | 140 | /** 141 | * @return boolean 142 | */ 143 | public function getSortable() 144 | { 145 | return $this->sortable; 146 | } 147 | 148 | /** 149 | * @param boolean $visible 150 | */ 151 | public function setVisible($visible) 152 | { 153 | $this->visible = $visible; 154 | } 155 | 156 | /** 157 | * @return boolean 158 | */ 159 | public function getVisible() 160 | { 161 | return $this->visible; 162 | } 163 | 164 | /** 165 | * @param boolean $autoEscape 166 | */ 167 | public function setAutoEscape($autoEscape) 168 | { 169 | $this->autoEscape = $autoEscape; 170 | } 171 | 172 | /** 173 | * @return boolean 174 | */ 175 | public function getAutoEscape() 176 | { 177 | return $this->autoEscape; 178 | } 179 | 180 | /** 181 | * @param boolean $translatable 182 | */ 183 | public function setTranslatable($translatable) 184 | { 185 | $this->translatable = $translatable; 186 | } 187 | 188 | /** 189 | * @return boolean 190 | */ 191 | public function getTranslatable() 192 | { 193 | return $this->translatable; 194 | } 195 | 196 | /** 197 | * @param string $category 198 | */ 199 | public function setCategory($category) 200 | { 201 | $this->category = $category; 202 | } 203 | 204 | /** 205 | * @return string 206 | */ 207 | public function getCategory() 208 | { 209 | return $this->category; 210 | } 211 | 212 | /** 213 | * @param boolean $nullIfNotExists 214 | */ 215 | public function setNullIfNotExists($nullIfNotExists) 216 | { 217 | $this->nullIfNotExists = $nullIfNotExists; 218 | } 219 | 220 | /** 221 | * @return boolean 222 | */ 223 | public function getNullIfNotExists() 224 | { 225 | return $this->nullIfNotExists; 226 | } 227 | 228 | /** 229 | * @param string $key 230 | * @return array 231 | * @throws DataGridException 232 | */ 233 | public function getData($key) 234 | { 235 | if (!array_key_exists($key, $this->dataList)) { 236 | throw new DataGridException( 237 | "key [$key] is not defined in the data-list (should be defined in the dataList parameter in the new Field..." 238 | ); 239 | } 240 | return $this->dataList[$key]; 241 | } 242 | 243 | /** 244 | * @return string[] 245 | */ 246 | public function getTagList() 247 | { 248 | return $this->tagList; 249 | } 250 | 251 | /** 252 | * @param string[] $tagList 253 | * @return self 254 | */ 255 | public function setTagList($tagList) 256 | { 257 | $this->tagList = $tagList; 258 | return $this; 259 | } 260 | 261 | /** 262 | * Returns true if the given $tag is present in the tag list of the field. 263 | * 264 | * @param $tag 265 | * @return bool 266 | */ 267 | public function hasTag($tag) 268 | { 269 | return \in_array($tag, $this->tagList, true); 270 | } 271 | 272 | /** 273 | * @return string 274 | */ 275 | public function getUniqueId(): ?string 276 | { 277 | return $this->uniqueId; 278 | } 279 | 280 | /** 281 | * @param string $uniqueId 282 | */ 283 | public function setUniqueId(string $uniqueId): void 284 | { 285 | $this->uniqueId = $uniqueId; 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /Grid/Grid.php: -------------------------------------------------------------------------------- 1 | isSelectorSelected($selectorField, $selectorValue)) { 51 | $uri = $this->urlTool->changeRequestQueryString( 52 | $this->requestUri, 53 | array( 54 | $this->getSelectorFieldFormName() => $selectorField, 55 | $this->getSelectorValueFormName() => $selectorValue 56 | ) 57 | ); 58 | } else { 59 | $uri = $this->urlTool->changeRequestQueryString( 60 | $this->requestUri, 61 | array( 62 | $this->getSelectorFieldFormName() => '', 63 | $this->getSelectorValueFormName() => '' 64 | ) 65 | ); 66 | } 67 | return $uri; 68 | } 69 | 70 | public function getSortUrl($fieldName) 71 | { 72 | $uri = $this->urlTool->changeRequestQueryString( 73 | $this->requestUri, 74 | $this->getSortFieldFormName(), 75 | $fieldName 76 | ); 77 | if ($fieldName == $this->getSortField()) { 78 | $order = ($this->getSortOrder() === 'ASC') ? 'DESC' : 'ASC'; 79 | } else { 80 | $order = 'ASC'; 81 | } 82 | 83 | return $this->urlTool->changeRequestQueryString( 84 | $uri, 85 | $this->getSortOrderFormName(), 86 | $order 87 | ); 88 | } 89 | public function getSortCssClass($fieldName) 90 | { 91 | $css = ''; 92 | if ($fieldName == $this->getSortField()) { 93 | $css .= ' kit-grid-sort '; 94 | $css .= ' kit-grid-sort-'.strtolower($this->getSortOrder()).' '; 95 | } 96 | 97 | return $css; 98 | } 99 | 100 | public function displayGridValue($row, Field $field) 101 | { 102 | $value = null; 103 | $fieldName = $field->getFieldName(); 104 | if (array_key_exists($fieldName, $row)) { 105 | $value = $row[$fieldName]; 106 | } 107 | 108 | // real treatment 109 | if (\is_callable($field->getFormatValueCallback())) { 110 | $callback = $field->getFormatValueCallback(); 111 | $reflection = new \ReflectionFunction($callback); 112 | if ($reflection->getNumberOfParameters() === 1) { 113 | $value = $callback($value); 114 | } elseif ($reflection->getNumberOfParameters() === 2) { 115 | $value = $callback($value, $row); 116 | } else { 117 | throw new DataGridException('Wrong number of parameters in the callback for field '.$field->getFieldName()); 118 | } 119 | } 120 | 121 | // send event for changing grid query builder 122 | $event = new DataGridEvent(); 123 | $event->set('value', $value); 124 | $event->set('row', $row); 125 | $event->set('field', $field); 126 | $this->dispatcher->dispatch(KitpagesDataGridEvents::ON_DISPLAY_GRID_VALUE_CONVERSION, $event); 127 | 128 | if (!$event->isDefaultPrevented()) { 129 | $value = $event->get('value'); 130 | if ($value instanceof \DateTime) { 131 | $returnValue = $value->format('Y-m-d H:i:s'); 132 | } else { 133 | $returnValue = $value; 134 | } 135 | $event->set('returnValue', $returnValue); 136 | } 137 | 138 | $this->dispatcher->dispatch(KitpagesDataGridEvents::AFTER_DISPLAY_GRID_VALUE_CONVERSION, $event); 139 | $returnValue = $event->get('returnValue'); 140 | 141 | // auto escape ? (if null, return null, without autoescape...) 142 | if ($field->getAutoEscape() && $returnValue !== null) { 143 | $returnValue = htmlspecialchars($returnValue); 144 | } 145 | 146 | return $returnValue; 147 | } 148 | 149 | public function getFilterFormName() 150 | { 151 | return 'kitdg_grid_'.$this->getGridConfig()->getName().'_filter'; 152 | } 153 | public function getSortFieldFormName() 154 | { 155 | return 'kitdg_grid_'.$this->getGridConfig()->getName().'_sort_field'; 156 | } 157 | public function getSortOrderFormName() 158 | { 159 | return 'kitdg_grid_'.$this->getGridConfig()->getName().'_sort_order'; 160 | } 161 | public function getSelectorCssSelected($selectorField, $selectorValue) 162 | { 163 | if ($this->isSelectorSelected($selectorField, $selectorValue)) { 164 | return 'kit-grid-selector-selected'; 165 | } else { 166 | return ; 167 | } 168 | } 169 | public function isSelectorSelected($selectorField, $selectorValue) 170 | { 171 | if ($this->getSelectorField() == $selectorField 172 | && $this->getSelectorValue() == $selectorValue) { 173 | return true; 174 | } else { 175 | return false; 176 | } 177 | } 178 | public function getSelectorFieldFormName() 179 | { 180 | return 'kitdg_grid_'.$this->getGridConfig()->getName().'_selector_field'; 181 | } 182 | public function getSelectorValueFormName() 183 | { 184 | return 'kitdg_grid_'.$this->getGridConfig()->getName().'_selector_value'; 185 | } 186 | 187 | public function getGridCssName() 188 | { 189 | return 'kit-grid-'.$this->getGridConfig()->getName(); 190 | } 191 | 192 | /** 193 | * @param \Kitpages\DataGridBundle\Grid\GridConfig $gridConfig 194 | */ 195 | public function setGridConfig($gridConfig) 196 | { 197 | $this->gridConfig = $gridConfig; 198 | } 199 | 200 | /** 201 | * @return \Kitpages\DataGridBundle\Grid\GridConfig 202 | */ 203 | public function getGridConfig() 204 | { 205 | return $this->gridConfig; 206 | } 207 | 208 | public function setItemList($itemList) 209 | { 210 | $this->itemList = $itemList; 211 | } 212 | 213 | public function getItemList() 214 | { 215 | return $this->itemList; 216 | } 217 | 218 | public function dump($escape = true) 219 | { 220 | $content = print_r($this->itemList, true); 221 | if ($escape) { 222 | $content = htmlspecialchars($content); 223 | } 224 | 225 | $html = '
';
226 |         $html .= $content;
227 |         $html .= '
'; 228 | return $html; 229 | } 230 | /** 231 | * @param \Kitpages\DataGridBundle\Paginator\Paginator $paginator 232 | */ 233 | public function setPaginator($paginator) 234 | { 235 | $this->paginator = $paginator; 236 | } 237 | 238 | /** 239 | * @return \Kitpages\DataGridBundle\Paginator\Paginator 240 | */ 241 | public function getPaginator() 242 | { 243 | return $this->paginator; 244 | } 245 | 246 | /** 247 | * @param \Kitpages\DataGridBundle\Tool\UrlTool $urlTool 248 | */ 249 | public function setUrlTool($urlTool) 250 | { 251 | $this->urlTool = $urlTool; 252 | } 253 | 254 | /** 255 | * @return \Kitpages\DataGridBundle\Tool\UrlTool 256 | */ 257 | public function getUrlTool() 258 | { 259 | return $this->urlTool; 260 | } 261 | 262 | /** 263 | * @param string $requestUri 264 | */ 265 | public function setRequestUri($requestUri) 266 | { 267 | $this->requestUri = $requestUri; 268 | } 269 | 270 | /** 271 | * @return string 272 | */ 273 | public function getRequestCurrentRoute() 274 | { 275 | return $this->requestCurrentRoute; 276 | } 277 | 278 | /** 279 | * @param string $requestCurrentRoute 280 | */ 281 | public function setRequestCurrentRoute($requestCurrentRoute) 282 | { 283 | $this->requestCurrentRoute = $requestCurrentRoute; 284 | } 285 | 286 | /** 287 | * @return array 288 | */ 289 | public function getRequestCurrentRouteParams() 290 | { 291 | return $this->requestCurrentRouteParams; 292 | } 293 | 294 | /** 295 | * @param array $requestCurrentRouteParams 296 | */ 297 | public function setRequestCurrentRouteParams($requestCurrentRouteParams) 298 | { 299 | $this->requestCurrentRouteParams = $requestCurrentRouteParams; 300 | } 301 | 302 | /** 303 | * @return string 304 | */ 305 | public function getRequestUri() 306 | { 307 | return $this->requestUri; 308 | } 309 | 310 | /** 311 | * @param string $filterValue 312 | */ 313 | public function setFilterValue($filterValue) 314 | { 315 | $this->filterValue = $filterValue; 316 | } 317 | 318 | /** 319 | * @return string 320 | */ 321 | public function getFilterValue() 322 | { 323 | return $this->filterValue; 324 | } 325 | 326 | /** 327 | * @param string $sortField 328 | */ 329 | public function setSortField($sortField) 330 | { 331 | $this->sortField = $sortField; 332 | } 333 | 334 | /** 335 | * @return string 336 | */ 337 | public function getSortField() 338 | { 339 | return $this->sortField; 340 | } 341 | 342 | /** 343 | * @param string $sortOrder 344 | */ 345 | public function setSortOrder($sortOrder) 346 | { 347 | $this->sortOrder = $sortOrder; 348 | } 349 | 350 | /** 351 | * @return string 352 | */ 353 | public function getSortOrder() 354 | { 355 | return $this->sortOrder; 356 | } 357 | 358 | /** 359 | * @param string $selectorField 360 | */ 361 | public function setSelectorField($selectorField) 362 | { 363 | $this->selectorField = $selectorField; 364 | } 365 | 366 | /** 367 | * @return string 368 | */ 369 | public function getSelectorField() 370 | { 371 | return $this->selectorField; 372 | } 373 | 374 | /** 375 | * @param string $selectorValue 376 | */ 377 | public function setSelectorValue($selectorValue) 378 | { 379 | $this->selectorValue = $selectorValue; 380 | } 381 | 382 | /** 383 | * @return string 384 | */ 385 | public function getSelectorValue() 386 | { 387 | return $this->selectorValue; 388 | } 389 | 390 | /** 391 | * @param boolean $debugMode 392 | */ 393 | public function setDebugMode($debugMode) 394 | { 395 | $this->debugMode = $debugMode; 396 | return $this; 397 | } 398 | 399 | /** 400 | * @return bool 401 | */ 402 | public function getDebugMode() 403 | { 404 | return $this->debugMode; 405 | } 406 | 407 | /** 408 | * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher 409 | */ 410 | public function setDispatcher(EventDispatcherInterface $dispatcher) 411 | { 412 | $this->dispatcher = $dispatcher; 413 | } 414 | 415 | /** 416 | * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface 417 | */ 418 | public function getDispatcher() 419 | { 420 | return $this->dispatcher; 421 | } 422 | 423 | } 424 | -------------------------------------------------------------------------------- /Grid/GridConfig.php: -------------------------------------------------------------------------------- 1 | fieldList[] = $field; 46 | 47 | return $this; 48 | } 49 | 50 | /** 51 | * @param string $name 52 | * 53 | * @return GridConfig Fluent interface 54 | */ 55 | public function setName($name) 56 | { 57 | $this->name = $name; 58 | 59 | return $this; 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | public function getName() 66 | { 67 | return $this->name; 68 | } 69 | 70 | /** 71 | * @param \Doctrine\ORM\QueryBuilder|null $queryBuilder 72 | * 73 | * @return GridConfig Fluent interface 74 | */ 75 | public function setQueryBuilder(QueryBuilder $queryBuilder) 76 | { 77 | $this->queryBuilder = $queryBuilder; 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * @return \Doctrine\ORM\QueryBuilder|null 84 | */ 85 | public function getQueryBuilder() 86 | { 87 | return $this->queryBuilder; 88 | } 89 | 90 | /** 91 | * @param PaginatorConfig $paginatorConfig 92 | * 93 | * @return GridConfig Fluent interface 94 | */ 95 | public function setPaginatorConfig(PaginatorConfig $paginatorConfig) 96 | { 97 | $this->paginatorConfig = $paginatorConfig; 98 | 99 | return $this; 100 | } 101 | 102 | /** 103 | * @return PaginatorConfig 104 | */ 105 | public function getPaginatorConfig() 106 | { 107 | return $this->paginatorConfig; 108 | } 109 | 110 | /** 111 | * @param array $fieldList 112 | * 113 | * @return GridConfig Fluent interface 114 | */ 115 | public function setFieldList($fieldList) 116 | { 117 | $this->fieldList = $fieldList; 118 | 119 | return $this; 120 | } 121 | 122 | /** 123 | * @return array 124 | */ 125 | public function getFieldList() 126 | { 127 | return $this->fieldList; 128 | } 129 | 130 | /** 131 | * returns the field corresponding to the name 132 | * 133 | * @param string $name 134 | * 135 | * @return Field|null $field 136 | */ 137 | public function getFieldByName($name) 138 | { 139 | foreach ($this->fieldList as $field) { 140 | if ($field->getFieldName() === $name) { 141 | return $field; 142 | } 143 | } 144 | 145 | return null; 146 | } 147 | 148 | /** 149 | * Returns a list of fields that contains the given $tag. 150 | * 151 | * @param $tag 152 | * @return Field[] 153 | */ 154 | public function getFieldListByTag($tag) 155 | { 156 | $matchingFieldList = array(); 157 | foreach ($this->fieldList as $field) { 158 | if ($field->hasTag($tag)) { 159 | $matchingFieldList[] = $field; 160 | } 161 | } 162 | return $matchingFieldList; 163 | } 164 | 165 | /** 166 | * @param string $countFieldName 167 | * 168 | * @return GridConfig Fluent interface 169 | */ 170 | public function setCountFieldName($countFieldName) 171 | { 172 | $this->countFieldName = $countFieldName; 173 | 174 | return $this; 175 | } 176 | 177 | /** 178 | * @return string 179 | */ 180 | public function getCountFieldName() 181 | { 182 | return $this->countFieldName; 183 | } 184 | 185 | /** 186 | * @param array 187 | * 188 | * @return GridConfig Fluent interface 189 | */ 190 | public function addSelector($selector) 191 | { 192 | $this->selectorList[] = $selector; 193 | 194 | return $this; 195 | } 196 | 197 | /** 198 | * @return array 199 | */ 200 | public function getSelectorList() 201 | { 202 | return $this->selectorList; 203 | } 204 | 205 | /** 206 | * @param array $selectorList 207 | * 208 | * @return GridConfig Fluent interface 209 | */ 210 | public function setSelectorList($selectorList) 211 | { 212 | $this->selectorList = $selectorList; 213 | 214 | return $this; 215 | } 216 | 217 | 218 | } 219 | -------------------------------------------------------------------------------- /Grid/GridManager.php: -------------------------------------------------------------------------------- 1 | dispatcher = $dispatcher; 49 | $this->paginatorManager = $paginatorManager; 50 | $this->itemListNormalizer = $itemListNormalizer; 51 | $this->hydratorClass = $hydratorClass; 52 | } 53 | 54 | //// 55 | // grid methods 56 | //// 57 | /** 58 | * get grid object filled 59 | * 60 | * @param \Kitpages\DataGridBundle\Grid\GridConfig $gridConfig 61 | * @param \Symfony\Component\HttpFoundation\Request $request 62 | * @param Grid $grid : the user can give a instance of Grid (or a subclass 63 | * of grid) if he wants the grid object to be manually initialized before 64 | * the getGrid call. 65 | * @return \Kitpages\DataGridBundle\Grid\Grid 66 | */ 67 | public function getGrid( 68 | GridConfig $gridConfig, 69 | Request $request, 70 | Grid $grid = null 71 | ) 72 | { 73 | $queryBuilder = $gridConfig->getQueryBuilder(); 74 | 75 | // create grid objet 76 | if ($grid === null) { 77 | $grid = new Grid(); 78 | } 79 | $grid->setGridConfig($gridConfig); 80 | $grid->setUrlTool(new UrlTool()); 81 | $grid->setRequestUri($request->getRequestUri()); 82 | $grid->setRequestCurrentRoute($request->attributes->get("_route")); 83 | $grid->setRequestCurrentRouteParams($request->attributes->get("_route_params")); 84 | $grid->setDispatcher($this->dispatcher); 85 | 86 | // create base request 87 | $gridQueryBuilder = clone($queryBuilder); 88 | 89 | // Apply filters 90 | $filter = $request->query->get($grid->getFilterFormName(),""); 91 | $this->applyFilter($gridQueryBuilder, $grid, $filter); 92 | 93 | // Apply selector 94 | $selectorField = $request->query->get($grid->getSelectorFieldFormName(),""); 95 | $selectorValue = $request->query->get($grid->getSelectorValueFormName(),""); 96 | $this->applySelector($gridQueryBuilder, $grid, $selectorField, $selectorValue); 97 | 98 | // Apply sorting 99 | $sortField = $request->query->get($grid->getSortFieldFormName(),""); 100 | $sortOrder = $request->query->get($grid->getSortOrderFormName(),""); 101 | $this->applySort($gridQueryBuilder, $grid, $sortField, $sortOrder); 102 | 103 | // build paginator 104 | $paginatorConfig = $gridConfig->getPaginatorConfig(); 105 | if ($paginatorConfig === null) { 106 | $paginatorConfig = new PaginatorConfig(); 107 | $paginatorConfig->setCountFieldName($gridConfig->getCountFieldName()); 108 | $paginatorConfig->setName($gridConfig->getName()); 109 | $paginatorConfig->setQueryBuilder($gridQueryBuilder); 110 | } 111 | if (is_null($paginatorConfig->getQueryBuilder())) { 112 | $paginatorConfig->setQueryBuilder($gridQueryBuilder); 113 | } 114 | $paginator = $this->paginatorManager->getPaginator($paginatorConfig, $request); 115 | $grid->setPaginator($paginator); 116 | 117 | // calculate limits 118 | $gridQueryBuilder->setMaxResults($paginator->getPaginatorConfig()->getItemCountInPage()); 119 | $gridQueryBuilder->setFirstResult(($paginator->getCurrentPage()-1) * $paginator->getPaginatorConfig()->getItemCountInPage()); 120 | 121 | // send event for changing grid query builder 122 | $event = new DataGridEvent(); 123 | $event->set("grid", $grid); 124 | $event->set("gridQueryBuilder", $gridQueryBuilder); 125 | $event->set("request", $request); 126 | $this->dispatcher->dispatch(KitpagesDataGridEvents::ON_GET_GRID_QUERY, $event); 127 | 128 | if (!$event->isDefaultPrevented()) { 129 | // execute request 130 | $query = $gridQueryBuilder->getQuery(); 131 | $event->set("query", $query); 132 | } 133 | 134 | $this->dispatcher->dispatch(KitpagesDataGridEvents::AFTER_GET_GRID_QUERY, $event); 135 | 136 | // hack : recover query from the event so the developper can build a new grid 137 | // from the gridQueryBuilder in the listener and reinject it in the event. 138 | $normalizedItemList = $this->itemListNormalizer->normalize( 139 | $event->get("query"), 140 | $event->get("gridQueryBuilder"), 141 | $this->hydratorClass 142 | ); 143 | 144 | // end normalization 145 | $grid->setItemList($normalizedItemList); 146 | 147 | return $grid; 148 | } 149 | 150 | protected function applyFilter(QueryBuilder $queryBuilder, Grid $grid, $filter) 151 | { 152 | if (!$filter) { 153 | return; 154 | } 155 | $event = new DataGridEvent(); 156 | $event->set("grid", $grid); 157 | $event->set("gridQueryBuilder", $queryBuilder); 158 | $event->set("filter", $filter); 159 | $this->dispatcher->dispatch(KitpagesDataGridEvents::ON_APPLY_FILTER, $event); 160 | 161 | if (!$event->isDefaultPrevented()) { 162 | $fieldList = $grid->getGridConfig()->getFieldList(); 163 | $filterRequestList = array(); 164 | foreach ($fieldList as $field) { 165 | if ($field->getFilterable()) { 166 | $filterRequestList[] = $queryBuilder->expr()->like($field->getFieldName(), ":filter"); 167 | } 168 | } 169 | if (count($filterRequestList) > 0) { 170 | $reflectionMethod = new \ReflectionMethod($queryBuilder->expr(), "orx"); 171 | $queryBuilder->andWhere($reflectionMethod->invokeArgs($queryBuilder->expr(), $filterRequestList)); 172 | $queryBuilder->setParameter("filter", "%".$filter."%"); 173 | } 174 | $grid->setFilterValue($filter); 175 | } 176 | $this->dispatcher->dispatch(KitpagesDataGridEvents::AFTER_APPLY_FILTER, $event); 177 | } 178 | 179 | protected function applySelector(QueryBuilder $queryBuilder, Grid $grid, $selectorField, $selectorValue) 180 | { 181 | if (!$selectorField) { 182 | return; 183 | } 184 | $event = new DataGridEvent(); 185 | $event->set("grid", $grid); 186 | $event->set("gridQueryBuilder", $queryBuilder); 187 | $event->set("selectorField", $selectorField); 188 | $event->set("selectorValue", $selectorValue); 189 | $this->dispatcher->dispatch(KitpagesDataGridEvents::ON_APPLY_SELECTOR, $event); 190 | 191 | if (!$event->isDefaultPrevented()) { 192 | $queryBuilder->andWhere($selectorField." = :selectorValue"); 193 | $queryBuilder->setParameter("selectorValue", $selectorValue); 194 | 195 | $grid->setSelectorField($selectorField); 196 | $grid->setSelectorValue($selectorValue); 197 | } 198 | $this->dispatcher->dispatch(KitpagesDataGridEvents::AFTER_APPLY_SELECTOR, $event); 199 | } 200 | 201 | protected function applySort(QueryBuilder $gridQueryBuilder, Grid $grid, $sortField, $sortOrder) 202 | { 203 | if (!$sortField) { 204 | return; 205 | } 206 | $event = new DataGridEvent(); 207 | $event->set("grid", $grid); 208 | $event->set("gridQueryBuilder", $gridQueryBuilder); 209 | $event->set("sortField", $sortField); 210 | $event->set("sortOrder", $sortOrder); 211 | $this->dispatcher->dispatch(KitpagesDataGridEvents::ON_APPLY_SORT, $event); 212 | 213 | if (!$event->isDefaultPrevented()) { 214 | $sortFieldObject = null; 215 | $fieldList = $grid->getGridConfig()->getFieldList(); 216 | foreach ($fieldList as $field) { 217 | if ($field->getFieldName() == $sortField) { 218 | if ($field->getSortable() === true) { 219 | $sortFieldObject = $field; 220 | break; 221 | } 222 | } 223 | } 224 | if (!$sortFieldObject) { 225 | return; 226 | } 227 | if ($sortOrder != "DESC") { 228 | $sortOrder = "ASC"; 229 | } 230 | $gridQueryBuilder->orderBy($sortField, $sortOrder); 231 | $grid->setSortField($sortField); 232 | $grid->setSortOrder($sortOrder); 233 | } 234 | 235 | $this->dispatcher->dispatch(KitpagesDataGridEvents::AFTER_APPLY_SORT, $event); 236 | } 237 | 238 | } 239 | -------------------------------------------------------------------------------- /Grid/Item.php: -------------------------------------------------------------------------------- 1 | entity = $entity; 16 | } 17 | 18 | /** 19 | * @return mixed 20 | */ 21 | public function getEntity() 22 | { 23 | return $this->entity; 24 | } 25 | 26 | /** 27 | * @param mixed $row 28 | */ 29 | public function setRow($row) 30 | { 31 | $this->row = $row; 32 | } 33 | 34 | /** 35 | * @return mixed 36 | */ 37 | public function getRow() 38 | { 39 | return $this->row; 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Grid/ItemListNormalizer/LegacyNormalizer.php: -------------------------------------------------------------------------------- 1 | getArrayResult(); 21 | 22 | // normalize result (for request of type $queryBuilder->select("item, bp, item.id * 3 as titi"); ) 23 | $normalizedItemList = array(); 24 | foreach ($itemList as $item) { 25 | $normalizedItem = array(); 26 | foreach ($item as $key => $val) { 27 | // hack : is_array is added according to this issue : https://github.com/kitpages/KitpagesDataGridBundle/issues/18 28 | // can't reproduce this error... 29 | if (is_int($key) && is_array($val)) { 30 | foreach ($val as $newKey => $newVal) { 31 | $normalizedItem[$newKey] = $newVal; 32 | } 33 | } else { 34 | $normalizedItem[$key] = $val; 35 | } 36 | } 37 | $normalizedItemList[] = $normalizedItem; 38 | } 39 | return $normalizedItemList; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Grid/ItemListNormalizer/NormalizerInterface.php: -------------------------------------------------------------------------------- 1 | getEntityManager()->getConfiguration(); 25 | $hydrator = new \ReflectionClass($hydratorClass); 26 | $hydratorName = $hydrator->getShortName(); 27 | $emConfig->addCustomHydrationMode($hydratorName, $hydratorClass); 28 | 29 | return $query->getResult($hydratorName); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Hydrators/DataGridHydrator.php: -------------------------------------------------------------------------------- 1 | _stmt->fetch(\PDO::FETCH_ASSOC)) { 15 | $this->hydrateRowData($data, $result); 16 | } 17 | return $result; 18 | } 19 | 20 | /** 21 | * {@inheritdoc} 22 | */ 23 | protected function hydrateRowData(array $data, array &$result) 24 | { 25 | $result[] = $this->gatherScalarRowData($data); 26 | } 27 | 28 | /** 29 | * Processes a row of the result set. 30 | * 31 | * Used for HYDRATE_SCALAR. This is a variant of _gatherRowData() that 32 | * simply converts column names to field names and properly converts the 33 | * values according to their types. The resulting row has the same number 34 | * of elements as before. 35 | * 36 | * @param array $data 37 | * 38 | * @return array The processed row. 39 | */ 40 | protected function gatherScalarRowData(&$data) 41 | { 42 | $rowData = array(); 43 | foreach ($data as $key => $value) { 44 | if (($cacheKeyInfo = $this->hydrateColumnInfo($key)) === null) { 45 | continue; 46 | } 47 | $fieldName = $cacheKeyInfo['fieldName']; 48 | // WARNING: BC break! We know this is the desired behavior to type convert values, but this 49 | // erroneous behavior exists since 2.0 and we're forced to keep compatibility. 50 | if ( ! isset($cacheKeyInfo['isScalar'])) { 51 | $dqlAlias = $cacheKeyInfo['dqlAlias']; 52 | $type = $cacheKeyInfo['type']; 53 | $fieldName = $dqlAlias . $this->getFieldSeparator() . $fieldName; 54 | $value = $type 55 | ? $type->convertToPHPValue($value, $this->_platform) 56 | : $value; 57 | } 58 | $rowData[$fieldName] = $value; 59 | } 60 | return $rowData; 61 | } 62 | 63 | protected function getFieldSeparator() 64 | { 65 | return '.'; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /KitpagesDataGridBundle.php: -------------------------------------------------------------------------------- 1 | minPage ; $i <= $this->maxPage ; $i++) { 27 | $tab[] = $i; 28 | } 29 | 30 | return $tab; 31 | } 32 | 33 | public function getUrl($key, $val) 34 | { 35 | return $this->urlTool->changeRequestQueryString( 36 | $this->requestUri, 37 | $this->paginatorConfig->getRequestQueryName($key), 38 | $val 39 | ); 40 | } 41 | 42 | public function setMaxPage($maxPage) 43 | { 44 | $this->maxPage = $maxPage; 45 | } 46 | 47 | public function getMaxPage() 48 | { 49 | return $this->maxPage; 50 | } 51 | 52 | public function setMinPage($minPage) 53 | { 54 | $this->minPage = $minPage; 55 | } 56 | 57 | public function getMinPage() 58 | { 59 | return $this->minPage; 60 | } 61 | 62 | public function setNextButtonPage($nextButtonPage) 63 | { 64 | $this->nextButtonPage = $nextButtonPage; 65 | } 66 | 67 | public function getNextButtonPage() 68 | { 69 | return $this->nextButtonPage; 70 | } 71 | 72 | public function setPreviousButtonPage($previousButtonPage) 73 | { 74 | $this->previousButtonPage = $previousButtonPage; 75 | } 76 | 77 | public function getPreviousButtonPage() 78 | { 79 | return $this->previousButtonPage; 80 | } 81 | 82 | public function setTotalItemCount($totalItemCount) 83 | { 84 | $this->totalItemCount = $totalItemCount; 85 | } 86 | 87 | public function getTotalItemCount() 88 | { 89 | return $this->totalItemCount; 90 | } 91 | 92 | public function setTotalPageCount($totalPageCount) 93 | { 94 | $this->totalPageCount = $totalPageCount; 95 | } 96 | 97 | public function getTotalPageCount() 98 | { 99 | return $this->totalPageCount; 100 | } 101 | 102 | public function setCurrentPage($currentPage) 103 | { 104 | $this->currentPage = $currentPage; 105 | } 106 | 107 | public function getCurrentPage() 108 | { 109 | return $this->currentPage; 110 | } 111 | 112 | /** 113 | * @param \Kitpages\DataGridBundle\Tool\UrlTool $urlTool 114 | */ 115 | public function setUrlTool($urlTool) 116 | { 117 | $this->urlTool = $urlTool; 118 | } 119 | 120 | /** 121 | * @return \Kitpages\DataGridBundle\Tool\UrlTool 122 | */ 123 | public function getUrlTool() 124 | { 125 | return $this->urlTool; 126 | } 127 | 128 | /** 129 | * @param \Kitpages\DataGridBundle\Paginator\PaginatorConfig $paginatorConfig 130 | */ 131 | public function setPaginatorConfig($paginatorConfig) 132 | { 133 | $this->paginatorConfig = $paginatorConfig; 134 | } 135 | 136 | /** 137 | * @return \Kitpages\DataGridBundle\Paginator\PaginatorConfig 138 | */ 139 | public function getPaginatorConfig() 140 | { 141 | return $this->paginatorConfig; 142 | } 143 | 144 | /** 145 | * @param string $requestUri 146 | */ 147 | public function setRequestUri($requestUri) 148 | { 149 | $this->requestUri = $requestUri; 150 | } 151 | 152 | /** 153 | * @return string 154 | */ 155 | public function getRequestUri() 156 | { 157 | return $this->requestUri; 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /Paginator/PaginatorConfig.php: -------------------------------------------------------------------------------- 1 | getName() . '_' . $key; 31 | } 32 | 33 | /** 34 | * @param int $itemCountInPage 35 | * 36 | * @return PaginatorConfig Fluent interface 37 | */ 38 | public function setItemCountInPage($itemCountInPage) 39 | { 40 | $this->itemCountInPage = $itemCountInPage; 41 | 42 | return $this; 43 | } 44 | 45 | /** 46 | * @return int 47 | */ 48 | public function getItemCountInPage() 49 | { 50 | return $this->itemCountInPage; 51 | } 52 | 53 | /** 54 | * @param string $name 55 | * 56 | * @return PaginatorConfig Fluent interface 57 | */ 58 | public function setName($name) 59 | { 60 | $this->name = $name; 61 | 62 | return $this; 63 | } 64 | 65 | /** 66 | * @return string 67 | */ 68 | public function getName() 69 | { 70 | return $this->name; 71 | } 72 | 73 | /** 74 | * @param \Doctrine\ORM\QueryBuilder|null $queryBuilder 75 | * 76 | * @return PaginatorConfig Fluent interface 77 | */ 78 | public function setQueryBuilder(QueryBuilder $queryBuilder) 79 | { 80 | $this->queryBuilder = $queryBuilder; 81 | 82 | return $this; 83 | } 84 | 85 | /** 86 | * @return \Doctrine\ORM\QueryBuilder|null 87 | */ 88 | public function getQueryBuilder() 89 | { 90 | return $this->queryBuilder; 91 | } 92 | 93 | /** 94 | * @param int $visiblePageCountInPaginator 95 | * 96 | * @return PaginatorConfig Fluent interface 97 | */ 98 | public function setVisiblePageCountInPaginator($visiblePageCountInPaginator) 99 | { 100 | $this->visiblePageCountInPaginator = $visiblePageCountInPaginator; 101 | 102 | return $this; 103 | } 104 | 105 | /** 106 | * @return int 107 | */ 108 | public function getVisiblePageCountInPaginator() 109 | { 110 | return $this->visiblePageCountInPaginator; 111 | } 112 | 113 | /** 114 | * @param string $countFieldName 115 | * 116 | * @return PaginatorConfig Fluent interface 117 | */ 118 | public function setCountFieldName($countFieldName) 119 | { 120 | $this->countFieldName = $countFieldName; 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * @return string 127 | */ 128 | public function getCountFieldName() 129 | { 130 | return $this->countFieldName; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Paginator/PaginatorManager.php: -------------------------------------------------------------------------------- 1 | dispatcher = $dispatcher; 29 | $this->paginatorParameterList = $paginatorParameterList; 30 | } 31 | 32 | //// 33 | // paginator 34 | //// 35 | /** 36 | * get Paginator object 37 | * 38 | * @param \Kitpages\DataGridBundle\Paginator\PaginatorConfig $paginatorConfig 39 | * @param \Symfony\Component\HttpFoundation\Request $request 40 | * @return \Kitpages\DataGridBundle\Paginator\Paginator 41 | */ 42 | public function getPaginator(PaginatorConfig $paginatorConfig, Request $request) 43 | { 44 | $queryBuilder = $paginatorConfig->getQueryBuilder(); 45 | // insert default values in paginator config 46 | $paginatorConfig = clone($paginatorConfig); 47 | if (is_null($paginatorConfig->getItemCountInPage())) { 48 | $paginatorConfig->setItemCountInPage($this->paginatorParameterList["item_count_in_page"]); 49 | } 50 | if (is_null($paginatorConfig->getVisiblePageCountInPaginator())) { 51 | $paginatorConfig->setVisiblePageCountInPaginator($this->paginatorParameterList["visible_page_count_in_paginator"]); 52 | } 53 | 54 | // create paginator object 55 | $paginator = new Paginator(); 56 | $paginator->setPaginatorConfig($paginatorConfig); 57 | $paginator->setUrlTool(new UrlTool()); 58 | $paginator->setRequestUri($request->getRequestUri()); 59 | 60 | // get currentPage 61 | $paginator->setCurrentPage($request->query->get($paginatorConfig->getRequestQueryName("currentPage"), 1)); 62 | 63 | // calculate total object count 64 | $countQueryBuilder = clone($queryBuilder); 65 | $countQueryBuilder->select("count(DISTINCT ".$paginatorConfig->getCountFieldName().")"); 66 | $countQueryBuilder->setMaxResults(null); 67 | $countQueryBuilder->setFirstResult(null); 68 | $countQueryBuilder->resetDQLPart('groupBy'); 69 | $countQueryBuilder->resetDQLPart('orderBy'); 70 | 71 | // event to change paginator query builder 72 | $event = new DataGridEvent(); 73 | $event->set("paginator", $paginator); 74 | $event->set("paginatorQueryBuilder", $countQueryBuilder); 75 | $event->set("request", $request); 76 | $this->dispatcher->dispatch(KitpagesDataGridEvents::ON_GET_PAGINATOR_QUERY, $event); 77 | 78 | if (!$event->isDefaultPrevented()) { 79 | $query = $countQueryBuilder->getQuery(); 80 | $event->set("query", $query); 81 | } 82 | $this->dispatcher->dispatch(KitpagesDataGridEvents::AFTER_GET_PAGINATOR_QUERY, $event); 83 | 84 | // hack : recover query from the event so the developper can build a new query 85 | // from the paginatorQueryBuilder in the listener and reinject it in the event. 86 | $query = $event->get("query"); 87 | 88 | try { 89 | $totalCount = $query->getSingleScalarResult(); 90 | $paginator->setTotalItemCount($totalCount); 91 | } catch (\Doctrine\ORM\NoResultException $e) { 92 | $paginator->setTotalItemCount(0); 93 | } 94 | 95 | // calculate total page count 96 | if ($paginator->getTotalItemCount() == 0) { 97 | $paginator->setTotalPageCount(0); 98 | } else { 99 | $paginator->setTotalPageCount( 100 | (int) ((($paginator->getTotalItemCount() - 1) / $paginatorConfig->getItemCountInPage()) + 1) 101 | ); 102 | } 103 | 104 | // change current page if needed 105 | if ($paginator->getCurrentPage() > $paginator->getTotalPageCount()) { 106 | $paginator->setCurrentPage(1); 107 | } 108 | 109 | // calculate nbPageLeft and nbPageRight 110 | $nbPageLeft = (int) ($paginatorConfig->getVisiblePageCountInPaginator() / 2); 111 | $nbPageRight = $paginatorConfig->getVisiblePageCountInPaginator() - 1 - $nbPageLeft ; 112 | 113 | // calculate lastPage to display 114 | $maxPage = min($paginator->getTotalPageCount(), $paginator->getCurrentPage() + $nbPageRight); 115 | // adapt minPage and maxPage 116 | $minPage = max(1, $maxPage-($paginatorConfig->getVisiblePageCountInPaginator() - 1)); 117 | $maxPage = min($paginator->getTotalPageCount(), $minPage + ($paginatorConfig->getVisiblePageCountInPaginator() - 1)); 118 | 119 | $paginator->setMinPage($minPage); 120 | $paginator->setMaxPage($maxPage); 121 | 122 | // calculate previousButton 123 | if ($paginator->getCurrentPage() == 1) { 124 | $paginator->setPreviousButtonPage(null); 125 | } else { 126 | $paginator->setPreviousButtonPage( $paginator->getCurrentPage() - 1 ); 127 | } 128 | // calculate nextButton 129 | if ($paginator->getCurrentPage() == $paginator->getTotalPageCount()) { 130 | $paginator->setNextButtonPage(null); 131 | } else { 132 | $paginator->setNextButtonPage( $paginator->getCurrentPage() + 1); 133 | } 134 | 135 | return $paginator; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | KitpagesDataGridBundle 2 | ======================== 3 | 4 | [![Build Status](https://travis-ci.org/kitpages/KitpagesDataGridBundle.svg?branch=master)](https://travis-ci.org/kitpages/KitpagesDataGridBundle) 5 | 6 | [![SensioLabsInsight](https://insight.sensiolabs.com/projects/df09ed10-4c61-4b1d-8cf7-5206901493c4/small.png)](https://insight.sensiolabs.com/projects/df09ed10-4c61-4b1d-8cf7-5206901493c4) 7 | 8 | This Symfony Bundle is a simple datagrid bundle. It aims to be easy to use and extensible. 9 | 10 | ## Warning version 3 11 | 12 | Version 3 is for symfony ~3.3 and ~4.0 and PHP 7, and only twig ~1.8 13 | 14 | Version 3 is here. You can switch to branch 2.x (or tags 2.x) if you want to stay on legacy version. 15 | 16 | There is no BC break in the usage between version 2 and version 3, but the version 17 | 3 is not compatible with symfony < 3.3. 18 | 19 | ## Warning version 2 20 | 21 | Version 2 is here. You can switch to branch 1.x (or tags 1.x) if you want to stay on legacy version. 22 | There are BC Breaks between version 1 and version 2. 23 | 24 | Actual state 25 | ============ 26 | 27 | see VERSIONS.md 28 | 29 | * v3.x is in beta, no change in funcionnality 30 | * v2.5.x is following version 2.5.x of doctrine 31 | * v2.4.x is following version 2.4.x of doctrine 32 | * v2.x is stable and production ready 33 | * v1.x is stable, production ready 34 | 35 | Features 36 | ======== 37 | 38 | * Display a Data Grid from a Doctrine 2 Query Builder 39 | * Automatic filter 40 | * Sorting on columns 41 | * Easy to configure 42 | * Easy to extend 43 | * Documented (in this readme for basics and in Resources/doc for advanced topics) 44 | * Paginator can be used as a standalone component 45 | * Change of DataGrid behaviour with events 46 | * Change of DataGrid presentation with twig embeds 47 | 48 | System Requirement 49 | ================== 50 | * jQuery has to be present on your pages 51 | * version 1.8+ of twig is mandatory (use of twig embeds) 52 | 53 | Documentation 54 | ============= 55 | 56 | The documentation is in this README and in [Resources/doc](https://github.com/kitpages/KitpagesDataGridBundle/tree/master/Resources/doc) 57 | 58 | * [Installation and simple user's guide : In this README](#installation) 59 | * [Grid Extended Use](https://github.com/kitpages/KitpagesDataGridBundle/tree/master/Resources/doc/10-GridExtendedUse.md) 60 | * [Standalone Paginator](https://github.com/kitpages/KitpagesDataGridBundle/tree/master/Resources/doc/20-StandalonePaginator.md) 61 | * [Events for extended use case](https://github.com/kitpages/KitpagesDataGridBundle/tree/master/Resources/doc/30-Events.md) 62 | 63 | 64 | Installation 65 | ============ 66 | You need to add the following lines in your deps : 67 | 68 | Add KitpagesChainBundle in your composer.json 69 | 70 | ``` 71 | { 72 | "require": { 73 | "kitpages/data-grid-bundle": "~2.4" // Use ~2.5 if you use doctrine >= 2.5 74 | } 75 | } 76 | ``` 77 | 78 | Now tell composer to download the bundle by running the step: 79 | 80 | ``` bash 81 | $ php composer.phar update kitpages/data-grid-bundle 82 | ``` 83 | 84 | AppKernel.php 85 | 86 | ``` php 87 | $bundles = array( 88 | ... 89 | new Kitpages\DataGridBundle\KitpagesDataGridBundle(), 90 | ); 91 | ``` 92 | 93 | Configuration in config.yml 94 | =========================== 95 | 96 | These values are default values. You can skip the configuration if it is ok for you. 97 | 98 | ```yaml 99 | kitpages_data_grid: 100 | grid: 101 | default_twig: KitpagesDataGridBundle:Grid:grid.html.twig 102 | paginator: 103 | default_twig: KitpagesDataGridBundle:Paginator:paginator.html.twig 104 | item_count_in_page: 50 105 | visible_page_count_in_paginator: 5 106 | ``` 107 | 108 | Note you can use the followin configuration in order to user Bootstrap 3 : 109 | 110 | ```yaml 111 | kitpages_data_grid: 112 | grid: 113 | default_twig: KitpagesDataGridBundle:Grid:bootstrap3-grid.html.twig 114 | paginator: 115 | default_twig: KitpagesDataGridBundle:Paginator:bootstrap3-paginator.html.twig 116 | ``` 117 | 118 | 119 | Simple Usage example 120 | ==================== 121 | 122 | ## In the controller 123 | 124 | ```php 125 | use Kitpages\DataGridBundle\Grid\GridConfig; 126 | use Kitpages\DataGridBundle\Grid\Field; 127 | use Symfony\Component\HttpFoundation\Request; 128 | 129 | class ContactController 130 | { 131 | public function productListAction(Request $request) 132 | { 133 | // create query builder 134 | $repository = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product'); 135 | $queryBuilder = $repository->createQueryBuilder('item') 136 | ->where('item.price > :price') 137 | ->setParameter('price', '19.90') 138 | ; 139 | 140 | $gridConfig = new GridConfig(); 141 | $gridConfig 142 | ->setQueryBuilder($queryBuilder) 143 | ->setCountFieldName('item.id') 144 | ->addField('item.id') 145 | ->addField('item.slug', array('filterable' => true)) 146 | ->addField('item.updatedAt', array( 147 | 'sortable' => true, 148 | 'formatValueCallback' => function($value) { return $value->format('Y/m/d'); } 149 | )) 150 | ; 151 | 152 | $gridManager = $this->get('kitpages_data_grid.grid_manager'); 153 | $grid = $gridManager->getGrid($gridConfig, $request); 154 | 155 | return $this->render('AppSiteBundle:Default:productList.html.twig', array( 156 | 'grid' => $grid 157 | )); 158 | } 159 | } 160 | ``` 161 | 162 | ## Twig associated 163 | 164 | In your twig you just have to put this code to display the grid you configured. 165 | 166 | {% embed kitpages_data_grid.grid.default_twig with {'grid': grid} %} 167 | {% endembed %} 168 | 169 | More advanced usage 170 | =================== 171 | 172 | ## In the controller 173 | 174 | same controller than before 175 | 176 | ## Twig associated 177 | 178 | If you want to add a column on the right of the table, you can put this code in your twig. 179 | 180 | {% embed kitpages_data_grid.grid.default_twig with {'grid': grid} %} 181 | 182 | {% block kit_grid_thead_column %} 183 | Action 184 | {% endblock %} 185 | 186 | {% block kit_grid_tbody_column %} 187 | Edit 188 | {% endblock %} 189 | 190 | {% endembed %} 191 | 192 | More advanced usage 193 | =================== 194 | 195 | ## In the controller 196 | 197 | ```php 198 | use Kitpages\DataGridBundle\Grid\GridConfig; 199 | use Kitpages\DataGridBundle\Grid\Field; 200 | use Symfony\Component\HttpFoundation\Request; 201 | 202 | class AdminController extends Controller 203 | { 204 | 205 | public function listAction(Request $request, $state) 206 | { 207 | // create query builder 208 | $em = $this->get('doctrine')->getEntityManager(); 209 | $queryBuilder = $em->createQueryBuilder() 210 | ->select('m, e, c') 211 | ->from('KitappMissionBundle:Mission', 'm') 212 | ->leftJoin('m.employee', 'e') 213 | ->leftJoin('m.client', 'c') 214 | ->where('m.state = :state') 215 | ->add('orderBy', 'm.updatedAt DESC') 216 | ->setParameter('state', $state) 217 | ; 218 | 219 | $gridConfig = new GridConfig(); 220 | $gridConfig 221 | ->setQueryBuilder($queryBuilder) 222 | ->setCountFieldName("m.id"); 223 | ->addField('m.title', array('label' => 'title', 'filterable' => true)) 224 | ->addField('m.country', array('filterable' => true)) 225 | ->addField('c.corporation', array('filterable' => true)) 226 | ->addField('e.lastname', array('filterable' => true)) 227 | ->addField('e.email', array('filterable' => true)) 228 | ; 229 | 230 | $gridManager = $this->get('kitpages_data_grid.grid_manager'); 231 | $grid = $gridManager->getGrid($gridConfig, $request); 232 | 233 | return $this->render('KitappMissionBundle:Admin:list.html.twig', array( 234 | 'grid' => $grid 235 | )); 236 | } 237 | } 238 | ``` 239 | 240 | ## Twig associated 241 | 242 | same Twig than before 243 | 244 | Field "as" 245 | ========== 246 | 247 | For request like 248 | 249 | $queryBuilder->select("item, item.id * 3 as foo"); 250 | 251 | You can display the foo field with 252 | 253 | $gridConfig->addField("item.id"); 254 | $gridConfig->addField("foo"); 255 | 256 | 257 | Events 258 | ====== 259 | 260 | You can modify the way this bundle works by listening events and modify some 261 | objects injected in the $event. 262 | 263 | see the event documentation in Resources/doc/30-Events.md 264 | 265 | Tags 266 | ==== 267 | 268 | Tag system is used to get some fields by tags. When you create a field, you can 269 | define some tags associated to this field. After that, in the grid config, you can 270 | find the fields that match this tag. 271 | 272 | // add tag as the third parameter of the field 273 | $gridConfig->addField("item.id", [], ['foo', 'bar']); 274 | $gridConfig->addField("foo", [], ['myTag', 'foo']); 275 | 276 | // get fieldList matching 'bar' tag. There is only one result. 277 | $fieldList = $gridConfig->getFieldListByTag('bar'); 278 | $fieldList[0] // -> this is the first Field (which name is 'item.id') 279 | 280 | Custom class for the $grid object 281 | ================================= 282 | 283 | By default, the following line 284 | ```php 285 | $grid = $gridManager->getGrid($gridConfig, $request); 286 | ``` 287 | returns an object of the type Grid 288 | 289 | You can use your own subclass of Grid. By default the GridManager creates the instance $grid of Grid but you can create the instance by yourself. 290 | 291 | ```php 292 | class CustomGrid extends Grid 293 | { 294 | public $myParameter; 295 | } 296 | $myCustomGrid = new CustomGrid(); 297 | $grid = $gridManager->getGrid($gridConfig, $request,$myCustomGrid); 298 | 299 | // now the $grid object is an instance of CustomGrid (it 300 | // is exactly the same object than $myCustomGrid, not cloned) 301 | ``` 302 | 303 | 304 | Reference guide 305 | =============== 306 | 307 | ## Add a field in the gridConfig 308 | 309 | when you add a field, you can set these parameters : 310 | 311 | ```php 312 | $gridConfig->addField('slug', array( 313 | 'label' => 'Mon slug', 314 | 'sortable' => false, 315 | 'visible' => true, 316 | 'filterable' => true, 317 | 'translatable' => true, 318 | 'formatValueCallback' => function($value) { return strtoupper($value); }, 319 | 'autoEscape' => true, 320 | 'category' => null, // only used by you for checking this value in your events if you want to... 321 | 'nullIfNotExists' => false, // for leftJoin, if value is not defined, this can return null instead of an exception 322 | )); 323 | ``` 324 | 325 | ## What can you personalize in your twig template 326 | 327 | With the embed system of twig 1.8 and more, you can override some parts of the default 328 | rendering (see example in the "More advanced usage" paragraph). 329 | 330 | You can consult the base twig template here to see what you can personalize. 331 | https://github.com/kitpages/KitpagesDataGridBundle/blob/master/Resources/views/Grid/grid.html.twig 332 | -------------------------------------------------------------------------------- /Resources/config/services.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Kitpages\DataGridBundle\Grid\GridManager 9 | Kitpages\DataGridBundle\Paginator\PaginatorManager 10 | Kitpages\DataGridBundle\Grid\ItemListNormalizer\LegacyNormalizer 11 | Kitpages\DataGridBundle\Grid\ItemListNormalizer\StandardNormalizer 12 | Kitpages\DataGridBundle\Twig\GlobalsTwigExtension 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | %kitpages_data_grid.grid.hydrator_class% 21 | 22 | 23 | 24 | %kitpages_data_grid.paginator% 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | %kitpages_data_grid.grid% 33 | %kitpages_data_grid.paginator% 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Resources/doc/10-GridExtendedUse.md: -------------------------------------------------------------------------------- 1 | Grid Extended Use 2 | ================= 3 | 4 | More than one Grids in one page 5 | ------------------------------- 6 | If you have more than one grid on a page, you have to name each grid with a different name. In your controller 7 | 8 | $gridConfig->setName("first-grid"); 9 | 10 | Change Paginator parameters of your grid 11 | ---------------------------------------- 12 | If you want to change the result number on each page for example. 13 | 14 | public function gridAction() 15 | { 16 | $gridManater = $this->get("kitpages_data_grid.grid_manager"); 17 | 18 | $repository = $this->getDoctrine()->getRepository('KitpagesShopBundle:OrderLine'); 19 | $queryBuilder = $repository->createQueryBuilder('ol'); 20 | $queryBuilder->select('ol'); 21 | 22 | $gridConfig = new GridConfig(); 23 | $gridConfig 24 | ->setQueryBuilder($queryBuilder) 25 | ->setCountFieldName('ol.id') 26 | ->addField('ol.id', array('sortable' => true)) 27 | ->addField('ol.shopReference', array( 28 | 'label' => 'Ref', 29 | 'filterable' => true, 30 | 'sortable' => true 31 | )) 32 | ->addField('ol.updatedAt', array( 33 | 'sortable' => true, 34 | 'formatValueCallback' => function ($value) { return $value->format('Y/m/d'); } 35 | )) 36 | ; 37 | 38 | // paginator configuration 39 | $gridPaginatorConfig = new PaginatorConfig(); 40 | $gridPaginatorConfig 41 | ->setName($gridConfig->getName()) 42 | ->setCountFieldName($gridConfig->getCountFieldName()) 43 | ->setItemCountInPage(10) 44 | ; 45 | $gridConfig->setPaginatorConfig($gridPaginatorConfig); 46 | 47 | $grid = $gridManager->getGrid($gridConfig, $request; 48 | 49 | return $this->render('AppSiteBundle:Default:grid.html.twig', array( 50 | 'grid' => $grid 51 | )); 52 | } 53 | 54 | Format some fields system wide 55 | ------------------------------ 56 | You can change the way datagrid displays a date for your entire website. Or you want add a link around every email. 57 | 58 | You can use events to do that : 59 | 60 | Before rendering a field, the datagrid sends an event KitpagesDataGridEvents::ON_DISPLAY_GRID_VALUE_CONVERSION that can 61 | replace the standard rendering way to display a field. 62 | 63 | After the default formatting, another event KitpagesDataGridEvents::AFTER_DISPLAY_GRID_VALUE_CONVERSION is sent. 64 | You can still here make some small modifications on the display result. 65 | 66 | We will create a EventListener to change the way to display these fields: 67 | 68 | ```php 69 | 'onConversion', 83 | KitpagesDataGridEvents::AFTER_DISPLAY_GRID_VALUE_CONVERSION => 'afterConversion' 84 | ); 85 | } 86 | 87 | public function onConversion(DataGridEvent $event) 88 | { 89 | // prevent default formatting 90 | $event->preventDefault(); 91 | 92 | // get value to display 93 | $value = $event->get("value"); 94 | 95 | // datetime formatting 96 | if ($value instanceof \DateTime) { 97 | $formatter = new \IntlDateFormatter( 98 | 'fr', 99 | \IntlDateFormatter::LONG, 100 | \IntlDateFormatter::SHORT, 101 | $value->getTimezone()->getName(), 102 | \IntlDateFormatter::GREGORIAN 103 | ); 104 | $event->set("returnValue", $formatter->format($value->getTimestamp())); 105 | return; 106 | } 107 | 108 | // email formating (note the way the autoEscape is modified) 109 | $field = $event->get("field"); 110 | if (strpos($field->getFieldName(), "email") !== false) { 111 | $field->setAutoEscape(false); 112 | $ret = ''.$value.''; 113 | $event->set("returnValue", $ret); 114 | return; 115 | } 116 | 117 | // for the other cases, copy the value to the return value 118 | $event->set("returnValue", $value); 119 | } 120 | 121 | public function afterConversion(DataGridEvent $event) 122 | { 123 | // set to uppercase all lastnames 124 | $field = $event->get("field"); 125 | if (strpos(strtolower($field->getFieldName()), "lastname") !== false) { 126 | $event->set("returnValue", strtoupper($event->get("returnValue")); 127 | return; 128 | } 129 | } 130 | } 131 | 132 | ``` 133 | 134 | And add the subscriber to the event listener. This is an example in a service.xml. 135 | ```xml 136 | 137 | 138 | 139 | ``` 140 | 141 | (Note that these events are not sent if a formatValueCallback is given for a field.) 142 | 143 | 144 | Select recursive field 145 | ---------------------- 146 | If you have for exemple a categorie table which as a recursion with parent_id, you must use 147 | the leftJoin() condition in your queryBuilder. 148 | 149 | public function gridAction(Request $request) 150 | { 151 | $em = $this->getDoctrine()->getEntityManager(); 152 | $queryBuilder = $em->createQueryBuilder(); 153 | $queryBuilder 154 | ->select(array('c', 'cp')) 155 | ->from('My\SiteBundle\Entity\Categorie', 'c') 156 | ->leftJoin('c.idParent', 'p') 157 | ; 158 | 159 | $gridConfig = new GridConfig(); 160 | $gridConfig 161 | ->setQueryBuilder($queryBuilder) 162 | ->setCountFieldName('c.id') 163 | ->addField('c.foo') 164 | ->addField('c.idParent', array( 165 | 'label' => 'Parent', 166 | 'formatValueCallback' => function($value) { return (!empty($value)) ? $value['foo'] : 'None'; } 167 | )) 168 | ; 169 | 170 | $gridManager = $this->get('kitpages_data_grid.grid_manager'); 171 | $grid = $gridManager->getGrid($gridConfig, $request); 172 | 173 | return $this->render('MySiteBundle:Default:grid.html.twig', array( 174 | 'grid' => $grid 175 | )); 176 | } 177 | 178 | Select collection from a 1-n relationship 179 | ----------------------------------------- 180 | If you have for exemple an article with a comments property wich is a 1-n relationship. You have the article id and you want to create a grid for the article's comments : 181 | 182 | public function gridAction(Request $request) 183 | { 184 | $manager = $this->getDoctrine()->getManager(); 185 | 186 | /* @var $qb \Doctrine\ORM\QueryBuilder */ 187 | $qb = $manager->createQueryBuilder('comments-list'); 188 | $qb->select('c1') 189 | ->from('Acme\MyBundle\Entity\Comment', 'c1') 190 | ->where( 191 | $qb->expr()->in( 192 | 'c1.id', 193 | $manager->createQueryBuilder('articles') 194 | ->select('c2.id') 195 | ->from('Acme\MyBundle\Entity\Article', 'a') 196 | ->join('a.comments', 'c2') 197 | ->where('a.id = :id_article') 198 | ->getDQL() 199 | ) 200 | ) 201 | ->orderBy('c1.created', 'DESC') 202 | ->setParameter('id_article', $articleId) 203 | ; 204 | 205 | $gridConfig = new GridConfig(); 206 | $gridConfig->setQueryBuilder($qb) 207 | $gridConfig->setCountFieldName("c1.id"); 208 | $gridConfig->addField("c1.comment", array("label" => "Comment")); 209 | $gridConfig->addField("c1.author", array("label" => "Author", "filterable" => true, "sortable" => true)); 210 | 211 | $gridManager = $this->get("kitpages_data_grid.grid_manager"); 212 | $grid = $gridManager->getGrid($gridConfig, $request); 213 | 214 | return $this->render('MySiteBundle:Default:grid.html.twig', array( 215 | 'grid' => $grid 216 | )); 217 | } 218 | 219 | formatValueCallback 220 | ------------------- 221 | If you want to format a data, you can use a simple callback. For example, if a data is a dateTime, you can format 222 | use that code : 223 | 224 | $gridConfig->addField( 225 | 'ol.updatedAt', 226 | array( 227 | 'label' => 'Updated at', 228 | 'sortable'=>true, 229 | 'formatValueCallback' => function($value) { return $value->format('Y/m/d'); } 230 | ) 231 | ); 232 | 233 | You can also have a second argument in your callback that will receive the entire row received from the query. 234 | 235 | $gridConfig->addField( 236 | 'ol.updatedAt', 237 | array( 238 | 'label' => 'Date and Id', 239 | 'sortable' => true, 240 | 'formatValueCallback' => function($value, $row) { return $value->format('Y/m/d') . '--' . $row['id']; } 241 | ) 242 | ); 243 | 244 | Customize field display using twig (bootstrap3 only) 245 | --- 246 | 247 | If you want to customize how a field is displayed using twig, use the "uniqueId" parameter. 248 | 249 | ```php 250 | $gridConfig = new GridConfig(); 251 | $gridConfig 252 | ->setQueryBuilder($queryBuilder) 253 | ->addField('d.createdAt', [ 254 | 'uniqueId' => 'createdAt', // <---- here 255 | 'label' => 'Created at', 256 | 'sortable' => true, 257 | 'filterable' => true, 258 | 'visible' => $this->isGranted(Roles::ADMIN), 259 | ]) 260 | // ... 261 | ``` 262 | 263 | And then in your twig template : 264 | 265 | ```twig 266 | {% embed '@KitpagesDataGrid/Grid/bootstrap3-grid.html.twig' %} 267 | {% block kit_grid_cell_id_createdAt %} 268 | {{ value|date('Y-m-d') }} by {{ item['d.authorName'] }} 269 | {% endblock %} 270 | {% endembed %} 271 | ``` 272 | 273 | Every column header has a custom CSS class, if you have defined a uniqueId : `kit-grid-header-createdAt` 274 | If you didn't, you can use your fieldName but "slug dashed" (d.createdAt become d-createdAt) so : `kit-grid-header-d-createdAt` 275 | 276 | Add selector action (to filter on a field with a value) 277 | ------------------------------------------------------- 278 | You can add button action filters with 279 | 280 | $gridConfig->addSelector(array('label' => 'button label', 'field' => 'c1.author', 'value' => 'Arthur Rimbaud')); 281 | $gridConfig->addSelector(array('label' => 'button label2', 'field' => 'c1.author', 'value' => 'Paul Verlaine')); 282 | $gridConfig->addSelector(array('label' => 'button label3', 'field' => 'c1.comment', 'value' => 'No comment')); 283 | 284 | 285 | 286 | Grid with a "GROUP BY" querybuilder 287 | ----------------------------------- 288 | For group by queries, watch out for the count field name you define. In the count query 289 | for the paginator, the groupBy part is removed form the queryBuilder and a distinct is 290 | added before the count field. 291 | 292 | // create query builder 293 | $repository = $this->getDoctrine()->getRepository('AcmeStoreBundle:Product'); 294 | $queryBuilder = $repository->createQueryBuilder("item") 295 | ->select('item.type, count(item.id) as cnt') 296 | ->groupBy('item.type') 297 | ->where('item.price > :price') 298 | ->setParameter('price', '19.90') 299 | ; 300 | 301 | $gridConfig = new GridConfig(); 302 | $gridConfig 303 | ->setQueryBuilder($queryBuilder) 304 | ->setCountFieldName('item.type') 305 | ->addField('item.type') 306 | ->addField('cnt') 307 | ; 308 | 309 | 310 | 311 | Render the item value with a twig template 312 | ------------------------------------------ 313 | 314 | You can format a data with a twig template by using the callback system : 315 | 316 | // get templating service 317 | $twig = $this->get('templating'); 318 | // add the field 319 | $gridConfig->addField( 320 | 'ol.updatedAt', 321 | array( 322 | "formatValueCallback"=>function($value, $row) use ($twig) { 323 | return $twig->render("AppSiteBundle:Default:grid-element.html.twig", array("row" => $row, 'value' => $value) ); 324 | }, 325 | "autoEscape" => false 326 | ) 327 | ); 328 | 329 | And the twig file could be grid-element.html.twig in the right bundle : 330 | 331 | 332 | id={{id}}
333 | date={{updatedAt | date ("Y/m/d") }} 334 |
335 | 336 | 337 | Add multiple action with checkboxes on the left 338 | ----------------------------------------------- 339 | 340 | You can add a form around the datagrid and add any input elements in the datagrid just by extending 341 | the embeded twig. 342 | 343 | Let's see in this example how to add checkboxes on the left of the grid for multiple actions. 344 | 345 | {% embed kitpages_data_grid.grid.default_twig with {'grid': grid} %} 346 | 347 | {% block kit_grid_before_table %} 348 |
349 | {% endblock %} 350 | 351 | {% block kit_grid_thead_before_column %} 352 | select 353 | {% endblock %} 354 | 355 | {% block kit_grid_tbody_before_column %} 356 | 357 | {% endblock %} 358 | 359 | {% block kit_grid_after_table %} 360 | 361 |
362 | {% endblock %} 363 | 364 | {% endembed %} 365 | 366 | You can see all the extension points of the data grid template in the 367 | file [views/Grid/grid.html.twig](https://github.com/kitpages/KitpagesDataGridBundle/blob/master/Resources/views/Grid/grid.html.twig) 368 | 369 | 370 | Add class to a table row 371 | ----------------------------------------------- 372 | 373 | You can add a table row class by extending `kit_grid_row_class` block: 374 | 375 | {% embed kitpages_data_grid.grid.default_twig with {'grid': grid} %} 376 | 377 | {% block kit_grid_row_class %} 378 | {% if item['t.deletedAt'] %} stroke {% endif %} 379 | {% endblock %} 380 | 381 | {% endembed %} 382 | -------------------------------------------------------------------------------- /Resources/doc/20-StandalonePaginator.md: -------------------------------------------------------------------------------- 1 | Standalone Paginator 2 | ==================== 3 | 4 | You can use the paginator as a standalone component. 5 | 6 | In your controller 7 | 8 | ```php 9 | get("kitpages_data_grid.paginator_manager"); 17 | 18 | $repository = $this->getDoctrine()->getRepository('KitpagesShopBundle:OrderLine'); 19 | $queryBuilder = $repository->createQueryBuilder("ol"); 20 | $queryBuilder->select("ol"); 21 | 22 | $paginatorConfig = new PaginatorConfig(); 23 | $paginatorConfig->setCountFieldName("ol.id"); 24 | $paginatorConfig->setQueryBuilder($queryBuilder); 25 | $paginator = $paginatorManager->getPaginator($paginatorConfig, $request); 26 | return $this->render('AppSiteBundle:Default:paginator.html.twig', array( 27 | "paginator" => $paginator 28 | )); 29 | } 30 | ``` 31 | 32 | In your twig : 33 | 34 | {% embed kitpages_data_grid.paginator.default_twig with {'paginator': paginator} %} 35 | {% endembed %} 36 | 37 | -------------------------------------------------------------------------------- /Resources/doc/30-Events.md: -------------------------------------------------------------------------------- 1 | Events 2 | ====== 3 | 4 | Usage sample 5 | ------------ 6 | Let's imagine you want to translate your results with the gedmo translatable. You have to follow these steps : 7 | 8 | * create a service that can alter the query 9 | * register this service as a listener of the event "kitpages_data_grid.after_get_grid_query" 10 | 11 | Service GridListener.php 12 | 13 | get("query")->setHint( 23 | \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 24 | 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker' 25 | ); 26 | } 27 | } 28 | 29 | Configuration to add in config.yml 30 | 31 | services: 32 | datagrid_listener: 33 | class: 'App\SiteBundle\EventListener\GridListener' 34 | tags: 35 | - { name: "kernel.event_listener", event: "kitpages_data_grid.after_get_grid_query", method: "afterGetGridQuery" } 36 | 37 | If you want an event on just one specific datagrid 38 | -------------------------------------------------- 39 | You have to name your datagrid in your controller : 40 | 41 | $gridConfig->setName("my-datagrid"); 42 | 43 | Then in your listener 44 | 45 | public function afterGetGridQuery(DataGridEvent $event) 46 | { 47 | $grid = $event->get("grid"); 48 | if ($grid->getGridConfig()->getName() == "my-datagrid") { 49 | // your specific way 50 | } 51 | } 52 | 53 | 54 | Event list 55 | ========== 56 | 57 | Events are listed in the file KitpagesDataGridEvents.php. Let's see how to use these events : 58 | 59 | kitpages_data_grid.on_get_grid_query 60 | ------------------------------------ 61 | Allow to modify the gridQueryBuilder before getting the query 62 | 63 | get("gridQueryBuilder"); 74 | $grid = $event->get("grid"); 75 | $request = $event->get("request"); 76 | 77 | // operation 78 | $query = $gridQueryBuilder->getQuery(); 79 | $event->set("query", $query); 80 | 81 | // cancel default behavior 82 | $event->preventDefault(); 83 | } 84 | } 85 | 86 | kitpages_data_grid.after_get_grid_query 87 | --------------------------------------- 88 | Allow to modify the query before execution 89 | 90 | get("gridQueryBuilder"); 101 | $grid = $event->get("grid"); 102 | $query = $event->get("query"); 103 | $request = $event->get("request"); 104 | 105 | // operation (translate result with gedmo translatable) 106 | $query->setHint( 107 | \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, 108 | 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker' 109 | ); 110 | } 111 | } 112 | 113 | kitpages_data_grid.on_apply_filter 114 | ---------------------------------- 115 | modify the way we can apply the filter 116 | 117 | get("gridQueryBuilder"); 128 | $grid = $event->get("grid"); 129 | $filter = $event->get("filter"); 130 | 131 | // operation (this is de default behavior, you can code your's here instead) 132 | $fieldList = $grid->getGridConfig()->getFieldList(); 133 | $filterRequestList = array(); 134 | foreach($fieldList as $field) { 135 | if ($field->getFilterable()) { 136 | $filterRequestList[] = $queryBuilder->expr()->like($field->getFieldName(), ":filter"); 137 | } 138 | } 139 | if (count($filterRequestList) > 0) { 140 | $reflectionMethod = new \ReflectionMethod($queryBuilder->expr(), "orx"); 141 | $queryBuilder->andWhere($reflectionMethod->invokeArgs($queryBuilder->expr(), $filterRequestList)); 142 | $queryBuilder->setParameter("filter", "%".$filter."%"); 143 | } 144 | $grid->setFilterValue($filter); 145 | 146 | // cancel default behavior 147 | $event->preventDefault(); 148 | } 149 | } 150 | 151 | kitpages_data_grid.after_apply_filter 152 | ---------------------------------- 153 | If you want to change something in the gridQueryBuilder after the filter 154 | 155 | get("gridQueryBuilder"); 166 | $grid = $event->get("grid"); 167 | $filter = $event->get("filter"); 168 | 169 | // change something 170 | 171 | } 172 | } 173 | 174 | kitpages_data_grid.on_apply_sort 175 | ---------------------------------- 176 | modify the way we can apply the sort 177 | 178 | get("gridQueryBuilder"); 189 | $grid = $event->get("grid"); // Kitpages\DataGridBundle\Grid\Grid object 190 | $sortField = $event->get("sortField"); // field name for the sort (string) 191 | $sortOrder = $event->get("sortOrder"); // order ("ASC" or "DESC") of the sort 192 | 193 | // operation (this is de default behavior, you can code your's here instead) 194 | $sortFieldObject = null; 195 | $fieldList = $grid->getGridConfig()->getFieldList(); 196 | foreach($fieldList as $field) { 197 | if ($field->getFieldName() == $sortField) { 198 | if ($field->getSortable() == true) { 199 | $sortFieldObject = $field; 200 | } 201 | break; 202 | } 203 | } 204 | if (!$sortFieldObject) { 205 | return; 206 | } 207 | if ($sortOrder != "DESC") { 208 | $sortOrder = "ASC"; 209 | } 210 | $gridQueryBuilder->orderBy($sortField, $sortOrder); 211 | $grid->setSortField($sortField); 212 | $grid->setSortOrder($sortOrder); 213 | 214 | // cancel default behavior 215 | $event->preventDefault(); 216 | } 217 | } 218 | 219 | kitpages_data_grid.after_apply_sort 220 | ---------------------------------- 221 | can modify the $queryBuilder after the sort 222 | 223 | get("gridQueryBuilder"); 234 | $grid = $event->get("grid"); // Kitpages\DataGridBundle\Grid\Grid object 235 | $sortField = $event->get("sortField"); // field name for the sort (string) 236 | $sortOrder = $event->get("sortOrder"); // order ("ASC" or "DESC") of the sort 237 | 238 | // change what you want in the $gridQueryBuilder 239 | 240 | } 241 | } 242 | 243 | kitpages_data_grid.on_get_paginator_query 244 | ------------------------------------ 245 | Allow to modify the paginatorQueryBuilder before getting the query 246 | 247 | get("paginatorQueryBuilder"); 258 | $paginator = $event->get("paginator"); 259 | $request = $event->get("request"); 260 | 261 | // operation 262 | $query = $paginatorQueryBuilder->getQuery(); 263 | $event->set("query", $query); 264 | 265 | // cancel default behavior 266 | $event->preventDefault(); 267 | } 268 | } 269 | 270 | kitpages_data_grid.after_get_paginator_query 271 | ------------------------------------ 272 | Allow to modify the query of the paginator 273 | 274 | get("paginatorQueryBuilder"); 285 | $paginator = $event->get("paginator"); 286 | $query = $event->get("query"); 287 | $request = $event->get("request"); 288 | 289 | // operation : do what you want 290 | } 291 | } 292 | 293 | kitpages_data_grid.on_display_grid_value_conversion 294 | --------------------------------------------------- 295 | Allow to reformat value displayed in a grid on this entire application. 296 | 297 | ```php 298 | namespace App\SiteBundle\EventListener; 299 | 300 | use Kitpages\DataGridBundle\Event\DataGridEvent; 301 | 302 | class GridListener 303 | { 304 | public function onConversion(DataGridEvent $event) 305 | { 306 | $event->preventDefault(); // remove default formating 307 | $value = $event->get("value"); 308 | if ($value instanceof \DateTime) { 309 | $returnValue = $value->format("m/d/Y"); 310 | } else { 311 | $returnValue = $value; 312 | } 313 | $event->set("returnValue", $returnValue); 314 | 315 | // accessible values in event: 316 | $event->get("field"); // field 317 | $event->get("row"); // entire row 318 | } 319 | } 320 | ``` 321 | 322 | kitpages_data_grid.after_display_grid_value_conversion 323 | --------------------------------------------------- 324 | Allow to reformat value displayed in a grid on this all application, but post default processing. 325 | 326 | This example transform result to uppercase 327 | 328 | ```php 329 | namespace App\SiteBundle\EventListener; 330 | 331 | use Kitpages\DataGridBundle\Event\DataGridEvent; 332 | 333 | class GridListener 334 | { 335 | public function afterConversion(DataGridEvent $event) 336 | { 337 | if ($event->get("field")->getCategory() == "need_uppercase") { 338 | $event->set("returnValue", strtoupper($event->get("returnValue"))); 339 | } 340 | } 341 | } 342 | ``` 343 | 344 | 345 | ## kitpages_data_grid.on_apply_selector 346 | 347 | Allows to change the way selector fields filters the results 348 | 349 | ```php 350 | get("gridQueryBuilder"); 361 | $grid = $event->get("grid"); 362 | $selectorField = $event->get("selectorField"); 363 | $selectorValue = $event->get("selectorValue"); 364 | 365 | $queryBuilder->andWhere($selectorField." = :selectorValue"); 366 | $queryBuilder->setParameter("selectorValue", $selectorValue); 367 | 368 | $grid->setSelectorField($selectorField); 369 | $grid->setSelectorValue($selectorValue); 370 | 371 | // cancel default behavior 372 | $event->preventDefault(); 373 | } 374 | } 375 | ``` 376 | -------------------------------------------------------------------------------- /Resources/doc/40-CookBook.md: -------------------------------------------------------------------------------- 1 | The CookBook 2 | ============ 3 | 4 | ## Use KitpagesDataGridBundle with Twitter Bootstrap 5 | 6 | You just have to use the twig embed bootstrap-grid.html.twig instead of grid.html.twig : 7 | 8 | ```twig 9 | {% embed 'KitpagesDataGridBundle:Grid:bootstrap-grid.html.twig' with {'grid': grid} %} 10 | {% endembed %} 11 | ``` 12 | 13 | You can also change the default embed in the config.yml file. 14 | -------------------------------------------------------------------------------- /Resources/public/css/base.css: -------------------------------------------------------------------------------- 1 | .kit-grid { 2 | } 3 | .kit-grid table { 4 | border: 1px solid #DDD; 5 | } 6 | .kit-grid td, .kit-grid th { 7 | padding: 2px 5px; 8 | border: 1px solid #DDD; 9 | } 10 | .kit-grid thead th { 11 | background-color: #EEE; 12 | } 13 | .kit-grid th.kit-grid-sort { 14 | background-color: #ffff66; 15 | } 16 | 17 | .kit-grid .kit-grid-filter { 18 | margin-bottom: 3px; 19 | margin-left: 10px; 20 | } -------------------------------------------------------------------------------- /Resources/translations/messages.cs.yml: -------------------------------------------------------------------------------- 1 | kitpages_data_grid: 2 | Filter: Filtrovat 3 | Apply: Použít 4 | Reset: Resetovat 5 | No_Data_Found: Nenalezena žádná data 6 | Next: Další 7 | Previous: Předchozí 8 | -------------------------------------------------------------------------------- /Resources/translations/messages.en.yml: -------------------------------------------------------------------------------- 1 | kitpages_data_grid: 2 | Filter: Filter 3 | Apply: Apply 4 | Reset: Reset 5 | No_Data_Found: No data found 6 | Next: Next 7 | Previous: Previous 8 | -------------------------------------------------------------------------------- /Resources/translations/messages.fr.yml: -------------------------------------------------------------------------------- 1 | kitpages_data_grid: 2 | Filter: Filtrer 3 | Apply: Appliquer 4 | Reset: Annuler 5 | No_Data_Found: Aucune donnée trouvée 6 | Next: Suivant 7 | Previous: Précédent 8 | 9 | -------------------------------------------------------------------------------- /Resources/translations/messages.it.yml: -------------------------------------------------------------------------------- 1 | kitpages_data_grid: 2 | Filter: Cerca 3 | Apply: Filtra 4 | Reset: Reset 5 | No_Data_Found: Nessun dato trovato 6 | Next: Prossimo 7 | Previous: Precedente 8 | -------------------------------------------------------------------------------- /Resources/translations/messages.nl.yml: -------------------------------------------------------------------------------- 1 | kitpages_data_grid: 2 | Filter: Filter 3 | Apply: Toepassen 4 | Reset: Resetten 5 | No_Data_Found: Geen gegevens gevonden 6 | Next: Volgende 7 | Previous: Vorige 8 | -------------------------------------------------------------------------------- /Resources/translations/messages.sk.yml: -------------------------------------------------------------------------------- 1 | kitpages_data_grid: 2 | Filter: Filtrovať 3 | Apply: Použiť 4 | Reset: Resetovať 5 | No_Data_Found: Žiadne dáta nenájdená 6 | Next: Ďalší 7 | Previous: Predchádzajúca 8 | -------------------------------------------------------------------------------- /Resources/views/Grid/bootstrap-grid.html.twig: -------------------------------------------------------------------------------- 1 | {% block kit_grid_css %}{%endblock%} 2 | 3 | {% block kit_grid_main %} 4 |
5 | {% block kit_grid_selector %} 6 | {% if grid.gridConfig.selectorList != null %} 7 | 12 | {% endif %} 13 | {% endblock %} 14 | {% block kit_grid_filter %} 15 |
16 | 21 |
22 | {% endblock %} 23 | {% block kit_grid_debug %} 24 | {% if grid.debugMode %} 25 | {{ grid.dump() | raw }} 26 | {% endif %} 27 | {% endblock %} 28 | {% block kit_grid_before_table %}{% endblock %} 29 | 30 | {% block kit_grid_thead %} 31 | 32 | 33 | {{ block('kit_grid_thead_before_column') }} 34 | {% for field in grid.gridConfig.fieldList %} 35 | {% if field.visible %} 36 | 43 | {% endif %} 44 | {% endfor %} 45 | {{ block('kit_grid_thead_column') }} 46 | 47 | 48 | {% endblock %} 49 | {% block kit_grid_tbody %} 50 | 51 | {% for item in grid.itemList %} 52 | 53 | {% block kit_grid_tbody_before_column %}{%endblock%} 54 | {% for field in grid.gridConfig.fieldList %} 55 | {% if field.visible %} 56 | 63 | {% endif %} 64 | {% endfor %} 65 | {{ block('kit_grid_tbody_column') }} 66 | 67 | {% else %} 68 | 69 | 70 | 71 | {% endfor %} 72 | 73 | {% endblock %} 74 |
37 | {% if field.sortable %} 38 | {{ field.label | trans }} 39 | {% else %} 40 | {{ field.label | trans }} 41 | {% endif %} 42 |
57 | {% if field.translatable %} 58 | {{ grid.displayGridValue ( item, field) | raw | trans }} 59 | {% else %} 60 | {{ grid.displayGridValue ( item, field) | raw }} 61 | {% endif %} 62 |
{{ "kitpages_data_grid.No_Data_Found" | trans }}
75 | {{ block('kit_grid_after_table') }} 76 | {% block kit_grid_paginator %} 77 | {% embed kitpages_data_grid.paginator.default_twig with {'paginator':grid.paginator} %} 78 | {% endembed %} 79 | {% endblock %} 80 | {{ block(' kit_grid_after_paginator') }} 81 |
82 | 83 | {% block kit_grid_javascript %} 84 | {% include "KitpagesDataGridBundle:Grid:javascript.html.twig" %} 85 | {% endblock kit_grid_javascript %} 86 | {% endblock kit_grid_main %} 87 | -------------------------------------------------------------------------------- /Resources/views/Grid/bootstrap3-grid.html.twig: -------------------------------------------------------------------------------- 1 | {% block kit_grid_main %} 2 |
3 | {% block kit_grid_selector %} 4 | {% if grid.gridConfig.selectorList != null %} 5 |
6 | 13 |
14 | {% endif %} 15 | {% endblock %} 16 | {% block kit_grid_filter %} 17 |
18 | 19 | 20 |
21 | 22 | 23 | 28 | 29 |
30 | 34 | {% block kit_grid_filter_reset_value %} 35 | {{ "kitpages_data_grid.Reset" | trans }} 36 | {% endblock %} 37 | 38 |
39 | {% endblock %} 40 | {% block kit_grid_debug %} 41 | {% if grid.debugMode %} 42 | {{ grid.dump() | raw }} 43 | {% endif %} 44 | {% endblock %} 45 | {% block kit_grid_before_table %}{% endblock %} 46 | {% block kit_grid_items %} 47 | 48 | {% block kit_grid_thead %} 49 | 50 | 51 | {% block kit_grid_thead_before_column %}{% endblock %} 52 | {% for field in grid.gridConfig.fieldList %} 53 | {% if field.visible %} 54 | {% set fieldNameSlugDashed = field.fieldName|replace({'.': '-'}) %} 55 | 62 | {% endif %} 63 | {% endfor %} 64 | {% block kit_grid_thead_column %}{% endblock %} 65 | 66 | 67 | {% endblock %} 68 | {% block kit_grid_tbody %} 69 | 70 | {% for item in grid.itemList %} 71 | 72 | {% block kit_grid_tbody_before_column %}{% endblock %} 73 | {% for field in grid.gridConfig.fieldList %} 74 | {% set value = grid.displayGridValue(item, field) %} 75 | {% set fieldNameSlugDashed = field.fieldName|replace({'.': '-'}) %} 76 | {% set fieldNameSlugUnderscored = field.fieldName|replace({'.': '_'}) %} 77 | {% set cellNameBlock = 'kit_grid_cell_name_' ~ fieldNameSlugUnderscored %} 78 | {% set cellIdBlock = 'kit_grid_cell_id_' %} 79 | {% set cellIdBlock = 'kit_grid_cell_id_' ~ field.uniqueId %} 80 | {% set fieldId = field.uniqueId is not null ? field.uniqueId : fieldNameSlugDashed %} 81 | {% set cellClassBlock = 'kit_grid_cell_class_' ~ fieldId %} 82 | 83 | {% if field.visible %} 84 | 105 | {% endif %} 106 | {% endfor %} 107 | {% block kit_grid_tbody_column %}{% endblock %} 108 | 109 | {% else %} 110 | 111 | 116 | 117 | {% endfor %} 118 | 119 | {% endblock %} 120 |
56 | {% if field.sortable %} 57 | {{ field.label | trans }} 58 | {% else %} 59 | {{ field.label | trans }} 60 | {% endif %} 61 |
93 | {% if block(cellNameBlock) is defined %} 94 | {{ block(cellNameBlock) }} 95 | {% elseif field.uniqueId is not null and block(cellIdBlock) is defined %} 96 | {{ block(cellIdBlock) }} 97 | {% else %} 98 | {% if field.translatable %} 99 | {{ grid.displayGridValue(item, field) | raw | trans }} 100 | {% else %} 101 | {{ grid.displayGridValue( item, field) | raw }} 102 | {% endif %} 103 | {% endif %} 104 |
112 | {% block kit_grid_no_data %} 113 | {{ "kitpages_data_grid.No-data-found" | trans }} 114 | {% endblock %} 115 |
121 | {% endblock %} 122 | {% block kit_grid_after_table %}{% endblock %} 123 | {% block kit_grid_paginator %} 124 | {% embed kitpages_data_grid.paginator.default_twig with {'paginator':grid.paginator} %} 125 | {% endembed %} 126 | {% endblock %} 127 | {% block kit_grid_after_paginator %}{% endblock %} 128 |
129 | {% block kit_grid_javascript %} 130 | {% include "KitpagesDataGridBundle:Grid:javascript.html.twig" %} 131 | {% endblock kit_grid_javascript %} 132 | {% endblock kit_grid_main %} 133 | -------------------------------------------------------------------------------- /Resources/views/Grid/grid.html.twig: -------------------------------------------------------------------------------- 1 | {% block kit_grid_css %}{%endblock%} 2 | 3 | {% block kit_grid_main %} 4 |
5 | {% block kit_grid_selector %} 6 | {% if grid.gridConfig.selectorList != null %} 7 |
8 | {% for selector in grid.gridConfig.selectorList %} 9 | {{ selector.label }} 10 | {% endfor %} 11 |
12 | {% endif %} 13 | {% endblock %} 14 | {% block kit_grid_filter %} 15 |
16 |
17 | 18 | 19 | 20 | {{ "kitpages_data_grid.Reset" | trans }} 21 |
22 |
23 | {% endblock %} 24 | {% block kit_grid_debug %} 25 | {% if grid.debugMode %} 26 | {{ grid.dump() | raw }} 27 | {% endif %} 28 | {% endblock %} 29 | {% block kit_grid_before_table %}{% endblock %} 30 | 31 | {% block kit_grid_thead %} 32 | 33 | 34 | {% block kit_grid_thead_before_column %}{% endblock %} 35 | {% for field in grid.gridConfig.fieldList %} 36 | {% if field.visible %} 37 | 44 | {% endif %} 45 | {% endfor %} 46 | {% block kit_grid_thead_column %}{% endblock %} 47 | 48 | 49 | {% endblock %} 50 | {% block kit_grid_tbody %} 51 | 52 | {% for item in grid.itemList %} 53 | 54 | {% block kit_grid_tbody_before_column %}{%endblock%} 55 | {% for field in grid.gridConfig.fieldList %} 56 | {% if field.visible %} 57 | 64 | {% endif %} 65 | {% endfor %} 66 | {% block kit_grid_tbody_column %}{% endblock %} 67 | 68 | {% else %} 69 | 70 | 71 | 72 | {% endfor %} 73 | 74 | {% endblock %} 75 |
38 | {% if field.sortable %} 39 | {{ field.label | trans }} 40 | {% else %} 41 | {{ field.label | trans }} 42 | {% endif %} 43 |
58 | {% if field.translatable %} 59 | {{ grid.displayGridValue ( item, field) | raw | trans }} 60 | {% else %} 61 | {{ grid.displayGridValue ( item, field) | raw }} 62 | {% endif %} 63 |
{% block kit_grid_no_data %}{{ "kitpages_data_grid.No_Data_Found" | trans }}{% endblock %}
76 | {% block kit_grid_after_table %}{% endblock %} 77 | {% block kit_grid_paginator %} 78 | {% embed kitpages_data_grid.paginator.default_twig with {'paginator':grid.paginator} %} 79 | {% endembed %} 80 | {% endblock %} 81 | {{ block(' kit_grid_after_paginator') }} 82 |
83 | {% block kit_grid_javascript %} 84 | {% include "KitpagesDataGridBundle:Grid:javascript.html.twig" %} 85 | {% endblock kit_grid_javascript %} 86 | {% endblock kit_grid_main %} 87 | -------------------------------------------------------------------------------- /Resources/views/Grid/javascript.html.twig: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /Resources/views/Grid/javascript_content.html.twig: -------------------------------------------------------------------------------- 1 | (function($) { 2 | "use strict"; 3 | 4 | var insertParamInQueryString = function (key, value) { 5 | key = encodeURIComponent(key); 6 | value = encodeURIComponent(value); 7 | 8 | var query = document.location.search.substr(1), 9 | kvp = query.length > 0 ? query.split('&') : [], 10 | i = kvp.length, 11 | x; 12 | 13 | while(i--) { 14 | x = kvp[i].split('='); 15 | if (x[0] == key) { 16 | x[1] = value; 17 | kvp[i] = x.join('='); 18 | break; 19 | } 20 | } 21 | 22 | if(i < 0) { 23 | kvp[kvp.length] = [key, value].join('='); 24 | } 25 | 26 | //this will reload the page, it's likely better to store this until finished 27 | document.location.search = kvp.join('&'); 28 | }; 29 | 30 | $('#{{grid.filterFormName}}_form').submit(function(e) { 31 | e.preventDefault(); 32 | var value = $('#{{grid.filterFormName}}').val(); 33 | insertParamInQueryString('{{grid.filterFormName}}', value); 34 | }); 35 | })(jQuery); 36 | -------------------------------------------------------------------------------- /Resources/views/Paginator/bootstrap-paginator.html.twig: -------------------------------------------------------------------------------- 1 | {% if paginator.totalPageCount > 1%} 2 | {% block kit_grid_paginator %} 3 | 26 | {% endblock kit_grid_paginator %} 27 | {% endif %} 28 | -------------------------------------------------------------------------------- /Resources/views/Paginator/bootstrap3-paginator.html.twig: -------------------------------------------------------------------------------- 1 | {% if paginator.totalPageCount > 1%} 2 | {% block kit_grid_paginator %} 3 | 28 | {% endblock kit_grid_paginator %} 29 | {% endif %} 30 | -------------------------------------------------------------------------------- /Resources/views/Paginator/paginator.html.twig: -------------------------------------------------------------------------------- 1 | {% if paginator.totalPageCount > 1%} 2 | {% block kit_grid_paginator %} 3 |
4 | {% if paginator.previousButtonPage %} 5 | 6 | < 7 | 8 | {% endif %} 9 | 10 | {% for page in paginator.pageRange %} 11 | {% if page != paginator.currentPage %} 12 | 13 | {{ page }} 14 | 15 | {% else %} 16 | {{ page }} 17 | {% endif %} 18 | {% endfor %} 19 | 20 | {% if paginator.nextButtonPage %} 21 | 22 | > 23 | 24 | {% endif %} 25 |
26 | {% endblock kit_grid_paginator %} 27 | {% endif %} 28 | -------------------------------------------------------------------------------- /Tests/AppKernel.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/app/config/config_'.$this->getEnvironment().'.yml'); 23 | } 24 | 25 | public function getCacheDir() 26 | { 27 | return $this->rootDir.'/app/cache/'.$this->environment; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/BundleOrmTestCase.php: -------------------------------------------------------------------------------- 1 | setAutoExit(false); 30 | $application->run(new ArrayInput(array( 31 | 'doctrine:schema:drop', 32 | '--force' => true 33 | ))); 34 | $application->run(new ArrayInput(array( 35 | 'doctrine:schema:create' 36 | ))); 37 | $this->em = $kernel->getContainer() 38 | ->get('doctrine') 39 | ->getManager(); 40 | $this->createTestEntities(); 41 | return $this->em; 42 | } 43 | 44 | public function getConnection() 45 | { 46 | $pdo = $this->em->getConnection(); 47 | return $this->createDefaultDBConnection($pdo, ':memory:'); 48 | } 49 | 50 | public function getEntityManager() 51 | { 52 | return $this->createEntityManager(); 53 | } 54 | 55 | /** 56 | * @throws \Doctrine\ORM\ORMException 57 | * @throws \Doctrine\ORM\OptimisticLockException 58 | */ 59 | public function createTestEntities() 60 | { 61 | // 62 | /* 63 | * 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | */ 78 | $this->em->persist($nodeAssoc = (new NodeAssoc()) 79 | ->setId(1) 80 | ->setName('test assoc') 81 | ); 82 | 83 | $node1 = (new Node()) 84 | ->setId(1) 85 | ->setContent('foobar') 86 | ->setUser('joe') 87 | ->setCreatedAt(new \DateTime('2010-04-24 17:15:23')) 88 | ->setParentId('') 89 | ; 90 | $this->em->persist($node1); 91 | 92 | $node2 = (new Node()) 93 | ->setId(2) 94 | ->setContent('I like it!') 95 | ->setUser('toto') 96 | ->setCreatedAt(new \DateTime('2010-04-26 12:14:20')) 97 | ->setParentId('1') 98 | ->setMainNode($node1) 99 | ; 100 | $this->em->persist($node2); 101 | 102 | $node3 = (new Node()) 103 | ->setId(3) 104 | ->setContent('I like it!') 105 | ->setUser('toto') 106 | ->setCreatedAt(new \DateTime('2010-04-27 12:14:20')) 107 | ->setParentId('1') 108 | ->setMainNode($node1) 109 | ; 110 | $this->em->persist($node3); 111 | 112 | $node4 = (new Node()) 113 | ->setId(4) 114 | ->setContent('Hello bob') 115 | ->setUser('toto') 116 | ->setCreatedAt(new \DateTime('2010-04-28 12:14:20')) 117 | ->setParentId('2') 118 | ; 119 | $this->em->persist($node4); 120 | 121 | $node5 = (new Node()) 122 | ->setId(5) 123 | ->setContent('I like it!') 124 | ->setUser('toto') 125 | ->setCreatedAt(new \DateTime('2010-04-29 12:14:20')) 126 | ; 127 | $this->em->persist($node5); 128 | 129 | $node6 = (new Node()) 130 | ->setId(6) 131 | ->setContent('Hello robert') 132 | ->setUser('foouser') 133 | ->setCreatedAt(new \DateTime('2010-04-26 12:14:20')) 134 | ; 135 | $this->em->persist($node6); 136 | 137 | $node7 = (new Node()) 138 | ->setId(7) 139 | ->setContent('I like it!') 140 | ->setUser('fös') 141 | ->setCreatedAt(new \DateTime('2010-04-17 12:14:20')) 142 | ; 143 | $this->em->persist($node7); 144 | 145 | $this->em->persist($node8 = (new Node()) 146 | ->setId(8) 147 | ->setContent('Hello foo') 148 | ->setUser('foouser') 149 | ->setCreatedAt(new \DateTime('2010-04-18 12:14:20')) 150 | ); 151 | 152 | $this->em->persist($node9 = (new Node()) 153 | ->setId(9) 154 | ->setContent('I fös it!') 155 | ->setUser('foo') 156 | ->setCreatedAt(new \DateTime('2010-04-19 12:14:20')) 157 | ); 158 | 159 | $this->em->persist($node10 = (new Node()) 160 | ->setId(10) 161 | ->setContent('I like it!') 162 | ->setUser('bar') 163 | ->setCreatedAt(new \DateTime('2010-04-20 12:14:20')) 164 | ); 165 | 166 | $this->em->persist($node11 = (new Node()) 167 | ->setId(11) 168 | ->setContent('I like it!') 169 | ->setUser('toto') 170 | ->setCreatedAt(new \DateTime('2010-04-21 12:14:20')) 171 | ->setMainNode($node1) 172 | ->setAssoc($nodeAssoc) 173 | ); 174 | 175 | // 176 | // 177 | // 178 | // 179 | // 180 | 181 | $this->em->flush(); 182 | 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /Tests/Fonctionnal/ConfigTest.php: -------------------------------------------------------------------------------- 1 | getContainer()->getParameter('kitpages_data_grid.grid'); 14 | $this->assertEquals("KitpagesDataGridBundle:Grid:grid-standard.html.twig", $gridParameters["default_twig"]); 15 | $this->assertEquals('\Kitpages\DataGridBundle\Hydrators\DataGridHydrator', $gridParameters["hydrator_class"]); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Fonctionnal/TwigExtensionTest.php: -------------------------------------------------------------------------------- 1 | getContainer()->get('templating'); 20 | $this->assertEquals( 21 | "KitpagesDataGridBundle:Grid:grid-standard.html.twig", 22 | $templating->render("globals.html.twig") 23 | ); 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Grid/ConversionSubscriber.php: -------------------------------------------------------------------------------- 1 | 'onConversion', 21 | KitpagesDataGridEvents::AFTER_DISPLAY_GRID_VALUE_CONVERSION => 'afterConversion' 22 | ); 23 | } 24 | 25 | public function setIsDefaultPrevented($val) { 26 | $this->isDefaultPrevented = $val; 27 | } 28 | 29 | public function setAfterActivated($val) { 30 | $this->afterActivated = $val; 31 | } 32 | 33 | public function onConversion(DataGridEvent $event) 34 | { 35 | if ($this->isDefaultPrevented) { 36 | $event->preventDefault(); 37 | $event->set("returnValue", $event->get("field")->getFieldName().';preventDefault;'.$event->get("value")); 38 | } 39 | else { 40 | $row = $event->get("row"); 41 | $event->set("value", $row["node.id"].';'.$event->get("value")); 42 | } 43 | 44 | } 45 | public function afterConversion(DataGridEvent $event) 46 | { 47 | if (!$this->afterActivated) { 48 | return; 49 | } 50 | $event->set("returnValue", "after;".$event->get("returnValue")); 51 | } 52 | } -------------------------------------------------------------------------------- /Tests/Grid/CustomGrid.php: -------------------------------------------------------------------------------- 1 | myCustomParamter; 20 | } 21 | 22 | /** 23 | * @param mixed $myCustomParamter 24 | */ 25 | public function setMyCustomParamter($myCustomParamter) 26 | { 27 | $this->myCustomParamter = $myCustomParamter; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Tests/Grid/FieldTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($field->getFieldName(), "id"); 13 | 14 | $field = new Field("phone", array( 15 | "label" => "Phone", 16 | "sortable" => true, 17 | "filterable" => true, 18 | "visible" => false, 19 | "formatValueCallback" => function($value) { return strtoupper($value);}, 20 | "autoEscape" => true, 21 | "translatable" => true, 22 | 'category' => "my.category", 23 | 'nullIfNotExists' => true 24 | )); 25 | $this->assertEquals("Phone", $field->getLabel()); 26 | $this->assertTrue($field->getSortable()); 27 | $this->assertTrue($field->getFilterable()); 28 | $this->assertFalse($field->getVisible()); 29 | $this->assertNotNull($field->getFormatValueCallback()); 30 | $this->assertTrue($field->getFilterable()); 31 | $this->assertEquals("my.category", $field->getCategory()); 32 | $this->assertTrue( $field->getNullIfNotExists()); 33 | 34 | $field = new Field("test"); 35 | $this->assertFalse( $field->getNullIfNotExists()); 36 | } 37 | 38 | public function testWrongParameterConstructor() 39 | { 40 | try { 41 | $field = new Field("phone", array( 42 | "foo" => "bar" 43 | )); 44 | $this->fail(); 45 | } catch (\InvalidArgumentException $e) { 46 | $this->assertTrue(true); 47 | } 48 | } 49 | 50 | public function testTagSystem() 51 | { 52 | $field = new Field( 53 | 'phone', 54 | array( 55 | 'label' => 'Phone', 56 | 'sortable' => true, 57 | 'filterable' => true, 58 | 'visible' => false, 59 | 'formatValueCallback' => function($value) { return strtoupper($value);}, 60 | 'autoEscape' => true, 61 | 'translatable' => true, 62 | 'category' => 'my.category', 63 | 'nullIfNotExists' => true 64 | ), 65 | array ( 66 | 'foo', 67 | 'bar' 68 | ) 69 | ); 70 | $this->assertTrue($field->hasTag('foo')); 71 | $this->assertFalse($field->hasTag('tutu')); 72 | $this->assertEquals(array('foo', 'bar'), $field->getTagList()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tests/Grid/GridConfigTest.php: -------------------------------------------------------------------------------- 1 | gridConfig = new GridConfig(); 19 | 20 | parent::setUp(); 21 | } 22 | 23 | public function testCanAddAndRetrieveSingleFieldLegacySyntax() 24 | { 25 | $fieldName = uniqid(); 26 | 27 | $this->gridConfig->addField(new Field($fieldName)); 28 | 29 | $this->assertInstanceOf('Kitpages\DataGridBundle\Grid\Field', $this->gridConfig->getFieldByName($fieldName)); 30 | } 31 | 32 | public function testCanAddAndRetrieveSingleFieldNewSyntax() 33 | { 34 | $fieldName = uniqid(); 35 | $this->gridConfig->addField($fieldName, array( 36 | 'label' => $fieldName, 37 | )); 38 | 39 | $this->assertInstanceOf(Field::class, $this->gridConfig->getFieldByName($fieldName)); 40 | $this->assertEquals($fieldName, $this->gridConfig->getFieldByName($fieldName)->getLabel()); 41 | } 42 | 43 | public function testAddFieldWrongArgumentType() 44 | { 45 | $arguments = array(true, 1, 2.2, array(), new \stdClass(), null, function () { }, ); 46 | 47 | foreach ($arguments as $argument) { 48 | try { 49 | $this->gridConfig->addField($argument); 50 | $this->fail(); 51 | } catch (\InvalidArgumentException $e) { 52 | $this->assertTrue(true); 53 | } 54 | } 55 | } 56 | 57 | public function testTags() 58 | { 59 | $this->gridConfig->addField( 60 | new Field('f1', array(), array('foo', 'bar')) 61 | ); 62 | $this->gridConfig->addField( 63 | new Field('f2', array(), array('bar')) 64 | ); 65 | $this->gridConfig->addField( 66 | new Field('f3', array(), array('foo', 'bar', 'biz')) 67 | ); 68 | $this->assertCount(1, $this->gridConfig->getFieldListByTag('biz')); 69 | $this->assertCount(2, $this->gridConfig->getFieldListByTag('foo')); 70 | $this->assertCount(3, $this->gridConfig->getFieldListByTag('bar')); 71 | $this->assertCount(0, $this->gridConfig->getFieldListByTag('gloubi')); 72 | $field = $this->gridConfig->getFieldListByTag('biz')[0]; 73 | $this->assertEquals('f3', $field->getFieldName()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tests/Grid/GridManagerTest.php: -------------------------------------------------------------------------------- 1 | setCountFieldName("node.id"); 35 | $paginatorConfig->setItemCountInPage(3); 36 | 37 | $gridConfig = new GridConfig(); 38 | $gridConfig->setPaginatorConfig($paginatorConfig); 39 | $gridConfig->setCountFieldName("node.id"); 40 | $gridConfig 41 | ->addField(new Field("node.id")) 42 | ->addField(new Field("node.createdAt", 43 | array( 44 | "sortable"=>true, 45 | "formatValueCallback" => function ($value) { return $value->format("Y/m/d"); } 46 | ) 47 | )); 48 | $gridConfig->addField(new Field("node.content", 49 | array( 50 | "formatValueCallback" => function ($value, $row) { return $value.":".$row["createdAt"]->format("Y"); } 51 | ) 52 | )); 53 | $gridConfig->addField(new Field("node.user", array( 54 | "filterable"=> true 55 | ))); 56 | return $gridConfig; 57 | } 58 | 59 | public function getGridManager() 60 | { 61 | // create EventDispatcher mock 62 | $service = new EventDispatcher(); 63 | $parameters = array( 64 | "default_twig" => "toto.html.twig", 65 | "item_count_in_page" => 50, 66 | "visible_page_count_in_paginator" => 5 67 | ); 68 | 69 | // normalizer 70 | $normalizer = new StandardNormalizer(); 71 | 72 | $gridManager = new GridManager($service, new PaginatorManager($service, $parameters), $normalizer, '\Kitpages\DataGridBundle\Hydrators\DataGridHydrator'); 73 | return $gridManager; 74 | 75 | } 76 | 77 | 78 | public function testGridBasic() 79 | { 80 | // create Request mock (ok this is not a mock....) 81 | $_SERVER["REQUEST_URI"] = "/foo"; 82 | $request = new \Symfony\Component\HttpFoundation\Request(); 83 | // create gridManager instance 84 | $gridManager = $this->getGridManager(); 85 | 86 | // create queryBuilder 87 | $em = $this->getEntityManager(); 88 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 89 | $queryBuilder = $repository->createQueryBuilder("node"); 90 | $queryBuilder->select("node"); 91 | 92 | $gridConfig = new GridConfig(); 93 | $gridConfig->setCountFieldName("node.id"); 94 | $gridConfig->addField(new Field("node.createdAt", 95 | array( 96 | "sortable"=>true, 97 | "formatValueCallback" => function ($value) { return $value->format("Y/m/d"); } 98 | ) 99 | )); 100 | $gridConfig->addField(new Field("node.content", 101 | array( 102 | "formatValueCallback" => function ($value, $row) { return $value.":".$row["node.createdAt"]->format("Y"); } 103 | ) 104 | )); 105 | 106 | // get paginator 107 | $gridConfig->setQueryBuilder($queryBuilder); 108 | $grid = $gridManager->getGrid($gridConfig, $request); 109 | $paginator = $grid->getPaginator(); 110 | 111 | // tests paginator 112 | $this->assertEquals(11, $paginator->getTotalItemCount()); 113 | 114 | // grid test 115 | $itemList = $grid->getItemList(); 116 | $this->assertEquals( 11 , count($itemList)); 117 | $this->assertEquals( 1 , $itemList[0]["node.id"]); 118 | // simple callback 119 | $this->assertEquals( "2010/04/24" , $grid->displayGridValue($itemList[0], $gridConfig->getFieldByName("node.createdAt"))); 120 | $this->assertEquals( "foobar:2010" , $grid->displayGridValue($itemList[0], $gridConfig->getFieldByName("node.content"))); 121 | 122 | // test $grid given in parameter 123 | $myCustomGrid = new CustomGrid(); 124 | $myCustomGrid->setMyCustomParamter('my parameter value'); 125 | $grid = $gridManager->getGrid($gridConfig, $request, $myCustomGrid); 126 | $paginator = $myCustomGrid->getPaginator(); 127 | 128 | // tests paginator 129 | $this->assertEquals(11, $paginator->getTotalItemCount()); 130 | 131 | // grid test 132 | $itemList = $myCustomGrid->getItemList(); 133 | $this->assertEquals( 11 , count($itemList)); 134 | $this->assertEquals( 1 , $itemList[0]["node.id"]); 135 | // custom grid test 136 | $this->assertEquals('my parameter value', $grid->getMyCustomParamter()); 137 | // simple callback 138 | $this->assertEquals( "2010/04/24" , $myCustomGrid->displayGridValue($itemList[0], $gridConfig->getFieldByName("node.createdAt"))); 139 | $this->assertEquals( "foobar:2010" , $myCustomGrid->displayGridValue($itemList[0], $gridConfig->getFieldByName("node.content"))); 140 | 141 | } 142 | 143 | public function testGridRelation() 144 | { 145 | // create Request mock (ok this is not a mock....) 146 | $request = new \Symfony\Component\HttpFoundation\Request(); 147 | $request->query->set("kitdg_paginator_paginator_currentPage", 2); 148 | // create gridManager instance 149 | $gridManager = $this->getGridManager(); 150 | 151 | // create queryBuilder 152 | $em = $this->getEntityManager(); 153 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 154 | $queryBuilder = $repository->createQueryBuilder("node"); 155 | $queryBuilder->select("node, node.id*2 as doubleId"); 156 | 157 | $gridConfig = $this->initGridConfig(); 158 | $gridConfig->addField(new Field("doubleId")); 159 | 160 | // get paginator 161 | $gridConfig->setQueryBuilder($queryBuilder); 162 | $grid = $gridManager->getGrid($gridConfig, $request); 163 | $paginator = $grid->getPaginator(); 164 | 165 | // tests paginator 166 | $this->assertEquals(11, $paginator->getTotalItemCount()); 167 | 168 | // grid test 169 | $itemList = $grid->getItemList(); 170 | $this->assertEquals( 3 , count($itemList)); 171 | $this->assertEquals( "paginator", $paginator->getPaginatorConfig()->getName()); 172 | $this->assertEquals( 2, $paginator->getCurrentPage() ); 173 | $this->assertEquals( 1, $paginator->getPreviousButtonPage()); 174 | $this->assertEquals( 3, $paginator->getNextButtonPage()); 175 | $this->assertEquals( 10 , $itemList[1]["doubleId"]); 176 | // simple callback 177 | } 178 | 179 | /* 180 | * Test added following this issue : https://github.com/kitpages/KitpagesDataGridBundle/issues/18 181 | * But I can't reproduce that bug... 182 | * TODO: go back here later and reproduce this issue... 183 | */ 184 | /** 185 | * @group testDQL 186 | */ 187 | public function testGridLeftJoin() 188 | { 189 | // create Request mock (ok this is not a mock....) 190 | $request = new \Symfony\Component\HttpFoundation\Request(); 191 | $request->query->set("kitdg_paginator_paginator_currentPage", 2); 192 | // create gridManager instance 193 | $gridManager = $this->getGridManager(); 194 | 195 | // create queryBuilder 196 | $em = $this->getEntityManager(); 197 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 198 | $queryBuilder = $repository->createQueryBuilder("node"); 199 | $queryBuilder->select("DISTINCT node.id as gouglou, node, count(sn.id) as intervals") 200 | ->leftJoin('node.subNodeList', 'sn') 201 | ->groupBy('node.id') 202 | ->orderBy('node.id', 'ASC'); 203 | 204 | $gridConfig = $this->initGridConfig(); 205 | $gridConfig->addField(new Field("doubleId")); 206 | 207 | // get paginator 208 | $gridConfig->setQueryBuilder($queryBuilder); 209 | $grid = $gridManager->getGrid( $gridConfig, $request); 210 | $paginator = $grid->getPaginator(); 211 | 212 | // tests paginator 213 | $this->assertEquals(11, $paginator->getTotalItemCount()); 214 | 215 | // grid test 216 | $itemList = $grid->getItemList(); 217 | $this->assertEquals( 3 , count($itemList)); 218 | $this->assertEquals( "paginator", $paginator->getPaginatorConfig()->getName()); 219 | $this->assertEquals( 2, $paginator->getCurrentPage() ); 220 | $this->assertEquals( 1, $paginator->getPreviousButtonPage()); 221 | $this->assertEquals( 3, $paginator->getNextButtonPage()); 222 | // simple callback 223 | } 224 | 225 | /** 226 | */ 227 | public function testGridLeftJoinGroupBy() 228 | { 229 | // create Request mock (ok this is not a mock....) 230 | $request = new \Symfony\Component\HttpFoundation\Request(); 231 | $request->query->set("kitdg_paginator_paginator_currentPage", 1); 232 | // create gridManager instance 233 | $gridManager = $this->getGridManager(); 234 | 235 | $em = $this->getEntityManager(); 236 | 237 | // create queryBuilder 238 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 239 | $queryBuilder = $repository->createQueryBuilder("node"); 240 | $queryBuilder->select("node, assoc, count(sn.id) as intervals") 241 | ->leftJoin('node.assoc', 'assoc') 242 | ->leftJoin('node.subNodeList', 'sn') 243 | ->groupBy('node.id') 244 | ->where('node.id = 11') 245 | ->orderBy('node.id', 'ASC'); 246 | 247 | $gridConfig = $this->initGridConfig(); 248 | $gridConfig->addField(new Field("assoc.id")); 249 | 250 | // get paginator 251 | $gridConfig->setQueryBuilder($queryBuilder); 252 | $grid = $gridManager->getGrid( $gridConfig, $request); 253 | 254 | // grid test 255 | $itemList = $grid->getItemList(); 256 | 257 | $this->assertEquals(1 , count($itemList)); 258 | 259 | $expected = array( 260 | 'node.content' => 'I like it!', 261 | 'node.user' => "toto", 262 | 'node.parentId' => 0, 263 | 'node.id' => 11, 264 | 'assoc.id' => 1, 265 | 'assoc.name' => 'test assoc', 266 | 'intervals' => '0', 267 | 'node.createdAt' => new \DateTime('2010-04-21 12:14:20') 268 | ); 269 | 270 | $this->assertEquals($expected, $itemList[0]); 271 | 272 | } 273 | 274 | 275 | public function testGridLeftJoinWithoutGroupBy() 276 | { 277 | // create Request mock (ok this is not a mock....) 278 | $request = new \Symfony\Component\HttpFoundation\Request(); 279 | // create gridManager instance 280 | $gridManager = $this->getGridManager(); 281 | 282 | // create queryBuilder 283 | /** @var EntityManager $em */ 284 | $em = $this->getEntityManager(); 285 | // $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 286 | // $queryBuilder = $repository->createQueryBuilder("node"); 287 | $queryBuilder = $em->createQueryBuilder(); 288 | $queryBuilder->select("node, mn") 289 | ->from('Kitpages\DataGridBundle\Tests\TestEntities\Node', 'node') 290 | ->leftJoin('node.mainNode', 'mn') 291 | ->orderBy('node.id', 'ASC') 292 | ; 293 | 294 | $gridConfig = $this->initGridConfig(); 295 | $nodeIdField = new Field("node.id"); 296 | $gridConfig->addField($nodeIdField); 297 | $mainNodeIdField = new Field("mn.id"); 298 | $gridConfig->addField($mainNodeIdField); 299 | 300 | // get paginator 301 | $gridConfig->setQueryBuilder($queryBuilder); 302 | $grid = $gridManager->getGrid($gridConfig, $request); 303 | $paginator = $grid->getPaginator(); 304 | 305 | // tests paginator 306 | $this->assertEquals(11, $paginator->getTotalItemCount()); 307 | 308 | // grid test 309 | $itemList = $grid->getItemList(); 310 | $this->assertEquals( 3 , count($itemList)); 311 | 312 | $cnt = 0; 313 | foreach($itemList as $item) { 314 | $cnt ++; 315 | $nodeId = $grid->displayGridValue($item, $nodeIdField); 316 | $this->assertEquals($cnt, $nodeId); 317 | 318 | $mainNodeId = $grid->displayGridValue($item, $mainNodeIdField); 319 | if ($cnt == 1) { 320 | // the first node should not avec a mainNodeId, see 3 first nodes of fixtures 321 | $this->assertEquals(null, $mainNodeId); 322 | } else { 323 | $this->assertEquals(1, $mainNodeId); 324 | } 325 | } 326 | 327 | $mainNodeIdField->setNullIfNotExists(true); 328 | $cnt = 0; 329 | foreach($itemList as $item) { 330 | $cnt ++; 331 | $nodeId = $grid->displayGridValue($item, $nodeIdField); 332 | $this->assertEquals($cnt, $nodeId); 333 | $mainNodeId = $grid->displayGridValue($item, $mainNodeIdField); 334 | if ($cnt == 1) { 335 | $this->assertTrue(is_null($mainNodeId)); 336 | } else { 337 | $this->assertEquals(1, $mainNodeId); 338 | } 339 | } 340 | } 341 | 342 | public function testGridFilter() 343 | { 344 | // create Request mock (ok this is not a mock....) 345 | $request = new \Symfony\Component\HttpFoundation\Request(); 346 | $request->query->set("kitdg_grid_grid_filter", "foouser"); 347 | $request->query->set("kitdg_grid_grid_sort_field", "node.createdAt"); 348 | $request->query->set("kitdg_paginator_paginator_currentPage", 2); 349 | // create gridManager instance 350 | $gridManager = $this->getGridManager(); 351 | 352 | // create queryBuilder 353 | $em = $this->getEntityManager(); 354 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 355 | $queryBuilder = $repository->createQueryBuilder("node"); 356 | $queryBuilder->select("node"); 357 | 358 | $gridConfig = $this->initGridConfig(); 359 | 360 | // get paginator 361 | $gridConfig->setQueryBuilder($queryBuilder); 362 | $grid = $gridManager->getGrid($gridConfig, $request); 363 | $paginator = $grid->getPaginator(); 364 | 365 | // tests paginator 366 | $this->assertEquals(2, $paginator->getTotalItemCount()); 367 | 368 | // grid test 369 | $itemList = $grid->getItemList(); 370 | $this->assertEquals( 2 , count($itemList)); 371 | $this->assertEquals( 8 , $itemList[0]["node.id"]); 372 | $this->assertEquals( 1 , $paginator->getCurrentPage()); 373 | 374 | $request->query->set("kitdg_grid_grid_sort_field", "node.user"); 375 | $gridConfig->setQueryBuilder($queryBuilder); 376 | $grid = $gridManager->getGrid($gridConfig, $request); 377 | $itemList = $grid->getItemList(); 378 | $this->assertEquals( 6 , $itemList[0]["node.id"]); 379 | 380 | $request->query->set("kitdg_grid_grid_filter", "foo"); 381 | $gridConfig->setQueryBuilder($queryBuilder); 382 | $grid = $gridManager->getGrid($gridConfig, $request); 383 | $itemList = $grid->getItemList(); 384 | $this->assertEquals( 3 , count($itemList)); 385 | 386 | } 387 | 388 | public function testGridUtf8Filter() 389 | { 390 | // create Request mock (ok this is not a mock....) 391 | $request = new \Symfony\Component\HttpFoundation\Request(); 392 | $request->query->set("kitdg_grid_grid_filter", "foouser"); 393 | $request->query->set("kitdg_grid_grid_sort_field", "node.createdAt"); 394 | $request->query->set("kitdg_paginator_paginator_currentPage", 2); 395 | // create gridManager instance 396 | $gridManager = $this->getGridManager(); 397 | 398 | // create queryBuilder 399 | $em = $this->getEntityManager(); 400 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 401 | $queryBuilder = $repository->createQueryBuilder("node"); 402 | $queryBuilder->select("node"); 403 | 404 | $gridConfig = $this->initGridConfig(); 405 | 406 | // get paginator 407 | $gridConfig->setQueryBuilder($queryBuilder); 408 | $grid = $gridManager->getGrid($gridConfig, $request); 409 | $paginator = $grid->getPaginator(); 410 | 411 | // tests paginator 412 | $this->assertEquals(2, $paginator->getTotalItemCount()); 413 | 414 | // grid test 415 | $itemList = $grid->getItemList(); 416 | $this->assertEquals( 2 , count($itemList)); 417 | $this->assertEquals( 8 , $itemList[0]["node.id"]); 418 | $this->assertEquals( 1 , $paginator->getCurrentPage()); 419 | 420 | $request->query->set("kitdg_grid_grid_filter", "fös"); 421 | $gridConfig->setQueryBuilder($queryBuilder); 422 | $grid = $gridManager->getGrid($gridConfig, $request); 423 | $itemList = $grid->getItemList(); 424 | $this->assertEquals( 1 , count($itemList)); 425 | 426 | } 427 | 428 | public function testGridSelector() 429 | { 430 | // create Request mock (ok this is not a mock....) 431 | $request = new \Symfony\Component\HttpFoundation\Request(); 432 | $request->query->set("kitdg_grid_grid_selector_field", "node.user"); 433 | $request->query->set("kitdg_grid_grid_selector_value", "foouser"); 434 | $request->query->set("kitdg_grid_grid_sort_field", "node.createdAt"); 435 | $request->query->set("kitdg_paginator_paginator_currentPage", 2); 436 | // create gridManager instance 437 | $gridManager = $this->getGridManager(); 438 | 439 | // create queryBuilder 440 | $em = $this->getEntityManager(); 441 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 442 | $queryBuilder = $repository->createQueryBuilder("node"); 443 | $queryBuilder->select("node"); 444 | 445 | $gridConfig = $this->initGridConfig(); 446 | 447 | // get paginator 448 | $gridConfig->setQueryBuilder($queryBuilder); 449 | $grid = $gridManager->getGrid($gridConfig, $request); 450 | $paginator = $grid->getPaginator(); 451 | 452 | // tests paginator 453 | $this->assertEquals(2, $paginator->getTotalItemCount()); 454 | 455 | // grid test 456 | $itemList = $grid->getItemList(); 457 | $this->assertEquals( 2 , count($itemList)); 458 | $this->assertEquals( 8 , $itemList[0]["node.id"]); 459 | $this->assertEquals( 1 , $paginator->getCurrentPage()); 460 | 461 | $request->query->set("kitdg_grid_grid_sort_field", "node.user"); 462 | $gridConfig->setQueryBuilder($queryBuilder); 463 | $grid = $gridManager->getGrid($gridConfig, $request); 464 | $itemList = $grid->getItemList(); 465 | $this->assertEquals( 6 , $itemList[0]["node.id"]); 466 | 467 | $request->query->set("kitdg_grid_grid_selector_value", "5"); 468 | $gridConfig->setQueryBuilder($queryBuilder); 469 | $grid = $gridManager->getGrid($gridConfig, $request); 470 | $itemList = $grid->getItemList(); 471 | $this->assertEquals( 0 , count($itemList)); 472 | 473 | } 474 | 475 | } 476 | -------------------------------------------------------------------------------- /Tests/Grid/GridTest.php: -------------------------------------------------------------------------------- 1 | grid = new Grid(); 21 | $this->grid->setDispatcher($dispatcher); 22 | $this->now = new \DateTime(); 23 | $this->row = array( 24 | "node.id" => 12, 25 | "company.name" => "Test Company", 26 | 'node.html' => "", 27 | "node.createdAt" => $this->now 28 | ); 29 | $this->mockField = $this->getMockBuilder('Kitpages\DataGridBundle\Grid\Field') 30 | ->disableOriginalConstructor() 31 | ->getMock(); 32 | } 33 | 34 | public function testConstructor() 35 | { 36 | $grid = new Grid(); 37 | $this->assertTrue($grid instanceof Grid); 38 | } 39 | 40 | public function testDisplayGridValue() 41 | { 42 | $this->mockField->expects($this->any()) 43 | ->method('getAutoEscape') 44 | ->will($this->returnValue(true)); 45 | 46 | $this->mockField->expects($this->any()) 47 | ->method('getFieldName') 48 | ->will($this->onConsecutiveCalls('company.name', 'node.createdAt', 'node.id', 'node.html')); 49 | 50 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 51 | $this->assertEquals("Test Company", $displayValue); 52 | 53 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 54 | $this->assertEquals($this->now->format("Y-m-d H:i:s"), $displayValue); 55 | 56 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 57 | $this->assertEquals(12, $displayValue); 58 | 59 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 60 | $this->assertEquals('<a>', $displayValue); 61 | } 62 | 63 | public function testDisplayGridValueAutoEscapeFalse() 64 | { 65 | $this->mockField->expects($this->any()) 66 | ->method('getAutoEscape') 67 | ->will($this->returnValue(false)); 68 | 69 | $this->mockField->expects($this->any()) 70 | ->method('getFieldName') 71 | ->will($this->onConsecutiveCalls('node.html')); 72 | 73 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 74 | $this->assertEquals('', $displayValue); 75 | } 76 | 77 | public function testDisplayGridValueCallbackSimple() 78 | { 79 | $this->mockField->expects($this->any()) 80 | ->method('getAutoEscape') 81 | ->will($this->returnValue(false)); 82 | $this->mockField->expects($this->any()) 83 | ->method('getFormatValueCallback') 84 | ->will($this->returnValue(function($value){ return strtoupper($value); })); 85 | 86 | $this->mockField->expects($this->any()) 87 | ->method('getFieldName') 88 | ->will($this->onConsecutiveCalls('company.name', 'node.html')); 89 | 90 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 91 | $this->assertEquals('TEST COMPANY', $displayValue); 92 | 93 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 94 | $this->assertEquals('', $displayValue); 95 | } 96 | 97 | public function testDisplayGridValueCallbackExtended() 98 | { 99 | $this->mockField->expects($this->any()) 100 | ->method('getAutoEscape') 101 | ->will($this->returnValue(true)); 102 | $this->mockField->expects($this->any()) 103 | ->method('getFormatValueCallback') 104 | ->will($this->returnValue(function($value, $row) { return strtoupper($value).';'.$row["node.id"]; })); 105 | 106 | $this->mockField->expects($this->any()) 107 | ->method('getFieldName') 108 | ->will($this->onConsecutiveCalls('company.name', 'node.html')); 109 | 110 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 111 | $this->assertEquals('TEST COMPANY;12', $displayValue); 112 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 113 | $this->assertEquals('<A>;12', $displayValue); 114 | } 115 | 116 | public function testDisplayGridValueConvertionEvent() 117 | { 118 | $this->mockField->expects($this->any()) 119 | ->method('getAutoEscape') 120 | ->will($this->returnValue(true)); 121 | 122 | $this->mockField->expects($this->any()) 123 | ->method('getFieldName') 124 | ->will($this->returnValue('company.name')); 125 | 126 | $subscriber = new ConversionSubscriber(); 127 | $dispatcher = new EventDispatcher(); 128 | $dispatcher->addSubscriber($subscriber); 129 | $this->grid->setDispatcher($dispatcher); 130 | 131 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 132 | $this->assertEquals('12;Test Company', $displayValue); 133 | 134 | $subscriber->setIsDefaultPrevented(true); 135 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 136 | $this->assertEquals('company.name;preventDefault;Test Company', $displayValue); 137 | 138 | $subscriber->setAfterActivated(true); 139 | $displayValue = $this->grid->displayGridValue($this->row, $this->mockField); 140 | $this->assertEquals('after;company.name;preventDefault;Test Company', $displayValue); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Tests/Hydrators/DataGridHydratorTest.php: -------------------------------------------------------------------------------- 1 | addEntityResult('Kitpages\DataGridBundle\Tests\TestEntities\Node', 'node'); 19 | $rsm->addEntityResult('Kitpages\DataGridBundle\Tests\TestEntities\NodeAssoc', 'assoc'); 20 | $rsm->addFieldResult('node', 'id1', 'id'); 21 | $rsm->addFieldResult('node', 'user2', 'user'); 22 | $rsm->addFieldResult('node', 'content3', 'content'); 23 | $rsm->addFieldResult('node', 'parent_id4', 'parentId'); 24 | $rsm->addFieldResult('node', 'created_at5', 'createdAt'); 25 | $rsm->addFieldResult('assoc', 'id6', 'id'); 26 | $rsm->addFieldResult('assoc', 'name7', 'name'); 27 | $rsm->addScalarResult('intervals8', 'intervals'); 28 | 29 | // Faked result set 30 | $resultSet = array( 31 | array( 32 | 'id1' => 11, 33 | 'user2' => 'toto', 34 | 'content3' => 'I like it!', 35 | 'parent_id4' => 0, 36 | 'created_at5' => new \DateTime('2010-04-21 12:14:20'), 37 | 'id6' => 1, 38 | 'name7' => 'tutu', 39 | 'intervals8' => 10 40 | ) 41 | ); 42 | 43 | $stmt = new HydratorMockStatement($resultSet); 44 | $hydrator = new DataGridHydrator($this->getEntityManager()); 45 | 46 | $result = $hydrator->hydrateAll($stmt, $rsm); 47 | 48 | $this->assertTrue(is_array($result)); 49 | $this->assertEquals(1, count($result)); 50 | 51 | $this->assertEquals(11, $result[0]['node.id']); 52 | $this->assertEquals('toto', $result[0]['node.user']); 53 | $this->assertEquals('I like it!', $result[0]['node.content']); 54 | $this->assertEquals(0, $result[0]['node.parentId']); 55 | $this->assertEquals(new \DateTime('2010-04-21 12:14:20'), $result[0]['node.createdAt']); 56 | $this->assertEquals(1, $result[0]['assoc.id']); 57 | $this->assertEquals('tutu', $result[0]['assoc.name']); 58 | $this->assertEquals(10, $result[0]['intervals']); 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/Mocks/HydratorMockStatement.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class HydratorMockStatement implements \IteratorAggregate, \Doctrine\DBAL\Driver\Statement 12 | { 13 | /** 14 | * @var array 15 | */ 16 | private $_resultSet; 17 | 18 | /** 19 | * Creates a new mock statement that will serve the provided fake result set to clients. 20 | * 21 | * @param array $resultSet The faked SQL result set. 22 | */ 23 | public function __construct(array $resultSet) 24 | { 25 | $this->_resultSet = $resultSet; 26 | } 27 | 28 | /** 29 | * Fetches all rows from the result set. 30 | * 31 | * @param int|null $fetchStyle 32 | * @param int|null $columnIndex 33 | * @param array|null $ctorArgs 34 | * 35 | * @return array 36 | */ 37 | public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null) 38 | { 39 | return $this->_resultSet; 40 | } 41 | 42 | /** 43 | * {@inheritdoc} 44 | */ 45 | public function fetchColumn($columnNumber = 0) 46 | { 47 | $row = current($this->_resultSet); 48 | if ( ! is_array($row)) return false; 49 | $val = array_shift($row); 50 | return $val !== null ? $val : false; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0) 57 | { 58 | $current = current($this->_resultSet); 59 | next($this->_resultSet); 60 | return $current; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function closeCursor() 67 | { 68 | return true; 69 | } 70 | 71 | /** 72 | * {@inheritdoc} 73 | */ 74 | public function bindValue($param, $value, $type = null) 75 | { 76 | } 77 | 78 | /** 79 | * {@inheritdoc} 80 | */ 81 | public function bindParam($column, &$variable, $type = null, $length = null) 82 | { 83 | } 84 | 85 | /** 86 | * {@inheritdoc} 87 | */ 88 | public function columnCount() 89 | { 90 | } 91 | 92 | /** 93 | * {@inheritdoc} 94 | */ 95 | public function errorCode() 96 | { 97 | } 98 | 99 | /** 100 | * {@inheritdoc} 101 | */ 102 | public function errorInfo() 103 | { 104 | } 105 | 106 | /** 107 | * {@inheritdoc} 108 | */ 109 | public function execute($params = array()) 110 | { 111 | } 112 | 113 | /** 114 | * {@inheritdoc} 115 | */ 116 | public function rowCount() 117 | { 118 | } 119 | 120 | /** 121 | * {@inheritdoc} 122 | */ 123 | public function getIterator() 124 | { 125 | return $this->_resultSet; 126 | } 127 | 128 | /** 129 | * {@inheritdoc} 130 | */ 131 | public function setFetchMode($fetchStyle, $arg2 = null, $arg3 = null) 132 | { 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Tests/Paginator/PaginatorManagerTest.php: -------------------------------------------------------------------------------- 1 | "toto.html.twig", 29 | "item_count_per_page" => 50, 30 | "visible_page_count_in_paginator" => 5 31 | ); 32 | // create paginatorManager 33 | $paginatorManager = new PaginatorManager($service, $parameters); 34 | return $paginatorManager; 35 | 36 | } 37 | public function testPaginator() 38 | { 39 | // create queryBuilder 40 | $em = $this->getEntityManager(); 41 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 42 | $queryBuilder = $repository->createQueryBuilder("node"); 43 | $queryBuilder->select("node"); 44 | 45 | // create Request mock (ok this is not a mock....) 46 | $request = new \Symfony\Component\HttpFoundation\Request(); 47 | $_SERVER["REQUEST_URI"] = "/foo"; 48 | $paginatorManager = $this->getPaginatorManager(); 49 | 50 | // configure paginator 51 | $paginatorConfig = new PaginatorConfig(); 52 | $paginatorConfig->setCountFieldName("node.id"); 53 | $paginatorConfig->setItemCountInPage(3); 54 | 55 | // get paginator 56 | $paginatorConfig->setQueryBuilder($queryBuilder); 57 | $paginator = $paginatorManager->getPaginator($paginatorConfig, $request); 58 | 59 | // tests 60 | $this->assertEquals(11, $paginator->getTotalItemCount()); 61 | 62 | $this->assertEquals(4, $paginator->getTotalPageCount()); 63 | 64 | $this->assertEquals(array(1,2,3,4), $paginator->getPageRange()); 65 | 66 | $this->assertEquals(1 , $paginator->getCurrentPage()); 67 | $this->assertEquals(2, $paginator->getNextButtonPage()); 68 | } 69 | 70 | public function testPaginatorGroupBy() 71 | { 72 | // create queryBuilder 73 | $em = $this->getEntityManager(); 74 | $repository = $em->getRepository('Kitpages\DataGridBundle\Tests\TestEntities\Node'); 75 | $queryBuilder = $repository->createQueryBuilder("node"); 76 | $queryBuilder->select("node.user, count(node.id) as cnt"); 77 | $queryBuilder->groupBy("node.user"); 78 | 79 | // create EventDispatcher mock 80 | $service = new EventDispatcher(); 81 | // create Request mock (ok this is not a mock....) 82 | $request = new \Symfony\Component\HttpFoundation\Request(); 83 | $_SERVER["REQUEST_URI"] = "/foo"; 84 | 85 | // create gridManager instance 86 | $paginatorManager = $this->getPaginatorManager(); 87 | 88 | 89 | // configure paginator 90 | $paginatorConfig = new PaginatorConfig(); 91 | $paginatorConfig->setCountFieldName("node.user"); 92 | $paginatorConfig->setItemCountInPage(3); 93 | 94 | // get paginator 95 | $paginatorConfig->setQueryBuilder($queryBuilder); 96 | $paginator = $paginatorManager->getPaginator($paginatorConfig, $request); 97 | 98 | // tests 99 | $this->assertEquals(6, $paginator->getTotalItemCount()); 100 | 101 | $this->assertEquals(2, $paginator->getTotalPageCount()); 102 | 103 | $this->assertEquals(array(1,2), $paginator->getPageRange()); 104 | 105 | $this->assertEquals(1 , $paginator->getCurrentPage()); 106 | $this->assertEquals(2, $paginator->getNextButtonPage()); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Tests/PhpunitTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/TestEntities/Node.php: -------------------------------------------------------------------------------- 1 | content = $content; 19 | return $this; 20 | } 21 | 22 | public function getContent() 23 | { 24 | return $this->content; 25 | } 26 | 27 | public function setCreatedAt($createdAt) 28 | { 29 | $this->createdAt = $createdAt; 30 | return $this; 31 | } 32 | 33 | public function getCreatedAt() 34 | { 35 | return $this->createdAt; 36 | } 37 | 38 | public function setId($id) 39 | { 40 | $this->id = $id; 41 | return $this; 42 | } 43 | 44 | public function getId() 45 | { 46 | return $this->id; 47 | } 48 | 49 | public function setParentId($parentId) 50 | { 51 | $this->parentId = $parentId; 52 | return $this; 53 | } 54 | 55 | public function getParentId() 56 | { 57 | return $this->parentId; 58 | } 59 | 60 | public function setUser($user) 61 | { 62 | $this->user = $user; 63 | return $this; 64 | } 65 | 66 | public function getUser() 67 | { 68 | return $this->user; 69 | } 70 | 71 | public function setMainNode($mainNode) 72 | { 73 | $this->mainNode = $mainNode; 74 | return $this; 75 | } 76 | 77 | public function getMainNode() 78 | { 79 | return $this->mainNode; 80 | } 81 | 82 | public function setSubNodeList($subNodeList) 83 | { 84 | $this->subNodeList = $subNodeList; 85 | return $this; 86 | } 87 | 88 | public function getSubNodeList() 89 | { 90 | return $this->subNodeList; 91 | } 92 | 93 | public function getAssoc() 94 | { 95 | return $this->assoc; 96 | } 97 | 98 | public function setAssoc($assoc) 99 | { 100 | $this->assoc = $assoc; 101 | return $this; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Tests/TestEntities/NodeAssoc.php: -------------------------------------------------------------------------------- 1 | id = $id; 21 | return $this; 22 | } 23 | 24 | public function getId() 25 | { 26 | return $this->id; 27 | } 28 | 29 | public function setName($name) 30 | { 31 | $this->name = $name; 32 | return $this; 33 | } 34 | 35 | public function getName() 36 | { 37 | return $this->name; 38 | return $this; 39 | } 40 | 41 | public function addNodeList(\Kitpages\DataGridBundle\Tests\TestEntities\Node $node) 42 | { 43 | $this->nodeList[] = $node; 44 | 45 | return $this; 46 | } 47 | 48 | public function removeNodeList(\Kitpages\DataGridBundle\Tests\TestEntities\Node $node) 49 | { 50 | $this->nodeList->removeElement($node); 51 | return $this; 52 | } 53 | 54 | public function getNodeList() 55 | { 56 | return $this->nodeList; 57 | return $this; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /Tests/Tool/UrlToolTest.php: -------------------------------------------------------------------------------- 1 | changeRequestQueryString( 15 | $url, 16 | "key1", 17 | "test" 18 | ); 19 | $this->assertEquals("/titi?key1=test&key2=val2", $newUrl); 20 | 21 | $newUrl = $urlTool->changeRequestQueryString( 22 | $url, 23 | array( 24 | "key1"=>"test1", 25 | "key2"=>"test2" 26 | ) 27 | ); 28 | $this->assertEquals("/titi?key1=test1&key2=test2", $newUrl); 29 | 30 | $newUrl = $urlTool->changeRequestQueryString( 31 | $url, 32 | "key3", 33 | "val3" 34 | ); 35 | $this->assertEquals("/titi?key1=val1&key2=val2&key3=val3", $newUrl); 36 | } 37 | 38 | public function testChangeRequestUtf8QueryString() 39 | { 40 | $urlTool = new UrlTool(); 41 | $url = "/titi?key1=val1&key2=val2"; 42 | $newUrl = $urlTool->changeRequestQueryString( 43 | $url, 44 | "key1", 45 | "fös" 46 | ); 47 | $this->assertEquals("/titi?key1=f%C3%B6s&key2=val2", $newUrl); 48 | $newUrl = $urlTool->changeRequestQueryString( 49 | $newUrl, 50 | "key3", 51 | "mystring=-+ glou" 52 | ); 53 | $this->assertEquals("/titi?key1=f%C3%B6s&key2=val2&key3=mystring%3D-%2B+glou", $newUrl); 54 | } 55 | 56 | public function testChangeRequestWithArray() 57 | { 58 | $urlTool = new UrlTool(); 59 | $url = "/titi?tab[]=val_tab1&tab[]=val_tab2&key2=val2"; 60 | 61 | $newUrl = $urlTool->changeRequestQueryString( 62 | $url, 63 | "key2", 64 | "glou" 65 | ); 66 | $this->assertEquals("/titi?tab%5B0%5D=val_tab1&tab%5B1%5D=val_tab2&key2=glou", $newUrl); 67 | 68 | $newUrl = $urlTool->changeRequestQueryString( 69 | $url, 70 | "tab", 71 | array("newval1", "newval2", "newval3") 72 | ); 73 | $this->assertEquals("/titi?tab%5B0%5D=newval1&tab%5B1%5D=newval2&tab%5B2%5D=newval3&key2=val2", $newUrl); 74 | 75 | 76 | 77 | $url = "/titi?tab[field1]=val_tab1&tab[field2]=val_tab2&key2=val2"; 78 | 79 | $newUrl = $urlTool->changeRequestQueryString( 80 | $url, 81 | "key2", 82 | "glou" 83 | ); 84 | $this->assertEquals("/titi?tab%5Bfield1%5D=val_tab1&tab%5Bfield2%5D=val_tab2&key2=glou", $newUrl); 85 | 86 | $newUrl = $urlTool->changeRequestQueryString( 87 | $url, 88 | "tab", 89 | array("field1" => "newval1", "field2" => "newval2", "newfield" => "newval") 90 | ); 91 | $this->assertEquals("/titi?tab%5Bfield1%5D=newval1&tab%5Bfield2%5D=newval2&tab%5Bnewfield%5D=newval&key2=val2", $newUrl); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Tests/app/Resources/config/doctrine/Node.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Tests/app/Resources/config/doctrine/NodeAssoc.orm.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Tests/app/Resources/views/globals.html.twig: -------------------------------------------------------------------------------- 1 | {{ kitpages_data_grid.grid.default_twig }} -------------------------------------------------------------------------------- /Tests/app/config/config_test.yml: -------------------------------------------------------------------------------- 1 | kitpages_data_grid: 2 | grid: 3 | default_twig: KitpagesDataGridBundle:Grid:grid-standard.html.twig 4 | 5 | framework: 6 | trusted_hosts: ~ 7 | secret: "test" 8 | translator: { fallback: "fr" } 9 | test: ~ 10 | templating: 11 | engines: ['twig'] 12 | router: 13 | resource: "%kernel.root_dir%/config/routing.yml" 14 | 15 | twig: 16 | paths: 17 | "%kernel.root_dir%/app/Resources/views/": __main__ 18 | doctrine: 19 | dbal: 20 | driver: 'pdo_sqlite' 21 | memory: true 22 | orm: 23 | entity_managers: 24 | default: 25 | mappings: 26 | Kitpages\DataGridBundle\Tests\TestEntities\Node: 27 | type: xml 28 | dir: "%kernel.root_dir%/app/Resources/config/doctrine" 29 | prefix: Kitpages\DataGridBundle\Tests\TestEntities 30 | -------------------------------------------------------------------------------- /Tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | $value); 17 | } else { 18 | $changeTab = $mixedKey; 19 | } 20 | 21 | $parseTab = parse_url($url); 22 | 23 | $queryString = ""; 24 | 25 | if (array_key_exists("query", $parseTab)) { 26 | $queryString = $parseTab["query"]; 27 | } 28 | 29 | parse_str($queryString, $query); 30 | 31 | foreach ($changeTab as $key => $val) { 32 | $query[$key] = $val; 33 | } 34 | 35 | $parseTab["query"] = http_build_query($query); 36 | 37 | return 38 | ((isset($parseTab['scheme'])) ? $parseTab['scheme'] . '://' : '') 39 | .((isset($parseTab['user'])) ? $parseTab['user'] . ((isset($parseTab['pass'])) ? ':' . $parseTab['pass'] : '') .'@' : '') 40 | .((isset($parseTab['host'])) ? $parseTab['host'] : '') 41 | .((isset($parseTab['port'])) ? ':' . $parseTab['port'] : '') 42 | .((isset($parseTab['path'])) ? $parseTab['path'] : '') 43 | .((isset($parseTab['query'])) ? '?' . $parseTab['query'] : '') 44 | .((isset($parseTab['fragment'])) ? '#' . $parseTab['fragment'] : ''); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Twig/GlobalsTwigExtension.php: -------------------------------------------------------------------------------- 1 | gridParameterList = $gridParameterList; 18 | $this->paginatorParameterList = $paginatorParameterList; 19 | } 20 | 21 | public function getGlobals() 22 | { 23 | return array( 24 | "kitpages_data_grid" => array( 25 | 'grid' => $this->gridParameterList, 26 | 'paginator' => $this->paginatorParameterList 27 | ) 28 | ); 29 | } 30 | /** 31 | * Returns the name of the extension. 32 | * 33 | * @return string The extension name 34 | */ 35 | public function getName() 36 | { 37 | return "kitpages_data_grid_globals_extension"; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /VERSIONS.md: -------------------------------------------------------------------------------- 1 | Versions : 2 | ========== 3 | 2020-09-04: v3.4.0 4 | 5 | * upgrade to doctrine-bundle 2.x 6 | 7 | 2018-10-10: v3.2.0 8 | 9 | * new : compatible with twig 2 10 | * fix : twig min version to 1.23 11 | 12 | 2018-03-19 : v3.1.1 13 | 14 | no bc break 15 | 16 | * fix issue #69 : tag list in parameter list of gridConfig->addField() 17 | 18 | 2018-02-22 : v3.1.0 19 | 20 | no bc break 21 | 22 | * new : adds the ability to use its own subclass of Grid as a result of 23 | gridManager->getGrid. 24 | 25 | 2017-12-20 : v3.0.0 26 | 27 | * new : compatible with symfony 3 and symfony 4 (but only twig ~1.8) 28 | * fix : unit tests for phpunit 6 29 | 30 | 2017-12-20 : v2.6.0 31 | 32 | * new : add tag system that allow to retrieve a field list in a grid config 33 | * fix : fix unit tests for a new version of doctrine 34 | 35 | 2014-12-19 : v2.4.0 36 | 37 | * new : bootstrap3 templates in order to display datagrids and paginator using standard bootstrap3 lib 38 | * new : no data configurable in embeded template 39 | * fix : no data message translation key fixed 40 | * fix : doc for standalone paginator 41 | 42 | 2014-11-10 : v2.3.1 43 | 44 | * no BC Break 45 | * fix: fix for cases of multiple join levels. recursion of normalization where wrong. 46 | 47 | 2014-11-07 : v2.3.0 48 | 49 | * no BC Break 50 | * fix: important fix when using datagrid inside a subrequest 51 | 52 | 2014-09-10 : v2.2.0 53 | 54 | * no BC Break 55 | * refactor: JS refactoring thanks to snovichkov 56 | * doc: fixes in doc 57 | 58 | 2014-07-28 : v2.1.1 59 | 60 | * no BC Break 61 | * fix: minor syntax fix in translations 62 | 63 | 2014-07-21 : v2.1.0 64 | 65 | * no BC Break 66 | * new: you can now chain a formatValueCallback and the event system 67 | 68 | 2014-07-08 : v2.0.0 69 | 70 | * WARINIG : BC Break, you can switch to branch 1.x 71 | * new : major refactoring: separation of paginator and grid 72 | * new : much easier access to fields 73 | * new : refactoring : normalizer is a new service 74 | * new : api changes: the queryBuilder is now in the GridConfiguration object 75 | * new : catagory and dataList : you can transfert data to a field to use these custom data in events 76 | * new : configuration for default twigs in config.yml 77 | 78 | 2013-06-12 : tag v1.10.0 79 | 80 | * fix : change url encoding in urlTools (+ phpunit) 81 | * fix : change url encoding in javascript 82 | * refactor : common javascript for every themes 83 | 84 | 2013-06-04 : tag v1.9.0 85 | 86 | * add selectors for predefined filters : (see [doc](https://github.com/kitpages/KitpagesDataGridBundle/blob/master/Resources/doc/10-GridExtendedUse.md#add-selector-action-to-filter-on-a-field-with-a-value) ) 87 | 88 | 2013-05-23 : tag v1.8.0 89 | 90 | * add a twitter bootstrap layout for the datagrid 91 | 92 | 2013-05-22 : tag v1.7.0 93 | 94 | * add category parameter in fields. This value is not used internally. You can use it for whatever. It can be useful 95 | for a global formatting of fields with convertion events (seen kitpages_data_grid.on_display_grid_value_conversion) 96 | * add a nullIfNotExists in fields. If you want to display values of a leftJoin query, if there is no value, you can 97 | get an exception. With this value set to true, null is returned without any exception. 98 | 99 | 2013-05-02 : tag v1.6.1 100 | 101 | * fix following issue #18 : https://github.com/kitpages/KitpagesDataGridBundle/issues/18 102 | 103 | 2013-04-10 : tag v1.6.0 104 | 105 | * new : global formatting system with events (see [doc](https://github.com/kitpages/KitpagesDataGridBundle/blob/master/Resources/doc/10-GridExtendedUse.md#format-some-fields-system-wide) ) 106 | 107 | 2013-04-09 : tag v1.5.0 108 | 109 | * fix: for accessing data with join relations 110 | * new: error messages more readable during twig displaying 111 | * test: much more unit tests on the Grid::displayGridValue(). Very sensitive method... 112 | * doc: more documentation (thanks to @choomz) 113 | * new : a (ridiculously simple) debug system 114 | 115 | 2013-03-12 : tag v1.4.0 116 | 117 | * unit test updated for composer and sf2.1 118 | * readme updated for sf2.1 119 | 120 | 2012-08-28 : tag v1.3.0 121 | 122 | * manage "group by" requests (thanks to tyx) 123 | * docs : render a cell with a twig file 124 | * docs : group by queries 125 | * fix : travis configuration 126 | * fix : composer dependency 127 | 128 | 2012-07-09 : tag v1.2.0 129 | 130 | * More documentation 131 | * a fluent interface 132 | * small bug fixes (line count for group by queries) 133 | * unit testing 134 | * code cleaning 135 | * twig templates more flexible 136 | * travis-ci integration 137 | * an more advanced format callback system. See [Resources/doc/10-GridExtendedUse.md](https://github.com/kitpages/KitpagesDataGridBundle/blob/master/Resources/doc/10-GridExtendedUse.md) 138 | * 2 new contributors 139 | 140 | 2012-05-23 : tag v1.1.1 141 | 142 | * doc refactoring 143 | 144 | 2012-05-23 : tag v1.1.0 145 | 146 | * added events for modifying the way the grid or the paginator works (see Resources/doc/30-Events.md) 147 | * modify the default twig in order to remove the filter form from the table. It is useful if you want add 148 | a form around the grid (let's imagine you add checkboxes on the left of the grid) 149 | * add documentation in Resources/doc/ 150 | 151 | 2012-05-21 : tag v1.0.1 152 | 153 | * composer.json added and link to packagist 154 | * normalization of results for request like $queryBuilder->select("item, item.id * 3 as foo"); // warning : see 155 | Limitations paragraph 156 | * add {% block kit_grid_thead_before_column %}{%endblock%} and {% block kit_grid_tbody_before_column %}{%endblock%} for 157 | adding columns before le natural column list 158 | 159 | 2012-05-17 : tag v1.0.0 160 | 161 | * sorting added 162 | * template twig more extendable 163 | * small fix 164 | * refactor in Grid Manager 165 | 166 | 2012-05-02 : 167 | 168 | * add possibility to have a join in jour queryBuilder 169 | * remove mandatory name for your entity 170 | 171 | migrations 172 | 173 | * you have to add the field name used for counting 174 | ** Ex : $gridConfig->setCountFieldName("item.id"); // for count(item.id) 175 | * you have to set complete field name instead of short field name 176 | ** Ex : $gridConfig->addField(new Field("item.id")); 177 | ** instead of just : $gridConfig->addField(new Field("id")); 178 | 179 | 2012-04-xx 180 | 181 | * creation 182 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kitpages/data-grid-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Symfony DataGridBundle", 5 | "keywords": ["Datagrid", "paginator", "filter", "sort"], 6 | "homepage": "https://github.com/kitpages/KitpagesDataGridBundle", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Philippe Le Van", 11 | "email": "philippe.levan@kitpages.fr" 12 | } 13 | ], 14 | "require": { 15 | "php": ">=7.1", 16 | "symfony/framework-bundle": "~3.3|~4.0", 17 | "symfony/dependency-injection": "~3.3|~4.0", 18 | "symfony/translation": "~3.3|~4.0", 19 | "symfony/templating": "~3.3|~4.0", 20 | "doctrine/orm": "~2.5", 21 | "doctrine/doctrine-bundle": "~1.8|~2.0", 22 | "twig/twig": "~1.23|~2.0", 23 | "symfony/twig-bundle": "~3.3|~4.0" 24 | }, 25 | "require-dev": { 26 | "symfony/class-loader": "~3.3|~4.0", 27 | "symfony/yaml": "~3.3|~4.0", 28 | "symfony/finder": "~3.3|~4.0", 29 | "symfony/browser-kit": "~3.3|~4.0", 30 | 31 | "phpunit/phpunit": "~6.1", 32 | "phpunit/dbunit": "~3.0" 33 | }, 34 | "autoload": { 35 | "psr-0": { "Kitpages\\DataGridBundle": "" } 36 | }, 37 | "target-dir": "Kitpages/DataGridBundle" 38 | } 39 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ./Tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ./ 18 | 19 | ./Resources 20 | ./Tests 21 | ./vendor 22 | 23 | 24 | 25 | 26 | --------------------------------------------------------------------------------