Shopware 6.7 introduces significant enhancements to the product listing page functionality, making it easier than ever to implement custom filters. This technical deep dive explores how to extend the default product listing with custom filter options while maintaining performance and SEO best practices.

Understanding the Product Listing Architecture

In Shopware 6.7, the product listing page is built on a robust filtering system that leverages the Criteria API for efficient data retrieval. The architecture consists of several key components:

  • ProductListingCriteriaEvent: Dispatched before criteria generation
  • ProductListingResult: Contains filtered product data
  • FilterFactory: Responsible for creating filter structures
  • ProductSearchResult: Final search result with aggregated data

The filtering system operates through a series of events that allow developers to hook into the process at various stages. Understanding this flow is crucial for implementing custom filters effectively.

Creating the Custom Filter Component

To begin, we'll create a custom filter that allows users to filter products by manufacturer color. This example demonstrates how to extend the existing filtering mechanism with minimal code changes.

First, let's examine the basic structure of our custom filter implementation:

<?php declare(strict_types=1);

namespace MyCustomPlugin\Core\Content\Product\Filter;

use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;

class ColorFilter
{
    public function applyColorFilter(Criteria $criteria, array $filter): void
    {
        if (!isset($filter['color'])) {
            return;
        }

        $colorFilter = new MultiFilter(
            MultiFilter::CONNECTION_AND,
            [
                new EqualsFilter('product.manufacturer.color', $filter['color'])
            ]
        );

        $criteria->addFilter($colorFilter);
    }
}

Implementing the Filter Factory

The FilterFactory is responsible for creating filter structures that are compatible with Shopware's front-end components. In Shopware 6.7, this component has been significantly enhanced to provide better integration between backend and frontend filtering.

<?php declare(strict_types=1);

namespace MyCustomPlugin\Core\Content\Product\Filter;

use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\TermsAggregation;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\Aggregation;

class CustomFilterFactory
{
    public function createColorFilter(Criteria $criteria): void
    {
        // Create aggregation for color options
        $colorAggregation = new TermsAggregation(
            'product_colors',
            'product.manufacturer.color',
            null,
            100
        );

        $criteria->addAggregation($colorAggregation);
    }

    public function getAvailableColors(Criteria $criteria): array
    {
        // This method would be called to retrieve available filter options
        return [];
    }
}

Hooking into the Product Listing Events

Shopware 6.7 provides several events that allow developers to modify the product listing behavior. The key events for filtering are:

  • ProductListingCriteriaEvent: Allows modification of criteria before execution
  • ProductListingResultEvent: Allows modification of results after execution

Here's how to implement these hooks:

<?php declare(strict_types=1);

namespace MyCustomPlugin\Subscriber;

use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Content\Product\Event\ProductListingCriteriaEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;

class ProductFilterSubscriber implements EventSubscriberInterface
{
    private RequestStack $requestStack;

    public function __construct(RequestStack $requestStack)
    {
        $this->requestStack = $requestStack;
    }

    public static function getSubscribedEvents(): array
    {
        return [
            ProductListingCriteriaEvent::class => 'onProductListingCriteria',
        ];
    }

    public function onProductListingCriteria(ProductListingCriteriaEvent $event): void
    {
        $request = $this->requestStack->getCurrentRequest();
        if (!$request) {
            return;
        }

        // Check if color filter is applied
        $colorFilter = $request->query->get('color');
        if ($colorFilter) {
            $criteria = $event->getCriteria();
            
            // Apply custom color filter logic
            $this->applyColorFilter($criteria, $colorFilter);
        }
    }

    private function applyColorFilter(Criteria $criteria, string $color): void
    {
        $criteria->addFilter(new EqualsFilter('product.manufacturer.color', $color));
    }
}

Frontend Integration and Template Configuration

The frontend integration requires creating a proper template structure that displays the custom filter options. Shopware 6.7's templating system supports dynamic filter rendering through the use of twig components.

{# @var productListingResult \Shopware\Core\Content\Product\SalesChannel\Listing\ProductListingResult #}
{# @var criteria \Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria #}

<div class="custom-filter-container">
    <h3>Color Filter</h3>
    <div class="filter-options">
        {% for color in productListingResult.aggregations.product_colors.buckets %}
            <a href="{{ path('frontend.product.listing', {
                'color': color.key
            }) }}" 
               class="filter-option {{ (criteria.filters.color is defined and criteria.filters.color.value == color.key) ? 'active' : '' }}">
                {{ color.key }}
            </a>
        {% endfor %}
    </div>
</div>

Performance Optimization Considerations

When implementing custom filters, performance optimization becomes crucial. Shopware 6.7 introduces several features to help with this:

<?php declare(strict_types=1);

namespace MyCustomPlugin\Core\Content\Product\Filter;

use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Aggregation\TermsAggregation;

class OptimizedFilterFactory
{
    public function createOptimizedColorFilter(Criteria $criteria): void
    {
        // Use cacheable aggregations for better performance
        $colorAggregation = new TermsAggregation(
            'product_colors',
            'product.manufacturer.color',
            null,
            50, // Limit results to prevent excessive memory usage
            [
                'order' => ['_count' => 'DESC']
            ]
        );

        $criteria->addAggregation($colorAggregation);
    }

    public function createFilteredCriteria(Criteria $criteria, array $filters): Criteria
    {
        $filteredCriteria = clone $criteria;
        
        // Apply filters with proper caching
        foreach ($filters as $filterName => $filterValue) {
            if ($filterName === 'color' && !empty($filterValue)) {
                $filteredCriteria->addFilter(
                    new EqualsFilter('product.manufacturer.color', $filterValue)
                );
            }
        }

        return $filteredCriteria;
    }
}

Advanced Filter Configuration with Sorting

Shopware 6.7 supports advanced sorting configurations that can be integrated with custom filters:

<?php declare(strict_types=1);

namespace MyCustomPlugin\Core\Content\Product\Filter;

use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;

class AdvancedFilterConfiguration
{
    public function configureSorting(Criteria $criteria, array $sortOptions): void
    {
        if (!empty($sortOptions['color'])) {
            // Add color-based sorting
            $criteria->addSorting(new FieldSorting('product.manufacturer.color', FieldSorting::ASCENDING));
        }
    }

    public function createMultiDimensionalFilter(Criteria $criteria, array $filters): void
    {
        $multiFilter = new MultiFilter(
            MultiFilter::CONNECTION_AND,
            [
                new EqualsFilter('product.manufacturer.color', $filters['color'] ?? null),
                new EqualsFilter('product.category.id', $filters['category'] ?? null)
            ]
        );

        $criteria->addFilter($multiFilter);
    }
}

Database Schema Considerations

For custom filters to work efficiently, proper database indexing is essential. The manufacturer color field should be indexed for optimal query performance:

-- Create index on manufacturer color for faster filtering
CREATE INDEX idx_manufacturer_color ON product(manufacturer_color);

-- For complex filtering scenarios
CREATE INDEX idx_product_category_color ON product(category_id, manufacturer_color);

Testing the Implementation

Proper testing ensures that custom filters work correctly and don't introduce regressions:

<?php declare(strict_types=1);

namespace MyCustomPlugin\Tests\Unit\Core\Content\Product\Filter;

use PHPUnit\Framework\TestCase;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use MyCustomPlugin\Core\Content\Product\Filter\ColorFilter;

class ColorFilterTest extends TestCase
{
    public function testApplyColorFilter(): void
    {
        $criteria = new Criteria();
        $filter = ['color' => 'red'];
        
        $colorFilter = new ColorFilter();
        $colorFilter->applyColorFilter($criteria, $filter);
        
        $this->assertCount(1, $criteria->getFilters());
    }
}

SEO and User Experience Best Practices

When implementing custom filters, consider SEO implications:

  1. URL Structure: Maintain clean URLs that are friendly to search engines
  2. Breadcrumb Navigation: Implement proper breadcrumb trails for filtered results
  3. Meta Information: Update meta titles and descriptions based on applied filters
  4. Accessibility: Ensure filters are accessible via keyboard navigation

Conclusion

Shopware 6.7 provides powerful tools for implementing custom filters on product listing pages. By leveraging the event system, Criteria API, and enhanced aggregation capabilities, developers can create sophisticated filtering experiences while maintaining performance standards.

The key to successful implementation lies in understanding the underlying architecture, optimizing database queries, and following best practices for both technical performance and user experience. The modular approach of Shopware 6.7 allows for clean separation of concerns, making custom filters maintainable and scalable across different project requirements.

This implementation demonstrates how modern e-commerce platforms can be extended to meet specific business needs while maintaining the robustness and performance that Shopware is known for.