From a8b5a138b107c11317da75e72627821393eae502 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 4 Apr 2026 18:33:13 +0200 Subject: [PATCH] Use TwoStepORMAdapter to enable sorting based on other columns like storage location, manufacturing status --- src/DataTables/ProjectBomEntriesDataTable.php | 63 +++++++++++++++---- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/src/DataTables/ProjectBomEntriesDataTable.php b/src/DataTables/ProjectBomEntriesDataTable.php index 709987e7..5449df71 100644 --- a/src/DataTables/ProjectBomEntriesDataTable.php +++ b/src/DataTables/ProjectBomEntriesDataTable.php @@ -22,21 +22,22 @@ declare(strict_types=1); namespace App\DataTables; +use App\DataTables\Adapters\TwoStepORMAdapter; use App\DataTables\Column\EntityColumn; use App\DataTables\Column\EnumColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\MarkdownColumn; use App\DataTables\Helpers\PartDataTableHelper; -use App\Entity\Attachments\Attachment; +use App\Doctrine\Helpers\FieldHelper; use App\Entity\Parts\Part; use App\Entity\Parts\ManufacturingStatus; use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Services\ElementTypeNameGenerator; use App\Services\EntityURLGenerator; use App\Services\Formatters\AmountFormatter; +use Doctrine\ORM\AbstractQuery; use Doctrine\ORM\QueryBuilder; use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider; -use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter; use Omines\DataTablesBundle\Column\TextColumn; use Omines\DataTablesBundle\DataTable; use Omines\DataTablesBundle\DataTableTypeInterface; @@ -152,6 +153,8 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ->add('manufacturing_status', EnumColumn::class, [ 'label' => $this->translator->trans('part.table.manufacturingStatus'), + 'data' => static fn(ProjectBOMEntry $context): ?ManufacturingStatus => $context->getPart()?->getManufacturingStatus(), + 'orderField' => 'part.manufacturing_status', 'class' => ManufacturingStatus::class, 'render' => function (?ManufacturingStatus $status, ProjectBOMEntry $context): string { if ($status === null) { @@ -211,11 +214,13 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface $dataTable->addOrderBy('name', DataTable::SORT_ASCENDING); - $dataTable->createAdapter(ORMAdapter::class, [ - 'entity' => Attachment::class, - 'query' => function (QueryBuilder $builder) use ($options): void { - $this->getQuery($builder, $options); + $dataTable->createAdapter(TwoStepORMAdapter::class, [ + 'entity' => ProjectBOMEntry::class, + 'hydrate' => AbstractQuery::HYDRATE_OBJECT, + 'filter_query' => function (QueryBuilder $builder) use ($options): void { + $this->getFilterQuery($builder, $options); }, + 'detail_query' => $this->getDetailQuery(...), 'criteria' => [ function (QueryBuilder $builder) use ($options): void { $this->buildCriteria($builder, $options); @@ -225,11 +230,45 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ]); } - private function getQuery(QueryBuilder $builder, array $options): void + private function getFilterQuery(QueryBuilder $builder, array $options): void { $builder - ->select('bom_entry') + ->select('bom_entry.id') + ->from(ProjectBOMEntry::class, 'bom_entry') + ->leftJoin('bom_entry.part', 'part') + ->leftJoin('part.category', 'category') + ->leftJoin('part.partLots', '_partLots') + ->leftJoin('_partLots.storage_location', '_storelocations') + ->leftJoin('part.footprint', 'footprint') + ->leftJoin('part.manufacturer', 'manufacturer') + ->leftJoin('part.partCustomState', 'partCustomState') + ->where('bom_entry.project = :project') + ->setParameter('project', $options['project']) + ->addGroupBy('bom_entry') + ->addGroupBy('part') + ->addGroupBy('category') + ->addGroupBy('footprint') + ->addGroupBy('manufacturer') + ->addGroupBy('partCustomState') + ; + } + + private function getDetailQuery(QueryBuilder $builder, array $filter_results): void + { + $ids = array_map(static fn (array $row) => $row['id'], $filter_results); + if ($ids === []) { + $ids = [-1]; + } + + $builder + ->select('bom_entry') ->addSelect('part') + ->addSelect('category') + ->addSelect('partLots') + ->addSelect('storelocations') + ->addSelect('footprint') + ->addSelect('manufacturer') + ->addSelect('partCustomState') ->from(ProjectBOMEntry::class, 'bom_entry') ->leftJoin('bom_entry.part', 'part') ->leftJoin('part.category', 'category') @@ -238,9 +277,9 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ->leftJoin('part.footprint', 'footprint') ->leftJoin('part.manufacturer', 'manufacturer') ->leftJoin('part.partCustomState', 'partCustomState') - ->where('bom_entry.project = :project') - ->setParameter('project', $options['project']) - + ->where('bom_entry.id IN (:ids)') + ->setParameter('ids', $ids) + ->addGroupBy('bom_entry') ->addGroupBy('part') ->addGroupBy('partLots') ->addGroupBy('category') @@ -249,6 +288,8 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface ->addGroupBy('manufacturer') ->addGroupBy('partCustomState') ; + + FieldHelper::addOrderByFieldParam($builder, 'bom_entry.id', 'ids'); } private function buildCriteria(QueryBuilder $builder, array $options): void