mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 01:25:55 +02:00
Use FetchJoinORMAdapter from DatatablesBundle instead of custom one.
This commit is contained in:
parent
7de2492480
commit
03bbdb699d
3 changed files with 1 additions and 514 deletions
|
@ -1,166 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published
|
|
||||||
* by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation; either version 2
|
|
||||||
* of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\DataTables\Adapter;
|
|
||||||
|
|
||||||
use Doctrine\ORM\Query;
|
|
||||||
use Doctrine\ORM\QueryBuilder;
|
|
||||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
|
||||||
use Omines\DataTablesBundle\Adapter\AdapterQuery;
|
|
||||||
use Omines\DataTablesBundle\Adapter\Doctrine\Event\ORMAdapterQueryEvent;
|
|
||||||
use Omines\DataTablesBundle\Column\AbstractColumn;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to ORMAdapter this class allows to access objects from the doctrine ORM.
|
|
||||||
* Unlike the default ORMAdapter supports Fetch Joins (additional entites are fetched from DB via joins) using
|
|
||||||
* the Doctrine Paginator.
|
|
||||||
*
|
|
||||||
* @author Jan Böhmer
|
|
||||||
*/
|
|
||||||
class FetchJoinORMAdapter extends ORMAdapter
|
|
||||||
{
|
|
||||||
protected $use_simple_total;
|
|
||||||
|
|
||||||
public function configure(array $options): void
|
|
||||||
{
|
|
||||||
parent::configure($options);
|
|
||||||
$this->use_simple_total = $options['simple_total_query'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResults(AdapterQuery $query): \Traversable
|
|
||||||
{
|
|
||||||
$builder = $query->get('qb');
|
|
||||||
$state = $query->getState();
|
|
||||||
|
|
||||||
// Apply definitive view state for current 'page' of the table
|
|
||||||
foreach ($state->getOrderBy() as [$column, $direction]) {
|
|
||||||
/** @var AbstractColumn $column */
|
|
||||||
if ($column->isOrderable()) {
|
|
||||||
$builder->addOrderBy($column->getOrderField(), $direction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($state->getLength() > 0) {
|
|
||||||
$builder
|
|
||||||
->setFirstResult($state->getStart())
|
|
||||||
->setMaxResults($state->getLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = $builder->getQuery();
|
|
||||||
$event = new ORMAdapterQueryEvent($query);
|
|
||||||
$state->getDataTable()->getEventDispatcher()->dispatch($event);
|
|
||||||
|
|
||||||
//Use Doctrine paginator for result iteration
|
|
||||||
$paginator = new Paginator($query);
|
|
||||||
|
|
||||||
foreach ($paginator->getIterator() as $result) {
|
|
||||||
yield $result;
|
|
||||||
$this->manager->detach($result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getCount(QueryBuilder $queryBuilder, string $identifier)
|
|
||||||
{
|
|
||||||
$paginator = new Paginator($queryBuilder);
|
|
||||||
|
|
||||||
return $paginator->count();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function configureOptions(OptionsResolver $resolver): void
|
|
||||||
{
|
|
||||||
parent::configureOptions($resolver);
|
|
||||||
|
|
||||||
//Enforce object hydration mode (fetch join only works for objects)
|
|
||||||
$resolver->addAllowedValues('hydrate', Query::HYDRATE_OBJECT);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add the possibility to replace the query for total entity count through a very simple one, to improve performance.
|
|
||||||
* You can only use this option, if you did not apply any criteria to your total count.
|
|
||||||
*/
|
|
||||||
$resolver->setDefault('simple_total_query', false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function prepareQuery(AdapterQuery $query): void
|
|
||||||
{
|
|
||||||
$state = $query->getState();
|
|
||||||
$query->set('qb', $builder = $this->createQueryBuilder($state));
|
|
||||||
$query->set('rootAlias', $rootAlias = $builder->getDQLPart('from')[0]->getAlias());
|
|
||||||
|
|
||||||
// Provide default field mappings if needed
|
|
||||||
foreach ($state->getDataTable()->getColumns() as $column) {
|
|
||||||
if (null === $column->getField() && isset($this->metadata->fieldMappings[$name = $column->getName()])) {
|
|
||||||
$column->setOption('field', "{$rootAlias}.{$name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var Query\Expr\From $fromClause */
|
|
||||||
$fromClause = $builder->getDQLPart('from')[0];
|
|
||||||
$identifier = "{$fromClause->getAlias()}.{$this->metadata->getSingleIdentifierFieldName()}";
|
|
||||||
|
|
||||||
//Use simpler (faster) total count query if the user wanted so...
|
|
||||||
if ($this->use_simple_total) {
|
|
||||||
$query->setTotalRows($this->getSimpleTotalCount($builder));
|
|
||||||
} else {
|
|
||||||
$query->setTotalRows($this->getCount($builder, $identifier));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get record count after filtering
|
|
||||||
$this->buildCriteria($builder, $state);
|
|
||||||
$query->setFilteredRows($this->getCount($builder, $identifier));
|
|
||||||
|
|
||||||
// Perform mapping of all referred fields and implied fields
|
|
||||||
$aliases = $this->getAliases($query);
|
|
||||||
$query->set('aliases', $aliases);
|
|
||||||
$query->setIdentifierPropertyPath($this->mapFieldToPropertyPath($identifier, $aliases));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getSimpleTotalCount(QueryBuilder $queryBuilder): int
|
|
||||||
{
|
|
||||||
/** The paginator count queries can be rather slow, so when query for total count (100ms or longer),
|
|
||||||
* just return the entity count.
|
|
||||||
*/
|
|
||||||
/** @var Query\Expr\From $from_expr */
|
|
||||||
$from_expr = $queryBuilder->getDQLPart('from')[0];
|
|
||||||
|
|
||||||
return $this->manager->getRepository($from_expr->getFrom())->count([]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,347 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Symfony DataTables Bundle
|
|
||||||
* (c) Omines Internetbureau B.V. - https://omines.nl/
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\DataTables\Adapter;
|
|
||||||
|
|
||||||
use Doctrine\Common\Persistence\ManagerRegistry;
|
|
||||||
use Doctrine\ORM\EntityManager;
|
|
||||||
use Doctrine\ORM\Query;
|
|
||||||
use Doctrine\ORM\QueryBuilder;
|
|
||||||
use Omines\DataTablesBundle\Adapter\AbstractAdapter;
|
|
||||||
use Omines\DataTablesBundle\Adapter\AdapterQuery;
|
|
||||||
use Omines\DataTablesBundle\Adapter\Doctrine\Event\ORMAdapterQueryEvent;
|
|
||||||
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\AutomaticQueryBuilder;
|
|
||||||
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\QueryBuilderProcessorInterface;
|
|
||||||
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
|
|
||||||
use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapterEvents;
|
|
||||||
use Omines\DataTablesBundle\Column\AbstractColumn;
|
|
||||||
use Omines\DataTablesBundle\DataTableState;
|
|
||||||
use Omines\DataTablesBundle\Exception\InvalidConfigurationException;
|
|
||||||
use Omines\DataTablesBundle\Exception\MissingDependencyException;
|
|
||||||
use Symfony\Component\OptionsResolver\Options;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ORMAdapter.
|
|
||||||
*
|
|
||||||
* @author Niels Keurentjes <niels.keurentjes@omines.com>
|
|
||||||
* @author Robbert Beesems <robbert.beesems@omines.com>
|
|
||||||
*/
|
|
||||||
class ORMAdapter extends AbstractAdapter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var EntityManager
|
|
||||||
*/
|
|
||||||
protected $manager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var \Doctrine\ORM\Mapping\ClassMetadata
|
|
||||||
*/
|
|
||||||
protected $metadata;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var QueryBuilderProcessorInterface[]
|
|
||||||
*/
|
|
||||||
protected $criteriaProcessors;
|
|
||||||
/**
|
|
||||||
* @var ManagerRegistry
|
|
||||||
*/
|
|
||||||
private $registry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $hydrationMode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var QueryBuilderProcessorInterface[]
|
|
||||||
*/
|
|
||||||
private $queryBuilderProcessors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DoctrineAdapter constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(?ManagerRegistry $registry = null)
|
|
||||||
{
|
|
||||||
if (null === $registry) {
|
|
||||||
throw new MissingDependencyException('Install doctrine/doctrine-bundle to use the ORMAdapter');
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::__construct();
|
|
||||||
$this->registry = $registry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function configure(array $options): void
|
|
||||||
{
|
|
||||||
$resolver = new OptionsResolver();
|
|
||||||
$this->configureOptions($resolver);
|
|
||||||
$options = $resolver->resolve($options);
|
|
||||||
|
|
||||||
// Enable automated mode or just get the general default entity manager
|
|
||||||
if (null === ($this->manager = $this->registry->getManagerForClass($options['entity']))) {
|
|
||||||
throw new InvalidConfigurationException(sprintf('Doctrine has no manager for entity "%s", is it correctly imported and referenced?', $options['entity']));
|
|
||||||
}
|
|
||||||
$this->metadata = $this->manager->getClassMetadata($options['entity']);
|
|
||||||
if (empty($options['query'])) {
|
|
||||||
$options['query'] = [new AutomaticQueryBuilder($this->manager, $this->metadata)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set options
|
|
||||||
$this->hydrationMode = $options['hydrate'];
|
|
||||||
$this->queryBuilderProcessors = $options['query'];
|
|
||||||
$this->criteriaProcessors = $options['criteria'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addCriteriaProcessor($processor): void
|
|
||||||
{
|
|
||||||
$this->criteriaProcessors[] = $this->normalizeProcessor($processor);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function prepareQuery(AdapterQuery $query): void
|
|
||||||
{
|
|
||||||
$state = $query->getState();
|
|
||||||
$query->set('qb', $builder = $this->createQueryBuilder($state));
|
|
||||||
$query->set('rootAlias', $rootAlias = $builder->getDQLPart('from')[0]->getAlias());
|
|
||||||
|
|
||||||
// Provide default field mappings if needed
|
|
||||||
foreach ($state->getDataTable()->getColumns() as $column) {
|
|
||||||
if (null === $column->getField() && isset($this->metadata->fieldMappings[$name = $column->getName()])) {
|
|
||||||
$column->setOption('field', "{$rootAlias}.{$name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var Query\Expr\From $fromClause */
|
|
||||||
$fromClause = $builder->getDQLPart('from')[0];
|
|
||||||
$identifier = "{$fromClause->getAlias()}.{$this->metadata->getSingleIdentifierFieldName()}";
|
|
||||||
$query->setTotalRows($this->getCount($builder, $identifier));
|
|
||||||
|
|
||||||
// Get record count after filtering
|
|
||||||
$this->buildCriteria($builder, $state);
|
|
||||||
$query->setFilteredRows($this->getCount($builder, $identifier));
|
|
||||||
|
|
||||||
// Perform mapping of all referred fields and implied fields
|
|
||||||
$aliases = $this->getAliases($query);
|
|
||||||
$query->set('aliases', $aliases);
|
|
||||||
$query->setIdentifierPropertyPath($this->mapFieldToPropertyPath($identifier, $aliases));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
protected function getAliases(AdapterQuery $query)
|
|
||||||
{
|
|
||||||
/** @var QueryBuilder $builder */
|
|
||||||
$builder = $query->get('qb');
|
|
||||||
$aliases = [];
|
|
||||||
|
|
||||||
/** @var Query\Expr\From $from */
|
|
||||||
foreach ($builder->getDQLPart('from') as $from) {
|
|
||||||
$aliases[$from->getAlias()] = [null, $this->manager->getMetadataFactory()->getMetadataFor($from->getFrom())];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alias all joins
|
|
||||||
foreach ($builder->getDQLPart('join') as $joins) {
|
|
||||||
/** @var Query\Expr\Join $join */
|
|
||||||
foreach ($joins as $join) {
|
|
||||||
if (false === mb_strstr($join->getJoin(), '.')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$origin, $target] = explode('.', $join->getJoin());
|
|
||||||
|
|
||||||
$mapping = $aliases[$origin][1]->getAssociationMapping($target);
|
|
||||||
$aliases[$join->getAlias()] = [$join->getJoin(), $this->manager->getMetadataFactory()->getMetadataFor($mapping['targetEntity'])];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function mapPropertyPath(AdapterQuery $query, AbstractColumn $column)
|
|
||||||
{
|
|
||||||
return $this->mapFieldToPropertyPath($column->getField(), $query->get('aliases'));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getResults(AdapterQuery $query): \Traversable
|
|
||||||
{
|
|
||||||
/** @var QueryBuilder $builder */
|
|
||||||
$builder = $query->get('qb');
|
|
||||||
$state = $query->getState();
|
|
||||||
|
|
||||||
// Apply definitive view state for current 'page' of the table
|
|
||||||
foreach ($state->getOrderBy() as [$column, $direction]) {
|
|
||||||
/** @var AbstractColumn $column */
|
|
||||||
if ($column->isOrderable()) {
|
|
||||||
$builder->addOrderBy($column->getOrderField(), $direction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($state->getLength() > 0) {
|
|
||||||
$builder
|
|
||||||
->setFirstResult($state->getStart())
|
|
||||||
->setMaxResults($state->getLength())
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
$q = $builder->getQuery();
|
|
||||||
$event = new ORMAdapterQueryEvent($q);
|
|
||||||
$state->getDataTable()->getEventDispatcher()->dispatch($event, ORMAdapterEvents::PRE_QUERY);
|
|
||||||
|
|
||||||
foreach ($query->iterate([], $this->hydrationMode) as $result) {
|
|
||||||
yield $entity = array_values($result)[0];
|
|
||||||
if (Query::HYDRATE_OBJECT === $this->hydrationMode) {
|
|
||||||
$this->manager->detach($entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function buildCriteria(QueryBuilder $queryBuilder, DataTableState $state): void
|
|
||||||
{
|
|
||||||
foreach ($this->criteriaProcessors as $provider) {
|
|
||||||
$provider->process($queryBuilder, $state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function createQueryBuilder(DataTableState $state): QueryBuilder
|
|
||||||
{
|
|
||||||
/** @var QueryBuilder $queryBuilder */
|
|
||||||
$queryBuilder = $this->manager->createQueryBuilder();
|
|
||||||
|
|
||||||
// Run all query builder processors in order
|
|
||||||
foreach ($this->queryBuilderProcessors as $processor) {
|
|
||||||
$processor->process($queryBuilder, $state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $queryBuilder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $identifier
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
protected function getCount(QueryBuilder $queryBuilder, string $identifier)
|
|
||||||
{
|
|
||||||
$qb = clone $queryBuilder;
|
|
||||||
|
|
||||||
$qb->resetDQLPart('orderBy');
|
|
||||||
$gb = $qb->getDQLPart('groupBy');
|
|
||||||
if (empty($gb) || !$this->hasGroupByPart($identifier, $gb)) {
|
|
||||||
$qb->select($qb->expr()->count($identifier));
|
|
||||||
|
|
||||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
|
||||||
}
|
|
||||||
$qb->resetDQLPart('groupBy');
|
|
||||||
$qb->select($qb->expr()->countDistinct($identifier));
|
|
||||||
|
|
||||||
return (int) $qb->getQuery()->getSingleScalarResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $identifier
|
|
||||||
* @param Query\Expr\GroupBy[] $gbList
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function hasGroupByPart($identifier, array $gbList)
|
|
||||||
{
|
|
||||||
foreach ($gbList as $gb) {
|
|
||||||
if (in_array($identifier, $gb->getParts(), true)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $field
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
protected function mapFieldToPropertyPath($field, array $aliases = [])
|
|
||||||
{
|
|
||||||
$parts = explode('.', $field);
|
|
||||||
if (count($parts) < 2) {
|
|
||||||
throw new InvalidConfigurationException(sprintf("Field name '%s' must consist at least of an alias and a field separated with a period", $field));
|
|
||||||
}
|
|
||||||
[$origin, $target] = $parts;
|
|
||||||
|
|
||||||
$path = [$target];
|
|
||||||
$current = $aliases[$origin][0];
|
|
||||||
|
|
||||||
while (null !== $current) {
|
|
||||||
[$origin, $target] = explode('.', $current);
|
|
||||||
$path[] = $target;
|
|
||||||
$current = $aliases[$origin][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Query::HYDRATE_ARRAY === $this->hydrationMode) {
|
|
||||||
return '['.implode('][', array_reverse($path)).']';
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode('.', array_reverse($path));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function configureOptions(OptionsResolver $resolver): void
|
|
||||||
{
|
|
||||||
$providerNormalizer = function (Options $options, $value) {
|
|
||||||
return array_map([$this, 'normalizeProcessor'], (array) $value);
|
|
||||||
};
|
|
||||||
|
|
||||||
$resolver
|
|
||||||
->setDefaults([
|
|
||||||
'hydrate' => Query::HYDRATE_OBJECT,
|
|
||||||
'query' => [],
|
|
||||||
'criteria' => static function (Options $options) {
|
|
||||||
return [new SearchCriteriaProvider()];
|
|
||||||
},
|
|
||||||
])
|
|
||||||
->setRequired('entity')
|
|
||||||
->setAllowedTypes('entity', ['string'])
|
|
||||||
->setAllowedTypes('hydrate', 'int')
|
|
||||||
->setAllowedTypes('query', [QueryBuilderProcessorInterface::class, 'array', 'callable'])
|
|
||||||
->setAllowedTypes('criteria', [QueryBuilderProcessorInterface::class, 'array', 'callable', 'null'])
|
|
||||||
->setNormalizer('query', $providerNormalizer)
|
|
||||||
->setNormalizer('criteria', $providerNormalizer)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param callable|QueryBuilderProcessorInterface $provider
|
|
||||||
*
|
|
||||||
* @return QueryBuilderProcessorInterface
|
|
||||||
*/
|
|
||||||
private function normalizeProcessor($provider)
|
|
||||||
{
|
|
||||||
if ($provider instanceof QueryBuilderProcessorInterface) {
|
|
||||||
return $provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_callable($provider)) {
|
|
||||||
return new class($provider) implements QueryBuilderProcessorInterface {
|
|
||||||
private $callable;
|
|
||||||
|
|
||||||
public function __construct(callable $value)
|
|
||||||
{
|
|
||||||
$this->callable = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function process(QueryBuilder $queryBuilder, DataTableState $state)
|
|
||||||
{
|
|
||||||
return call_user_func($this->callable, $queryBuilder, $state);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidConfigurationException('Provider must be a callable or implement QueryBuilderProcessorInterface');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,7 +42,6 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\DataTables;
|
namespace App\DataTables;
|
||||||
|
|
||||||
use App\DataTables\Adapter\FetchJoinORMAdapter;
|
|
||||||
use App\DataTables\Column\EntityColumn;
|
use App\DataTables\Column\EntityColumn;
|
||||||
use App\DataTables\Column\IconLinkColumn;
|
use App\DataTables\Column\IconLinkColumn;
|
||||||
use App\DataTables\Column\LocaleDateTimeColumn;
|
use App\DataTables\Column\LocaleDateTimeColumn;
|
||||||
|
@ -61,6 +60,7 @@ use App\Services\Attachments\PartPreviewGenerator;
|
||||||
use App\Services\EntityURLGenerator;
|
use App\Services\EntityURLGenerator;
|
||||||
use App\Services\Trees\NodesListBuilder;
|
use App\Services\Trees\NodesListBuilder;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use Omines\DataTablesBundle\Adapter\Doctrine\FetchJoinORMAdapter;
|
||||||
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
|
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
|
||||||
use Omines\DataTablesBundle\Column\BoolColumn;
|
use Omines\DataTablesBundle\Column\BoolColumn;
|
||||||
use Omines\DataTablesBundle\Column\MapColumn;
|
use Omines\DataTablesBundle\Column\MapColumn;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue