Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions docs/src/docs/features/query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Query

The data table requires a query to fetch the data. This query can be passed directly to the factory or defined as a default option in the data table type.

[[toc]]

## Ways to handle the query

There are several ways to pass the query to a DataTable.

### Passing the query to the factory

The query can be passed directly to the factory in the controller:

```php
use App\DataTable\Type\ProductDataTableType;
use Kreyu\Bundle\DataTableBundle\DataTableFactoryAwareTrait;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class ProductController extends AbstractController
{
use DataTableFactoryAwareTrait;

public function index(Request $request): Response
{
// $query can be a QueryBuilder, an array, etc.
$dataTable = $this->createDataTable(ProductDataTableType::class, $query);
$dataTable->handleRequest($request);

return $this->render('product/index.html.twig', [
'products' => $dataTable->createView(),
]);
}
}
```

### Defining a default value

You can provide a default value for the `query` option in your DataTable type. This avoids having to recreate the query builder every time you create the DataTable.

Similar to how Symfony forms allow initializing `data` when no data is provided, you can initialize the `query` option.

To do this, add the `query` option to your DataTable type:

```php
use Kreyu\Bundle\DataTableBundle\Type\AbstractDataTableType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Repository\ProductRepository;

class ProductDataTableType extends AbstractDataTableType
{
public function __construct(
private ProductRepository $productRepository,
) {
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'query' => $this->productRepository->createQueryBuilder('p'),
]);
}
}
```

This allows you to define a default configuration that is fully overridable.

When a default value is defined, it is no longer mandatory to pass the query as the second argument of the `createDataTable` method.

```php
$dataTable = $this->createDataTable(ProductDataTableType::class);
```

## Overriding the option

When you define a default `query`, you can still override it when creating the data table in the controller:

```php
$dataTable = $this->createDataTable(ProductDataTableType::class, $customQuery);
```

Or by passing it in the options:

```php
$dataTable = $this->createDataTable(ProductDataTableType::class, null, [
'query' => $customQuery,
]);
```

### Extending the default query

If you want to reuse the default query defined in the `ProductDataTableType` and add a condition from the controller, you can use the `OptionsResolver` normalizer:

```php
use App\DataTable\Type\ProductDataTableType;
use Kreyu\Bundle\DataTableBundle\DataTableFactoryAwareTrait;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\OptionsResolver\Options;
use Doctrine\ORM\QueryBuilder;

class ProductController extends AbstractController
{
use DataTableFactoryAwareTrait;

public function index()
{
$dataTable = $this->createDataTable(ProductDataTableType::class, null, [
'query' => function (Options $options, $query) {
if ($query instanceof QueryBuilder) {
$query->andWhere('p.active = :active')
->setParameter('active', true);
}

return $query;
},
]);
}
}
```
1 change: 1 addition & 0 deletions docs/src/docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ If you want to include your application here, open an issue, create a pull reque
- [Exporting](features/exporting.md) with or without applied pagination, filters and personalization
- [Theming](features/theming.md) of every part of the bundle using Twig
- [Data source agnostic](features/extensibility.md) with Doctrine ORM supported out of the box
- [Query](features/query.md) to fetch the data from any source
- [Asynchronicity](features/asynchronicity.md) thanks to integration with Turbo (with prefetching enabled by default)

## Use cases
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Kreyu\Bundle\DataTableBundle\DataTableFactoryInterface;
use Kreyu\Bundle\DataTableBundle\DataTableInterface;
use Kreyu\Bundle\DataTableBundle\DataTableView;
use Kreyu\Bundle\DataTableBundle\Query\ProxyQueryInterface;
use Kreyu\Bundle\DataTableBundle\Type\DataTableTypeInterface;
use Kreyu\Bundle\DataTableBundle\Type\ResolvedDataTableTypeInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
Expand Down Expand Up @@ -42,9 +41,9 @@ public function getTypeExtensions(): array
return $this->proxiedType->getTypeExtensions();
}

