├── .gitignore ├── .travis.yml ├── AliDatatableBundle.php ├── DependencyInjection ├── AliDatatableExtension.php └── Configuration.php ├── EventListener └── DatatableListener.php ├── LICENSE ├── README.md ├── Resources ├── config │ ├── schema │ │ └── datatable-1.0.xsd │ └── services.yml ├── doc │ └── index.md ├── public │ ├── css │ │ └── dataTables.bootstrap.min.css │ ├── images │ │ └── sample_01.png │ ├── js │ │ ├── dataTables.bootstrap.min.js │ │ ├── jquery.dataTables.min.js │ │ └── jquery.min.js │ └── third-party │ │ └── bootstrap │ │ ├── css │ │ ├── bootstrap-theme.css │ │ ├── bootstrap-theme.css.map │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ └── bootstrap.min.css │ │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ │ └── js │ │ ├── bootstrap.js │ │ ├── bootstrap.min.js │ │ └── npm.js ├── translations │ ├── messages.ar.yml │ ├── messages.cn.yml │ ├── messages.de.yml │ ├── messages.en.yml │ ├── messages.es.yml │ ├── messages.fr.yml │ ├── messages.it.yml │ ├── messages.nl.yml │ ├── messages.pl.yml │ ├── messages.ru.yml │ ├── messages.tr.yml │ └── messages.ua.yml └── views │ ├── Internal │ └── script.html.twig │ ├── Main │ └── index.html.twig │ └── Renderers │ ├── _actions.html.twig │ └── _default.html.twig ├── Tests ├── AppKernel.php ├── BaseTestCase.php ├── TestBundle │ ├── Controller │ │ └── DefaultController.php │ ├── Entity │ │ ├── Category.php │ │ ├── Feature.php │ │ └── Product.php │ ├── Resources │ │ ├── config │ │ │ └── routing.yml │ │ └── views │ │ │ └── Default │ │ │ └── index.html.twig │ ├── TestBundle.php │ └── Tests │ │ └── Controller │ │ └── DefaultControllerTest.php ├── Util │ ├── DatatableTest.php │ ├── Factory │ │ └── Query │ │ │ └── DoctrineBuilderTest.php │ └── Formatter │ │ └── RendererTest.php ├── bootstrap.php └── config │ └── config.yml ├── Twig └── Extension │ └── AliDatatableExtension.php ├── Util ├── Datatable.php ├── Factory │ ├── Prototype │ │ └── PrototypeBuilder.php │ └── Query │ │ ├── DoctrineBuilder.php │ │ └── QueryInterface.php └── Formatter │ └── Renderer.php ├── composer.json └── phpunit.xml.dist /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.phar 3 | composer.lock 4 | phpunit.xml 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | cache: 6 | directories: 7 | - $HOME/.composer/cache 8 | 9 | matrix: 10 | allow_failures: 11 | - php: 5.6 12 | - php: 7.0 13 | 14 | fast_finish: true 15 | 16 | include: 17 | - php: 5.4 18 | env: SYMFONY_VERSION='2.6.*' COMPOSER_FLAGS="--prefer-lowest" SYMFONY_DEPRECATIONS_HELPER=weak 19 | 20 | - php: 5.5 21 | env: SYMFONY_VERSION='2.8.*' 22 | 23 | - php: 5.6 24 | env: SYMFONY_VERSION='2.8.*' 25 | - php: 5.6 26 | env: SYMFONY_VERSION='3.0.*' 27 | 28 | - php: 7.0 29 | env: SYMFONY_VERSION='2.8.*' 30 | - php: 7.0 31 | env: SYMFONY_VERSION='3.0.*' 32 | 33 | 34 | before_install: 35 | - composer self-update 36 | - if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony=$SYMFONY_VERSION; else composer require --dev --no-update symfony/symfony; fi 37 | 38 | install: composer update --prefer-dist --no-interaction $COMPOSER_FLAGS 39 | 40 | script: 41 | - phpunit --coverage-text 42 | 43 | notifications: 44 | email: ali.hichem@mail.com -------------------------------------------------------------------------------- /AliDatatableBundle.php: -------------------------------------------------------------------------------- 1 | load('services.yml'); 25 | $conf = new Configuration(); 26 | $config = $this->processConfiguration($conf, $configs); 27 | $container->setParameter('ali_datatable', $config); 28 | } 29 | 30 | /** 31 | * {@inheritDoc} 32 | */ 33 | public function getXsdValidationBasePath() 34 | { 35 | return __DIR__ . '/../Resources/config/schema'; 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | public function getNamespace() 42 | { 43 | return 'http://symfony.com/schema/dic/doctrine'; 44 | } 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | public function getConfiguration(array $config, ContainerBuilder $container) 50 | { 51 | return new Configuration($container->getParameter('kernel.debug')); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | root('ali_datatable'); 22 | 23 | $rootNode 24 | ->children() 25 | ->arrayNode('all') 26 | ->children() 27 | ->scalarNode('action')->defaultTrue()->end() 28 | ->scalarNode('search')->defaultFalse()->end() 29 | ->end() 30 | ->end() 31 | ->arrayNode('js') 32 | ->useAttributeAsKey('name') 33 | ->prototype('scalar') 34 | ->end() 35 | ->end() 36 | ->end() 37 | ; 38 | 39 | return $treeBuilder; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /EventListener/DatatableListener.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Ali\DatatableBundle\EventListener; 13 | 14 | use Symfony\Component\HttpFoundation\Request; 15 | use Symfony\Component\HttpFoundation\Response; 16 | use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag; 17 | use Symfony\Component\HttpKernel\Event\FilterResponseEvent; 18 | use Symfony\Component\HttpKernel\KernelEvents; 19 | use Symfony\Component\EventDispatcher\EventSubscriberInterface; 20 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; 21 | 22 | /** 23 | * DatatableListener injects the js content to the bottom of the body element. 24 | * (heavely copied from WebDebugToolbarListener from core Symfony2 core) 25 | * 26 | * @see \Symfony\Bundle\WebProfilerBundle\EventListener\WebDebugToolbarListener 27 | * @author Ali Hichem 28 | */ 29 | class DatatableListener implements EventSubscriberInterface 30 | { 31 | 32 | /** 33 | * On kernel response event 34 | * 35 | * @param FilterResponseEvent $event 36 | * @return void|null 37 | */ 38 | public function onKernelResponse(FilterResponseEvent $event) 39 | { 40 | $response = $event->getResponse(); 41 | $request = $event->getRequest(); 42 | if (!$event->isMasterRequest()) 43 | { 44 | return; 45 | } 46 | if ($request->isXmlHttpRequest()) 47 | { 48 | return; 49 | } 50 | $this->_injectDatatableScript($response, $request); 51 | } 52 | 53 | /** 54 | * Injects the datatable scripts into the given HTTP Response. 55 | * 56 | * @param Response $response A Response instance 57 | */ 58 | protected function _injectDatatableScript(Response $response, Request $request) 59 | { 60 | $content = $response->getContent(); 61 | $pos_body = strripos($content, ''); 62 | if (!$pos_body) 63 | { 64 | return; 65 | } 66 | $session = $request->getSession(); 67 | $dom = '', $pos_body, 0); 83 | $response->setContent($content); 84 | } 85 | $pos_dta = strripos($content, $dom); 86 | $content = substr_replace($content, $dta_script, $pos_dta + strlen($dom), 0); 87 | $response->setContent($content); 88 | } 89 | 90 | public static function getSubscribedEvents() 91 | { 92 | return array( 93 | KernelEvents::RESPONSE => array('onKernelResponse', -127), 94 | ); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2014 Ali Hichem 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: This bundle is not maintained anymore. 2 | ============================================ 3 | 4 | 5 | AliDatatableBundle 6 | ================== 7 | 8 | [![Build Status](https://secure.travis-ci.org/AliHichem/AliDatatableBundle.png?branch=master)](http://travis-ci.org/AliHichem/AliDatatableBundle) 9 | 10 | [![GitHub issues](https://img.shields.io/github/issues/AliHichem/AliDatatableBundle.svg)](https://github.com/AliHichem/AliDatatableBundle/issues) 11 | [![GitHub forks](https://img.shields.io/github/forks/AliHichem/AliDatatableBundle.svg)](https://github.com/AliHichem/AliDatatableBundle/network) 12 | [![GitHub stars](https://img.shields.io/github/stars/AliHichem/AliDatatableBundle.svg)](https://github.com/AliHichem/AliDatatableBundle/stargazers) 13 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/AliHichem/AliDatatableBundle/master/LICENSE) 14 | 15 | 16 | The Datatable bundle for symfony2 allow for easily integration of the [jQuery Datatable plugin](http://datatables.net/) with the doctrine2 entities having twitter bootstrap theme. 17 | This bundle provides a way to make a projection of a doctrine2 entity to a powerful jquery datagrid. It mainly includes: 18 | 19 | * datatable service container: to manage the datatable as a service. 20 | * twig extension: for view integration. 21 | * dynamic pager handler : no need to set your pager. 22 | * default action link builder: if activated, the bundle generates default edit/delete links. 23 | * support doctrine2 association. 24 | * support of doctrine query builder. 25 | * support of column search. 26 | * support of custom twig/phpClosure renderers. 27 | * support of custom grouped actions. 28 | * datatable configuration javascript code (even for multiple datatables) is grouped, minimized and moved to the bottom of the html body element automatically. 29 | * twitter bootstrap integration. 30 | 31 | 32 |
Screenshot
33 | 34 | [Read the Documentation](https://github.com/AliHichem/AliDatatableBundle/blob/master/Resources/doc/index.md) 35 | -------------------------------------------------------------------------------- /Resources/config/schema/datatable-1.0.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Resources/config/services.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | datatable.class: Ali\DatatableBundle\Util\Datatable 3 | 4 | services: 5 | datatable: 6 | class: "%datatable.class%" 7 | arguments: ['@service_container'] 8 | scope: prototype 9 | calls: 10 | - [setEntityManager, ['@doctrine.orm.entity_manager']] 11 | 12 | datatable.twig.extension: 13 | class: Ali\DatatableBundle\Twig\Extension\AliDatatableExtension 14 | arguments: ['@service_container'] 15 | tags: 16 | - { name: twig.extension } 17 | 18 | datatable.listener: 19 | class: Ali\DatatableBundle\EventListener\DatatableListener 20 | arguments: [ '@twig' ] 21 | tags: 22 | - { name: kernel.event_listener, event: kernel.response, method: onKernelResponse, priority: 255 } 23 | -------------------------------------------------------------------------------- /Resources/doc/index.md: -------------------------------------------------------------------------------- 1 | AliDatatableBundle 2 | ================== 3 | 4 | The Datatable bundle for symfony2 allow for easily integration of the [jQuery Datatable plugin](http://datatables.net/) with the doctrine2 entities having twitter bootstrap theme. 5 | This bundle provides a way to make a projection of a doctrine2 entity to a powerful jquery datagrid. 6 | 7 | ------------------------------------ 8 | ##### [Installation](#installation-1) 9 | 10 | 1. [Download AliDatatableBundle using composer](#step-1-download-alidatatablebundle) 11 | 2. [Enable the Bundle](#step-2--enable-the-bundle) 12 | 3. [Configure your application's config.yml](#step-3--activate-the-main-configs) 13 | 14 | ##### [How to use AliDatatableBundle ?](#-how-to-use-alidatatablebundle-) 15 | ##### [Rendering inside Twig](#-rendering-inside-twig) 16 | ##### [Advanced php config](#-advanced-php-config) 17 | ##### [Use of search filters](#-use-of-search-filters) 18 | 19 | * [Activate search globally](#activate-search-globally) 20 | * [Set search fields](#set-search-fields) (new) 21 | 22 | ##### [Multiple actions](#-multiple-actions) (new) 23 | ##### [Custom renderer](#-custom-renderer) 24 | ##### [Translation](#-translation) 25 | ##### [Multiple datatable in the same view](#-multiple-datatable-in-the-same-view) 26 | 27 | --------------------------------------- 28 | 29 | ### Installation 30 | 31 | Installation is a quick (I promise!) 3 step process: 32 | 33 | 1. [Download AliDatatableBundle using composer](#step-1-download-alidatatablebundle) 34 | 2. [Enable the Bundle](#step-2--enable-the-bundle) 35 | 3. [Configure your application's config.yml](#step-3--activate-the-main-configs) 36 | 37 | ##### Step 1: Download AliDatatableBundle 38 | 39 | ###### twitter bootstrapped (v >= 2.0) 40 | 41 | Add datatable bundle in your composer.json as below: 42 | 43 | ```js 44 | "require": { 45 | ... 46 | "ali/datatable": "~2.0" 47 | } 48 | ``` 49 | 50 | Update/install with this command: 51 | 52 | ``` 53 | php composer.phar update ali/datatable 54 | ``` 55 | 56 | ###### Classic theme datatable 57 | 58 | Add datatable bundle in your composer.json as below: 59 | 60 | ```js 61 | "require": { 62 | ... 63 | "ali/datatable": "~1.4" 64 | } 65 | ``` 66 | 67 | Update/install with this command: 68 | 69 | ``` 70 | php composer.phar update ali/datatable 71 | ``` 72 | 73 | 74 | ##### Step 2: Enable the bundle 75 | 76 | register the bundle 77 | 78 | ```php 79 | public function registerBundles() 80 | { 81 | $bundles = array( 82 | ... 83 | new Ali\DatatableBundle\AliDatatableBundle(), 84 | ); 85 | ``` 86 | 87 | (only for symfony < 2.1 ) 88 | add the namespace to the autoloader 89 | 90 | ```php 91 | $loader->registerNamespaces(array( 92 | ... 93 | 'Ali' => __DIR__.'/../vendor/bundles', 94 | )); 95 | ``` 96 | 97 | generate the assets symlinks 98 | 99 | ``` 100 | $ app/console assets:install --symlink web 101 | ``` 102 | 103 | ##### Step 3: Activate the main configs 104 | 105 | in this section you can put the global config that you want to set for all the instance of datatable in your project. 106 | 107 | ###### To keep it to default 108 | 109 | ``` 110 | # app/config/config.yml 111 | ali_datatable: 112 | all: ~ 113 | js: ~ 114 | ``` 115 | 116 | the "js" config will be applied to datatable exactly like you do with "$().datatable({ you config });" , you can even put javascript code. 117 | Note: all you js config have to string typed, make sure to use (") as delimiters. 118 | 119 | ###### Config sample 120 | 121 | ``` 122 | ali_datatable: 123 | all: 124 | action: true 125 | search: false 126 | js: 127 | iDisplayLength: "10" 128 | aLengthMenu: "[[5,10, 25, 50, -1], [5,10, 25, 50, 'All']]" 129 | bJQueryUI: "false" 130 | fnPreDrawCallback: | 131 | function( e ) { 132 | // you custom code goes here 133 | } 134 | ``` 135 | 136 | ### # How to use AliDatatableBundle ? 137 | 138 | Assuming for example that you need a grid in your "index" action, create in your controller method as below: 139 | 140 | ```php 141 | /** 142 | * set datatable configs 143 | * 144 | * @return \Ali\DatatableBundle\Util\Datatable 145 | */ 146 | private function _datatable() 147 | { 148 | return $this->get('datatable') 149 | ->setEntityManager($em) // Optional 150 | ->setEntity("XXXMyBundle:Entity", "x") // Replace "XXXMyBundle:Entity" by your entity 151 | ->setFields( 152 | array( 153 | "Name" => 'x.name', // Declaration for fields: 154 | "Address" => 'x.address', // "label" => "alias.field_attribute_for_dql" 155 | "_identifier_" => 'x.id') // you have to put the identifier field without label. Do not replace the "_identifier_" 156 | ) 157 | ->setWhere( // set your dql where statement 158 | 'x.address = :address', 159 | array('address' => 'Paris') 160 | ) 161 | ->setOrder("x.created", "desc") // it's also possible to set the default order 162 | ->setHasAction(true); // you can disable action column from here by setting "false". 163 | } 164 | 165 | 166 | /** 167 | * Grid action 168 | * @return Response 169 | */ 170 | public function gridAction() 171 | { 172 | return $this->_datatable()->execute(); // call the "execute" method in your grid action 173 | } 174 | 175 | /** 176 | * Lists all entities. 177 | * @return Response 178 | */ 179 | public function indexAction() 180 | { 181 | $this->_datatable(); // call the datatable config initializer 182 | return $this->render('XXXMyBundle:Module:index.html.twig'); // replace "XXXMyBundle:Module:index.html.twig" by yours 183 | } 184 | ``` 185 | 186 | ### # Rendering inside Twig 187 | 188 | ```js 189 | 190 | 191 | 192 | {% stylesheets 193 | 'bundles/alidatatable/third-party/bootstrap/css/bootstrap.min.css' 194 | 'bundles/alidatatable/css/dataTables.bootstrap.min.css' 195 | filter='cssrewrite' 196 | %} 197 | 198 | {% endstylesheets %} 199 | {% javascripts 200 | 'bundles/alidatatable/js/jquery.min.js' 201 | 'bundles/alidatatable/js/jquery.dataTables.min.js' 202 | 'bundles/alidatatable/js/dataTables.bootstrap.min.js' 203 | %} 204 | 205 | {% endjavascripts %} 206 | 207 | 208 | {{ datatable({ 209 | 'edit_route' : 'RouteForYourEntity_edit', 210 | 'delete_route' : 'RouteForYourEntity_delete', 211 | 'js' : { 212 | 'sAjaxSource' : path('RouteForYour_grid_action') 213 | } 214 | }) 215 | }} 216 | ``` 217 | 218 | 219 | Advanced Use of datatable 220 | ------------------------- 221 | 222 | ### # Advanced php config 223 | 224 | Assuming the example above, you can add your joins and where statements 225 | 226 | ```php 227 | /** 228 | * set datatable configs 229 | * 230 | * @return \Ali\DatatableBundle\Util\Datatable 231 | */ 232 | private function _datatable() 233 | { 234 | return $this->get('datatable') 235 | ->setEntity("XXXMyBundle:Entity", "x") // replace "XXXMyBundle:Entity" by your entity 236 | ->setFields( 237 | array( 238 | "Name" => 'x.name', // Declaration for fields: 239 | "Address" => 'x.address', // "label" => "alias.field_attribute_for_dql" 240 | "Group" => 'g.name', 241 | "Team" => 't.name', 242 | "_identifier_" => 'x.id') // you have to put the identifier field without label. Do not replace the "_identifier_" 243 | ) 244 | ->addJoin('x.group', 'g', \Doctrine\ORM\Query\Expr\Join::INNER_JOIN) 245 | ->addJoin('x.team', 't', \Doctrine\ORM\Query\Expr\Join::INNER_JOIN) 246 | ->setWhere( // set your dql where statement 247 | 'x.address = :address', 248 | array('address' => 'Paris') 249 | ) 250 | ->setOrder("x.created", "desc") // it's also possible to set the default order 251 | ->setHasAction(true); // you can disable action column from here by setting "false". 252 | } 253 | ``` 254 | 255 | ### # Use of search filters 256 | 257 | 258 | * [Activate search globally](#activate-search-globally) 259 | * [Set search fields](#set-search-fields) 260 | 261 | ###### Activate search globally 262 | 263 | The filtering functionality that is very useful for quickly search through the information from the database - however the search is only built in one way : the individual column filtering. 264 | 265 | By default the filtering functionality is disabled, to get it working you just need to activate it from your configuration method like this : 266 | 267 | ```php 268 | private function _datatable() 269 | { 270 | return $this->get('datatable') 271 | //... 272 | ->setSearch(TRUE); 273 | } 274 | ``` 275 | ###### Set search fields 276 | 277 | You can set fields where you want to enable your search , by default search wont be active for actions column but you might want to disable search for other columns. 278 | Let say you want search to be active only for "field1" and "field3", you just need to activate search for the approriate column key and your datatable config should be : 279 | 280 | ```php 281 | /** 282 | * set datatable configs 283 | * 284 | * @return \Ali\DatatableBundle\Util\Datatable 285 | */ 286 | private function _datatable() 287 | { 288 | $datatable = $this->get('datatable'); 289 | return $datatable->setEntity("XXXMyBundle:Entity", "x") 290 | ->setFields( 291 | array( 292 | "label of field1" => 'x.field1', // column key 0 293 | "label of field2" => 'x.field2', // column key 1 294 | "label of field3" => 'x.field3', // column key 2 295 | "_identifier_" => 'x.id') // column key 3 296 | ) 297 | ->setSearch(true) 298 | ->setSearchFields(array(0,2)) 299 | ; 300 | } 301 | ``` 302 | 303 | ### # Multiple actions 304 | 305 | Sometimes, it's good to be able to do the same action on multiple records like deleting, activating, moving ... 306 | Well this is very easy to add to your datatable: all what you need is to declare your multiple action as follow 307 | 308 | 309 | ```php 310 | /** 311 | * set datatable configs 312 | * 313 | * @return \Ali\DatatableBundle\Util\Datatable 314 | */ 315 | private function _datatable() 316 | { 317 | $datatable = $this->get('datatable'); 318 | return $datatable->setEntity("XXXMyBundle:Entity", "x") 319 | ->setFields( 320 | array( 321 | "label of field1" => 'x.field1', // column key 0 322 | "label of field2" => 'x.field2', // column key 1 323 | "_identifier_" => 'x.id') // column key 2 324 | ) 325 | ->setMultiple( 326 | array( 327 | 'delete' => array( 328 | 'title' => 'Delete', 329 | 'route' => 'multiple_delete_route' // path to multiple delete route 330 | ) 331 | ) 332 | ) 333 | ; 334 | } 335 | ``` 336 | 337 | Then all what you have to do is to add the necessary logic in your "multiple_delete_route" (or whatever your route is for). 338 | In that action , you can get the selected ids by : 339 | 340 | ```php 341 | $data = $this->getRequest()->get('dataTables'); 342 | $ids = $data['actions']; 343 | ``` 344 | 345 | ### # Custom renderer 346 | 347 | **Twig renderers** 348 | 349 | To set your own column structure, you can use a custom twig renderer as below: In this example you can find how to set the use of the default twig renderer for action fields which you can override as your own needs. 350 | 351 | ```php 352 | /** 353 | * set datatable configs 354 | * 355 | * @return \Ali\DatatableBundle\Util\Datatable 356 | */ 357 | private function _datatable() 358 | { 359 | $datatable = $this->get('datatable'); 360 | return $datatable->setEntity("XXXMyBundle:Entity", "x") 361 | ->setFields( 362 | array( 363 | "label of field1" => 'x.field1', 364 | "label of field2" => 'x.field2', 365 | "_identifier_" => 'x.id') 366 | ) 367 | ->setRenderers( 368 | array( 369 | 2 => array( 370 | 'view' => 'AliDatatableBundle:Renderers:_actions.html.twig', 371 | 'params' => array( 372 | 'edit_route' => 'route_edit', 373 | 'delete_route' => 'route_delete', 374 | 'delete_form_prototype' => $datatable->getPrototype('delete_form') 375 | ), 376 | ), 377 | ) 378 | ) 379 | ->setHasAction(true); 380 | } 381 | ``` 382 | 383 | In a twig renderer you can have access the the field value using dt_item variable 384 | ``` 385 | {{ dt_item }} 386 | ``` 387 | or access the entire entity object using dt_obj variable 388 | ``` 389 | {{ dt_obj.username }} 390 | ``` 391 | NOTE: be careful of LAZY LOADING when using dt_obj ! 392 | 393 | **PHP Closures** 394 | 395 | Assuming the example above, you can set your custom fields renderer using [PHP Closures](http://php.net/manual/en/class.closure.php). 396 | 397 | ```php 398 | /** 399 | * set datatable configs 400 | * 401 | * @return \Ali\DatatableBundle\Util\Datatable 402 | */ 403 | private function _datatable() 404 | { 405 | $controller_instance = $this; 406 | return $this->get('datatable') 407 | ->setEntity("XXXMyBundle:Entity", "x") // replace "XXXMyBundle:Entity" by your entity 408 | ->setFields( 409 | array( 410 | "Name" => 'x.name', // Declaration for fields: 411 | "Address" => 'x.address', // "label" => "alias.field_attribute_for_dql" 412 | "_identifier_" => 'x.id') // you have to put the identifier field without label. Do not replace the "_identifier_" 413 | ) 414 | ->setRenderer( 415 | function(&$data) use ($controller_instance) 416 | { 417 | foreach ($data as $key => $value) 418 | { 419 | if ($key == 1) // 1 => address field 420 | { 421 | $data[$key] = $controller_instance 422 | ->get('templating') 423 | ->render( 424 | 'XXXMyBundle:Module:_grid_entity.html.twig', 425 | array('data' => $value) 426 | ); 427 | } 428 | } 429 | } 430 | ) 431 | ->setOrder("x.created", "desc") // it's also possible to set the default order 432 | ->setHasAction(true); // you can disable action column from here by setting "false". 433 | } 434 | ``` 435 | 436 |
Screenshot
437 | 438 | ### # Translation 439 | 440 | You can set your own translated labels by adding in your translation catalog entries as below: 441 | 442 | ``` 443 | ali: 444 | common: 445 | action: Actions 446 | confirm_delete: 'Are you sure to delete this item ?' 447 | delete: delete 448 | edit: edit 449 | no_action: "(can't remove)" 450 | sProcessing: "Processing..." 451 | sLengthMenu: "Show _MENU_ entries" 452 | sZeroRecords: "No matching records found" 453 | sInfo: "Showing _START_ to _END_ of _TOTAL_ entries" 454 | sInfoEmpty: "Showing 0 to 0 of 0 entries" 455 | sInfoFiltered: "(filtered from _MAX_ total entries)" 456 | sInfoPostFix: "" 457 | sSearch: "Search:" 458 | sLoadingRecords: "" 459 | sFirst: "First" 460 | sPrevious: "Previous" 461 | sNext: "Next" 462 | sLast: "Last" 463 | search: "Search" 464 | ``` 465 | 466 | 467 | This bundle includes nine translation catalogs: Arabic, Chinese, Dutch, English, Spanish, French, Italian, Russian and Turkish 468 | To get more translated entries, you can follow the [official datatable translation](http://datatables.net/plug-ins/i18n#English) 469 | 470 | ### # Doctrine query builder 471 | 472 | To use your own query object to supply to the datatable object, you can perform this action using your proper "doctrine query object": AliDatatableBundle allow (since tag 1.2.0) to manipulate the query object provider which is now a doctrine query builder object, you can use it to update the query in all its components except of course in the selected field part. 473 | 474 | This is a classic config before using the doctrine query builder: 475 | 476 | ```php 477 | private function _datatable() 478 | { 479 | $datatable = $this->get('datatable') 480 | ->setEntity("XXXBundle:Entity", "e") 481 | ->setFields( 482 | array( 483 | "column1 label" => 'e.column1', 484 | "_identifier_" => 'e.id') 485 | ) 486 | ->setWhere( 487 | 'e.column1 = :column1', 488 | array('column1' => '1' ) 489 | ) 490 | ->setOrder("e.created", "desc"); 491 | 492 | $qb = $datatable->getQueryBuilder()->getDoctrineQueryBuilder(); 493 | // This is the doctrine query builder object , you can 494 | // retrieve it and include your own change 495 | 496 | return $datatable; 497 | } 498 | ``` 499 | 500 | This is a config that uses a doctrine query object a query builder : 501 | 502 | ```php 503 | private function _datatable() 504 | { 505 | $qb = $this->getDoctrine()->getEntityManager()->createQueryBuilder(); 506 | $qb->from("XXXBundle:Entity", "e") 507 | ->where('e.column1 = :column1') 508 | ->setParameters(array('column1' = 0)) 509 | ->orderBy("e.created", "desc"); 510 | 511 | $datatable = $this->get('datatable') 512 | ->setFields( 513 | array( 514 | "Column 1 label" => 'e.column1', 515 | "_identifier_" => 'e.id') 516 | ); 517 | 518 | $datatable->getQueryBuilder()->setDoctrineQueryBuilder($qb); 519 | 520 | return $datatable; 521 | } 522 | ``` 523 | 524 | ### # Multiple datatable in the same view 525 | 526 | To declare multiple datatables in the same view, you have to set the datatable identifier in you controller with "setDatatableId": Each of your databale config methods ( _datatable() , _datatable_1() .. _datatable_n() ) needs to set the same identifier used in your view: 527 | 528 | **In the controller** 529 | 530 | 531 | ```php 532 | protected function _datatable() 533 | { 534 | // ... 535 | return $this->get('datatable') 536 | ->setDatatableId('dta-unique-id_1') 537 | ->setEntity("XXXMyBundle:Entity", "x") 538 | // ... 539 | } 540 | 541 | protected function _datatableSecond() 542 | { 543 | // ... 544 | return $this->get('datatable') 545 | ->setDatatableId('dta-unique-id_2') 546 | ->setEntity("YYYMyBundle:Entity", "y") 547 | // ... 548 | } 549 | ``` 550 | 551 | **In the view** 552 | 553 | ```js 554 | {{ 555 | datatable({ 556 | 'id' : 'dta-unique-id_1', 557 | ... 558 | 'js' : { 559 | 'sAjaxSource' : path('RouteForYour_grid_action_1') 560 | } 561 | }) 562 | }} 563 | 564 | {{ 565 | datatable({ 566 | 'id' : 'dta-unique-id_2', 567 | ... 568 | 'js' : { 569 | 'sAjaxSource' : path('RouteForYour_grid_action_2') 570 | } 571 | }) 572 | }} 573 | ``` 574 | -------------------------------------------------------------------------------- /Resources/public/css/dataTables.bootstrap.min.css: -------------------------------------------------------------------------------- 1 | div.dataTables_length label{font-weight:400;text-align:left;white-space:nowrap}div.dataTables_length select{width:75px;display:inline-block}div.dataTables_filter{text-align:right}div.dataTables_filter label{font-weight:400;white-space:nowrap;text-align:left}div.dataTables_filter input{margin-left:.5em;display:inline-block;width:auto}div.dataTables_info{padding-top:8px;white-space:nowrap}div.dataTables_paginate{margin:0;white-space:nowrap;text-align:right}div.dataTables_paginate ul.pagination{margin:2px 0;white-space:nowrap}@media screen and (max-width:767px){div.dataTables_filter,div.dataTables_info,div.dataTables_length,div.dataTables_paginate,div.dataTables_wrapper>div.row>div{text-align:center}div.DTTT{margin-bottom:.5em}}table.dataTable td,table.dataTable th{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}table.dataTable{clear:both;margin-top:6px!important;margin-bottom:6px!important;max-width:none!important}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:after{position:absolute;top:8px;right:8px;display:block;font-family:'Glyphicons Halflings';opacity:.5}table.dataTable thead .sorting:after{opacity:.2;content:"\e150"}table.dataTable thead .sorting_asc:after{content:"\e155"}table.dataTable thead .sorting_desc:after{content:"\e156"}div.dataTables_scrollBody table.dataTable thead .sorting:after,div.dataTables_scrollBody table.dataTable thead .sorting_asc:after,div.dataTables_scrollBody table.dataTable thead .sorting_desc:after{display:none}table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{color:#eee}table.dataTable thead>tr>th{padding-right:30px}table.dataTable th:active{outline:0}table.dataTable.table-condensed thead>tr>th{padding-right:20px}table.dataTable.table-condensed thead .sorting:after,table.dataTable.table-condensed thead .sorting_asc:after,table.dataTable.table-condensed thead .sorting_desc:after{top:6px;right:6px}div.dataTables_scrollHead table{margin-bottom:0!important;border-bottom-left-radius:0;border-bottom-right-radius:0}div.dataTables_scrollHead table thead tr:last-child td:first-child,div.dataTables_scrollHead table thead tr:last-child th:first-child{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}div.dataTables_scrollBody table{border-top:none;margin-top:0!important;margin-bottom:0!important}div.dataTables_scrollBody tbody tr:first-child td,div.dataTables_scrollBody tbody tr:first-child th{border-top:none}div.dataTables_scrollFoot table{margin-top:0!important;border-top:none}table.table-bordered.dataTable{border-collapse:separate!important}table.table-bordered thead td,table.table-bordered thead th{border-left-width:0;border-top-width:0}table.table-bordered tbody td,table.table-bordered tbody th,table.table-bordered tfoot td,table.table-bordered tfoot th{border-left-width:0;border-bottom-width:0}table.table-bordered td:last-child,table.table-bordered th:last-child{border-right-width:0}div.dataTables_scrollHead table.table-bordered{border-bottom-width:0}.table.dataTable tbody tr.active td,.table.dataTable tbody tr.active th{background-color:#08C;color:#fff}.table.dataTable tbody tr.active:hover td,.table.dataTable tbody tr.active:hover th{background-color:#0075b0!important}.table.dataTable tbody tr.active td>a,.table.dataTable tbody tr.active th>a{color:#fff}.table-striped.dataTable tbody tr.active:nth-child(odd) td,.table-striped.dataTable tbody tr.active:nth-child(odd) th{background-color:#017ebc}table.DTTT_selectable tbody tr{cursor:pointer}div.DTTT .btn:hover{text-decoration:none!important}ul.DTTT_dropdown.dropdown-menu{z-index:2003}ul.DTTT_dropdown.dropdown-menu a{color:#333!important}ul.DTTT_dropdown.dropdown-menu li{position:relative}ul.DTTT_dropdown.dropdown-menu li:hover a{background-color:#08c;color:#fff!important}div.DTTT_collection_background{z-index:2002}div.DTTT_print_info{position:fixed;top:50%;left:50%;width:400px;height:150px;margin-left:-200px;margin-top:-75px;text-align:center;color:#333;padding:10px 30px;opacity:.95;background-color:#fff;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0,0,0,.5);box-shadow:0 3px 7px rgba(0,0,0,.5)}div.DTTT_print_info h6{font-weight:400;font-size:28px;line-height:28px;margin:1em}div.DTTT_print_info p{font-size:14px;line-height:20px}div.dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:60px;margin-left:-50%;margin-top:-25px;padding-top:20px;padding-bottom:20px;text-align:center;font-size:1.2em;background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(25%,rgba(255,255,255,.9)),color-stop(75%,rgba(255,255,255,.9)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,.9) 25%,rgba(255,255,255,.9) 75%,rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,.9) 25%,rgba(255,255,255,.9) 75%,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,.9) 25%,rgba(255,255,255,.9) 75%,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,.9) 25%,rgba(255,255,255,.9) 75%,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,.9) 25%,rgba(255,255,255,.9) 75%,rgba(255,255,255,0) 100%)}div.DTFC_LeftFootWrapper table,div.DTFC_LeftHeadWrapper table,div.DTFC_RightFootWrapper table,div.DTFC_RightHeadWrapper table,table.DTFC_Cloned tr.even{background-color:#fff;margin-bottom:0}div.DTFC_LeftHeadWrapper table,div.DTFC_RightHeadWrapper table{border-bottom:none!important;margin-bottom:0!important;border-top-right-radius:0!important;border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child,div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child,div.DTFC_RightHeadWrapper table thead tr:last-child td:first-child,div.DTFC_RightHeadWrapper table thead tr:last-child th:first-child{border-bottom-left-radius:0!important;border-bottom-right-radius:0!important}div.DTFC_LeftBodyWrapper table,div.DTFC_RightBodyWrapper table{border-top:none;margin:0!important}div.DTFC_LeftBodyWrapper tbody tr:first-child td,div.DTFC_LeftBodyWrapper tbody tr:first-child th,div.DTFC_RightBodyWrapper tbody tr:first-child td,div.DTFC_RightBodyWrapper tbody tr:first-child th{border-top:none}div.DTFC_LeftFootWrapper table,div.DTFC_RightFootWrapper table{border-top:none;margin-top:0!important}div.DTFC_LeftBodyWrapper table.dataTable thead .sorting:after,div.DTFC_LeftBodyWrapper table.dataTable thead .sorting_asc:after,div.DTFC_LeftBodyWrapper table.dataTable thead .sorting_desc:after,div.DTFC_RightBodyWrapper table.dataTable thead .sorting:after,div.DTFC_RightBodyWrapper table.dataTable thead .sorting_asc:after,div.DTFC_RightBodyWrapper table.dataTable thead .sorting_desc:after{display:none}div.FixedHeader_Cloned table{margin:0!important} 2 | -------------------------------------------------------------------------------- /Resources/public/images/sample_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliHichem/AliDatatableBundle/36f2097ce1eacf419bfd379dece119b1f0bbe907/Resources/public/images/sample_01.png -------------------------------------------------------------------------------- /Resources/public/js/dataTables.bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | DataTables Bootstrap 3 integration 3 | ©2011-2014 SpryMedia Ltd - datatables.net/license 4 | */ 5 | (function(l,q){var e=function(b,c){b.extend(!0,c.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-5'i><'col-sm-7'p>>",renderer:"bootstrap"});b.extend(c.ext.classes,{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm"});c.ext.renderer.pageButton.bootstrap=function(g,e,r,s,i,m){var t=new c.Api(g),u=g.oClasses,j=g.oLanguage.oPaginate,d,f,n=0,p=function(c,e){var k,h,o,a,l=function(a){a.preventDefault(); 6 | b(a.currentTarget).hasClass("disabled")||t.page(a.data.action).draw(!1)};k=0;for(h=e.length;k",{"class":u.sPageButton+" "+ 7 | f,id:0===r&&"string"===typeof a?g.sTableId+"_"+a:null}).append(b("",{href:"#","aria-controls":g.sTableId,"data-dt-idx":n,tabindex:g.iTabIndex}).html(d)).appendTo(c),g.oApi._fnBindAction(o,{action:a},l),n++)}},h;try{h=b(q.activeElement).data("dt-idx")}catch(l){}p(b(e).empty().html('