Skip to content

Added a doctrine querybuilder adapter #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

dovereem
Copy link
Contributor

@dovereem dovereem commented Oct 7, 2022

Added an adapter that lets you use a basic 'query' to create a datatable.

Usage example:

        use Doctrine\DBAL\Connection;

       ...

        $table = $this->dataTableFactory->create()
            ->createAdapter(QueryBuilderAdapter::class, [
                'queryBuilder' => $this->connection->createQueryBuilder()
                    ->select('*')
                    ->from('someTable')
            ])->...

Limitations:

  • Support for column search has not been implemented. Global search, sorting etc. is working properly.

@curry684
Copy link
Member

Thanks for the PR :) However to consider merging we will need:

  • tests
  • no commented code
  • (some sort of) documentation

Also I would recommend renaming the adapter to DBALAdapter as that is more accurately what it does, and you could then abstract the options away to either accepting a SQL query as a string or a Query Builder.

@Chris53897
Copy link
Contributor

@dovereem It would be great to have this implemented. Thanks for the PR
Is there a plan to further work on this to get this merged?

@dovereem
Copy link
Contributor Author

@Chris53897 This has been off my radar for a while (as those things go..) but I'll try to spend some time on this in the near future. No promises on the timeframe though. Busy times :(

@dovereem
Copy link
Contributor Author

dovereem commented Feb 7, 2023

@curry684 Could you clarify this comment? I unfortunately don't understand what it is you want abstracted here. Could be my fault because I did not provide a usage example.

and you could then abstract the options away to either accepting a SQL query as a string or a Query Builder

Usage of the adapter currently is something like this:

$dataTableFactory = new \Omines\DataTablesBundle\DataTableFactory(/** .... **/ );

$queryBuilder = $this->connection->createQueryBuilder()
    ->select('id, email. name')
    ->from('users');

$table = $dataTableFactory->create()
    ->createAdapter(DBALAdapter::class, [
        'queryBuilder' => $queryBuilder
    ])
    ->add('id', NumberColumn::class, [
        'label' => '#'
    ])
    ->add('email', TextColumn::class, [
        'label' => 'E-Mail'
    ])
    ->add('name', TextColumn::class, [
        'label' => 'Name',
        'searchable' => true,
        'globalSearchable' => false,
        'orderable' => false
    ])
    ->addOrderBy('ID', DataTable::SORT_DESCENDING)
    ->handleRequest($request);

if ($table->isCallback()) {
    return $table->getResponse();
}

@curry684
Copy link
Member

curry684 commented Feb 7, 2023

Thank you for the modifications. I reviewed and discussed this PR with co-author @shades684 and regrettably in your PR we see the exact same problems occur that made us not implement a DBAL Adapter in the first place - your count implementation will break with any kind of SQL construct more complex than a straight query (which breaks the prime reason for sometimes preferring DBAL over ORM), similar for the searching functions which will break on int or date fields etc. etc.

In general we feel it is unlikely that a good generic DBAL adapter can be implemented which will be sufficiently abstracted to distribute with the bundle, as you will in general always be hardcoding towards a specific use case. And for that it is trivial to implement an adapter at the project level.

We think in general it's a better idea that we improve the bundled ArrayAdapter some day, as it provides a most-of-the-way implementation for easily feeding with a SQL result.

To illustrate that last point, if you modify your example code above like this:

$result = $this->connection->createQueryBuilder()
    ->select('id, email. name')
    ->from('users')
    ->executeQuery()
    ->fetchAllAssociative();

$table = $dataTableFactory->create()
    ->createAdapter(ArrayAdapter::class, $result)

It's already working the same.

Do keep in mind that the current implementation of ArrayAdapter is iffy, it serves its purpose but also has enough cases where it does not really hold up.

@dovereem
Copy link
Contributor Author

dovereem commented Feb 7, 2023

No problem at all. Things work fine when the adapter is part of the application instead of in the package. I submitted it just in case someone found it useful.

My main reason for writing the dbal adapter instead of using ORM is that many of my applications are legacy applications converted to symfony applications. These have legacy databases which take way too long to convert to doctrine entities. I expect I am not the only one that regularly encounters situations like this.

You are right though. This adapter works fine with a straight query with a couple of joins, but I can see quite a few possible queries that would break this.

@curry684
Copy link
Member

curry684 commented Feb 7, 2023

I added an example to my previous comment on how the ArrayAdapter would probably already suffice for the legacy use case.

@dovereem
Copy link
Contributor Author

dovereem commented Feb 7, 2023

Thank you for the example. I guess that would work for most use-cases.

In my personal case however this would not work well because of big datasets (100.000+ rows of data). I want the pagination, sorting and searching but stuffing all data in an array like that would probably cause some issues.

@curry684
Copy link
Member

curry684 commented Feb 7, 2023

Specifically those 3 things will break right away once you start to actually use SQL features out of ORM context :)

-- Fresh hell for any kind of pagination, filtering or sorting
SELECT name, sum(amount) 
FROM user u 
JOIN order o 
GROUP BY name 
HAVING count(amount) > 3;

I'm closing the PR as we agree there is no sufficiently generic implementation that warrants distributing it with the bundle.

@curry684 curry684 closed this Feb 7, 2023
@enboig
Copy link

enboig commented Oct 3, 2024

I ended here looking for counters for my own implementation of datatables in a symfony project. My queries joins, group by, and aggregate functions; and I ended using a paginator (in some cases it really slow down some queries, so I made this optional).

            if ($slowCount) {
                $paginator = new Paginator($countQuery);
                if ($countQuery->getDQLPart("having")) {
                    $recordsFiltered = count($paginator);
                } else {
                    $recordsFiltered = $paginator->setUseOutputWalkers(false)->count();
                }
            } else {
                $countQuery->select('COUNT(' . ($groupBy != null ? 'DISTINCT ' . $groupBy : BaseDTRepository::ALIAS) . ')');
                $recordsFiltered = $countQuery
                    ->getQuery()
                    ->getSingleScalarResult();
            }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants