phpeventsadmincriteriashopware6

shopware 6 create custom order filter in admin panel


I want to create a custom filter in the admin panel, in the order overview. Therefore I added a filter to the filter list:

Shopware.Component.override('sw-order-list', {
...
           // Custom Filter hinzufügen
            filterOptions['supplier-filter'] = {
                property: 'customFields.supplier',
                type: 'multi-select-filter',
                label: this.$tc('swedi-order.filters.customFilter.label'),
                placeholder: this.$tc('swedi-order.filters.customFilter.placeholder'),
                valueProperty: 'id',
                labelProperty: 'name',
                options: this.suppliers,
                loading: this.isSupplierLoading,
                schema: {
                    entity: 'supplier',
                    referenceField: 'id',
                },
                custom: true, // Mark as custom for your logic
            };

And a subscriber:

  public static function getSubscribedEvents(): array
    {
        return [
            EntitySearchedEvent::class => 'onOrderSearchCriteria',
            'order.written' => 'onOrderWritten',
            'order.loaded' => 'onOrderLoaded',
            // Add more events as needed
        ];
    }

    public function onOrderSearchCriteria($event): void
    {
        $msgEvent = '[OrderFilterSubscriber] Event: ' . get_class($event);
        $msgContext = '[OrderFilterSubscriber] Context: ' . print_r($event->getContext(), true);
        $msgCriteria = '[OrderFilterSubscriber] Criteria: ' . print_r($event->getCriteria()->getFilters(), true);

        $this->logger->info($msgEvent);
        $this->logger->info($msgContext);
        $this->logger->info($msgCriteria);

        error_log($msgEvent);
        error_log($msgContext);
        error_log($msgCriteria);
    }

when I open the order overview in admin panel, it fires the entity loaded event, but no search event, so I cannot modify the search criteria.

Anyone can help? Thanks! :)


Solution

  • I solved it!

    The solution was to create a decorator, since there is no suitable event to listen to. So to create a custom order filter in the admin I did the following:

    UI override:

    Shopware.Component.override('sw-order-list', {
        data() {
            return {
                suppliers: [],
                isSupplierLoading: false,
            };
        },
    
        created() {
            if (!this.defaultFilters.includes('supplier-filter')) {
                this.defaultFilters.push('supplier-filter');
            }
    
            this.loadSuppliers();
        },
    
        computed: {
            /**
             * Erweiterung der bestehenden Filteroptionen
             */
            listFilterOptions() {
                const filterOptions = this.$super('listFilterOptions');
    
                // Custom Filter hinzufügen
                filterOptions['supplier-filter'] = {
                    property: 'customFields.supplier',
                    type: 'multi-select-filter',
                    label: this.$tc('swedi-order.filters.customFilter.label'),
                    placeholder: this.$tc('swedi-order.filters.customFilter.placeholder'),
                    valueProperty: 'id',
                    labelProperty: 'name',
                    options: this.suppliers,
                    loading: this.isSupplierLoading,
                };
    
                return filterOptions;
            },
        },
    
    

    And for the backend, the decorator:

    <?php declare(strict_types=1);
    
    namespace SwediPlugin\Decorator;
    
    use Doctrine\DBAL\Connection;
    use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
    use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
    use Shopware\Core\Framework\Context;
    use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
    use Shopware\Core\Framework\Uuid\Uuid;
    use Psr\Log\LoggerInterface;
    use Shopware\Core\Checkout\Order\OrderCollection;
    use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
    
    
    
    
    class OrderRepositoryDecorator extends EntityRepository
    {
        private EntityRepository $inner;
        private Connection $connection;
        private LoggerInterface $logger;
    
        public function __construct(EntityRepository $inner, LoggerInterface $logger)
        {
            $this->inner = $inner;
            $this->connection = \Shopware\Core\Kernel::getConnection();
            $this->logger = $logger;
        }
    
        public function getDefinition(): EntityDefinition
        {
            return $this->inner->getDefinition();
        }
    
        public function search(Criteria $criteria, Context $context): EntitySearchResult
        {
            try {
                $supplierIds = [];
                $newFilters = [];
    
                // 1. Filter durchlaufen und "customFields.supplier" herausfiltern
                foreach ($criteria->getFilters() as $filter) {
                    if (
                        $filter instanceof \Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter
                        && $filter->getField() === 'order.customFields.supplier'
                    ) {
    
                        $supplierIds = $filter->getValue();
                    } else {
                        $newFilters[] = $filter;
                    }
                }
    
                // 2. Filter zurücksetzen und neu setzen
                $criteria->resetFilters();
                foreach ($newFilters as $filter) {
                    $criteria->addFilter($filter);
                }
    
                // 3. Standard-Suche ausführen, wenn keine Supplier-IDs vorhanden sind
                if (empty($supplierIds)) {
                    return $this->inner->search($criteria, $context);
                }
    
                // 4. DBAL-Abfrage ausführen
                $orderIds = $this->fetchOrderIdsBySupplierIds($supplierIds);
    
                if (empty($orderIds)) {
                    return new EntitySearchResult(
                        'order', // <- Muss ein String sein, kein OrderDefinition-Objekt
                        0,
                        new OrderCollection(),
                        null,
                        $criteria,
                        $context
                    );
                }
    
                // 5. Criteria auf die gefundenen Order-IDs beschränken
                $criteria->setIds($orderIds);
    
                // 6. Originale Suche ausführen
                $searchResult = $this->inner->search($criteria, $context);
    
                // 7. EntitySearchResult erstellen und zurückgeben
                return new EntitySearchResult(
                    'order',
                    $searchResult->getTotal(),
                    $searchResult->getEntities(),
                    $searchResult->getAggregations(),
                    $criteria,
                    $context
                );
    
            } catch (\Throwable $e) {
                $this->logger->error('Fehler im OrderRepositoryDecorator: ' . $e->getMessage());
                $this->logger->error('Stack Trace: ' . $e->getTraceAsString());
    
                throw new \Exception('Ein Fehler ist im OrderRepositoryDecorator aufgetreten. Siehe Log für Details.');
            }
        }
    
        /**
         * Führt eine DBAL-Abfrage aus, um die Order-IDs anhand der Supplier-IDs zu ermitteln.
         */
        private function fetchOrderIdsBySupplierIds(array $supplierIds): array
        {
            // UUIDs in Binärformat umwandeln
            $binarySupplierIds = array_map(fn($id) => Uuid::fromHexToBytes($id), $supplierIds);
    
            $query = '
            SELECT DISTINCT LOWER(HEX(o.id)) AS order_id
            FROM `order` o
            INNER JOIN `order_line_item` line_items ON o.id = line_items.order_id
            LEFT JOIN `pickware_erp_product_supplier_configuration` config ON line_items.product_id = config.product_id
            LEFT JOIN `pickware_erp_supplier` supplier ON config.supplier_id = supplier.id
            WHERE supplier.id IN (:supplierIds)
        ';
    
            try {
                $results = $this->connection->fetchFirstColumn($query, [
                    'supplierIds' => $binarySupplierIds, // Binärwerte verwenden
                ], [
                    'supplierIds' => Connection::PARAM_STR_ARRAY,
                ]);
    
                return $results;
    
            } catch (\Throwable $e) {
                $this->logger->error('OrderRepositoryDecorator DBAL Fehler: ' . $e->getMessage());
                throw $e;
            }
        }
    
    
    }