public function createBuilder(DataTableFactoryInterface $factory, string $name, ?ProxyQueryInterface $query = null, array $options = []): DataTableBuilderInterface
public function createBuilder(DataTableFactoryInterface $factory, string $name, array $options = []): DataTableBuilderInterface
{
$builder = $this->proxiedType->createBuilder($factory, $name, $query, $options);
$builder = $this->proxiedType->createBuilder($factory, $name, $options);
$builder->setAttribute('data_collector/passed_options', $options);
$builder->setType($this);

Expand Down
3 changes: 2 additions & 1 deletion src/DataTableBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,11 @@ class DataTableBuilder extends DataTableConfigBuilder implements DataTableBuilde
*/
private array $unresolvedExporters = [];

private ?ProxyQueryInterface $query = null;

public function __construct(
string $name,
ResolvedDataTableTypeInterface $type,
private ?ProxyQueryInterface $query = null,
EventDispatcherInterface $dispatcher = new EventDispatcher(),
array $options = [],
) {
Expand Down
16 changes: 11 additions & 5 deletions src/DataTableFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ public function createNamedBuilder(string $name, string $type = DataTableType::c
{
$query = $data;

$type = $this->registry->getType($type);

$builder = $type->createBuilder($this, $name, $options);

$type->buildDataTable($builder, $builder->getOptions());

if (null === $data && $builder->hasOption('query')) {
$data = $builder->getOption('query');
}

if (null !== $data && !$data instanceof ProxyQueryInterface) {
foreach ($this->registry->getProxyQueryFactories() as $proxyQueryFactory) {
if ($proxyQueryFactory->supports($data)) {
Expand All @@ -42,11 +52,7 @@ public function createNamedBuilder(string $name, string $type = DataTableType::c
}
}

$type = $this->registry->getType($type);

$builder = $type->createBuilder($this, $name, $query, $options);

$type->buildDataTable($builder, $builder->getOptions());
$builder->setQuery($query);

return $builder;
}
Expand Down
5 changes: 2 additions & 3 deletions src/Type/ResolvedDataTableType.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
use Kreyu\Bundle\DataTableBundle\DataTableInterface;
use Kreyu\Bundle\DataTableBundle\DataTableView;
use Kreyu\Bundle\DataTableBundle\Extension\DataTableTypeExtensionInterface;
use Kreyu\Bundle\DataTableBundle\Query\ProxyQueryInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\OptionsResolver\Exception\ExceptionInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
Expand Down Expand Up @@ -52,15 +51,15 @@ public function getTypeExtensions(): array
/**
* @throws ExceptionInterface
*/
public function createBuilder(DataTableFactoryInterface $factory, string $name, ?ProxyQueryInterface $query = null, array $options = []): DataTableBuilderInterface
public function createBuilder(DataTableFactoryInterface $factory, string $name, array $options = []): DataTableBuilderInterface
{
try {
$options = $this->getOptionsResolver()->resolve($options);
} catch (ExceptionInterface $exception) {
throw new $exception(sprintf('An error has occurred resolving the options of the data table "%s": ', get_debug_type($this->getInnerType())).$exception->getMessage(), $exception->getCode(), $exception);
}

return new DataTableBuilder($name, $this, $query, new EventDispatcher(), $options);
return new DataTableBuilder($name, $this, new EventDispatcher(), $options);
}

public function createView(DataTableInterface $dataTable): DataTableView
Expand Down
3 changes: 1 addition & 2 deletions src/Type/ResolvedDataTableTypeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Kreyu\Bundle\DataTableBundle\DataTableInterface;
use Kreyu\Bundle\DataTableBundle\DataTableView;
use Kreyu\Bundle\DataTableBundle\Extension\DataTableTypeExtensionInterface;
use Kreyu\Bundle\DataTableBundle\Query\ProxyQueryInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

interface ResolvedDataTableTypeInterface
Expand All @@ -28,7 +27,7 @@ public function getTypeExtensions(): array;
/**
* @param array<string, mixed> $options
*/
public function createBuilder(DataTableFactoryInterface $factory, string $name, ?ProxyQueryInterface $query = null, array $options = []): DataTableBuilderInterface;
public function createBuilder(DataTableFactoryInterface $factory, string $name, array $options = []): DataTableBuilderInterface;

public function createView(DataTableInterface $dataTable): DataTableView;

Expand Down
7 changes: 5 additions & 2 deletions tests/Unit/DataTableBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -735,13 +735,16 @@ public function testGetDataTableResolvesExporters()

private function createBuilder(): DataTableBuilder
{
return new DataTableBuilder(
$builder = new DataTableBuilder(
name: 'foo',
type: $this->createStub(ResolvedDataTableTypeInterface::class),
query: $this->createStub(ProxyQueryInterface::class),
dispatcher: $this->createStub(EventDispatcherInterface::class),
options: [],
);

$builder->setQuery(query: $this->createStub(ProxyQueryInterface::class));

return $builder;
}

private function createColumnFactory(): ColumnFactory
Expand Down