Skip to content

Commit e0387ab

Browse files
MaximePinotcurry684
authored andcommitted
Add DataTable Events (#76)
* Add DataTable Events * Add Events in the documentation * Fix test error with Symfony ^3.4 * Resolve requested changes
1 parent 33fb34a commit e0387ab

File tree

16 files changed

+307
-32
lines changed

16 files changed

+307
-32
lines changed

docs/source/index.html.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,28 @@ Note that implementing your own criteria overrides the default, meaning searchin
300300
longer work automatically. Add the `SearchCriteriaProvider` manually to combine the default behavior
301301
with your own implementation.
302302

303+
### Events
304+
305+
```php?start_inline=1
306+
$table->createAdapter(ORMAdapter::class, [
307+
'entity' => Employee::class,
308+
'query' => function (QueryBuilder $builder) {
309+
$builder
310+
->select('e')
311+
->addSelect('c')
312+
->from(Employee::class, 'e')
313+
->leftJoin('e.company', 'c')
314+
;
315+
},
316+
]);
317+
318+
$table->addEventListener(ORMAdapterEvents::PRE_QUERY, function(ORMAdapterQueryEvent $event) {
319+
$event->getQuery()->useResultCache(true)->useQueryCache(true);
320+
});
321+
```
322+
The `PRE_QUERY` event is dispatched after the QueryBuilder built the Query
323+
and before the iteration starts. It can be useful to configure the cache.
324+
303325
## Elastica
304326

305327
```php?start_inline=1
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* Symfony DataTables Bundle
5+
* (c) Omines Internetbureau B.V. - https://omines.nl/
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace Omines\DataTablesBundle\Adapter\Doctrine\Event;
14+
15+
use Doctrine\ORM\Query;
16+
use Symfony\Component\EventDispatcher\Event;
17+
18+
/**
19+
* @author Maxime Pinot <[email protected]>
20+
*/
21+
class ORMAdapterQueryEvent extends Event
22+
{
23+
/** @var Query */
24+
protected $query;
25+
26+
/**
27+
* ORMAdapterQueryEvent constructor.
28+
*
29+
* @param Query $query
30+
*/
31+
public function __construct(Query $query)
32+
{
33+
$this->query = $query;
34+
}
35+
36+
/**
37+
* @return Query
38+
*/
39+
public function getQuery(): Query
40+
{
41+
return $this->query;
42+
}
43+
}

src/Adapter/Doctrine/ORMAdapter.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Doctrine\ORM\QueryBuilder;
1818
use Omines\DataTablesBundle\Adapter\AbstractAdapter;
1919
use Omines\DataTablesBundle\Adapter\AdapterQuery;
20+
use Omines\DataTablesBundle\Adapter\Doctrine\Event\ORMAdapterQueryEvent;
2021
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\AutomaticQueryBuilder;
2122
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\QueryBuilderProcessorInterface;
2223
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
@@ -151,10 +152,10 @@ protected function getAliases(AdapterQuery $query)
151152
foreach ($builder->getDQLPart('join') as $joins) {
152153
/** @var Query\Expr\Join $join */
153154
foreach ($joins as $join) {
154-
if(strstr($join->getJoin(), '.') === false){
155+
if (false === mb_strstr($join->getJoin(), '.')) {
155156
continue;
156-
}
157-
157+
}
158+
158159
list($origin, $target) = explode('.', $join->getJoin());
159160

160161
$mapping = $aliases[$origin][1]->getAssociationMapping($target);
@@ -197,8 +198,11 @@ protected function getResults(AdapterQuery $query): \Traversable
197198
;
198199
}
199200

200-
//!$this->query->getHint(self::HINT_DISTINCT) || isset($this->selectedClasses[$joinedDqlAlias])
201-
foreach ($builder->getQuery()->iterate([], $this->hydrationMode) as $result) {
201+
$query = $builder->getQuery();
202+
$event = new ORMAdapterQueryEvent($query);
203+
$state->getDataTable()->getEventDispatcher()->dispatch(ORMAdapterEvents::PRE_QUERY, $event);
204+
205+
foreach ($query->iterate([], $this->hydrationMode) as $result) {
202206
yield $entity = array_values($result)[0];
203207
if (Query::HYDRATE_OBJECT === $this->hydrationMode) {
204208
$this->manager->detach($entity);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* Symfony DataTables Bundle
5+
* (c) Omines Internetbureau B.V. - https://omines.nl/
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace Omines\DataTablesBundle\Adapter\Doctrine;
14+
15+
/**
16+
* Available events.
17+
*
18+
* @author Maxime Pinot <[email protected]>
19+
*/
20+
final class ORMAdapterEvents
21+
{
22+
/**
23+
* The PRE_QUERY event is dispatched after the QueryBuilder
24+
* built the Query and before the iteration starts.
25+
*
26+
* It can be useful to configure the cache.
27+
*
28+
* @Event("Omines\DataTablesBundle\Adapter\Doctrine\Event\ORMAdapterQueryEvent")
29+
*/
30+
const PRE_QUERY = 'omines_datatables.ormadapter.pre_query';
31+
}

src/DataTable.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Omines\DataTablesBundle\Exception\InvalidArgumentException;
2020
use Omines\DataTablesBundle\Exception\InvalidConfigurationException;
2121
use Omines\DataTablesBundle\Exception\InvalidStateException;
22+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2223
use Symfony\Component\HttpFoundation\JsonResponse;
2324
use Symfony\Component\HttpFoundation\Request;
2425
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -65,6 +66,9 @@ class DataTable
6566
/** @var array<string, AbstractColumn> */
6667
protected $columnsByName = [];
6768

69+
/** @var EventDispatcherInterface */
70+
protected $eventDispatcher;
71+
6872
/** @var string */
6973
protected $method = Request::METHOD_POST;
7074

@@ -104,11 +108,14 @@ class DataTable
104108
/**
105109
* DataTable constructor.
106110
*
111+
* @param EventDispatcherInterface $eventDispatcher
107112
* @param array $options
108113
* @param Instantiator|null $instantiator
109114
*/
110-
public function __construct(array $options = [], Instantiator $instantiator = null)
115+
public function __construct(EventDispatcherInterface $eventDispatcher, array $options = [], Instantiator $instantiator = null)
111116
{
117+
$this->eventDispatcher = $eventDispatcher;
118+
112119
$this->instantiator = $instantiator ?? new Instantiator();
113120

114121
$resolver = new OptionsResolver();
@@ -138,6 +145,24 @@ public function add(string $name, string $type, array $options = [])
138145
return $this;
139146
}
140147

148+
/**
149+
* Adds an event listener to an event on this DataTable.
150+
*
151+
* @param string $eventName The name of the event to listen to
152+
* @param callable $listener The listener to execute
153+
* @param int $priority The priority of the listener. Listeners
154+
* with a higher priority are called before
155+
* listeners with a lower priority.
156+
*
157+
* @return $this
158+
*/
159+
public function addEventListener(string $eventName, callable $listener, int $priority = 0): self
160+
{
161+
$this->eventDispatcher->addListener($eventName, $listener, $priority);
162+
163+
return $this;
164+
}
165+
141166
/**
142167
* @param int|string|AbstractColumn $column
143168
* @param string $direction
@@ -204,6 +229,14 @@ public function getColumns(): array
204229
return $this->columns;
205230
}
206231

232+
/**
233+
* @return EventDispatcherInterface
234+
*/
235+
public function getEventDispatcher(): EventDispatcherInterface
236+
{
237+
return $this->eventDispatcher;
238+
}
239+
207240
/**
208241
* @return bool
209242
*/

src/DataTableFactory.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
namespace Omines\DataTablesBundle;
1414

1515
use Omines\DataTablesBundle\DependencyInjection\Instantiator;
16+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
1617
use Symfony\Component\HttpFoundation\Request;
1718

1819
class DataTableFactory
@@ -29,17 +30,23 @@ class DataTableFactory
2930
/** @var array */
3031
protected $config;
3132

33+
/** @var EventDispatcherInterface */
34+
protected $eventDispatcher;
35+
3236
/**
3337
* DataTableFactory constructor.
3438
*
3539
* @param array $config
3640
* @param DataTableRendererInterface $renderer
41+
* @param Instantiator $instantiator
42+
* @param EventDispatcherInterface $eventDispatcher
3743
*/
38-
public function __construct(array $config, DataTableRendererInterface $renderer, Instantiator $instantiator)
44+
public function __construct(array $config, DataTableRendererInterface $renderer, Instantiator $instantiator, EventDispatcherInterface $eventDispatcher)
3945
{
4046
$this->config = $config;
4147
$this->renderer = $renderer;
4248
$this->instantiator = $instantiator;
49+
$this->eventDispatcher = $eventDispatcher;
4350
}
4451

4552
/**
@@ -50,7 +57,7 @@ public function create(array $options = [])
5057
{
5158
$config = $this->config;
5259

53-
return (new DataTable(array_merge($config['options'] ?? [], $options), $this->instantiator))
60+
return (new DataTable($this->eventDispatcher, array_merge($config['options'] ?? [], $options), $this->instantiator))
5461
->setRenderer($this->renderer)
5562
->setMethod($config['method'] ?? Request::METHOD_POST)
5663
->setPersistState($config['persist_state'] ?? 'fragment')

src/Resources/config/services.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<argument>%datatables.config%</argument>
2424
<argument type="service" id="datatables.renderer" />
2525
<argument type="service" id="Omines\DataTablesBundle\DependencyInjection\Instantiator" />
26+
<argument type="service" id="event_dispatcher" />
2627
</service>
2728

2829
<!-- Support services -->
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* Symfony DataTables Bundle
5+
* (c) Omines Internetbureau B.V. - https://omines.nl/
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace Tests\Fixtures\AppBundle\Controller;
14+
15+
use Omines\DataTablesBundle\Adapter\Doctrine\Event\ORMAdapterQueryEvent;
16+
use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter;
17+
use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapterEvents;
18+
use Omines\DataTablesBundle\Column\TextColumn;
19+
use Omines\DataTablesBundle\Controller\DataTablesTrait;
20+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
21+
use Symfony\Component\HttpFoundation\Request;
22+
use Symfony\Component\HttpFoundation\Response;
23+
use Tests\Fixtures\AppBundle\Entity\Employee;
24+
25+
/**
26+
* @author Maxime Pinot <[email protected]>
27+
*/
28+
class ORMAdapterEventsController extends AbstractController
29+
{
30+
const PRE_QUERY_RESULT_CACHE_ID = 'datatable_result_cache';
31+
32+
use DataTablesTrait;
33+
34+
public function preQueryAction(Request $request): Response
35+
{
36+
$datatable = $this->createDataTable()
37+
->add('firstName', TextColumn::class)
38+
->add('lastName', TextColumn::class)
39+
->add('company', TextColumn::class, ['field' => 'company.name'])
40+
->createAdapter(ORMAdapter::class, [
41+
'entity' => Employee::class,
42+
])
43+
->addEventListener(ORMAdapterEvents::PRE_QUERY, function (ORMAdapterQueryEvent $event) {
44+
$event->getQuery()->useResultCache(true, 0, self::PRE_QUERY_RESULT_CACHE_ID);
45+
});
46+
47+
return $datatable->handleRequest($request)->getResponse();
48+
}
49+
}

tests/Fixtures/config.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ framework:
1313
test: ~
1414
profiler:
1515
collect: false
16+
cache:
17+
pools:
18+
doctrine.result_cache_pool:
19+
adapter: cache.app
20+
doctrine.system_cache_pool:
21+
adapter: cache.system
1622

1723
doctrine:
1824
dbal:
@@ -23,6 +29,15 @@ doctrine:
2329
auto_generate_proxy_classes: '%kernel.debug%'
2430
naming_strategy: doctrine.orm.naming_strategy.underscore
2531
auto_mapping: true
32+
metadata_cache_driver:
33+
type: service
34+
id: doctrine.system_cache_provider
35+
query_cache_driver:
36+
type: service
37+
id: doctrine.system_cache_provider
38+
result_cache_driver:
39+
type: service
40+
id: doctrine.result_cache_provider
2641

2742
twig:
2843
debug: '%kernel.debug%'

tests/Fixtures/routing.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,6 @@ grouped2:
2929
employee.edit:
3030
path: /employee/{id}
3131

32+
orm_adapter_events.pre_query:
33+
path: /orm-adapter-events/pre-query
34+
controller: Tests\Fixtures\AppBundle\Controller\ORMAdapterEventsController::preQueryAction

0 commit comments

Comments
 (0)